文章目录
基础知识
点积 矩阵-向量积 矩阵乘法 按元素乘法称为_Hadamard积_
# 1. 点积 对应元素相乘然后求和
torch.dot(x, y) = torch.sum(x * y)
# 2. 矩阵-向量积 (martex-vector)
torch.mv(A, x)
# 3. 矩阵乘法 (martex-martex)
torch.mm(A, B)
# 上述矩阵乘法支持支二维,下面的更加泛化
torch.matmul(X, w)
# 4. 按对应的元素乘法称为Hadamard积
A * B = torch.dot(A, B)
自动微分
2.5. 自动微分 — 动手学深度学习 2.0.0 documentation
有标量微分和非标量微分 (标量就是只有一个值)
import torch
x = torch.arange(4.0) # tensor([0., 1., 2., 3.])
x.requires_grad_(True) # 等价于x=torch.arange(4.0,requires_grad=True)
print(x.grad) # 默认值是None
y = 2 * torch.dot(x, x) # 得到一个标量
y.backward() # 标量情况下:反向传播计算梯度
print(x.grad) # tensor([ 0., 4., 8., 12.])
# 在默认情况下,PyTorch会累积梯度,我们需要清除之前的值
x.grad.zero_()
y = 2 * x * x # 得到一个非标量
y.backward(torch.ones_like(x)) # 等价于 y.sum().backward()
print(x.grad) # tensor([ 0., 4., 8., 12.])
线性神经网络
线性回归 y = w*X + b
自己实现
- 生成数据:
def synthetic_data(w, b, num_examples):
"""生成y=Xw+b+噪声"""
X = torch.normal(0, 1, (num_examples, len(w))) #正态分布
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)
- 读取数据
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] # 异步返回 相当于多个return
batch_size = 10
for X, y in data_iter(batch_size, features, labels):
print(X, '\n', y) # 获取batch_size大小的 样本 和 标签
break
- 初始化参数
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
def squared_loss(y_hat, y): #@save
"""均方损失"""
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
- 定义优化算法
# params 就是 w、b
def sgd(params, lr, batch_size):
"""小批量随机梯度下降"""
with torch.no_grad(): # 禁用该上下文内的所有操作对计算图的跟踪,从而不会计算梯度,为了自定义梯度下降?
for param in params:
param -= lr * param.grad / batch_size # x += y 是原地赋值 引用 所以参数全局改变
param.grad.zero_()
- 训练
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) # X和y的小批量损失
# 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
# 并以此计算关于[w,b]的梯度
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.042790
# epoch 2, loss 0.000162
# epoch 3, loss 0.000051
- 检验
print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差: {true_b - b}')
# w的估计误差: tensor([-1.3804e-04, 5.7936e-05], grad_fn=<SubBackward0>)
# b的估计误差: tensor([0.0006], grad_fn=<RsubBackward1>)
api实现
- 生成数据 (与之前一样)
def synthetic_data(w, b, num_examples):
"""生成y=Xw+b+噪声"""
X = torch.normal(0, 1, (num_examples, len(w))) #正态分布
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)
- 读取数据 (用API DataoLoader)
# data_arrays = (X, y)
def load_array(data_arrays, batch_size, is_train=True):
"""构造一个PyTorch数据迭代器"""
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))) # 打印
- 自定义模型和 损失函数
# nn是神经网络的缩写
from torch import nn
# 第一个指定输入特征形状,即2,第二个指定输出特征形状,输出特征形状为单个标量,因此为1
net = nn.Sequential(nn.Linear(2, 1))
# 计算均方误差使用的是MSELoss类,也称为L2平方范数。 默认情况下,它返回所有样本损失的平均值。
loss = nn.MSELoss()
- 初始化参数
net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)
- 定义优化算法
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)
trainer.zero_grad()
l.backward()
trainer.step() # 用反向传播的结果 更新参数
l = loss(net(features), labels)
print(f'epoch {epoch + 1}, loss {l:f}')
- 检验
w = net[0].weight.data
print('w的估计误差:', true_w - w.reshape(true_w.shape))
b = net[0].bias.data
print('b的估计误差:', true_b - b)
Softmax 回归
自己实现
- 数据集准备
def load_data_fashion_mnist(batch_size, resize=None):
"""下载Fashion-MNIST数据集,然后将其加载到内存中"""
trans = [transforms.ToTensor()]
if resize:
trans.insert(0, transforms.Resize(resize))
trans = transforms.Compose(trans)
mnist_train = torchvision.datasets.FashionMNIST(
root="../data", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.FashionMNIST(
root="../data", train=False, transform=trans, download=True)
return (data.DataLoader(mnist_train, batch_size, shuffle=True,
num_workers=4),
data.DataLoader(mnist_test, batch_size, shuffle=False,
num_workers=4))
batch_size = 256
train_iter, test_iter = load_data_fashion_mnist(batch_size)
- 初始化模型参数
num_inputs = 784 # 图像=28*28*1 = 784
num_outputs = 10 # 输出10分类
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
b = torch.zeros(num_outputs, requires_grad=True)
- Softmax操作
def softmax(X):
X_exp = torch.exp(X)
partition = X_exp.sum(1, keepdim=True) # 列维度降维,对每一行求和
return X_exp / partition # 这里应用了广播机制
- 定义模型和损失函数
def net(X):
return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)
# 交叉损失
def cross_entropy(y_hat, y):
return - torch.log(y_hat[range(len(y_hat)), y])
- 分类精度
def accuracy(y_hat, y):
"""计算预测正确的数量"""
if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:
y_hat = y_hat.argmax(axis=1) # 找到每一行中 最大的列的索引
cmp = y_hat.type(y.dtype) == y
return float(cmp.type(y.dtype).sum())
# 在测试集上测试
def evaluate_accuracy(net, data_iter):
"""计算在指定数据集上模型的精度"""
if isinstance(net, torch.nn.Module):
net.eval() # 将模型设置为评估模式
metric = Accumulator(2) # 正确预测数、预测总数 # 保存中间变量
with torch.no_grad():
for X, y in data_iter:
metric.add(accuracy(net(X), y), y.numel())
return metric[0] / metric[1]
class Accumulator: #保存中间变量的类
"""在n个变量上累加"""
def __init__(self, n):
self.data = [0.0] * n
def add(self, *args):
self.data = [a + float(b) for a, b in zip(self.data, args)]
def reset(self):
self.data = [0.0] * len(self.data)
def __getitem__(self, idx):
return self.data[idx]
- 训练
def train_epoch_ch3(net, train_iter, loss, updater):
"""训练模型一个迭代周期(定义见第3章)"""
# 将模型设置为训练模式
if isinstance(net, torch.nn.Module):
net.train()
# 训练损失总和、训练准确度总和、样本数
metric = Accumulator(3)
for X, y in train_iter:
# 计算梯度并更新参数
y_hat = net(X)
l = loss(y_hat, y)
if isinstance(updater, torch.optim.Optimizer):
# 使用PyTorch内置的优化器和损失函数
updater.zero_grad()
l.mean().backward()
updater.step()
else:
# 使用定制的优化器和损失函数
l.sum().backward()
updater(X.shape[0])
metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
# 返回训练损失和训练精度
return metric[0] / metric[2], metric[1] / metric[2]
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater): #@save
"""训练模型(定义见第3章)"""
for epoch in range(num_epochs):
train_metrics = train_epoch_ch3(net, train_iter, loss, updater)
test_acc = evaluate_accuracy(net, test_iter)
lr = 0.1
def updater(batch_size):
return d2l.sgd([W, b], lr, batch_size)
num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater)
- 预测
def predict_ch3(net, test_iter, n=6):
"""预测标签(定义见第3章)"""
for X, y in test_iter:
break
trues = d2l.get_fashion_mnist_labels(y)
preds = d2l.get_fashion_mnist_labels(net(X).argmax(axis=1))
titles = [true +'\n' + pred for true, pred in zip(trues, preds)]
d2l.show_images(
X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n])
predict_ch3(net, test_iter)
api实现
- 数据集准备(和之前一样)
- 初始化模型参数
# PyTorch不会隐式地调整输入的形状。因此,
# 我们在线性层前定义了展平层(flatten),来调整网络输入的形状
# 原始数据[1,28,28] Linear的输入是一个确定的数,所以要先展开,
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))
def init_weights(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight, std=0.01)
net.apply(init_weights);
- 重新审视Softmax的实现
# 如果你设置reduction='mean',那么损失函数会返回批次中所有样本损失的平均值;
# 如果你设置reduction='sum',则返回所有样本损失的总和。
# none 每个样本的损失值
loss = nn.CrossEntropyLoss(reduction='none')
- 优化算法
trainer = torch.optim.SGD(net.parameters(), lr=0.1)
- 训练
num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
多层感知机
多层感知机
自己实现
# 1. 数据集
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
num_inputs, num_outputs, num_hiddens = 784, 10, 256
# 2. 初始化参数
W1 = nn.Parameter(torch.randn(
num_inputs, num_hiddens, requires_grad=True) * 0.01)
b1 = nn.Parameter(torch.zeros(num_hiddens, requires_grad=True))
W2 = nn.Parameter(torch.randn(
num_hiddens, num_outputs, requires_grad=True) * 0.01)
b2 = nn.Parameter(torch.zeros(num_outputs, requires_grad=True))
params = [W1, b1, W2, b2]
# 3. 激活函数
def relu(X):
a = torch.zeros_like(X)
return torch.max(X, a)
# 4. 模型
def net(X):
X = X.reshape((-1, num_inputs))
H = relu(X@W1 + b1) # 这里“@”代表矩阵乘法
return (H@W2 + b2)
# 5. 损失函数
loss = nn.CrossEntropyLoss(reduction='none')
# 6. 训练
num_epochs, lr = 10, 0.1
updater = torch.optim.SGD(params, lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, updater)
api实现
# 1. 模型
net = nn.Sequential(nn.Flatten(),
nn.Linear(784, 256),
nn.ReLU(),
nn.Linear(256, 10))
def init_weights(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight, std=0.01)
net.apply(init_weights)
# 2、 训练 下面是一样的,只有上面不一样
batch_size, lr, num_epochs = 256, 0.1, 10
loss = nn.CrossEntropyLoss(reduction='none')
trainer = torch.optim.SGD(net.parameters(), lr=lr)
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
深度学习计算
自定义块
- 普通方式
net = nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))
- 自定义方式
class MLP(nn.Module):
# 用模型参数声明层。这里,我们声明两个全连接的层
def __init__(self):
# 调用MLP的父类Module的构造函数来执行必要的初始化。
# 这样,在类实例化时也可以指定其他函数参数,例如模型参数params(稍后将介绍)
super().__init__()
self.hidden = nn.Linear(20, 256) # 隐藏层
self.out = nn.Linear(256, 10) # 输出层
# 定义模型的前向传播,即如何根据输入X返回所需的模型输出
# 反向传播自动计算微分
def forward(self, X):
# 注意,这里我们使用ReLU的函数版本,其在nn.functional模块中定义。
return self.out(F.relu(self.hidden(X)))
初始化参数
# 我们还可以对某些块应用不同的初始化方法。 例如,下面我们使用Xavier初始化方法初始化第一个神经网络层, 然后将第三个神经网络层初始化为常量值42。
def init_xavier(m):
if type(m) == nn.Linear:
nn.init.xavier_uniform_(m.weight)
def init_42(m):
if type(m) == nn.Linear:
nn.init.constant_(m.weight, 42)
net[0].apply(init_xavier)
net[2].apply(init_42)
print(net[0].weight.data[0])
print(net[2].weight.data)