日期
check-box
check-box
线性回归
线性模型
- 给定维输入
- 线性模型有一个维权重和一个标量偏差:,
- 输出是输入的加权和:
- 向量版本:
线性模型可以看作是单层神经网络
我们已经有了一个模型,可以做预测,那么如何衡量预测的好坏呢?
衡量预估质量
- 比较真实值和估计值,例如房屋售价和股价
- 假设是真实值,是估计值,我们可以比较
- 这个叫做平方损失
接下来,我们就需要学习参数
训练数据
- 收集一些数据点来决定参数值(权重和偏差),例如过去6个月卖的房子
- 这被称之为训练数据,通常越多越好
- 假设我们有个样本,记,
参数学习
- 训练损失:
- 最小化受损失来学习参数
显示解(线性模型才有)
- 将偏差加入权重
- 损失是凸函数,所以最优解满足:
总结
- 线性回归是对维输入的加权,外加偏差;
- 使用平方损失来衡量预测值和真实值的差异;
- 线性回归有显示解;
- 线性回归可以看作是单层神经网络
基础优化算法
梯度下降
- 挑选一个初始值
- 重复迭代参数
- 沿梯度方向将增加损失函数值
- 学习率:步长的超参数
小批量随机梯度下降
- 在整个训练集上计算梯度太昂贵
- 一个深度网络模型可能需要数分钟至数小时
- 我们可以随机采样个样本来近似损失
- 是批量大小,另一个重要的超参数
总结
- 梯度下降通过不断沿着反梯度方向更新参数求解
- 小批量随机梯度下降是深度学习默认的求解算法
- 两个重要的超参数是批量大小和额学习率
线性回归的从零开始实现
- 根据带有噪声的线性模型构造一个人造数据集
features
中的每一行都包含一个二维数据样本,labels
中的每一行都包含一维标签值(一个标量)
- 定义一个
data_iter
函数,该函数接受批量大小、特征矩阵和标签向量作为输入,生成大小为batch_size
的小批量
这里,
features[batch_indices]
实际上是高级索引,其中的 batch_indices
是想要索引的张量- 定义初始化模型参数
因为要对这两个参数进行更新,所以需要
requires_grad = True
- 定义模型
- 定义损失函数
注意,因为
y_hat
和 y
可能一个是行向量,一个是列向量,所以需要 reshape
一下- 定义优化算法
因为在更新参数的时候不需要计算梯度,所以用
with torch.no_grad()
- 定义超参数:
- 训练过程
- 比较真实参数和通过训练学到的参数来评估训练的成功程度
线性回归的简洁实现
- 通过深度学习框架简洁实现线性回归模型,生成数据集
- 调用框架中现有的API来读取数据
data.TensorDataset
:data.TensorDataset
是 PyTorch 中的数据集类,它用于包装张量数据。它的主要作用是将特征和标签等张量数据结合在一起,以便于在模型训练和评估时访问。TensorDataset
接受多个torch.Tensor
作为输入参数,并假设这些张量的第一个维度(即样本数)相同。每次从TensorDataset
中取样时,会返回这些张量在相同位置的元素。- 例如,
data_arrays
可能是包含特征和标签的两个张量(features, labels)
,使用data_arrays
将其解包,传递给TensorDataset
,创建一个包含特征和标签的数据集。 data.DataLoader
:data.DataLoader
是 PyTorch 提供的一个迭代器,用于加载数据集。它提供了对数据的自动批处理、随机打乱(如果需要)、并行加载等功能,非常适合在训练深度学习模型时使用。DataLoader
的参数包括:dataset
:要加载的数据集对象(如上面创建的TensorDataset
)。batch_size
:每个批次的样本数量。shuffle
:是否在每个epoch开始时对数据进行洗牌,is_train=True
时通常设为True
以增加模型的泛化能力。- 返回的
DataLoader
对象可以像 Python 的迭代器一样使用,每次迭代返回一个批次的数据。 next
:next()
是一个内置的 Python 函数,用于从迭代器中获取下一个元素。- 在您的代码中,
iter(data_iter)
创建了一个data_iter
的迭代器,next(iter(data_iter))
则返回这个迭代器的第一个批次数据。通过使用next()
,您可以查看数据加载器返回的批次数据的内容(特征和标签)。 iter
:iter()
是一个内置的 Python 函数,用于获取可迭代对象的迭代器。- 在 PyTorch 中,
DataLoader
是一个可迭代对象,这意味着你可以用iter(data_iter)
来创建一个迭代器对象,然后用next()
从中获取数据。 iter(data_iter)
将data_iter
转换为一个迭代器,允许通过next()
函数获取数据,直到数据耗尽或达到批次结束。data.TensorDataset
用于创建一个包含特征和标签的数据集。data.DataLoader
提供了一个方便的方式来按批次加载数据集。next
从迭代器中获取下一个数据批次。iter
将可迭代对象(如DataLoader
)转换为一个迭代器。data_arrays
是什么?data_arrays
是一个包含多个张量的集合,通常是一个元组或列表。在你的例子中,data_arrays
包含两个张量,一个是特征(features
),另一个是标签(labels
)。比如:- 这个元组的第一个元素是包含所有样本特征的张量,第二个元素是对应的标签张量。
- 为什么要用
data_arrays
? - 在 Python 中,
操作符用于“解包”一个可迭代对象(如列表、元组)。这意味着如果你有一个包含两个元素的元组
data_arrays = (features, labels)
,使用data_arrays
会将其解包为features, labels
,然后作为单独的参数传递给函数。 - 在你的代码中:
- 什么是可迭代对象?
- 可迭代对象(Iterable)是指可以逐一返回其中元素的对象,例如列表(
list
)、元组(tuple
)、字符串(string
)、字典(dict
)等。可迭代对象实现了__iter__()
方法,使你可以用for
循环遍历它们。 - 举个例子,一个列表是可迭代的:
- 什么是迭代器?
- 迭代器(Iterator)是一个对象,它定义了如何访问和遍历一个容器(例如列表、元组等)的元素。迭代器实现了
__next__()
方法,可以逐个返回元素,直到没有元素为止,它会抛出一个StopIteration
异常。 - 迭代器的两个主要方法:
__iter__()
: 返回迭代器对象自身,这使得对象可以在for
循环中使用。__next__()
: 返回容器中的下一个元素,直到耗尽元素。- 例如,你可以通过
iter()
函数将可迭代对象转换为迭代器,然后通过next()
函数手动获取下一个元素: DataLoader
就是一个可迭代对象,每次你调用iter(data_iter)
时,它返回的data_iter
是一个迭代器对象。你可以使用next()
来获取下一个批次的数据。data_arrays
是包含多个张量的元组,data_arrays
用于将元组解包为单独的张量参数传递给函数。- 可迭代对象 是一个可以遍历的对象,如列表、元组、字符串等。
- 迭代器 是一个可以逐个访问元素的对象,提供了
__iter__()
和__next__()
方法。在 Python 中,iter()
用于从可迭代对象中获取迭代器,next()
用于从迭代器中获取下一个元素。
1. dataset = data.TensorDataset(*data_arrays)
是什么用法?
2. data.DataLoader
是什么用法?
3. next
是什么用法?
4. iter
是什么用法?
总结
这些函数共同作用,提供了从数据集中按批次读取数据的机制,这是训练机器学习模型的重要步骤。
1. data_arrays
是什么?为什么要用 data_arrays
?
这里的
*data_arrays
实际上是把 data_arrays
这个元组解包成两个独立的张量参数传给 TensorDataset
,等价于:这样
TensorDataset
就能够同时接收特征和标签,形成一个成对的数据集。2. 什么是迭代器?什么是可迭代对象?
这里的
my_list
就是一个可迭代对象,for
循环通过调用 __iter__()
来获取迭代器并遍历其中的元素。总结
这些概念对于理解 Python 的循环、数据加载以及很多其他操作都是非常重要的。
- 使用框架的预定义好的层
nn.Linear(2,1)
意思是输入维度是2,输出维度是1- 初始化模型参数
- 存储多个模块:
nn.Sequential
可以包含多个nn.Module
子类(如线性层、激活函数等)。你可以用它来构建一个包含多层的模型,将各个层依次存入Sequential
对象中。- 有序管理:
- 容器中的模块具有顺序,输入会按顺序通过这些模块。
nn.Sequential
自动为你管理这些模块的连接和数据传递。 - 调用和执行:
- 用
nn.Sequential
构建的模型可以像一个单一的nn.Module
那样被调用。在训练或推理过程中,输入数据会被逐层传递给容器里的每一个层。 - 简化代码:
nn.Sequential
允许你以更直观、更简洁的方式定义神经网络结构,不必手动编写每层的连接逻辑。- 存储多个元素:容器可以持有多个数据元素,可以是同类型也可以是不同类型。
- 支持遍历操作:容器通常是可迭代的,因此你可以用
for
循环来遍历其中的元素。 - 灵活性:容器提供数据的有序、无序、键值对存储等不同的管理方式,以满足不同场景的需求。
nn.Sequential
被称为一个容器是因为它能在内部存储和管理一组有序的神经网络层或模块。这种描述的背景在于,nn.Sequential
不只是一个简单的数据结构,而是一个功能强大的工具,在构建和管理序列化的神经网络模型时尤其有用。为什么 Sequential
是一个容器?
Python 中容器的概念
在 Python 中,容器一般指能够存储和组织多个元素的数据结构。常见的容器包括列表(
list
)、字典(dict
)、元组(tuple
)和集合(set
)。这些容器的特性如下:在这个背景下,
nn.Sequential
被视作一个容器是因为它提供了类似的功能:它将多个神经网络层有序地“存储”和“组织”在一起,并允许用户对这些层进行遍历(从输入到输出的传递过程)和操作。总结一下,
nn.Sequential
作为一个容器的理念在于它让你能够轻松地构建、管理和使用一系列的神经网络模块,以实现特定的深度学习任务。而了解 Python 中的容器概念可以帮助你更好地设计和组织代码结构。- 计算均方误差使用的是
MSELoss
类,也成为平方范数
- 实例化
SGD
实例
- 训练过程代码与之前从零开始实现时所做的非常相似
trainer.step()
是什么?trainer.step()
通常是 PyTorch 中优化器(optimizer)的step()
方法。这个方法的作用是更新模型的参数。- 具体来说,
step()
会根据之前计算并存储在参数.grad
属性中的梯度来调整参数。优化器的具体更新方式(如 SGD、Adam 等)取决于你初始化trainer
时使用的优化算法。 - 典型的优化步骤:
- 在每次计算完梯度后,你会调用
optimizer.step()
来更新模型的参数,使得它们朝着损失降低的方向前进。 trainer.zero_grad()
则是用来清除所有参数的梯度,以便在下一个训练步骤中重新计算梯度。
与我们手动实现的不一样,这里的
w
、 b
是 net
网络的参数,因此不需要我们手动传入; trainer.step()
是什么用法?
举个例子,假设你使用的是
torch.optim.SGD
作为优化器: