《动手学深度学习(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 = 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.rhkb.cn/news/320009.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【c++】Resharper 去掉中文注释拼写

参考大神&#xff1a; Resharper 去掉注释拼写 reshaper的中文注释一堆下划线&#xff0c;看的很累、很乱&#xff1a; options 里 在code inspetion里 搜索 去掉 Typo in comment 就可以不在中文注释提示 重启vs reshaperd 中文注释下划线没了。小番茄的还在。

jsPDF + html2canvas + Vue3 + ts项目内,分页导出当前页面为PDF、A 页面内导出 B 页面的内容为PDF,隐藏导出按钮等多余元素

jsPDF html2canvas Vue3 ts Arco Design项目&#xff0c;分页导出当前页面为PDF、A 页面内导出 B 页面的内容为PDF&#xff0c;隐藏导出按钮等多余元素… 1.下载所需依赖 pnpm install --save html2canvaspnpm install --save jspdf引入依赖 <script setup lang"…

2010NOIP普及组真题 3. 导弹拦截

线上OJ&#xff1a; 一本通&#xff1a;http://ybt.ssoier.cn:8088/problem_show.php?pid1951 核心思想&#xff1a; 1、我们把导弹分为区间1和区间2来看。1#拦截区间1&#xff0c;2#拦截区间2。 2、则&#xff1a;1#的拦截半径为区间1 中 最远的导弹&#xff0c;而2#的拦截半…

关于 c++的模板库中的数组模板 is_array_v的测试

&#xff08;1&#xff09;该模板的源代码如下&#xff1a; template <class> // determine whether type argument is an array bool is_array_v false;template <class _Ty, size_t _Nx> bool is_array_v<_Ty[_Nx]> true;template <class _Ty>…

学习java中的interface接口

1.了解接口 java提供了一个关键字interface&#xff0c;用这个关键字我们可以定义出一个特殊的结构&#xff1a;接口 格式&#xff1a; public interface 接口名{ //成员变量&#xff08;常量&#xff09; //成员方法&#xff08;抽象方法&#xff09; } 注意&#xff1a;接…

Python——Fastapi管理平台(打包+优化)

目录 一、配置多个表 1、后端项目改造 2、导包报错——需要修改&#xff08;2个地方&#xff09; 3、启动后端&#xff08;查看是否有问题&#xff09; 4、配置前端 二、打包——成exe文件&#xff08;不包含static文件&#xff09;简单 1、后端修改 2、前端修改 3、运行打包命…

[论文笔记]Longformer: The Long-Document Transformer

引言 今天带来论文Longformer: The Long-Document Transformer的笔记。 基于Transformer的模型由于其自注意力操作而无法处理长序列&#xff0c;该操作随着序列长度呈二次扩展。为了解决这一限制&#xff0c;本篇工作提出了Longformer&#xff0c;其注意力机制随着序列长度呈…

批量邮箱API发送邮件的方法?如何使用API?

批量邮箱API发送邮件效率怎么样&#xff1f;API接口发信的优势&#xff1f; 批量发送邮件已经成为许多企业、机构或个人进行营销推广、信息传递的重要手段。然而&#xff0c;如何高效、准确地实现批量邮箱发送&#xff0c;却是许多人面临的难题。AokSend就来探讨一下利用API进…

Python基础学习之logging模块

在Python编程中&#xff0c;日志记录&#xff08;Logging&#xff09;是一个非常重要的功能。它不仅可以帮助我们追踪和调试代码中的错误&#xff0c;还可以记录程序运行时的关键信息&#xff0c;以便后续分析和优化。Python标准库中的logging模块为我们提供了强大的日志记录功…

C++从入门到精通——模板

模板 前言一、泛型编程二、函数模板函数模板的概念函数模板格式示例 函数模板的原理函数模板的实例化隐式实例化显式实例化示例 auto做模板函数的返回值模板参数的匹配原则总结 三、类模板类模板的定义格式类模板的实例化 前言 C模板是C语言中的一种泛型编程技术&#xff0c;可…

IDEA--debug

1. 单点调试的三个级别 Step into&#xff1a;在单步执行时&#xff0c;遇到子函数就进入并且继续单步执行。Step over&#xff1a;在单步执行时&#xff0c;在函数内遇到子函数时不会进入子函数内单步执行&#xff0c;而是将子函数整个执行完再停止&#xff0c;也就是把子函数…

【保姆级教程】用IDEA2023版本给RuoYi-Vue添加子模块

文章目录 前言添加子模块新建子模块新建子模块界面&#xff1f;新建子模块界面&#xff01; 修改pom依赖配置RuoYiApplication添加测试接口配置接口权限测试 前言 若依前后端分离框架能够极大方便当前开发任务&#xff0c;并且使用的技术栈也相当丰富&#xff0c;但是目前只提…

ThingsBoard通知中心讲解

1、概述 2、案例 2.1、通知发送方式 2.2、发送通知 3、Templates模板 3.1、Add new template添加新模板 1、概述 ThingsBoard 通知中心是一个用于在平台内发送、管理和自动化通知的综合工具。它允许多种通知方法&#xff0c;包括网络、电子邮件、移动应用程序、短信以及与 …

基于SpringBoot+Vue点餐系统设计和实现(源码+LW+部署讲解)

&#x1f339;作者简介&#xff1a;✌全网粉丝10W&#xff0c;前大厂员工&#xff0c;多篇互联网电商推荐系统专利&#xff0c;现有多家创业公司&#xff0c;致力于建站、运营、SEO、网赚等赛道。也是csdn特邀作者、博客专家、Java领域优质创作者&#xff0c;博客之星、掘金/华…

go mod

常用命令 初始化模块 go mod init 模块名下载 go.mod 文件中指明的所有依赖 go mod download github.com/gin-gonic/ginv1.9.(依赖路径)依赖对其&#xff08;使引用的都是所依赖的&#xff09; go mod tidy编辑go.mod go mod edit go mod edit -require"github.com/g…

哪个品牌的骨传导耳机好用?精选五大高性能热门骨传导耳机款式推荐!

我作为一名热衷于音乐的数码博主&#xff0c;在选购产品前也习惯于先浏览各种榜单。最近&#xff0c;我发现关于骨传导耳机的讨论热度极高&#xff0c;有人认为骨传导耳机是非常值得入手的新型蓝牙耳机&#xff0c;也有人认为骨传导耳机只是智商税的产品。经过深入调查后&#…

【计算机网络】FTP站点配置搭建教程以及相关问题解决方案(超详细)

文章目录 1、安装Window Server 20082、搭建FTP环境&#xff08;1&#xff09;安装FTP服务器&#xff08;2&#xff09;配置FTP服务器&#xff08;3&#xff09;测试FTP连接 3、遇到的问题以及解决方案&#xff08;1&#xff09;Windows无法访问此文件夹&#xff08;2&#xff…

Android Ant编译环境配置(Win)

1、 载ant包: 2、设置环境变量&#xff1a; 3、检查是否设置成功及版本 4、执行命令&#xff1a; android update project -p . -n “projectname”&#xff08;例如&#xff1a;android update project --target 1 -p . -n “Couplet”&#xff09;(只输入红色部分也是可以的…

AC/DC电源模块的高效能源管理与效率优化

BOSHIDA AC/DC电源模块的高效能源管理与效率优化 AC/DC电源模块是一种常见的电源转换装置&#xff0c;用于将交流电转换为直流电。它被广泛应用于各种电子设备中&#xff0c;如计算机、通信设备、工业自动化设备等。在现代化的科技社会中&#xff0c;高效能源管理和效率优化变…

swift微调多模态大语言模型

微调训练数据集指定方式的问题请教 Issue #813 modelscope/swift GitHubQwen1.5微调训练脚本中&#xff0c;我用到了--dataset new_data.jsonl 这个选项&#xff0c; 可以训练成功&#xff0c;但我看文档有提到--custom_train_dataset_path这个选项&#xff0c;这两个有什么…