线性回归
线性回归
标签(空格分隔): 深度学习
我们举一个实际的例子:我们希望根据房屋的面积和房龄来预计房屋价格。为了开发一个能够预测房价的模型,我们需要收集一个真实的数据集。
-
该数据集包括:房屋的销售价格,房龄和面积。在机器学习的术语中,该数据集成为训练数据集或训练集,每一行数据被称为样本,也可成为数据点或者数据样本。我们预测的目标(在这个例子中是房屋价格)成为标签或目标。预测所根据的自变量成为特征或者协变量。
-
通常我们使用\(n\)来表示数据集中的样本数量。对搜因为\(i\)的样本,其输入表示为\(X^{(i)}=[x_{1}^{(i)},x_2^{(2)}]^T\),其输出对应的标签是\(y^{(i)}\)
线性模型
线性假设是指目标可以表示为特征的加权和,如下面的式子:$$price = \omega_{area}\cdot area+\omega_{age}\cdot age +b \tag1$$
中的\(\omega_{area}\)和\(\omega_{age}\)称为权重,\(b\)称为偏置(bias),或称为偏移量(offset)、截距(intercept)。权重决定了每个特征对我们预测值的影响,偏执是指当所有特征都取为0时,预测值应该为多少。即使现实中不会有任何房子的面积是0 或者房龄正好为0年,我们仍然需要偏置项。如果没有偏置项,我们模型表的能力将受到限制。严格的讲\(公式(1)\)*是输入特征的一个仿射变换**。仿射变换的特点就是通过加u去哪和特征进行线性变换,并通过偏置项来进行平移。
给定一个数据集,我们的目标是寻找模型的权重\(W\)和偏置\(b\),使得根据模型做出的预测答题符合数据里的真实价格。输出的预测值由输入特征通过线性模型的仿射变换决定,仿射变换由所选权重和偏置决定。
- 有些学科往往只关注有少量特征的数据集,在这些学科中,建模时经常想这样通过长形式显示地表达。而在机器学习领域,我们通常使用的是高维数据集,建模时采用线性代数表示法会比较方便。当我们的输入包含\(d\)个特征时、我们将预测结果\(\hat{y}\)(通常使用“尖号”符号表示估计值)表示为:$$\hat{y}=\omega_1x_1+\dots+\omega_dx_d+b \tag2$$
将特征向量放到\(x\in\mathbb{R}^d\)中,并将所有权重放到\(w\in\mathbb{R}^d\)中,我们可以用点积形式来简洁的表达模型:$$\hat{y}=w^Tx+b\tag3$$
在公式3中,向量\(x\)对用于单个数据特征的样本。用符号表示的矩阵\(X\in\mathbb{R}^{x\times{d}}\)可以很方便的引用我们整个数据集的n个样本。其中,X的每一行是一个样本,每一列是一种特征。
对于特征集合\(X\),预测值\(\hat{y}\in\mathbb{R}^n\)可以通过\(矩阵-向量\)乘法表示为:\(\hat{y}=Xw+b\tag4\)
损失函数
在我们开始考虑如何用模型拟合数据之前,我们需要确定一个拟合程度的度量。损失函数能够量化目标的实际值和预测值之间的差距。通常我们回选择非负数作为损失,且数值越小表示损失越小,完美与测试的损失为0,回归问题中最常用的损失函数是平方误差数。当样本\(i\)的预测值为\(\hat{y}^i\)其对应的真是标签为\(y^{(i)}\)时,平方误差可以定义为以下公式:$$l{(i)}(\boldsymbol{w},b)=\frac{1}{2}(\hat{y}-y{i})2\tag5$$
常数\(\frac{1}{2}\)不会带来本质的差别,但这样在形式上稍微简单一些,表现为我们对损失函数求导后常数系数为1. 此处的平方时因为 较大的差异值可以带来更大的损失函数。为了度量模型在整个数据集上的质量,我们需要计算在训练集n个样本上的损失均值。$$L(\boldsymbol{w},b)=\frac{1}{n}\sum_{i=1}nl(\boldsymbol{w},b)=\frac{1}{2n}\sum_{i=1}n(wTx{(i)}+b-y)^2\tag6$$
在训练模型的时候,我们希望能够寻找一组参数,这组参数可以最小换在所有训练样本上的总损失$$(\boldsymbol{w}*,b*)=argmin L(w,b)\tag7$$
解析解
线性回归刚好是一个很简单的优化问题。线性回归的解可以用一个公式简单的表达出来,这类解叫做解析解(analytical solution)。首先,我们将偏置b合并到参数w中。合并方法是在包含所有参数的矩阵中附加一列。我们预测的问题是最小化\(\Vert y-Xw\Vert^2\tag8\)。在这个损失平面上有一个临界点,这个临界点对应于整个区域的损失最小值。将损失关于\(\boldsymbol{w}\)的倒数设为0,得到解析解:\(w^*=(X^TX)^{-1}X^Ty\tag9\)像线性回归这样的解析问题存在解析解,但并不是所有的问题都存在解析解。解析解可以很好的进行数学分析,但解析解的限制很严格,导致它无法应用在深度学习里面。
小批量随机梯度下降
即使在我们无法得到解析解的情况下,我们仍然可以有效的训练模型。在许多任务上,那些难以优化的模型效果要更好。因此,弄清楚如何训练这些难以优化的模型是非常重要的。
本书中我们用到一种名梯度下降(gradient descent)的方法,这种方法几乎可以优化所有深度学习模型。它通过不断的在损失函数的递减的方向更新参数来降低误差。
梯度下降最简单的用法是计算损失函数(数据集中所有样本的损失均值)关于模型参数的导数(在这里也可以称为梯度)。但是在实际的执行中可能会非常慢:因为在每一次更新参数之前,我们必须便利整个数据集。因此我们通常会在每次需要计算更新的时候随机抽取一批小样本,这种变体叫做小样本随机梯度下降(minibatch stochastic gradient descent)。
在每次迭代中,我们首先随机抽取一个小批量\(\beta\),它是由固定数量的训练样本组成的。然后,我们计算小批量的平均损失关于模型参数的导数(也称之为梯度)。最后,我们将梯度乘以一个预先确定正数\(\eta\),并从当前参数的值中减掉。
- 根据权重\(\omega\)和偏置\(\beta\)生成num_example个人造数据。其中x是训练集的数据,y是标签,在标签上加上一点标准差为0.01的噪音,
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
def synthetic_data(w, b, num_examples):
x = torch.normal(0, 1, (num_examples, len(w)))
y = torch.matmul(x, w) + b
y += torch.normal(0, 0.01, y.shape)
return x, y.reshape((-1, 1))
生成每批大小为batch_size的数据进行小样本训练,获取总的样本数量,然后将其打乱。返回batch_size大小的特征和标签。
def data_iter(batch_size, features, labels):
num_example = len(features) # 特征数量
indices = list(range(num_example))
random.shuffle(indices) # 洗牌 特征顺序打乱。
for i in range(0, num_example, batch_size):
batch_indices = torch.tensor(indices[i:min(i + batch_size, num_example)])
yield features[batch_indices], labels[batch_indices]
在开始下批量随机梯度下贱优化我们的模型参数之前,我们需要一些参数,在下面的代码当中,我们从均值为0,标准差为0.01的正态分布中随机采样随机数来初始化权重,并将偏置初始化为0.
w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
在初始参数之后,我们的任务是更新这些参数,直到这些参数足够你和我们的数据,每次更新都需要计算损失函数关于模型参数的梯度,有了这个梯度,我们就可以向减小损失的方向更新每个参数。
- w 预设的均值为0, 标准差为0.01
- b 预设的初值为0
- x 有大量(n)的已知数据
- y 有大量(n)的已知数据
目标$$L(\boldsymbol{w},b)=\frac{1}{n}\sum_{i=1}nl(\boldsymbol{w},b)=\frac{1}{2n}\sum_{i=1}n(wTx{(i)+b}-y)^2\tag6$$通过改变\(\boldsymbol{w},b\)尽量降低\(L\).
定义模型
def linreg(x,w,b):
return torch.matmul(x,w)+b
损失函数
def squared_loss(y_hat, y):
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
优化算法
介绍小批量随机梯度下降的工作示例。
在每一步中,使用从数据集中随机抽取的一个小批量,然后根据参数计算损失的梯度。接下来,朝着减少损失的方向更新我们的参数。下面的函数实现小批量随机梯度下降更新。该函数接受模型参数集合,学习速率和批量大小作为输入。每一步的更新大小由学习速率\(lr\)决定。因为我们计算的损失是一个批量样本的综合,所以我们用批量大小来归一化步长,这样步长大小就不会取决于我们对批量大小的选择。
def sgd(params,lr,batch_size):
with torch.no_grad():
for param in params:
param -= lr* param.grad/batch_size
param.grad.zero_()
训练
在每次迭代中,我们读取一个小批量训练样本,并通过我们的模型来获取一组预测。计算完损失之后,我们开始反向传播,存储每个参数的梯度。最后我们调用sgd来更新模型参数。
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss
for epoch in range(num_epochs):
for x,y in data_iter(batch_size,features,labels):
l = loss(net(x,w,b),y)
l.sum().backward()
sgd([w,b],lr,batch_size)
with torch.no_grad():
train_l = loss(net(features,w,b),labels)
print(f'epoch {epoch+1},loss{float(train_l.mean()):f}')