摘要:本实验从0开始,不使用pytorch的现成模型,直接搭建线性回归模型。
0、问题描述
假设特征向量为 w w w(设为 [ − 2 , − 3.4 ] T [-2,-3.4]^T [−2,−3.4]T), b = 4.2 b=4.2 b=4.2,样本为 X X X,输出为 y y y,同时加上一点噪声 ϵ \epsilon ϵ:
y = X w + b + ϵ y=Xw+b+\epsilon y=Xw+b+ϵ
后续就是要通过对大量 X , y X,y X,y关系的学习探索 w , b w,b w,b的取值。
1、构建训练数据集
我们首先定义synthetic_data
函数,用于生成一个
1
,
000
1,000
1,000行的数据集。由于我们知道模型参数的真实值,因此不设测试函数。
- 生成数据的规则为:指定均值和方差,构建高斯分布随机数据。
- pytorch在做矩阵乘法的时候,若不满足左矩阵列数=右矩阵行数,例如计算 X m , n w 1 , n X_{m,n}w_{1,n} Xm,nw1,n,会自动调节。
# 生成 y = Xw + b + 噪声
def synthetic_data(w, b, num_examples):
# X为均值为0,标准差为1,num_examples*len(w)的矩阵,作为初始参数值
X = torch.normal(0, 1, (num_examples, len(w)))
print("X =", X.shape)
print("w =", w.shape)
# 矩阵乘法,输出标量
# w为1*len(w)的向量
y = torch.matmul(X, w) + b
print("y =", y.shape)
y += torch.normal(0, 0.01, y.shape)
return X, y.reshape((-1, 1)) # -1 for auto
true_w = torch.tensor([-2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
运行后,输出数据规模:
X = torch.Size([1000, 2])
w = torch.Size([2])
y = torch.Size([1000])
使用d2l库将训练数据进行可视化处理:
d2l.set_figsize()
d2l.plt.scatter(features[:,1].detach().numpy(), \
labels.detach().numpy(),1)
输出如下:
可见,生成的数据符合线性分布。
2、批量化读取数据
定义data_iter函数,生成batch_size的小批量训练数据:
# 生成小批量数据
# 例如,batch_size = 10,则返回10组[w|y]样本
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]
batch_size=10
for X,y in data_iter(batch_size,features,labels):
print("X =", X, "\ny =", y)
break # 仅输出一组数据
这个函数的作用是:使用yield
断点,每调用一次,生成不同的、不重复的,规模为batch_size
的特征矩阵和label向量,即为:
X
∣
y
X | y
X∣y。
3、定义模型
包括模型参数初始化、定义模型等步骤
-
模型参数初始化:由于本实验的模型仅有两个参数 w , b w,b w,b,因此通过高斯随机分布给出初始值。
-
模型定义:所谓的模型
model
,其实就是一个函数,输入特征矩阵 X X X、参数 w , b w, b w,b,输出回归值 y y y,这个过程和BP算法的前向传播过程基本一致。 -
损失函数:使用平方损失函数: l o s s = 1 2 ( y ∗ − y ) 2 loss=\frac{1}{2}(y^*-y)^2 loss=21(y∗−y)2
-
优化算法:使用SGD函数,小批量梯度下降算法。
# 初始化模型参数w, b
# 定义向量的时候 打开requires_grad开关
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
print('w =', w, '\nb =', b)
# 定义模型
def linreg(X, w, b):
return torch.matmul(X, w) + b
# 定义优化算法-小批量随机梯度下降
def sgd(params, lr, batch_size):
with torch.no_grad():
for param in params:
# print(f'param is {param}')
param -= lr * param.grad / batch_size
param.grad.zero_()
4、训练过程
- 设置学习率、训练轮数、模型和损失函数等;
- 对于每一轮训练而言:
- 按照之前划分的
batch_size
,一批一批地读取训练集,对每个批次的数据:- 套用model计算回归值 y y y,与真实值 y ∗ y^* y∗对比计算损失;
- 计算梯度;
- 使用SGD优化算法更新参数 w , b w,b w,b。
- 将梯度追踪关闭,计算该epoch的整体平均损失,输出 m e a n [ 1 2 ( y ∗ − y ) 2 ] mean[\frac{1}{2}(y^*-y)^2] mean[21(y∗−y)2](由于该步仅为了输出epoch的整体损失,故不参与梯度下降过程)
- 按照之前划分的
# 训练过程
lr = 0.03
num_epochs = 3
model = linreg
loss = squared_loss
for epoch in range(num_epochs):
for X, y in data_iter(batch_size, features, labels):
l = loss(model(X, w, b), y)
l.sum().backward()
sgd([w, b], lr, batch_size)
with torch.no_grad():
train_l = loss(model(features, w, b), labels)
# print(train_l)
print(f'epoch {epoch+1}, loss {float(train_l.mean()):f}')
输出如下:
epoch 1, loss 0.026238
epoch 2, loss 0.000093
epoch 3, loss 0.000046
5、比较模型误差
我们训练出了一个模型,也就是模型参数 w , b w,b w,b。和真实值做对比,输出误差:
print(f'err of w: {true_w - w.reshape(true_w.shape)}')
print(f'err of b: {true_b - b}')
本次训练, w w w的误差向量为 [ − 0.0005 , − 0.0003 ] T [-0.0005, -0.0003]^T [−0.0005,−0.0003]T, b b b的误差为 0.0002 0.0002 0.0002,效果较好。
6、继续实验
你也可以调节超参数的值,重复实验以寻求最优解。此处就不赘述了。
⭐ 如果你看到这里了,不如点个免费的赞吧~