前言
线性回归模型是在目前的机器学习领域入门级的必修课,其基本框架简单易上手,基本逻辑清晰好理解,本文主要来进行介绍线性回归的基本思想与实现过程。
1.线性回归的基本思想
线性回归可以追溯到19世纪初, 它在回归的各种标准工具中最简单而且最流行。线性回归基于几个简单的假设:首先,假设自变量x和因变量y之间的关系是线性的,即y可以表示为x中元素的加权和,这里通常允许包含观测值的一些噪声;其次,我们假设任何噪声都比较正常,如噪声遵循正态分布。
在高维数据之中,我们通常表示模型如下:
,
其中,权重 和偏置
是在模型中需要被进行预测的量。其实简而言之,线性回归的思路就是在寻找x与y之间的关系,确定具体的系数,将之前用于定性分析的"正相关"和"负相关"转为定量描述,探究到底相关程度有多少的一个问题。
基于真实值与预测值之间存在误差,我们需要设计损失函数来比较不同参数预测下的精度问题,本文中设计的误差函数为平方误差函数,表达式如下:
.
误差函数越小,则代表我们预测得到的参数越精准,换而言之,在模型训练过程中,就是为了寻找一组参数,这组参数可以最小化在所有样本上的总损失。
朝着最小化总损失的这个目标,在计算过程中,我们采用小批量随机梯度下降法 进行迭代,以寻找最优参数,算法的步骤如下: (1)初始化模型参数的值,如随机初始化 ; (2)从数据集中随机抽取小批量样本且在负梯度的方向上更新参数,并不断迭代这一步骤。 对于平方损失和仿射变换,我们可以明确地写成如下形式:
,
.
其中,代表小批量 中包含的样本,计算其数量后用于进行平均,
代表学习率,可以简单理解为没有迭代更新所探索的步长。
在实际问题分析中,线性回归问题一般需要考虑噪声,特别的,在正态噪声(高斯噪声)下的线性回归模型便会变为:
.
其中,噪声服从正态分布,在后续 文章中我们将详细推导在高斯噪声的假设下,最小化均方误差等价于对线性模型的极大似然估计。
基于上述的简单介绍,我们已经大概了解了线性回归的基本思路,就是在寻找最小化损失函数的道路上,利用小批量梯度随机下降法去寻找自变量与因变量之间的线性关系。
2.线性回归的实现
本小节将按照"生成数据集→读取数据集→初始化模型参数→定义模型→定义损失函数→定义优化算法→训练"的基本逻辑框架进行展开,逐部分拆解线性回归问题的实现。
2.1生成数据集
python
# 生成数据集
def synthetic_data(w, b, num_examples): #@save
"""生成y=Xw+b+噪声"""
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))
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
代码解读:
①本数据集中共基于标准正态分布生成了1000组随机样本;
②本次模拟中的权重真值为2与-3.4,偏置真值为4.2;
③本次模拟中的噪声为服从期望为0,方差为0.01的高斯分布的高斯噪声。
2.2读取数据集
python
#读取数据集
def data_iter(batch_size, features, labels):
num_examples = len(features)
indices = list(range(num_examples))
# 这些样本是随机读取的,没有特定的顺序
random.shuffle(indices)
for i in range(0, num_examples, batch_size):
batch_indices = torch.tensor(
indices[i: min(i + batch_size, num_examples)])
yield features[batch_indices], labels[batch_indices]
代码解读:
①数据读取过程中采用随机读取,并无特定顺序;
②遍历过程中选择按批次遍历,每一批次中的样本数量提前预设好;
③代码中还运用了"边界保护",以解决样本总数无法被整除问题。
2.3初始化模型参数
python
#初始化模型参数
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
代码解读:
①模型参数首先进行初始化,相当于寻找一个模型进行迭代的起点;
②此处参数的设置是来自于期望为0,方差为0.01的正态分布,而参数
则设置为0。
2.4定义模型
python
#定义模型
def linreg(X, w, b): #@save
"""线性回归模型"""
return torch.matmul(X, w) + b
代码解读:
采用矩阵-向量乘法构建线性回归模型。
2.5定义损失函数
python
#定义损失函数
def squared_loss(y_hat, y): #@save
"""均方损失"""
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
代码解读:
采用均方误差函数作为损失函数。
2.6定义优化算法
python
#定义优化算法
def sgd(params, lr, batch_size): #@save
"""小批量随机梯度下降"""
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
代码解读:
采用小批量随机梯度下降法作为优化迭代算法。
2.7训练
python
#训练
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) # X和y的小批量损失
# 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
# 并以此计算关于[w,b]的梯度
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}')
代码解读:
①设置学习率与迭代轮次之后便可以开始计算与寻找,不断更新参数后便可以得到最终结果;
②代码中可以自动更新梯度;
③最后输出每一轮次的损失函数的函数值,可以观察得到,函数值在不断变小。
3.小结
经过上述的介绍,本文便完成了对于线性回归的介绍,本专栏系列文章均参考李沐老师的《动手学深度学习(PyTorch版)》,线性回归模型逻辑清晰,是作为开始学习深度学习的入门课程。