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

news/2024/5/19 13:21:00

《动手学深度学习(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 = 256def get_dataloader_workers():  #@save"""使用4个进程来读取数据"""return 4train_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 = 10W = 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] * ndef 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, fmtsdef add(self, x, y):# 向图表中添加多个数据点if not hasattr(y, "__len__"):y = [y]n = len(y)if not hasattr(x, "__len__"):x = [x] * nif 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_metricsassert train_loss < 0.5, train_lossassert train_acc <= 1 and train_acc > 0.7, train_accassert test_acc <= 1 and test_acc > 0.7, test_acc
  • 在Python中,assert 语句用作一种调试辅助工具,它会检查指定的条件表达式。如果条件表达式结果为 True,程序会继续执行;如果条件表达式结果为 False,则会引发一个 AssertionError 异常。assert 语句通常用于确保程序在某个特定状态下运行,或者验证某些假设条件是否为真。
  • 使用 assert 语句进行条件断言,确保训练损失小于0.5,训练准确度和测试准确度都应该大于0.7且不超过1。这些断言是为了验证训练过程是否达到预期的质量标准。

设置损失函数

lr = 0.1def 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:breaktrues = 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.mrgr.cn/p/40083316

相关文章

百舸实践之「埋点数据深度治理与应用」 | 京东云技术团队

一、背景 随着公司和业务的不断发展,百舸平台也从单一内容投放转向了以流量和数据为基础的流量运营模式。在这个转变过程中,数据深度治理与应用的重要性尤为凸显,在数据深度治理过程中,需要将用户行为数据、投放素材以及投放效果三者紧密的串联起来。数据深度治理和应用,一…

【网络原理】HTTPS 的工作过程

系列文章目录 【网络通信基础】网络中的常见基本概念 【网络编程】网络编程中的基本概念及Java实现UDP、TCP客户端服务器程序&#xff08;万字博文&#xff09; 【网络原理】UDP协议的报文结构 及 校验和字段的错误检测机制&#xff08;CRC算法、MD5算法&#xff09; 【网络…

【数据结构(邓俊辉)学习笔记】列表03——有序列表

文章目录 0. 概述1. 唯一化2. 查找2.1 实现2.2 顺序查找2.3 复杂度 0. 概述 介绍下有序列表。 若列表中所有节点的逻辑次序与其大小次序完全一致&#xff0c;则称作有序列表&#xff08;sorted list&#xff09;。为保证节点之间可以定义次序&#xff0c;依然假定元素类型T直接…

【进阶篇】基于 Redis 实现分布式锁的全过程

这一篇文章拖了有点久,虽然在项目中使用分布式锁的频率比较高,但整理成文章发布出来还是花了一点时间。在一些移动端、用户量大的互联网项目中,经常会使用到 Redis 分布式锁作为控制访问高并发的工具。目录前言一、关于分布式锁二、RedLock 红锁(不推荐)三、基于 setIfAbs…

视频的二维码是怎么做的?快速实现扫码看视频的方法

视频的二维码现在有很多的应用场景&#xff0c;用这种方式来分享视频能够实现视频的快速传播&#xff0c;现在用户大多习惯通过扫码的方式来获取信息&#xff0c;二维码可以提供更好的用户体验。 以二维码为媒介来存储视频时&#xff0c;可以使用视频二维码生成器来快速制作相…

pdf.js源码解析-渲染的时序分析

首先来一张总结的图,也就是pdf.js在解析和渲染pdf的一个时序图,下图:首先要明白,pdf.js在渲染pdf的时候是做分层渲染,也就是时间展现的内容是通过canvas进行绘制的,而我们通过鼠标进行选择时候的内容是通过dom进行普通渲染,也就是 <div>123</div> 这样的普通…

权威SAFe大规模敏捷企业级内训及SAFe敏捷认证

​ SAFe – Scaled Agile Framework是目前全球运用最广泛的大规模敏捷框架,也是成长最快、最被认可、最有价值的规模化敏捷框架,目前全球SAFe认证专业人士已达80万人,福布斯100强的70%都在实施SAFe。本课程是一个2天的 SAFe权威培训课程,在课程中,学员将系统地学习大规模敏…

Flutter笔记:Widgets Easier组件库(2)阴影盒子

Flutter笔记 Widgets Easier组件库&#xff08;2&#xff09;&#xff1a;阴影盒子 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress o…

nuxt3使用记录六:禁用莫名其妙的Tailwind CSS(html文件大大减小)

发现这个问题是因为&#xff0c;今天我突然很好奇&#xff0c;我发现之前构建的自动产生的200.html和404.html足足290k&#xff0c;怎么这么大呢&#xff1f;不是很占用我带宽&#xff1f; 一个啥东西都没有的静态页面&#xff0c;凭啥这么大&#xff01;所以我就想着手动把他…

ping ip、域名、端口

一、pingping baidu.com ping 192.168.9.9 综上所诉,ping命令的时候格式为(注意ping后面需要跟上一个空格) ①ping IP地址或主机域名 ②ping IP地址或主机域名+命令参数 ③ ping 命令参数+IP地址或主机域名 ping命令参数说明查看ping命令帮助 ping /? 输入上面的命令,我们…

leetcode-缺失的第一个正整数-96

题目要求 思路 1.这里的题目要求刚好符合map和unordered_map 2.创建一个对应map把元素添加进去&#xff0c;用map.find(res)进行查找&#xff0c;如果存在返回指向该元素的迭代器&#xff0c;否则返回map::end()。 代码实现 class Solution { public:int minNumberDisappeare…

使用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: …

探索企业级项目管理的最优策略

企业的项目管理应该采取综合性的方式,结合多种工具和方法来确保项目的成功。zz-plan 甘特图是其中一种非常有用的工具,它可以帮助项目经理和团队成员可视化地展示项目的时间线和进度。以下是采取合适项目管理方式时需要考虑的几个关键点,结合甘特图的使用: 项目规划:在项目…

Redis开源社区持续壮大,华为云为Valkey项目注入新的活力

作为Valkey社区的Technical Steering Committee member,华为云将持续参与社区建设。摘要:作为Valkey社区的Technical Steering Committee member,华为云将持续参与社区建设。一、背景 今年3月21日,Redis Labs宣布从Redis 7.4版本开始,将原先比较宽松的BSD源码使用协议修改…

TigerTrade

目录 老虎国际纳斯达克上市公司 Windows客户端软件 主界面 个股页面,采用左中右方式布局,左侧自选列表,中间图表,右侧是自选和下单器。图表正下方是资产、持仓和订单模块,该模块支持隐藏。持仓和订单闪电下单 闪电下单器,旨在为用户提供更快捷的下单方式闪电下单设置页面…

激光打印机打印唐草纹路流程

一、先弄一张 唐草.png 图片 大概长这样ps 抠图出来 然后 菜单栏 编辑 =》 自由变换 然后导出png 成了这个样子二、 使用 CorelDRAW X4 SP2 精简版 导入这个图 1 菜单栏 位图 =》 转换为 位图 2 菜单栏 位图 =》 轮廓描摹 =》 线条图 大概长这样3 导出为 plt 文件 三、 使用…

【HEVC简介】CTU、CU、PU、TU结构

参考文献:见《High Efficiency Video Coding (HEVC)》Block Structures and Parallelism Features in HEVC章节CTU:coding tree unit,编码树单元,LCU对于YUV=420格式的彩色视频:一个CTU由一个CTB of the luma samples 、2个CTBs of the chroma samples和相关的语法元素组成…

FileBird Pro插件下载:革新您的WordPress媒体库管理

WordPress媒体库是您网站的重要组成部分&#xff0c;它存储了所有的图片、视频、文档等文件。但随着网站的扩展&#xff0c;媒体库的管理变得越来越复杂。FileBird Pro插件&#xff0c;作为一款专为WordPress用户设计的媒体库管理工具&#xff0c;以其直观的界面和强大的功能&a…

Spring Boot + 事务钩子函数,打造高效支付系统!

作者:avengerEug 链接:https://juejin.cn/post/6984574787511123999 前言 经过前面对Spring AOP、事务的总结,我们已经对它们有了一个比较感性的认知了。 今天,我继续安利一个独门绝技:Spring 事务的钩子函数。单纯的讲技术可能比较枯燥乏味。接下来,我将以一个实际的案…