搭建一个包含一层的全连接层的网络,实现2维数据的回归。y = w1 * x1 + w2 * x2 + b
梯度下降:挑选一个初始值W0
,使用公式 W t = W t − 1 − η ∂ ι W t − 1 W_{t}=W_{t-1}-\eta \frac{\partial \iota }{W_{t-1}} Wt=Wt−1−ηWt−1∂ι 重复迭代更新W
。其中,沿着梯度方向将增加损失函数值;学习率是代表步长的超参数。
小批量随机梯度下降:计算梯度的时候需要对损失函数求导,如果在整个数据集上算梯度太贵了,可以随机采样b个样本来近似损失。b是批量大小。
选择学习率:不能太小,每一次走的步长很小,需要走很多的步骤,每一个步骤都需要计算梯度,而计算梯度是非常贵的;不能太大,可能导致越过最优值,一直在震荡。
选择批量大小:不能太小,每次计算量太小,不适合并行来最大利用计算资源;不能太大,内存消耗增加,浪费计算,例如如果所有样本都是相同的。
小结:梯度下降通过不断沿着反梯度方向更新参数求解,小批量随机梯度下降是深度学习默认的求解算法,两个重要的超参数是批量大小和学习率。
线性回归是对n维输入的加权,外加偏置,可以看作是单层神经网络。线性回归使用平方损失来衡量预测值和真实值间的差异,有显示解。
# 1.导入模块
import random
import torch
# 2. 制作数据集
def synthetic_data(w, b, num):
"""生成 y = XW + b + 噪声"""
X = torch.normal(0, 1, (num, len(w))) # 均值为0,方差为1
y = torch.matmul(X, w) + b
y += torch.normal(0, 0.01, 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)
# print(features, labels)
# 3.批量处理
def data_iter(batch_size, features, labels):
num = len(features)
indices = list(range(num))
random.shuffle(indices) # 打乱索引
for i in range(0, num, batch_size):
batch_indices = torch.tensor(
indices[i:min(i + batch_size, num)]
)
yield features[batch_indices], labels[batch_indices] # 迭代返回
batch_size = 10
# for X, y in data_iter(batch_size, features, labels):
# print(X, '\n', y)
# 4.定义初始化模型
w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
def linreg(X, w, b):
"""线性回归模型"""
return torch.matmul(X, w) + b
# 5.定义损失函数
def squared_loss(y_hat, y): # y_hat为预测值
"""均方损失"""
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
# 6.优化算法
def sgd(params, lr, batch_size):
"""小批量随机梯度下降"""
with torch.no_grad(): # 优化的过程中不需要梯度的计算
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
# 7.训练
lr = 0.05
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形状是(batch_size, 1),而不是一个标量。
l.sum().backward()
sgd([w, b], lr, batch_size) # 使用参数的梯度更新参数
with torch.no_grad():
train_l = loss(net(features, w, b), labels)
print('epoch: %d loss: %.4f' % (epoch+1, train_l.mean().item()))
# 训练完的参数
print(w, b)
import torch
import torch.utils.data as Data # 读取数据
import torch.nn as nn
from torch.nn import init # 初始化模型参数
import numpy as np
# 1.生成数据集
num_features = 2 # 样本的特征数
num_examples = 1000 # 样本个数
true_w = [2, -4.3]
true_b = 4.2
# 生成样本
features = torch.tensor(np.random.normal(0, 1, (num_examples, num_features)), dtype=torch.float)
# 生成标签
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
# 给标签添加噪声
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)
# 2.读取数据
batch_size = 10
dataset = Data.TensorDataset(features, labels) # 将训练数据的特征和标签组合
data_iter = Data.DataLoader(dataset, batch_size, shuffle=True) # 随机读取小批量
# torch.nn仅支持输入一个batch的样本,如果只有单个样本,可使用input.unsqueeze(0)来添加一维。
# 3.定义模型
# 方法1
class LinearNet(nn.Module):
def __init__(self, n_feature):
super(LinearNet, self).__init__()
self.linear = nn.Linear(n_feature, 1)
# forward 定义前向传播
def forward(self, x):
y = self.linear(x)
return y
model = LinearNet(num_features)
## 方法2
# model = nn.Sequential(
# nn.Linear(num_features, 1)
# )
## 方法3
# model = nn.Sequential()
# model.add_module('linear', nn.Linear(num_features, 1))
# 查看模型所有的可学习参数
# for param in model.parameters():
# print(param)
# 4.初始化模型参数
init.normal_(model.linear.weight, mean=0, std=0.01) # 将权重参数每个元素初始化为随机采样于均值为0、标准差为0.01的正态分布
init.constant_(model.linear.bias, val=0) # 用val值填充
# 5.配置方法
loss = nn.MSELoss() # 均方误差
optimizer = torch.optim.SGD(model.parameters(), lr=0.03)
# 6.训练
num_epochs = 3
for epoch in range(1, num_epochs + 1):
for X, y in data_iter:
output = model(X) # 前向传播
l = loss(output, y.view(-1, 1)) # 计算loss
optimizer.zero_grad() # 梯度清零,等价于net.zero_grad()
l.backward() # 反向传播
optimizer.step() # 调用optim实例的step函数来迭代模型参数
print('epoch %d, loss: %f' % (epoch, l.item()))
# 7.查看模型参数
dense = model.linear
print(true_w, dense.weight)
print(true_b, dense.bias)
(1)工具包
torch.utils.data模块提供了有关数据处理的工具,
torch.nn模块定义了大量神经网络的层,
torch.nn.init模块定义了各种初始化方法,
torch.optim模块提供了很多常用的优化算法。
(2)访问网络参数
# 网络的定义方式不同,访问网络参数的方法也不一样。尤其在参数初始化时注意。
# class定义的方法时
model.linear.weight
# Sequential定义时,
model[0].weight
(3)学习率扩展
# 可以为不同子网络设置不同的学习率
optimizer =optim.SGD([
# 如果对某个参数不指定学习率,就使用最外层的默认学习率
{'params': net.subnet1.parameters()}, # lr=0.03
{'params': net.subnet2.parameters(), 'lr': 0.01}
], lr=0.03)
# 不想让学习率固定成一个常数,调整学习率
for param_group in optimizer.param_groups:
param_group['lr'] *= 0.1 # 学习率为之前的0.1倍