线性回归
输出层不当成一层 输入层和权重层放一起
x和y是列向量
因为loss=1/2(y-y_hat)^2
又因为y_hat的平均值=1/n(xi*w+b)=1/n(Xw+b)
唯一一个有显示解的模型
基础优化方法
不但更新w
负梯度:下降最快的方向 即图中负梯度的方向
n:学习率 表示我沿着这个方向每次走多远
类似下山的时候每次沿着最陡的地方走一大步 这样很快就可以到山低
学习率太小需要走很多步 计算很多次梯度
学习率太大会震荡 错过 并不是真正的在下降
从零开始实现整个方法,包括数据流水线、模型。损失函数和小批量随机梯度下降优化器。
import random
import torch
import matplotlib.pyplot as plt
# from d2l import torch as d2l
def sy_data(w,b,num_examples):
# 生成y=Xw+b+噪音 X中n行有w列
X = torch.normal(0, 1, (num_examples, len(w)))
y = torch.matmul(X, w)+b
# Xw的内积+b
y += torch.normal(0, 0.01, y.shape)
# 再加一个均值为0 方差为0.01的噪音
print(X,y,y.reshape((-1, 1)))
# 一个形状尺寸可以为-1。在这种情况下,该值是根据数组的长度和其余维来推断的。 1表示1列,是列向量
return X, y.reshape((-1, 1))
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = sy_data(true_w, true_b, 1000)
print('features:', features[0], '\nlabel:', labels[0])
# a=plt.scatter(features[:, 1].detach().numpy(),
# labels.detach().numpy(), 1);
# print(a)
'''
features: tensor([1.1566, 0.7321])
label: tensor([4.0169])
'''
# 接受批量大小 特征矩阵和标签向量作为输入 生成batch_size的小批量
def data_iter(batch_size, features, labels):
num_examples = len(features)
indices = list(range(num_examples))# 生成数字0-999
# 这些样本随机读取 没有特定顺序
random.shuffle(indices)
print(random.shuffle(indices))
for i in range(0, num_examples, batch_size):
batch_indices = torch.tensor(
indices[i:min(i + batch_size,num_examples)]
)
# 间隔batch_size取数 赋予batch_indicse
'''
例如[248, 62, 699, 776, 517, 85, 90, 352, 545, 841]
tensor([248, 62, 699, 776, 517, 85, 90, 352, 545, 841])
'''
print(indices[i:min(i + batch_size,num_examples)])
print(batch_indices)
yield features[batch_indices],labels[batch_indices]
batch_size = 10
for X, y in data_iter(batch_size, features, labels):
print(X, y)
break
'''
选取样本
X 10*2 因为batchsize=10 w=2
y 10*1
tensor([[-0.6701, 1.0900],
[ 0.9038, -0.1050],
[ 0.0928, 1.0312],
[ 0.2287, -1.0536],
[-0.5450, -0.8574],
[-1.0709, -0.6346],
[-0.4716, -0.9125],
[ 1.1762, 1.4156],
[-0.3838, 0.2382],
[-1.1287, -0.0346]])
tensor([[-0.8472],
[ 6.3714],
[ 0.8639],
[ 8.2421],
[ 6.0209],
[ 4.2200],
[ 6.3587],
[ 1.7371],
[ 2.6191],
[ 2.0615]])
'''
# 定义 初始化模型参数
w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
print(w, b)
# 定义模型
def line(X, w, b):
return torch.matmul(X, w) + b
# 定义损失函数
def squared_loss(y_hat, y):
# 均方损失
return (y_hat - y.reshape(y_hat.shape))**2/2# 这里不做均值
# 定义优化算法
'''
小批量随机梯度下降
'''
def sgd(params, lr, batch_size):
# 更新时不要梯度 params是参数列表 [w,b]
with torch.no_grad():
for p in params:
p -= lr*p.grad/batch_size# 因为在损失函数中没有求均值 所以这里求均值
# 手动设置梯度为0 这样下一次就不会累积上一次的梯度
p.grad.zero_()
# 训练过程
lr = 0.03
num_epochs = 3 #整个数据扫三遍
net = line
loss = squared_loss
for epoch in range(num_epochs):
# 每次处理批量大小的X和y
for X,y in data_iter(batch_size, features, labels):
l = loss(net(X, w, b),y)
#将X,w,b放进net中做预测,然后预测y与真实y做损失
# 损失就是长为一个批量大小的向量(‘batch_size’,1)
l.sum().backward() # 求和之后算梯度
sgd([w, b], lr, batch_size) # 对w,b进行更新
# 评价进度,不需要计算梯度
with torch.no_grad():
train_l = loss(net(features, w, b), labels)
print(f'epoch{epoch+1}, loss {float(train_l.mean()):f}')
# 比较真实参数和通过训练学到的参数来评估训练的成功程度
print(f'w的估计误差:{true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差:{true_b - b}')
'''
可以通过增大lr和epoch 使得loss变小
lr=0.03 epoch=3
epoch3, loss 14.084221
w的估计误差:tensor([ 1.7688, -3.2452], grad_fn=<SubBackward0>)
b的估计误差:tensor([3.7988], grad_fn=<RsubBackward1>)
lr=0.5 epoch=10
epoch10, loss 0.000207
w的估计误差:tensor([-0.0082, -0.0007], grad_fn=<SubBackward0>)
b的估计误差:tensor([0.0150], grad_fn=<RsubBackward1>)
'''
使用深度学习框架来简洁地实现线性回归模型生成数据集
import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2l
true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = d2l.synthetic_data(true_w, true_b, 1000)
# 调用框架中现有的API来读取数据
def load_array(data_arrays, batch_size, is_train=True):
"""构造一个pytorch数据迭代器"""
dataset = data.TensorDataset(*data_arrays) # 将X和 y传进去
# 得到数据集之后调用DataLoader
# shuffle:是不是需要随机打乱顺序
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))) # 得到x和y
# 使用框架的预定义好的层
from torch import nn
net = nn.Sequential(nn.Linear(2, 1)) # 输入和输出的维度
# 初始化模型参数
# net[0]访问linear,.weight访问w,.data真实值,normal_使用正态分布替换掉data值
print(net[0].weight.data.normal_(0, 0.01))
# bias偏差设置为0
print(net[0].bias.data.fill_(0))
# 计算均方误差使用的是MSELoss类(L2范数)
loss = nn.MSELoss()
# 实例化SGD实例
# net.parameters():所有的参数
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本身带有模型参数
trainer.zero_grad() # 梯度清零
l.backward()
trainer.step() # 模型更新
l = loss(net(features), labels)
print(f'epoch{epoch + 1}, loss {l:f}')
问题答疑:
-
关于神经网络 我们是通过误差反馈修改参数
-
batchsize越小收敛越小 一定的噪音使得网络不会偏 泛化性更好
-
针对batchsize大小的数据集进行网络训练的时候,网络中每个参数更新时的减去的梯度是batchsize中每个样本对应参数梯度求和后取得的平均值
-
随机梯度下降的‘随机’指的是在样本中随机采集batchsize大小个的元素
-
统计模型和优化模型 我关心的是收敛到哪 不是收敛快不快
-
使用yield而不是return 是因为每次生成一个batchsize接着生成就好,还有就是因为习惯
-
如果样本大小不是批量数的整数倍,那需要随机剔除多余的样本
吗?答:如果是1000=个样本 batchsize=60 (1)就取剩下的40个(2)丢掉剩下的40个(3)从下一个epoch再补20个 -
判断收敛?1用验证数据集看其梯度有没有增加 2看两个epoch直接目标函数已经变化不大了就表示收敛了
-
本质上我们为什么要用SGD(随机梯度下降),是因为大部分的实际loss太复杂,推导不出导数为0的解么?只能逐个batch去逼近? 答:是的