一、线性回归的从零开始(摆脱封装好的算法)
尽管现在已经有很多的深度学习框架已经封装好了线性回归这一算法,让我们可以随时调用,简单且方便,但是这会导致很多人难以理解这一算法是如何工作的,怎么一步一步实现线性回归算法的。因此本文以pytorch框架为基础,仅使用Tensor和autograd来实现一个线性回归。
二、代码示例讲解
0.导入相关包
%matplotlib inline
import torch
from IPython import display
from matplotlib import pyplot as plt
import numpy as np
import random
1.手写数据集
构造训练数据集,本文的数据集样本为1000,输入个数(特征值)为2,使用线性回归真实权重和偏差b=4.2,样本特征则为1000 x 2 的张量,以及一个随机噪声项 ϵ,最终标签公式为:
其中噪声项 ϵ 服从均值为0、标准差为0.01的正态分布。噪声代表了数据集中无意义的干扰。
num_inputs = 2 #样本特征值个数
num_examples = 1000 #样本个数
true_w = [2, -3.4] #权重值
true_b = 4.2 #偏差因子
features = torch.randn(num_examples, num_inputs,
dtype=torch.float32) #随机生成数据类型为float的1000 x 2的矩阵
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b #目标值(预测值)
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()),
dtype=torch.float32) #每个标签(目标值)随机添加均值为0,标准差为0.01,满足正态分布的噪声项
#打印特征值和标签值的形状,以及他们各自第一行的信息
print('features:',features.shape,'|labels:',labels.shape)
print(features[0], labels[0])
输出结果:features
的每一行是一个长度为2的向量,而labels
的每一行是一个长度为1的向量(标量)。
2.查看数据的散点图
plt.figure(figsize=(4,3)) #设置画布大小
plt.scatter(features[:,1].numpy(),labels.numpy(),1) #绘制散点图,点大小为1,因为features是tensor类型,需转成numpy
plt.show()
输出的散点图:
3.读取数据
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):
j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)]) # 最后一次可能不足一个batch
yield features.index_select(0, j), labels.index_select(0, j)
4.初始化模型参数
w = torch.tensor(np.random.normal(0, 0.01, (num_inputs, 1)), dtype=torch.float32) #将权重初始化为均值为0,标准差为0.01的正态随机数
b = torch.zeros(1, dtype=torch.float32) #将偏差因子初始化为0
#因在训练过程中需要对这些参数进行求梯度来迭代更新,因此需要设置requires_grad=True
w.requires_grad_(requires_grad=True)
b.requires_grad_(requires_grad=True)
5.定义模型
def linreg(X, w, b):
return torch.mm(X, w) + b #mm函数是torch包的乘法函数
相当于返回了一个公式:
6.定义损失函数
def squared_loss(y_hat, y):
# 注意这里返回的是向量, 另外, pytorch里的MSELoss并没有除以 2
return (y_hat - y.view(y_hat.size())) ** 2 / 2 #y_hat是预测值,同时也要将真实值y的形状保持和预测值一样
7.定义优化算法
以下的sgd
函数实现了小批量随机梯度下降算法。它通过不断迭代模型参数来优化损失函数。这里自动求梯度模块计算得来的梯度是一个批量样本的梯度和。我们将它除以批量大小来得到平均值。
def sgd(params, lr, batch_size): #lr为学习率,params为线性回归模型的参数(weight和bias)
for param in params:
param.data -= lr * param.grad / batch_size # 注意这里更改param时用的param.data
8.训练模型
在训练中,我们将多次迭代模型参数。在每次迭代中,我们根据当前读取的小批量数据样本(特征X
和标签y
),通过调用反向函数backward
计算小批量随机梯度,并调用优化算法sgd
迭代模型参数。由于我们之前设批量大小batch_size
为10,每个小批量的损失l
的形状为(10, 1)。。由于变量l并不是一个标量,所以我们可以调用.sum()
将其求和得到一个标量,再运行l.backward()
得到该变量有关模型参数的梯度。注意在每次更新完参数后不要忘了将参数的梯度清零。
lr = 0.03
num_epochs = 3
net = linreg #调用线性回归函数
loss = squared_loss #调用损失函数
batch_size = 10 #批量大小
for epoch in range(num_epochs): # 训练模型一共需要num_epochs个迭代周期
# 在每一个迭代周期中,会使用训练数据集中所有样本一次(假设样本数能够被批量大小整除)。
# X和y分别是小批量样本的特征和标签
for X, y in data_iter(batch_size, features, labels):
l = loss(net(X, w, b), y).sum() # l是有关小批量X和y的损失
l.backward() # 小批量的损失对模型参数求梯度
sgd([w, b], lr, batch_size) # 使用小批量随机梯度下降迭代模型参数
# 梯度清零
w.grad.data.zero_()
b.grad.data.zero_()
train_l = loss(net(features, w, b), labels)
print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item())) #mean()为均值函数
#输出
'''
epoch 1, loss 0.028127
epoch 2, loss 0.000095
epoch 3, loss 0.000050
'''
训练完成后还可以查看真实值与预测值的参数值
print(true_w, '\n', w)
print(true_b, '\n', b)
#输出
'''
[2, -3.4]
tensor([[ 1.9998],
[-3.3998]], requires_grad=True)
4.2
tensor([4.2001], requires_grad=True)
'''
全部代码(自取 ):https://github.com/SallBryant/Nerual-Net-Simple-example/blob/main/Linear-zero_to_one.ipynb
分享自己所学的知识,向开源分享的人学习,他们是榜样,为大家学习知识降低了门槛。花时间来创作博客的动力来源于莫凡大佬的传教!