《动手学深度学习(Pytorch版)》Task03:线性神经网络——4.29打卡

《动手学深度学习(Pytorch版)》Task03:线性神经网络

  • 线性回归
    • 基本元素
      • 线性模型
      • 损失函数
      • 随机梯度下降
    • 正态分布与平方损失
  • 线性回归的从零开始实现
    • 读取数据集
    • 初始化模型参数
    • 定义模型
    • 定义损失函数
    • 定义优化算法
    • 训练
  • 线性回归的简洁实现
    • 读取数据集
    • 定义模型
    • 初始化模型参数
    • 定义损失函数
    • 定义优化算法
    • 训练
  • softmax回归
    • 分类问题
    • 网络架构
    • softmax运算
    • 损失函数
      • 信息量
      • 交叉熵
    • 模型预测和评估
  • 图像分类数据集
    • 读取小批量
  • softmax回归的从零开始实现
    • 读取数据
    • 初始化模型参数
    • 定义softmax操作
    • 定义模型
    • 定义损失函数
    • 分类精度
    • 训练
      • 一个迭代周期的训练模型
      • 整体训练模型
      • 设置损失函数
    • 预测
    • 训练过程
  • softmax回归的简洁实现
    • 初始化模型参数
    • 定义损失函数
    • 优化算法
    • 训练

线性回归

回归(regression):为一个或多个自变量与因变量之间关系建模的一类方法,表示输入和输出之间的关系。

预测(prediction)/推断(inference):给定特征估计目标的过程

经典回归问题:预测价格(房屋、股票等)、预测住院时间(针对住院病人等)、 预测需求(零售销量等)。

基本元素

以房屋预测为例:

任务:根据房屋的面积(平方英尺)和房龄(年)来估算房屋价格(美元)。

特征(feature)/协变量(covariate):预测所依据的自变量(面积和房龄)

训练数据集(training data set)/训练集(training set):真实的房屋销售价格、面积和房龄等数据集

样本(sample)/数据点(data point)/数据样本(data instance):每行数据(比如一次房屋交易相对应的数据)

线性模型

线性假设是指目标(房屋价格)可以表示为特征(面积和房龄)的加权和

p r i c e = w a r e a ⋅ a r e a + w a g e ⋅ a g e + b \mathrm{price} = w_{\mathrm{area}} \cdot \mathrm{area} + w_{\mathrm{age}} \cdot \mathrm{age} + b price=wareaarea+wageage+b

权重(weight):决定每个特征对预测值的影响,如: w a r e a w_{\mathrm{area}} warea w a g e w_{\mathrm{age}} wage

偏置(bias)/偏移量(offset)/截距(intercept):当所有特征都取值为0时,预测值应该为多少,如: b b b

仿射变换(affine transformation):通过加权和特征进行线性变换(linear transformation), 并通过偏置项来进行平移(translation)

预测结果:通常使用 y ^ \hat{y} y^表示 y y y的估计值
y ^ = w 1 x 1 + . . . + w d x d + b \hat{y} = w_1 x_1 + ... + w_d x_d + b y^=w1x1+...+wdxd+b
用点积形式表示:
y ^ = w ⊤ x + b \hat{y} = \mathbf{w}^\top \mathbf{x} + b y^=wx+b
用矩阵-向量乘法表示:
y ^ = X w + b {\hat{\mathbf{y}}} = \mathbf{X} \mathbf{w} + b y^=Xw+b
线性模型可以看作单层神经网络,权重是一层,输出是一层

损失函数

损失函数(loss function):量化目标的实际值与预测值之间的差距。

回归问题中最常用的损失函数是平方误差函数。公式定义为:
l ( i ) ( w , b ) = 1 2 ( y ^ ( i ) − y ( i ) ) 2 l^{(i)}(\mathbf{w}, b) = \frac{1}{2} \left(\hat{y}^{(i)} - y^{(i)}\right)^2 l(i)(w,b)=21(y^(i)y(i))2
计算在训练集𝑛个样本上的损失均值(也等价于求和)。
L ( w , b ) = 1 n ∑ i = 1 n l ( i ) ( w , b ) = 1 n ∑ i = 1 n 1 2 ( w ⊤ x ( i ) + b − y ( i ) ) 2 L(\mathbf{w}, b) =\frac{1}{n}\sum_{i=1}^n l^{(i)}(\mathbf{w}, b) =\frac{1}{n} \sum_{i=1}^n \frac{1}{2}\left(\mathbf{w}^\top \mathbf{x}^{(i)} + b - y^{(i)}\right)^2 L(w,b)=n1i=1nl(i)(w,b)=n1i=1n21(wx(i)+by(i))2
最小化损失
w ∗ , b ∗ = argmin ⁡ w , b   L ( w , b ) \mathbf{w}^*, b^* = \operatorname*{argmin}_{\mathbf{w}, b}\ L(\mathbf{w}, b) w,b=w,bargmin L(w,b)

随机梯度下降

梯度下降(gradient descent):最常用的损失函数求优化模型方法

小批量随机梯度下降(minibatch stochastic gradient descent):在每次需要计算更新的时候随机抽取一小批样本。

image-20240426154356784

image-20240426161229023

批量大小(batch size): b b b每个小批量中的样本数

学习率(learning rate): η \eta η ,每次走多长,步长的超参数

超参数(hyperparameter):可以调整但在训练过程中不更新的参数

调参(hyperparameter tuning):选择超参数的过程

泛化(generalization):找到一组参数,这组参数能够在我们从未见过的数据上实现较低的损失

正态分布与平方损失

正态分布(normal distribution)/高斯分布(Gaussian distribution):

若随机变量 x x x具有均值 μ \mu μ和方差 σ 2 \sigma^2 σ2(标准差 σ \sigma σ),其正态分布概率密度函数如下:

p ( x ) = 1 2 π σ 2 exp ⁡ ( − 1 2 σ 2 ( x − μ ) 2 ) p(x) = \frac{1}{\sqrt{2 \pi \sigma^2}} \exp\left(-\frac{1}{2 \sigma^2} (x - \mu)^2\right) p(x)=2πσ2 1exp(2σ21(xμ)2)

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)
  • 改变均值会产生沿𝑥轴的偏移
  • 增加方差将会分散分布、降低其峰值

**均方误差损失函数(简称均方损失)(MSE):**对于一组数据,模型预测的值与真实值之间的差异的平方和的平均值被定义为均方误差。

极大似然估计量(MLE):对于给定的数据集和一个概率分布,MLE寻找使得观测到的数据概率(即“似然”)最大化的参数值。

在高斯噪声的假设下,最小化均方误差等价于对线性模型的极大似然估计

  1. 最小化均方误差(MSE)

    • 在统计学和机器学习中,均方误差是衡量模型预测准确性的一种常用指标。对于一组数据,模型预测的值与真实值之间的差异的平方和的平均值被定义为均方误差。
    • 如果我们有一个线性模型 $ y = X\beta + \epsilon$ ,其中 ( y ) 是观测到的响应变量, X X X 是设计矩阵(包含了所有的预测变量), b e t a beta beta是模型参数, e p s i l o n epsilon epsilon是误差项。
    • 均方误差可以表示为
      MSE = 1 n ∑ i = 1 n ( y i − X i β ) 2 \text{MSE} = \frac{1}{n}\sum_{i=1}^{n} (y_i - X_i\beta)^2 MSE=n1i=1n(yiXiβ)2
      ,其中 ( n ) 是样本数,( $y_i KaTeX parse error: Can't use function '\)' in math mode at position 1: \̲)̲ 是第 \( i \) 个观测… X_i$ ) 是第 ( i ) 个观测值的预测变量,( β \beta β ) 是模型参数。
    • 最小化MSE即寻找最佳的模型参数 ( $\hat{\beta} $),使得 ( MSE \text{MSE} MSE ) 最小。
  2. 极大似然估计(MLE)

    • 极大似然估计是一种基于概率模型的参数估计方法。对于给定的数据集和一个概率分布,MLE寻找使得观测到的数据概率(即“似然”)最大化的参数值。
    • 假设观测误差 ( ϵ \epsilon ϵ ) 遵循一个以零为均值的高斯(或正态)分布,那么每一个观测值 ( y i y_i yi ) 也会遵循一个以 ( X i β X_i\beta Xiβ ) 为均值的高斯分布。
    • 高斯分布的似然函数可以写作
      L ( β ) = ∏ i = 1 n 1 2 π σ exp ⁡ ( − ( y i − X i β ) 2 2 σ 2 ) L(\beta) = \prod_{i=1}^{n} \frac{1}{\sqrt{2\pi}\sigma} \exp \left( -\frac{(y_i - X_i\beta)^2}{2\sigma^2} \right) L(β)=i=1n2π σ1exp(2σ2(yiXiβ)2)
      ,其中 ( σ \sigma σ) 是高斯噪声的标准差。
    • 极大似然估计的目标是找到参数 ( β \beta β ),以最大化似然函数 ( $L(\beta) $)。
  3. 等价性

    • 当我们取似然函数 ( L ( β ) L(\beta) L(β) ) 的对数(称为对数似然函数),并对其进行最大化,我们实际上是在最小化 ( y i − X i β ) 2 (y_i - X_i\beta)^2 (yiXiβ)2 的负和,因为对数函数是单调递增的,而负号是因为对数似然通常有一个负的指数项。
    • 对数似然函数的最大化与最小化均方误差在数学上是等价的,因为这两个优化问题都会导致同样的最优参数 β ^ \hat{\beta} β^

因此,在高斯噪声的假设下,通过最小化均方误差来求解线性模型的参数等价于对模型参数进行极大似然估计,即两种方法都会得到相同的参数估计结果。这表明在某些条件下,频率学派的估计方法(如最小二乘法)和贝叶斯学派的估计方法(如极大似然估计)是可以互相转换的

线性回归的从零开始实现

数据流水线、模型、损失函数和小批量随机梯度下降优化器

读取数据集

训练模型:

  • 对数据集进行遍历
  • 每次抽取一小批量样本
  • 使用它们来更新我们的模型

data_iter函数: 生成大小为batch_size的小批量

# input: 批量大小、特征矩阵、标签向量
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]

初始化模型参数

通过从均值为0、标准差为0.01的正态分布中采样随机数来初始化权重, 并将偏置初始化为0。

w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)

初始化参数之后,我们的任务是更新这些参数,直到这些参数足够拟合我们的数据。

采用梯度下降法更新参数,并使用pytorch 自动微分

定义模型

将模型的输入和参数同模型的输出关联起来。

# 输入特征X、模型权重w、偏置b
def linreg(X, w, b):  #@save
    """线性回归模型"""
    return torch.matmul(X, w) + b

torch.matmul :tensor的乘法

定义损失函数

定义平方损失函数作为损失函数

# input: 预测值、真实值
def squared_loss(y_hat, y):  #@save
    """均方损失"""
    return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2

定义优化算法

小批量随机梯度下降

  • 在每一步中,使用从数据集中随机抽取的一个小批量,然后根据参数计算损失的梯度。
  • 朝着减少损失的方向更新我们的参数。
# input: 参数集合、学习速率和批量大小
def sgd(params, lr, batch_size):  #@save
    """小批量随机梯度下降"""
    with torch.no_grad():  # 暂时设置选定的代码块中操作不会跟踪梯度
        for param in params:
            param -= lr * param.grad / batch_size
            # pytorch不会设为0,需要手动
            param.grad.zero_()

训练

训练过程: 正向传播->计算损失->反向传播->参数更新

  1. 在每次迭代(epoch)中,我们读取一小批量训练样本,并通过我们的模型来获得一组预测(正向传播)。
  2. 计算完损失后,我们开始反向传播[计算每个参数(权重和偏差)的偏导数(梯度)],存储每个参数的梯度。
  3. 调用优化算法sgd来更新模型参数。

参数设置

#学习率
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}')

真实参数和通过训练学到的参数确实非常接近

深度网络中存在许多参数组合能够实现高度精确的预测。

习题参考

线性回归的简洁实现

通过使用深度学习框架来简洁地实现线性回归模型

读取数据集

调用API读取数据

data_arrays :包括特征矩阵和对应的标签数组

batch_size:定义每个批次包含的样本数

is_train:是否希望数据迭代器对象在每个迭代周期内打乱数据

# input:
def load_array(data_arrays, batch_size, is_train=True):  #@save
    """构造一个PyTorch数据迭代器"""
    dataset = data.TensorDataset(*data_arrays)
    return data.DataLoader(dataset, batch_size, shuffle=is_train)
  • data.TensorDataset 是一个 PyTorch 类,用于封装数据和目标张量。通过提供的 data_arrays(一个包含特征和标签的元组或列表),它创建了一个数据集,其中每个元素是一个特征和标签对。
  • *data_arrays 使用星号(*)操作符,这意味着如果 data_arrays 是一个元组或列表,则它会被解包成多个独立的参数传递给 TensorDataset
  • data.DataLoader 是一个迭代器,用于加载 PyTorch 数据集。它提供了一个简便的方式来自动批处理数据,并且根据需要进行数据的洗牌和多线程加载。
  • shuffle=is_train 根据 is_train 的值决定是否在每个 epoch 开始时打乱数据。这是训练时常用的做法,因为它有助于模型泛化,避免因数据顺序引入的偏差。

使用iter构造Python迭代器

batch_size = 10
data_iter = load_array((features, labels), batch_size)

使用next从迭代器中获取第一项

next(iter(data_iter))

定义模型

使用框架的预定义好的层:只关注哪些层构造模型,而不关注实现细节。

# nn是神经网络的缩写
from torch import nn
net = nn.Sequential(nn.Linear(2, 1))
  • 定义一个模型变量net,它是一个Sequential类的实例。
  • Sequential类是一个容器,将多个层串联在一起,形成一个神经网络模型。
  • nn.Linear(2, 1)是一个线性层,它将输入的维度从 2 维降到 1 维。
    • 第一个参数 2 是输入特征的维度,表示输入数据是一个二维向量(或者说包含两个特征)。
    • 第二个参数 1 是输出特征的维度,表示线性层将输入的特征映射到一个一维向量上。

初始化模型参数

使用预定义方法来初始化参数

通过net[0]选择网络中的第一个图层

使用weight.databias.data方法访问参数

使用替换方法normal_fill_来重写参数值

net[0].weight.data.normal_(0, 0.01)
net[0].bias.data.fill_(0)
  • net[0].weight.data.normal_(0, 0.01)每个权重参数从均值为0、标准差为0.01的正态分布中随机采样
  • net[0].bias.data.fill_(0) 偏置参数将初始化为零。

定义损失函数

计算均方误差使用的是MSELoss类,也称为平方 L 2 L_2 L2范数

loss = nn.MSELoss()

定义优化算法

小批量随机梯度下降算法

trainer = torch.optim.SGD(net.parameters(), lr=0.03)
  • torch.optim 是 PyTorch 中的优化器模块,提供了各种优化算法的实现。
  • SGD 是随机梯度下降(Stochastic Gradient Descent)的缩写,是一种常用的优化算法之一,用于更新神经网络的参数。
  • net.parameters() 是一个生成器,用于指定获取神经网络模型 net 中的所有可学习参数。
  • lr=0.03 是学习率(learning rate)的设置,表示每次更新参数时的步长大小。

训练

num_epochs = 3 # 定义迭代周期,训练轮数为 3 轮。
for epoch in range(num_epochs):
    for X, y in data_iter:
        l = loss(net(X) ,y) # 调用net(X)生成预测,并且计算预测值与真实标签之间的损失(前向传播)
        trainer.zero_grad() #清零之前保存在优化器中的梯度,以便进行新一轮的梯度计算。
        l.backward() # 反向传播计算梯度
        trainer.step() # 调用优化器,根据梯度更新模型的参数
    l = loss(net(features), labels)# 计算整个训练集上模型的损失值
    print(f'epoch {epoch + 1}, loss {l:f}')

真实参数和训练获得的模型参数非常接近

Q:如果将小批量的总损失替换为小批量损失的平均值,需要如何更改学习率?

A:将学习率缩小为之前的1/n

Q:用Huber损失代替原损失

# huber损失对应Pytorch的SmoothL1损失
loss = nn.SmoothL1Loss(beta=0.5)

Q:如何访问线性回归的梯度?

net[0].weight.grad, net[0].bias.grad

softmax回归

分类问题:

  • 硬分类:属于哪个类别
  • 软分类:属于每个类别的概率

分类问题

图像分类问题:

假设每次输入是一个 2 × 2 2\times2 2×2的灰度图像。
我们可以用一个标量表示每个像素值,每个图像对应四个特征 x 1 , x 2 , x 3 , x 4 x_1, x_2, x_3, x_4 x1,x2,x3,x4
此外,假设每个图像属于类别“猫”“鸡”和“狗”中的一个。

独热编码(one-hot encoding):一个向量,它的分量和类别一样多。 类别对应的分量设置为1,其他所有分量设置为0。

利用one-hot encoding,设置标签 y y y是一个三维向量,
其中 ( 1 , 0 , 0 ) (1, 0, 0) (1,0,0)对应于“猫”、 ( 0 , 1 , 0 ) (0, 1, 0) (0,1,0)对应于“鸡”、 ( 0 , 0 , 1 ) (0, 0, 1) (0,0,1)对应于“狗”:

y ∈ { ( 1 , 0 , 0 ) , ( 0 , 1 , 0 ) , ( 0 , 0 , 1 ) } . y \in \{(1, 0, 0), (0, 1, 0), (0, 0, 1)\}. y{(1,0,0),(0,1,0),(0,0,1)}.

网络架构

建立一个有多个输出的模型,每个类别对应一个输出。

我们有4个特征和3个可能的输出类别

image-20240429170620235

向量形式表达为 o = W x + b \mathbf{o} = \mathbf{W} \mathbf{x} + \mathbf{b} o=Wx+b

softmax运算

校准(calibration):训练一个目标函数,来激励模型精准地估计概率。

softmax函数:能够将未规范化的预测变换为非负数并且总和为1,同时让模型保持可导的性质。
y ^ = s o f t m a x ( o ) 其中 y ^ j = exp ⁡ ( o j ) ∑ k exp ⁡ ( o k ) \hat{\mathbf{y}} = \mathrm{softmax}(\mathbf{o})\quad \text{其中}\quad \hat{y}_j = \frac{\exp(o_j)}{\sum_k \exp(o_k)} y^=softmax(o)其中y^j=kexp(ok)exp(oj)
softmax回归是一个线性模型(linear model):softmax回归的输出仍然由输入特征的仿射变换决定。

最大值为预测结果:
y ^ = argmax ⁡ i o i \hat y = \operatorname*{argmax}_i o_i y^=iargmaxoi
概率 y y y y ^ \hat{y} y^的区别作为损失

softmax回归的矢量计算表达式:
O = X W + b , Y ^ = s o f t m a x ( O ) . \begin{aligned} \mathbf{O} &= \mathbf{X} \mathbf{W} + \mathbf{b}, \\ \hat{\mathbf{Y}} & = \mathrm{softmax}(\mathbf{O}). \end{aligned} OY^=XW+b,=softmax(O).

损失函数

交叉熵损失(cross-entropy loss):所有标签分布的预期损失值。

信息论的核心思想是量化数据中的信息内容。

这个量化的数据称作熵
H [ P ] = ∑ j − P ( j ) log ⁡ P ( j ) H[P] = \sum_j - P(j) \log P(j) H[P]=jP(j)logP(j)

信息量

如果我们很容易预测下一个数据,那么这个数据就很容易压缩。

如果我们不能完全预测每一个事件,会感到惊异在观察一个事件 j j j时,并赋予它(主观)概率 P ( j ) P(j) P(j),事件的 P ( j ) P(j) P(j)较低时,越惊异,该事件的信息量也就更大。
log ⁡ 1 P ( j ) = − log ⁡ P ( j ) \log \frac{1}{P(j)} = -\log P(j) logP(j)1=logP(j)
熵是当分配的概率真正匹配数据生成过程时的信息量的期望

交叉熵

交叉熵可以抽象理解为:主观概率为𝑄的观察者在看到根据概率𝑃生成的数据时的预期惊异

交叉熵 P P P Q Q Q,记为 H ( P , Q ) H(P, Q) H(P,Q)
H ( P , Q ) = ∑ i − P ( i ) log ⁡ Q ( i ) H(P, Q)= \sum_i - P(i) \log Q(i) H(P,Q)=iP(i)logQ(i)
构建损失函数:
l ( y , y ^ ) = − ∑ j = 1 q y j log ⁡ y ^ j l(\mathbf{y}, \hat{\mathbf{y}}) = - \sum_{j=1}^q y_j \log \hat{y}_j l(y,y^)=j=1qyjlogy^j
不关心非正确的预测值,只关心正确的预测值的置信度有多大,对真实类别的值求log然后求负数

其梯度是真实概率和预测概率的区别:
∂ o j l ( y , y ^ ) = s o f t m a x ( o ) j − y j \partial_{o_j} l(\mathbf{y}, \hat{\mathbf{y}}) = \mathrm{softmax}(\mathbf{o})_j - y_j ojl(y,y^)=softmax(o)jyj

模型预测和评估

精度(accuracy):正确预测数/预测总数

图像分类数据集

Fashion-MNIST是一个服装分类数据集,由10个类别的图像组成,分别为t-shirt(T恤)、trouser(裤子)、pullover(套衫)、dress(连衣裙)、coat(外套)、sandal(凉鞋)、shirt(衬衫)、sneaker(运动鞋)、bag(包)和ankle boot(短靴)。

每个类别由训练数据集(train dataset)中的6000张图像 和测试数据集(test dataset)中的1000张图像组成。 因此,训练集和测试集分别包含60000和10000张图像。

每个输入图像的高度和宽度均为28像素。 数据集由灰度图像组成,其通道数为1。简洁表示为高度 h h h像素,宽度 w w w像素,记为 h × w h \times w h×w或( h h h, w w w)。

下载数据集

def load_data_fashion_mnist(batch_size, resize=None):  #@save
    """下载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=get_dataloader_workers()),
            data.DataLoader(mnist_test, batch_size, shuffle=False,
                            num_workers=get_dataloader_workers()))
  • transforms.ToTensor()通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式,将图像数据转换为 Tensor 类型,并且进行归一化处理,以便于输入到神经网络中进行训练或推理。

报错:RuntimeError: Error downloading train-images-idx3-ubyte.gz

image-20240429190712051

原因:没有安装最新版本的torchvision

pip install torchvision --upgrade

安装完就可以正常下载了

读取小批量

  • 使用内置的数据迭代器读取数据
  • 每次都会读取一小批量数据,大小为batch_size
  • 随机打乱所有样本
batch_size = 256

def get_dataloader_workers():  #@save
    """使用4个进程来读取数据"""
    return 4

train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True,
                             num_workers=get_dataloader_workers())

softmax回归的从零开始实现

读取数据

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

初始化模型参数

原始数据集中的每个样本都是28×28的图像,将他们展平,得到输入为784维的向量

10个类别输出维度为10

权重784×10的矩阵

偏置1×10的行向量

num_inputs = 784
num_outputs = 10

W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True) # 正态分布初始化
b = torch.zeros(num_outputs, requires_grad=True)

定义softmax操作

X = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
X.sum(0, keepdim=True), X.sum(1, keepdim=True) 
  • keepdim=True保持原始张量的轴数

实现softmax步骤:

  1. 对每个项求幂(使用exp);
  2. 对每一行求和(小批量中每个样本是一行),得到每个样本的规范化常数;
  3. 将每一行除以其规范化常数,确保结果的和为1。
def softmax(X):
    X_exp = torch.exp(X)
    partition = X_exp.sum(1, keepdim=True)
    return X_exp / partition  # 这里应用了广播机制

PS:

在一个二维张量中,axis=0 表示沿着行的方向进行操作,而 axis=1 表示沿着列的方向进行操作。

所以,指定 axis=0 表示对每一进行求和,而指定 axis=1 表示对每一进行求和。

定义模型

softmax回归模型

def net(X):
    return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)
  • torch.matmul(X.reshape((-1, W.shape[0])), W)操作将图像展平为向量

定义损失函数

交叉熵损失函数

交叉熵采用真实标签的预测概率的负对数似然。

def cross_entropy(y_hat, y):
    return - torch.log(y_hat[range(len(y_hat)), y])

分类精度

分类精度:正确预测数量与总预测数量之比。

def accuracy(y_hat, y):  #@save
    """计算预测正确的数量"""
    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())

精确率:

accuracy(y_hat, y) / len(y)

评估在指定数据集上任意模型net的精度

# input: 神经网络模型、数据迭代器
def evaluate_accuracy(net, data_iter):  #@save
    """计算在指定数据集上模型的精度"""
    if isinstance(net, torch.nn.Module): #检查 net 是否为 PyTorch 的神经网络模型
        net.eval()  # 将模型设置为评估模式
    metric = Accumulator(2)  # 存储正确预测数、预测总数
    with torch.no_grad(): #设置不需要梯度计算的上下文环境
        for X, y in data_iter:
            metric.add(accuracy(net(X), y), y.numel()) #计算每个批次的准确率,并将正确预测数和预测总数累加到 metric 中。
    return metric[0] / metric[1] #整个数据集上的精度,即所有正确预测的样本数除以总样本数。

定义实用程序类Accumulator,用于对多个变量进行累加。

Accumulator实例中创建了2个变量, 分别用于存储正确预测的数量和预测的总数量

class Accumulator:  #@save
    """在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]

训练

定义画图函数Animator

class Animator:  #@save
    """在动画中绘制数据"""
    def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,
                 ylim=None, xscale='linear', yscale='linear',
                 fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,
                 figsize=(3.5, 2.5)):
        # 增量地绘制多条线
        if legend is None:
            legend = []
        d2l.use_svg_display()
        self.fig, self.axes = d2l.plt.subplots(nrows, ncols, figsize=figsize)
        if nrows * ncols == 1:
            self.axes = [self.axes, ]
        # 使用lambda函数捕获参数
        self.config_axes = lambda: d2l.set_axes(
            self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
        self.X, self.Y, self.fmts = None, None, fmts

    def add(self, x, y):
        # 向图表中添加多个数据点
        if not hasattr(y, "__len__"):
            y = [y]
        n = len(y)
        if not hasattr(x, "__len__"):
            x = [x] * n
        if not self.X:
            self.X = [[] for _ in range(n)]
        if not self.Y:
            self.Y = [[] for _ in range(n)]
        for i, (a, b) in enumerate(zip(x, y)):
            if a is not None and b is not None:
                self.X[i].append(a)
                self.Y[i].append(b)
        self.axes[0].cla()
        for x, y, fmt in zip(self.X, self.Y, self.fmts):
            self.axes[0].plot(x, y, fmt)
        self.config_axes()
        display.display(self.fig)
        display.clear_output(wait=True)

一个迭代周期的训练模型

def train_epoch_ch3(net, train_iter, loss, updater):  #@save
    """训练模型一个迭代周期(定义见第3章)"""
    # 将模型设置为训练模式
    if isinstance(net, torch.nn.Module): #检查 net 是否为 PyTorch 的神经网络模型
        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): # 检查 updater 对象是否是一个有效的优化器对象
            # 使用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]
  • isinstance() 函数用于检查一个对象是否是指定类的实例。
  • updater是更新模型参数的常用函数,它接受批量大小作为参数。 它可以是d2l.sgd函数,也可以是框架的内置优化函数。

整体训练模型

def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):  #@save
    """训练模型(定义见第3章)"""
    animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],
                        legend=['train loss', 'train acc', 'test acc']) #可视化性能指标
    for epoch in range(num_epochs):
        train_metrics = train_epoch_ch3(net, train_iter, loss, updater) # 训练度量指标:训练损失和训练精度
        test_acc = evaluate_accuracy(net, test_iter) # 计算训练精确率
        animator.add(epoch + 1, train_metrics + (test_acc,)) # 动态更新图表
    train_loss, train_acc = train_metrics
    assert train_loss < 0.5, train_loss
    assert train_acc <= 1 and train_acc > 0.7, train_acc
    assert test_acc <= 1 and test_acc > 0.7, test_acc
  • 在Python中,assert 语句用作一种调试辅助工具,它会检查指定的条件表达式。如果条件表达式结果为 True,程序会继续执行;如果条件表达式结果为 False,则会引发一个 AssertionError 异常。assert 语句通常用于确保程序在某个特定状态下运行,或者验证某些假设条件是否为真。
  • 使用 assert 语句进行条件断言,确保训练损失小于0.5,训练准确度和测试准确度都应该大于0.7且不超过1。这些断言是为了验证训练过程是否达到预期的质量标准。

设置损失函数

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)

image-20240429205650707

预测

对图像进行分类预测

#在测试集预测少量数据
def predict_ch3(net, test_iter, n=6):  #@save
    """预测标签(定义见第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)
  • get_fashion_mnist_labels 函数(通常定义在辅助库 d2l 中)将标签索引转换为人类可读的标签名。
  • net(X) 计算每个图像的预测结果
  • .argmax(axis=1) 找出每个预测结果中概率最高的类别的索引
  • d2l.show_images(X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n]):这个函数调用显示前 n 个图像。图像数据 X[0:n] 被重塑成 (n, 28, 28) 的格式,因为原始 Fashion-MNIST 图像是28x28像素的灰度图。参数 1n 指定每行显示1个图像,总共显示 n 个图像。titles[0:n] 为每个图像提供一个标题。

输出:实际标签(文本输出的第一行)模型预测(文本输出的第二行)

image-20240429210043266

训练过程

  • 读取数据
  • 定义模型和损失函数
  • 使用优化算法训练模型

softmax回归的简洁实现

初始化模型参数

# PyTorch不会隐式地调整输入的形状。因此,
# 我们在线性层前定义了展平层(flatten),来调整网络输入的形状
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10)) #将输入数据展平,定义输入784维、输出10维的线性层

def init_weights(m): # 如果是线性层
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01) #使用正态分布初始化线性层 m 的权重

net.apply(init_weights);
  • nn.Flatten(): 这是一个用于将输入数据展平的层。在这个例子中,输入数据的形状通常是 (batch_size, 1, 28, 28),表示一个批次中包含的样本数、通道数(灰度图像为 1)、图像的高度和宽度。
  • nn.init.normal_ 是 PyTorch 提供的一个函数,用于对张量进行正态分布初始化。接受两个参数:
    • m.weight 表示线性层的权重
    • std=0.01 表示正态分布的标准差为 0.01。

定义损失函数

loss = nn.CrossEntropyLoss(reduction='none')

reduction='none' :指定损失的计算方式,表示不进行降维操作

优化算法

使用学习率为0.1小批量随机梯度下降作为优化算法

trainer = torch.optim.SGD(net.parameters(), lr=0.1)

训练

num_epochs = 10
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

image-20240429211904554

Q:为什么测试精度会在一段时间后降低?

A:模型过拟合,可以加入正则化dropout

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/583876.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c#创建新项目

确保已安装.NET Core SDK。&#xff08;visual studio installer中可安装&#xff09; cmd中先引用到文件夹目录下。 mkdir MyConsoleApp MyConsoleApp是项目文件夹的名字。 mkdir 是一个命令行工具&#xff0c;用于在文件系统中创建新的目录&#xff08;文件夹&#xff09;…

用OpenCV先去除边框线,以提升OCR准确率

在OpenCV的魔力下&#xff0c;我们如魔法师般巧妙地抹去表格的边框线&#xff0c;让文字如诗如画地跃然纸上。 首先&#xff0c;我们挥动魔杖&#xff0c;将五彩斑斓的图像转化为单一的灰度世界&#xff0c;如同将一幅绚丽的油画化为水墨画&#xff0c;通过cv2.cvtColor()函数的…

主机ping不通虚拟机/虚拟机ping不通主机/xhell连接不了虚拟机/win10或win11系统升级导致无法连接到虚拟机

解决方案 重置网卡 找虚拟机ip&#xff0c;第二个inet对应的就是虚拟机ip地址 xshell连接 参考: 主机ping不通虚拟机

认识认识DHCP

文章目录 认识认识DHCP一、什么是DHCP&#xff1f;1.1、为什么要使用DHCP&#xff1f;1.2、DHCP是怎么工作的&#xff1f;1.2.1、客户端首次接入网络的工作原理1.2.2、客户端重用曾经使用过的地址的工作原理1.2.3、客户端更新租期的工作原理 二、配置DHCP Server&#xff0c;为…

电子式汽车机油压力传感器的接线方法及特点

电子式机油压力传感器由厚膜压力传感器芯片、信号处理电路、外壳、固定电路板装置和两根引线&#xff08;信号线和报警线&#xff09;组成。信号处理电路由电源电路、传感器补偿电路、调零电路、电压放大电路、电流放大电路、滤波电路和报警电路组成。 厚膜压力传感器是20世纪…

【UE5】动态播放媒体

最近项目中有一个需求&#xff0c;需要将场景中的42块屏幕都显示媒体内容&#xff0c;想着如果每一块屏幕都创建一个MediaPlayer资产、一个MediaSource资产、一个MediaTexture资产及创建对应的Material&#xff0c;就是4*42168个资产需要维护了&#xff0c;所以想着就全部采用动…

如何看待Agent AI智能体的未来

Agent AI智能体的未来 Agent AI智能体&#xff0c;也称为自主代理或智能代理&#xff0c;是指能够自主执行任务、与环境交互并作出决策的计算机程序或系统。这些智能体通常具备学习、适应和推理的能力&#xff0c;能够在复杂和不确定的环境中执行任务。随着技术的进步&#xf…

【OC和红移的双面材质】

OC和红移的双面材质 2021-12-23 18:36 rs oc 评论(0)

TiDB 利用binlog 恢复-反解析binlog

我们知道TiDB的binlog记录了所有已经执行成功的dml语句&#xff0c;类似mysql binlog row模式 &#xff0c;TiDB官方也提供了reparo可以进行解析binlog&#xff0c;如下所示: [2024/04/26 20:58:02.136 08:00] [INFO] [config.go:153] ["Parsed start TSO"] [ts449…

Linux网络抓包工具tcpdump是如何实现抓包的,在哪个位置抓包的?

Linux网络抓包工具tcpdump是如何实现抓包的&#xff0c;在哪个位置抓包的&#xff1f; 1. tcpdump抓包架构2. BPF介绍3. 从内核层面看tcpdump抓包流程3.1. 创建socket套接字3.2. 挂载BPF程序 4. 网络收包抓取5. 网络发包抓取6. 疑问和思考6.1 tcpdump抓包跟网卡、内核之间的顺序…

golang beego结合wire依赖注入及自动路由

1 安装wire 1.1 通过命令直接安装 go install github.com/google/wire/cmd/wirelatest 1.2 通过go get方式安装 go get github.com/google/wire/cmd/wire进入目录编译 cd C:\Users\leell\go\pkg\mod\github.com\google\wirev0.6.0\cmd\wire go build 然后将wire.exe移动到…

C++中list的使用

文章目录 一、 list简介二、 构造函数1. 默认构造函数2. 拷贝构造3. 迭代器区间初始化4. 插入n个值为x的数据5. 代码示例 三、 容量和元素访问1. empty()2. size()3. max_size()3. back()4. front()5. 代码示例 四、 增删查改1. push_back()2. push_front()3. emplace_back()4.…

设计模式之装饰者模式DecoratorPattern(四)

一、模板模式介绍 模板方法模式&#xff08;Template Method Pattern&#xff09;&#xff0c;又叫模板模式&#xff08;Template Pattern&#xff09;&#xff0c; 在一个抽象类公开定义了执行它的方法的模板。它的子类可以更需要重写方法实现&#xff0c;但可以成为典型类中…

d16(149-153)-勇敢开始Java,咖啡拯救人生

跳过了p151 四小时的讲题我不敢听&#xff1a;) Stream Stream流&#xff0c;是JDK8后新增的API&#xff0c;可以用于操作集合或者数组的数据 优势&#xff1a;大量结合了Lambda的语法风格&#xff0c;该方式更强大更简单&#xff0c;代码简洁&#xff0c;可读性好 常用方法 …

Mycat(三)读写分离双主双从

文章目录 搭建双主双从双主机配置双从机配置双从配置两个主机互相复制停止从服务复制功能重新配置主从 修改 Mycat 的集群配置实现多种主从双主双从集群角色划分增加两个数据源修改集群配置文件读写分离配置扩展&#xff08;1&#xff09;读写分离(一主一从,无备)(m是主,s是从)…

Grafana页面嵌入自建Web应用页面

目录 一、应用场景 二、实现方式 1、修改Grafana配置文件 2、获取监控页面url 3、隐藏左侧和顶部菜单 一、应用场景 需要将Grafana监控页面嵌入自建Web应用页面&#xff0c;使Grafana监控页面成为自建Web应用的一部分。 二、实现方式 总体思路&#xff1a;修改Grafana配…

刷机维修进阶教程-----红米note7 修复基带 更改参数 nv损坏故障 实例步骤操作解析

在前面的博文中我有说过。不管刷更改参数还是修复基带,尽可能的情况下备份一些主要分区,上期讲了小米6x 小米5 小米6这些机型更改参数的具体步骤。今天的教程以红米note7为例解析下改参数和修复nv损坏的具体步骤,两者操作实际没有什么冲突。有兴趣的友友建议多看下我关于…

10G MAC层设计系列-(2)MAC RX模块

一、概述 MAC RX模块的需要进行解码、对齐、CRC校验。 因为在空闲的时候10G PCS/PMA会一直向外吐空闲符&#xff08;x07&#xff09;所以需要根据开始符、结束符将有效数据从码流中截取&#xff0c;也就是解码。 因为开始字符的所在位置有两种形式&#xff0c;而结束字符的位…

大数据学习笔记14-Hive基础2

一、数据字段类型 数据类型 &#xff1a;LanguageManual Types - Apache Hive - Apache Software Foundation 基本数据类型 数值相关类型 整数 tinyint smallint int bigint 小数 float double decimal 精度最高 日期类型 date 日期 timestamps 日期时间 字符串类型 s…

UE C++ 链表

目录 概要单链表双向链表头插入尾插入中间插入删除查找 小结 概要 链表 简单说明&#xff0c;链表有单链表&#xff0c;双向链表&#xff0c;循环链表(本篇文章以UE c代码说明)。链表的操作&#xff0c;插入&#xff0c;删除&#xff0c;查找。插入&#xff0c;删除效率高&…