动手学深度学习PyTorch版

本文介绍了在Python中使用概率理论进行实验模拟(如掷骰子实验),随机梯度下降的基本概念,以及正态分布和平方损失的应用。还详细展示了如何实现线性回归和Softmax回归,包括数据预处理、模型定义和训练过程,以及使用Fashion-MNIST数据集进行图像分类的例子。
摘要由CSDN通过智能技术生成

概率

# 用于处理日期和时间
%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具有均值\mu和方差\sigma ^{2}(标准差\sigma) ,其正态分布概率密度函数如下:

p(x)=\frac{1}{\sqrt{2\varpi \sigma ^{2}}}exp(-\frac{1}{2\sigma ^{2}}(x-\mu )^{2})

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()回归的输出层也是全连接层

交叉熵损失

l(y,\hat{y})=-\Sigma ^{q}_{j=1}y_{j}log\hat{y}_{j}

其中每个\hat{y}_{i}可以视为对给定任意输入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)

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值