线性神经网络之线性回归的实现
1.生成数据集
使用线性模型参数w=[2,−3.4]⊤、b=4.2和噪声项ϵ生成数据集及其标签:
y
=
X
w
+
b
+
ϵ
y=Xw+b+ϵ
y=Xw+b+ϵ
代码实现:
def synthetic_data(w, b, num_examples): #@save
"""生成y = Xw + b + 噪声。"""
X = torch.normal(0, 1, (num_examples, len(w))) #均值为0,方差为1,n行,列数为w的长度
y = torch.matmul(X,w) + b #X * W + b
y += torch.normal(0, 0.01, y.shape) #噪声C, 均值为0,方差为1,形状和y相同
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)
2.读取数据集
定义一个函数,函数能打乱数据集中的样本并以小批量方式获取数据。
定义一个data_iter
函数, 该函数接收批量大小、特征矩阵和标签向量作为输入,生成大小为batch_size
的小批量。每个小批量包含一组特征和标签。
具体实现:
def data_iter(batch_size, features, labels):
num_examples = len(features)
indices = list(range(num_examples)) #转成python的list
#这些样本是随机读取的,没有特定的顺序
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] #返回feature和labels
测试:
batch_size = 5
for X, y in data_iter(batch_size, features, labels):
print(X, '\n', y)
break
输出结果:
tensor([[ 1.6279, -0.4273],
[-0.1371, -0.3923],
[-0.2569, 0.4756],
[-1.0388, 1.2875],
[-0.2165, 0.1080]])
tensor([[ 8.8961],
[ 5.2645],
[ 2.0648],
[-2.2444],
[ 3.3931]])
3.初始化模型参数
在我们开始用小批量随机梯度下降优化我们的模型参数之前,我们需要先有一些参数。
我们通过从均值为0、标准差为0.01的正态分布中采样随机数来初始化权重,并将偏置初始化为0。
具体实现:
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True) #需要计算梯度
b = torch.zeros(1, requires_grad=True)
4.定义模型
定义模型,将模型的输入和参数同模型的输出关联起来。
def linreg(X, w, b):
"""线性回归模型"""
return torch.matmul(X, w) + b #矩阵 * 向量 + 偏差
5.定义损失函数
需要计算损失函数的梯度,所以我们应该先定义损失函数。使用平方损失函数。
在实现中,我们需要将真实值y
的形状转换为和预测值y_hat
的形状相同。
具体实现:
def squared_loss(y_hat, y):
"""均方损失"""
return (y_hat - y.reshape(y_hat.shape))**2 / 2
6.定义优化算法
在每一步中,使用从数据集中随机抽取的一个小批量,然后根据参数计算损失的梯度。接下来,朝着减少损失的方向更新我们的参数。
接下来要实现小批量随机梯度下降更新。该函数接受模型参数集合、学习速率和批量大小作为输入。每一步更新的大小由学习速率lr
决定。 因为我们计算的损失是一个批量样本的总和,所以我们用批量大小(batch_size
)来归一化步长,这样步长大小就不会取决于我们对批量大小的选择。
具体实现:
def sgd(params, lr, batch_size): #lr:学习率,
"""小批量随机梯度下降。"""
with torch.no_grad():#更新时不需要梯度计算
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
7.训练
在每次迭代中,我们读取一小批量训练样本,并通过我们的模型来获得一组预测。 计算完损失后,我们开始反向传播,存储每个参数的梯度。最后,我们调用优化算法 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) #预测y和真实y做损失
#l为向量
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}')
运行结果:
epoch 1, loss 0.000105
epoch 2, loss 0.000046
epoch 3, loss 0.000046