简单且流行的基本回归模型
对n维输入 x n x_n xn的加权 w n w_n wn再加偏差 b b b
平方损失函数
l ( y , y ^ ) = 1 2 ( y − y ^ ) 2 l(y, \hat y) = \frac 1 2(y - \hat y)^2 l(y,y^)=21(y−y^)2
以上求解结果越小区别越小,结果越大区别越大
训练
利用现有数据制作训练集,通常情况下越多越好
线性模型有显示解析解,但实际过程基本不会有线性问题出现,不追求显示解
优化方法
小批量随机梯度下降(默认算法)
直接使用梯度下降太过于复杂,需要对整个训练集数据操作。
采样b个样本来近似全部数据,b是另一个重要的超参数-采样批量大小(batch)
总结
不断地沿着反梯度方向(梯度方向是增长最快的方向)更新参数
b b b和 η \eta η是很重要超参数
1.训练数据准备
这里的函数synthetic_data实现了一个带有噪声的线性模型如下:
y = w x + b + c y = wx + b + c y=wx+b+c
取 w w w = [ 2 , − 3.4 ] T [2, -3.4]^T [2,−3.4]T b = 4.2 b = 4.2 b=4.2
import torch
import random
# 此函数会根据输入的w,b的值成对输出y x 我们用作训练集数据
def synthetic_data(w, b, num_examples):
x = torch.normal(0, 1, (num_examples, len(w)))
y = torch.matmul(x, w) + b # pytorch的乘法
y += torch.normal(0, 0.01, y.shape) # 加上噪声
return x, y.reshape((-1, 1))
输入w,b的真值测试一下,生成1000个
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
# 输出看一下
for i in range(1000):
print("数据 ", features[i], " 标签 ", labels[i])
部分输出如下:
数据 tensor([1.9494, 1.3384]) 标签 tensor([3.5615])
数据 tensor([ 1.0765, -0.0897]) 标签 tensor([6.6455])
数据 tensor([-0.3591, -0.9325]) 标签 tensor([6.6370])
数据 tensor([-1.2803, -0.2001]) 标签 tensor([2.3254])
数据 tensor([-1.3962, 0.8183]) 标签 tensor([-1.3768])
数据 tensor([ 0.5650, -0.4530]) 标签 tensor([6.8746])
数据 tensor([-0.5573, -0.3996]) 标签 tensor([4.4393])
...
2.抽取batch
# 这里实现的是,每次从训练集中抽取一个batch的数据进行训练
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_size个数据取一个,可以做到不重复抽取且每个数据都会用到
batch_indices = torch.tensor(indices[i: min(i + batch_size, num_examples)]) # 防止最后一组没有一个batch_size的数据
'''
yield 生成器 学识浅薄惭愧 从没用过的python关键字
查了下暂时理解成return 每次需要时候迭代一次 比list省内存
每次需要用到下一个的时候才调用
'''
yield features[batch_indices], labels[batch_indices]
以下代码测试输出:
batch_size = 10
for x, y in data_iter(batch_size, features, labels):
print(x)
print(y)
部分输出如下:
tensor([[-0.4891, -0.2155],
[-0.4724, 2.2419],
[ 0.8081, -0.0244],
[ 0.1264, 0.8761],
[ 1.5874, 2.0016],
[ 1.8350, 0.3115],
[ 0.2307, -1.1173],
[-1.0151, -0.4539],
[ 0.0415, -0.4983],
[ 0.7697, 0.4390]])
tensor([[ 3.9540],
[-4.3682],
[ 5.8896],
[ 1.4842],
[ 0.5696],
[ 6.8205],
[ 8.4443],
[ 3.7195],
[ 5.9762],
[ 4.2288]])
...
3.模型设置
很容易理解,实现一个线性模型:
y = w x + b y = wx + b y=wx+b
def linreg(x, w, b):
return torch.matmul(x, w) + b
4.损失函数
定义上述均方根损失函数
def squard_loss(y_hat, y):
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2 # 这里的reshape目的是为了统一计算,过程中可能一个行向量一个列向量,也可以在之前的步骤中规定好
5.优化算法
小批量随机梯度下降算法的手动实现
# lr 学习率
# batch_size 批量大小
def sgd(params, lr, batch_size):
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size # 之前没有求均值,在这里求
param.grad.zero_() # 防止梯度累加
6.训练验证
# 定义超参数
lr = 0.03 # 学习率
num_epochs = 3 # 数据扫三遍
# 以下是为了多个网络方便编程,感觉像是函数别名
net = linreg
loss = squared_loss
# w b 都需要记录梯度
w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
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() # 这里l形状(batch_size, 1) 不是标量,因此计算梯度利用sum()计算
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-string语法 {}内直接显示为变量 :f是保留6位精度
print(f"w的估计误差:{true_w - w.reshape(true_w.shape)}")
print(f"b的估计误差:{true_b - b}")
结果如下:
epoch 1, loss 0.034460
epoch 2, loss 0.000128
epoch 3, loss 0.000056
w的估计误差:tensor([0.0005, 0.0001], grad_fn=<SubBackward0>)
b的估计误差:tensor([5.6744e-05], grad_fn=<RsubBackward1>)
1.准备数据集
import torch
import torch.utils import data
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
2.构造了一个pytorch的数据迭代器
def load_array(data_arrays, batch_size, is_train=True):
# 此处*将列表元素分别当做两个参数传入 即features 和 labels
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)
print(next(iter(data_iter)))
输出如下:
# 此处为了清晰我手动调整了格式
[
tensor([[-0.1956, -0.9790],
[-0.8501, 1.4546],
[ 0.0941, 1.3185],
[ 1.6486, -0.5259],
[ 0.9986, 0.7224],
[ 0.0788, -0.0592],
[-0.7319, 0.0070],
[-1.3904, -1.5994],
[ 0.0679, 0.9106],
[ 0.1502, -1.1432]]),
tensor([[ 7.1412],
[-2.4247],
[-0.0891],
[ 9.2879],
[ 3.7425],
[ 4.5828],
[ 2.7132],
[ 6.8642],
[ 1.2361],
[ 8.3878]])
]
3.模型定义
from torch import nn # 这个类有大量定义好的层
# 此处Squential是将这个线性层放到一个list中,深度神经网络中更方便
net = nn.Squential(nn.Linear(2, 1)) # Linear全连接层、线性层
4.损失函数和优化算法
nn.MSELoss() # 均方误差损失函数
torch.optim.SGD(网络参数, 学习率) # 小批量随机梯度下降算法
5.训练验证
# 初始化参数
net[0].weight.data.normal_(0, 0.01) # w
net[0].bias.data.fill_(0) # b
# 均方误差 平方L2范数
loss = nn.MSELoss()
trainer = torch.optim.SGD(net.parameters(), lr=0.03)
# 次数
num_epochs = 3
# 训练
for epoch in range(num_epochs):
for x,y in data_iter: # 拿出一个批次数据
l = loss(net(x), y) # net已经初始化w、b无需传入 net(x) 是预测值 y是标签
trainer.zero_grad() # 梯度清零
l.backward() # 计算梯度 pytorch自动做了sum操作
trainer.step() # 更新模型w b
l = loss(net(features), labels) # 计算一下偏差
print(f"epoch {epoch + 1}, loss {l:f}")
输出如下:
epoch 1, loss 0.000309
epoch 2, loss 0.000104
epoch 3, loss 0.000105