1. 内容来源
2. 底层实现
2.1 数据生成
本文使用以下公式加上噪声生成训练及测试数据:
代码实现:
import random
import torch
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.001, 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)
2.2 数据集生成
为之后代码可以直接迭代获取批量数据,自定义dataset生成函数
代码实现:
# Dataset Generation
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
2.3 模型参数初始化
由于回归目标有三个参数,可与单层神经网络对应,故使用1层、2神经元的网络训练,则使用如下代码初始化参数并注明需要梯度(方便后续自动求解梯度进行反向传播优化)
代码实现:
# Parameters Initialization
w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
2.4 模型构建
在单层神经网络中,前向运算即
代码实现:
# Linear Regression Model
def linreg(X, w, b):
return torch.matmul(X, w) + b
Loss函数使用均方误差
代码实现:
def squared_Loss(yhat, y):
return (yhat - y.reshape(yhat.shape)) ** 2 / 2
优化方法使用随机梯度下降,前面参数初始化时已注明需要梯度,这里.grad可以直接获取当前参数的梯度,则当前参数减去learning rate * grad即为随机梯度下降(由于torch的grad会自动累加,每次更新完参数需要手动.grad.zero_()置零)
代码实现:
def sgd(params, lr, batch_size):
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
2.5 模型训练
learning rate和epochs总数设置为0.03和10。
每次优化循环中使用data_iter取出数据,使用loss计算损失序列,loss求和后使用.backward()更新每个参数的梯度,使用sgd利用梯度更新参数。
代码实现:
# Training
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}')
结果:
epoch 1, loss 0.033998
epoch 2, loss 0.000075
epoch 3, loss 0.000001
3 简洁实现
3.1 数据生成
同第2节
代码实现:
import numpy as np
import torch
from torch.utils import data
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels =utils.synthetic_data(true_w, true_b, 1000)
def load_array(data_arrays, batch_size, is_train=True):
dataset = data.TensorDataset(*data_arrays)
return data.DataLoader(dataset, batch_size, shuffle=is_train)
batch_size = 10
data_iter = load_array((features, labels), batch_size)
3.2 模型构建
使用torch.nn.Linear(2, 1)代替第2节中手写的单层网络
代码实现:
# Model
from torch import nn
net = nn.Sequential(nn.Linear(2, 1))
使用net[0].weight.data/net[0].bias.data分别取出线性层的w和b,分别进行初始化
# Parameters
net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)
Loss函数和SGD使用torch.nn.MSELoss()和torch.optim.SGD()
代码实现:
loss = nn.MSELoss()
trainer = torch.optim.SGD(net.parameters(), lr=0.03)
3.3 模型训练
与手写模型不同,使用trainer.zero_grad()将模型参数梯度置零,loss.backward()计算参数梯度,step()更新模型参数。
代码实现:
# Training
num_epochs = 3
for epoch in range(num_epochs):
for X, y in data_iter:
l = loss(net(X), y)
trainer.zero_grad()
l.backward()
trainer.step() # Model parameters update
l = loss(net(features), labels)
print(f'epoch {epoch + 1}, loss {l:f}')
结果:
epoch 1, loss 0.000295
epoch 2, loss 0.000001
epoch 3, loss 0.000001