概率
# 用于处理日期和时间
%pip install pytz
# 包含了许多用于数学、科学和工程的函数
%pip install scipy
# 在Jupyter笔记本中嵌入绘图
%matplotlib inline
import torch
# multinomial是一个用于生成多项式分布的类
from torch.distributions import multinomial
from d2l import torch as d2l
概率向量
# 输入的是概率向量
fair_probs = torch.ones([6]) / 6
# 输出是另一个相同长度的向量:它在索引i处的值是采样结果中i出现的次数
multinomial.Multinomial(1, fair_probs).sample()
# tensor([0., 1., 0., 0., 0., 0.])
multinomial.Multinomial(10, fair_probs).sample()
# tensor([2., 2., 1., 3., 1., 1.])
掷骰子实验
进行500组实验,每组抽取10个样本
counts = multinomial.Multinomial(10, fair_probs).sample((500, ))
print(counts)
cum_counts = counts.cumsum(dim=0)
print(cum_counts)
# total_count = cum_counts.sum()
# estimates = cum_counts / total_count
estimates = cum_counts / cum_counts.sum(dim=1, keepdims=True)
print(estimates)
# estimates = estimates.view(-1, 1)
d2l.set_figsize((6, 4.5))
for i in range(6):
d2l.plt.plot(estimates[:, i].numpy(),
label=("P(die=" + str(i + 1) + ")"))
d2l.plt.axhline(y=0.167, color='black', linestyle='dashed')
d2l.plt.gca().set_xlabel('Groups of experiments')
d2l.plt.gca().set_ylabel('Estimated probability')
d2l.plt.legend()
线性神经网络
随机梯度下降
梯度下降最简单的用法是计算损失函数(数据集中所有样本的损失均值)关于模型参数的导数(也可称为梯度),但是在每次更新参数之前,我们必须遍历整个数据集。因此,我们在每次需要计算更新的时候随机抽取一小批样本,这种变体叫做小批量随机梯度下降。
正态分布与平方损失
若随机变量x具有均值和方差
(标准差
) ,其正态分布概率密度函数如下:
import math
import numpy as np
from d2l import torch as d2l
# 计算正态分布
def normal(x, mu, sigma):
p = 1 / math.sqrt(2 * math.pi * sigma**2)
return p * np.exp(-0.5 / sigma**2 * (x - mu)**2)
# 可视化正态分布
# 再次使用numpy进行可视化
x = np.arange(-7, 7, 0.01)
# 均值和标准差对
params = [(0, 1), (0, 2), (3, 1)]
d2l.plot(x, [normal(x, mu, sigma) for mu, sigma in params], xlabel='x', ylabel='p(x)', figsize=(4.5, 2.5),
legend=[f'mean{mu}, std{sigma}' for mu, sigma in params])
线性回归从零开始实现
生成数据集
import torch
# 生成数据集
def synthetic_data(w, b, num_examples): #@save
# 生成噪声
# 生成一个正态分布(高斯分布)的张量,其中均值为0,标准差为1; 形状为 (num_examples, len(w)) 的张量 x
x = torch.normal(0, 1, (num_examples, len(w)))
# 矩阵乘法,表示将输入数据 x 与权重向量 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)
# features中的每一行都包含一个二维数据样本
print('features:', features[0], '\nlabel:',labels[0])
生成散点图
from d2l import torch as d2l
# 这个函数可以统一图表的大小
d2l.set_figsize()
# 从 features 张量中选取第二列的数据,与 labels 中的数据一起,绘制一个散点图
# detach() 方法用于从当前计算图中分离张量,并返回一个新的张量。我们就可以在不触发梯度计算的情况下访问它的数据
# 1表示每个点的大小
d2l.plt.scatter(features[:, 1].detach().numpy(), labels.detach().numpy(), 1);
读取数据集
我们需要定义一个data_iter函数,该函数接收批量大小、特征矩阵和标签向量作为输入,生成大小为batch_size的小批量。每个小批量包含一组特征和标签
import random
num_examples = features.shape[0]
# 读取数据集
# 定义一个函数,该函数接受批量大小、特征矩阵和标签向量作为输入,生成大小为batch_size的小批量。
# 每个小批量包含一组特征和标签
def data_iter(batch_size, features, labels):
# 创建一个包含从 0 到 num_examples - 1 的整数列表
indices = list(range(num_examples))
# 随机打乱 indices 列表中的元素顺序
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 关键字返回这些数据。yield 会暂停函数的执行,直到下一次迭代时再恢复,
yield features[batch_indices], labels[batch_indices]
batch_size = 10
for x, y in data_iter(batch_size, features, labels):
print(x, '\n', y)
break
初始化模型参数
在用小批量随机梯度下降优化模型参数之前,需要先有一些参数。我们通过从均值为0、标准差为0.01的正态分布中抽样随机数来初始化权重,并将偏置初始化为0
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
定义模型
def linreg(x, w, b): #@save
# 线性回归模型
return torch.matmul(x, w) + b
广播机制:当我们用一个向量加一个标量时,标量会被加到向量的每个分量上
定义损失函数
# 定义损失函数
def squared_loss(y_hat, y): #@save
# 均方损失
return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
定义优化算法
# 定义优化算法
def sgd(params, lr, batch_size): #@save
# 小批量梯度下降
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
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):
# x和y的小批量损失
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}')
线性回归的简洁实现
生成数据集
# 生成数据集
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)
读取数据集
# data_arrays(一个或多个张量数组,这些数组将用于构造数据集)
# batch_size(每个批次中样本的数量)
def load_array(data_arrays, batch_size, is_train=True): #@save
"构造一个PyTorch数据迭代器"
# 如果 data_arrays 是一个包含三个张量的元组,那么这些张量将作为三个单独的参数传递给 TensorDataset
# *data_arrays 表示将 data_arrays 中的所有元素作为单独的参数传递给 TensorDataset 的构造函数
dataset = data.TensorDataset(*data_arrays)
# 创建并返回了一个 DataLoader 对象,该对象用于在训练或评估过程中迭代数据集。
# DataLoader 提供了批处理、随机打乱和并行加载等功能
return data.DataLoader(dataset, batch_size, shuffle=is_train)
batch_size = 10
data_iter = load_array((features, labels), batch_size)
迭代器中获取第一项
# iter(data_iter): 这个调用会返回一个迭代器对象,该对象可以用于遍历 data_iter 中的元素。
# 在PyTorch的上下文中,DataLoader 会生成批次的数据,所以每次迭代都会返回一个批次的数据。
next(iter(data_iter))
定义模型
# 定义模型
from torch import nn
# 我们将两个参数传递到nn.Linear中,第一个参数指定输入特征形状,第二个参数指定输出特征形状
net = nn.Sequential(nn.Linear(2, 1))
# 初始化模型参数
# 每个权重参数应该从均值为0,标准差为0.01的正态分布中随机取样,偏置参数将初始化为0
net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)
# 定义损失函数
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)
trainer.zero_grad()
l.backward()
trainer.step()
l = loss(net(features), labels)
print(f'open{epoch + 1}, loss:{l:f}')
Softmax()回归
为了估计所有可能类别的条件概率,我们需要一个有多个输出的模型,每个类别对应一个输出。
Softmax()回归的输出层也是全连接层
交叉熵损失
其中每个可以视为对给定任意输入x的每个类的条件概率
图像分类数据集
%matplotlib inline
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
from d2l import torch as d2l
# 调用 d2l.use_svg_display() 函数时,它会设置 D2L 库中的图像显示函数,以便使用 SVG 格式来显示图像。
# 允许你无损地放大或缩小图像。
d2l.use_svg_display()
读取数据集
# 我们可以通过框架中的内置函数将Fashion-MNIST数据集下载并读取到内存中
# 通过ToTensor实例将图像数据从PIL类型变换为32位浮点数形式,除以255使得所有像素的数值均为0-1
trans = transforms.ToTensor()
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)
len(mnist_train), len(mnist_test)
# 数据集由灰度图像组成,其通道数为1
# (60000, 10000)
mnist_train[0][0].shape
创建函数可视化样本
# scale:可选参数,用于调整图像显示的大小
def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): #@save
# 绘制图像列表
figsize = (num_cols * scale, num_rows * scale)
# 使用 d2l.plt.subplots 创建一个具有指定行数和列数的子图网格,并将其存储在 axes 变量中
_, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
# 将 axes 从二维数组转换为一维数组,以便在循环中迭代
axes = axes.flatten()
for i, (ax, img) in enumerate(zip(axes, imgs)):
# 检查当前图像 img 是否为 PyTorch 张量
if torch.is_tensor(img):
# 图像张量
# 如果图像是 PyTorch 张量,则将其转换为 NumPy 数组并显示在当前的子图上
ax.imshow(img.numpy())
else:
# 如果图像不是 PyTorch 张量,则将其直接显示在当前的子图上
ax.imshow(img)
# 隐藏当前子图的 x 轴和y轴
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
if titles:
ax.set_title(titles[i])
# 返回包含所有子图的数组
return axes
from torch.utils.data import DataLoader
x, y = next(iter(data.DataLoader(mnist_train, batch_size=18)))
show_images(x.reshape(18, 28, 28), 2, 9, titles=get_fashion_mnist_labels(y))
Softmax的简洁实现
# 初始化模型参数
# PyTorch不会隐式地调整输入的形状
# 线性层前定义展平层来调整网络输入的形状
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))
# nn.Linear(784, 10): 这是一个全连接层(也称为线性层)。
# 它接受一个形状为(batch_size, 784)的输入,并输出一个形状为(batch_size, 10)的张量
def init_weights(m):
# 接受一个参数m,这个参数是一个神经网络模块
if type(m) == nn.Linear: # 是否是一个全连接层
# 如果m是一个全连接层,这行代码将其权重初始化为正态分布,标准差为0.01。
# 注意这里使用了_后缀,这意味着该操作会直接在模块m的权重上进行修改,而不是返回一个新的张量。
nn.init.normal_(m.weight, std=0.01)
# 遍历net中的所有模块,并对每一个全连接层(nn.Linear)执行权重初始化操作
net.apply(init_weights)