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

news2024/11/25 0:28:27

《动手学深度学习(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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Find My无人机|苹果Find My技术与无人机结合,智能防丢,全球定位

无人机是利用无线电遥控设备和自备的程序控制装置操纵的不载人飞机&#xff0c;或者由车载计算机完全地或间歇地自主地操作。无人机按应用领域&#xff0c;可分为军用与民用。军用方面&#xff0c;无人机分为侦察机和靶机。民用方面&#xff0c;无人机行业应用&#xff0c;是无…

【介绍下Selenium】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

机器学习:深入解析SVM的核心概念(问题与解答篇)【二、对偶问题】

对偶问题 **问题一&#xff1a;什么叫做凸二次优化问题&#xff1f;而且为什么符合凸二次优化问题&#xff1f;**为什么约束条件也是凸的半空间&#xff08;Half-Space&#xff09;凸集&#xff08;Convex Set&#xff09;半空间是凸集的例子SVM 约束定义的半空间总结 **问题二…

Web 服务器解析漏洞 原理以及修复方法

漏洞名称 &#xff1a;Web服务器解析漏洞 漏洞描述&#xff1a; 服务器相关中间件存在一些解析漏洞&#xff0c;攻击者可通过上传一定格式的文件&#xff0c;被服务器的中间件进行了解析&#xff0c;这样就对系统造成一定危害。常见的服务器解析漏洞涉及的中间件有IIS&#x…

踏上R语言之旅:解锁数据世界的神秘密码(四)

文章目录 前言一、多元线性回归1.多元线性回归模型的建立2.多元线性回归模型的检验 二、多元线性相关分析1.矩阵相关分析2.复相关分析 三、回归变量的选择方法1.变量选择准则2.变量选择的常用准则3.逐步回归分析 总结 前言 回归分析研究的主要对象是客观事物变量间的统计关系。…

操作系统安全:安全审计,Windows系统日志详解,Windows事件ID汇总

「作者简介」&#xff1a;2022年北京冬奥会网络安全中国代表队&#xff0c;CSDN Top100&#xff0c;就职奇安信多年&#xff0c;以实战工作为基础对安全知识体系进行总结与归纳&#xff0c;著作适用于快速入门的 《网络安全自学教程》&#xff0c;内容涵盖系统安全、信息收集等…

使用OkHttp 缓存 API 调用提高Android应用性能

使用OkHttp 缓存 API 调用提高Android应用性能 坦率地说&#xff0c;我们都遇到过这样的情况——焦急地刷新应用&#xff0c;看着加载图标不停地旋转&#xff0c;等待那个至关重要的 API 响应。这样的等待我们已经是炉火纯青了&#xff0c;是吧&#xff1f;手指有节奏地轻敲屏…

【Node.js工程师养成计划】之express框架

一、Express 官网&#xff1a;http://www.expressjs.com.cn express 是一个基于内置核心 http 模块的&#xff0c;一个第三方的包&#xff0c;专注于 web 服务器的构建。 Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用&…

LabVIEW机械臂控制与图像处理示教平台

LabVIEW机械臂控制与图像处理示教平台 随着工业自动化技术的快速发展&#xff0c;工业机器人在制造业中的应用越来越广泛&#xff0c;它们在提高生产效率、降低人工成本以及保证产品质量方面发挥着重要作用。然而&#xff0c;传统的工业机器人编程和操作需要专业知识&#xff…

React正式更新!开始学习React 19!

本文为原创文章&#xff0c;原文链接&#xff1a;J实验室&#xff0c;未经授权请勿转载 今年2月份&#xff0c;React 发布消息确认今年发布 v19 版本&#xff0c;尘封两年的版本号终于要更新了&#xff08;详情点击&#xff1a;React 19 发布在即&#xff0c;抢先学习一下新特性…

FSNotes for Mac v6.7.1中文激活版:强大的笔记管理工具

FSNotes for Mac是一款功能强大的文本处理与笔记管理工具&#xff0c;为Mac用户提供了一个直观、高效的笔记记录和整理平台。 FSNotes for Mac v6.7.1中文激活版下载 FSNotes支持Markdown语法&#xff0c;使用户能够轻松设置笔记格式并添加链接、图像等元素&#xff0c;实现笔记…

Linux下启动jenkins报错问题解决

jenkins端口报错 java.io.IOException: Failed to start Jettyat winstone.Launcher.<init>(Launcher.java:209)at winstone.Launcher.main(Launcher.java:496)at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at java.base/jdk.int…

【氮化镓】AlGaN/GaN HEMTs沟道温度测量

文章是关于AlGaN/GaN HEMTs&#xff08;高电子迁移率晶体管&#xff09;在不同基底&#xff08;如蓝宝石和硅&#xff09;上生长时&#xff0c;通过直流&#xff08;DC&#xff09;特性方法确定沟道温度的研究。文章由J. Kuzmk, P. Javorka, A. Alam, M. Marso, M. Heuken, 和 …

微软如何打造数字零售力航母系列科普04 - 微软联合Adobe在微软365应用程序中工作时推出新的生成式AI功能

微软和Adobe正在合作&#xff0c;将情境营销见解和工作流程引入微软Copilot&#xff0c;以提供生成的人工智能功能&#xff0c;使营销人员和营销团队能够在自然的工作流程中实现更多目标。 这些新的集成功能将在生产力和协作工具&#xff08;如Outlook、Teams和Word&#xff0…

JAVA基础——集合框架(List与Set)

数据结构 什么是数据结构 数据结构就是用来装数据以及数据与之间关系的一种集合。如何把相关联的数据存储到计算机&#xff0c;为后续的分析提供有效的数据源&#xff0c;是数据结构产生的由来。数据结构就是计算机存储、组织数据的方式。好的数据结构&#xff0c;让我们做起事…

使用QT完成如图的游戏登录界面 使用信号和槽完成密文明文密码转换,重置账号和密码,登录校验 详细代码在主页下载

头文件: #ifndef LOGINWIDGET_H #define LOGINWIDGET_H #include <QLineEdit> #include <QPushButton> #include <QWidget> class LoginWidget : public QWidget {Q_OBJECT public: LoginWidget(QWidget *parent = 0); ~LoginWidget(); public slots: …

基于uniapp+微信小程序的智能停车场管理小程序,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

Three.js杂记(十五)—— 汽车展览(下)

在上一篇文章Three.js杂记&#xff08;十四&#xff09;—— 汽车展览上 - 掘金 (juejin.cn)中主要对切换相机不同位置和鼠标拖拽移动相机焦点做了简单的应用。 那么现在聊聊该如何实现汽车模型自带的三种动画展示了&#xff0c;实际上可以是两种汽车前后盖打开和汽车4车门打开…

C# APS.NET CORE 6.0 WebApi在IIS部署报错

今天尝试着把基于 APS.NET CORE6.0开发的webAPI程序部署到IIS中&#xff0c;当打开网站地址时报错&#xff0c;无法打开&#xff0c;于是查找资料最终进行了解决。 打开 IIS →模块 查看列表中是否存在 AspNetCoreModuleV2&#xff0c;如下&#xff1a; 对应的应用池需要选择“…

小程序SSL证书更新指南

随着网络技术的不断发展&#xff0c;小程序已经成为许多企业和个人进行业务推广和服务提供的重要平台。在享受小程序带来的便利和高效的同时&#xff0c;我们也必须重视其安全性问题。SSL证书作为保障小程序数据传输安全的重要手段&#xff0c;其更新工作不容忽视。本文将为大家…