《动手学深度学习(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.mzph.cn/news/830340.shtml

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

相关文章

帕累托森林李朝政博士受聘「天工开物开源基金会」专家顾问

导语&#xff1a; 开源铸造了当前最前沿的科技引擎。开源驱动了软件生态&#xff0c;也以指数级速度驱动硬件生态。 3月中旬&#xff0c;天工开物开源基金会授予李朝政博士专家顾问&#xff0c;表彰他积极推动参与中国智能软件生态的建设&#xff0c;期待一起共筑未来新生态。…

Python_AI库 Pandas的时间序列操作详解

Python_AI库 Pandas的时间序列操作详解 本文默认读者具备以下技能&#xff1a; 熟悉python基础知识&#xff0c;vscode或其它编辑工具 了解pandas,matplotlib的基础操作 具备自主扩展学习能力 在数据分析和处理中&#xff0c;时间序列数据是一类常见且重要的数据类型。大量的…

CSS实现各种优惠券效果

一、左半圆效果 <style style"text/css">.coupon {width: 240px;height: 100px;margin-top: 15px;background-color: #ff6347;-webkit-mask: radial-gradient(circle at left center, transparent 20px, red 20px); } </style><div class"coupon…

TruLens

文章目录 一、关于 TruLensHow it works 二、安装三、快速使用Get DataInCreate Vector StoreBuild RAG from scratchSet up feedback functions.Construct the appRun the app 一、关于 TruLens Evaluate and Track LLM Applications 官网&#xff1a;https://www.trulens.o…

linux,从零安装mysql 8.0.30 ,并且更新至mysql 8.0.36

前言&#xff1a; 系统使用的CentOS 7&#xff0c;系统默认最小安装。 一、基础配置 配置虚拟机IP&#xff0c;需要更改的内容&#xff0c;如下红框中 修改之后 至此&#xff0c;基础配置完成。注意&#xff1a;此处虚拟机网络适配器使用的是&#xff1a;桥接模式 二、软件…

掌握Lazada自养号测评技巧,轻松提升产品销量与排名

Lazada店铺销量不佳&#xff0c;时常让卖家们感到困扰。然而&#xff0c;仅仅感叹和自我安慰并不能解决问题。作为卖家&#xff0c;我们需要专注于打牢基础&#xff0c;尤其是要深入了解Lazada店铺测评的益处及其运用技巧。通过巧妙地结合运营策略和测评方法&#xff0c;我们可…

Django框架之请求生命周期流程图

一、引言 WSGI、wsgiref、uwsgi三者是什么关系? WSGI是协议&#xff0c;小写的wsgiref和uwsgi是实现该协议的功能模块 缓存数据库 提前已经将你想要的数据准备好了&#xff0c;需要的时候直接拿就可以&#xff0c;提高了效率和响应时间。 eg:当你在修改你的数据的时候&…

vscode查看变量小技巧

vscode查看变量有3种方法 print()输出要查看的变量&#xff0c;此方法适用于所有编程软件安装jupyter&#xff0c;右键run in interactive window—在交互窗口运行&#xff0c;之后点击变量即可查看 通过调试查看&#xff0c;使用于大多编程软件。打断点&#xff0c;调试后会…

【SZU计算机网络实验】从rdt到GBN,这实验居然实现了TCP的可靠数据传输机制?

前言 一个实验六个任务&#xff0c;实验文档一划划不到底。。看来老师们是真下功夫了啊 本文主要展示了作者在完成SZU计算机网络实验3的思路及过程&#xff0c;实验主要包括&#xff1a; 理解rdt2.1实现rdt2.2实现rdt3.0实现回退N步&#xff08;GBN&#xff09;机制实现面向…

2000.1-2023.8中国经济政策不确定性指数数据(日度、月度)

2000.1-2023.8中国经济政策不确定性指数数据&#xff08;日度、月度&#xff09; 1、时间&#xff1a;日度&#xff1a;2001.1.1-2022.06.17&#xff0c;月度2000.1-2023.8 2、指标&#xff1a;CNEPU&#xff08;经济政策不确定性指数&#xff09; 3、来源&#xff1a;China…

Linux网络-文件传输协议之FTP服务(附带命令及截图)

目录 一.FTP简介 二.FTP的数据模式 1.主动模式 2.被动模式 3.两种模式比较 三.安装配置vsftpd 1.安装vsftpd 1.1.安装前关闭防火墙 1.2.安装vsftpd 1.3.查看 1.4.备份 2.配置 3.重启后生效 四.相关实验 1.以win为例 1.1.设置并测试测试连通性 1.2.在终端里创建…

Redis基本數據結構 ― List

Redis基本數據結構 ― List 介紹常用命令範例1. 將元素推入List中2. 取得List內容3. 彈出元素 介紹 Redis中的List結構是一個雙向鏈表。 LPUSH LPOP StackLPUSH RPOP QueueLPUSH BRPOP Queue(消息隊列) 常用命令 命令功能LPUSH將元素推入列表左端RPUSH將元素推入列表右…

ubuntu20.04安装RabbitMQ 3.11.19+Erlang 25.3.1

1、检查RabbitMQ、Erlang版本 Erlang Version Requirements | RabbitMQ 2、ubuntu20.04对应的是 focal 3、下载安装Erlang 下载地址&#xff1a;http://packages.erlang-solutions.com/erlang/debian/pool/ sudo dpkg -i esl-erlang_25.3-1~ubuntu~focal_amd64.deb sudo apt…

C++ 如何实现原子性

1.操作系统如何实现原子性 在单处理器,单核,运行多线程的情况下,我们不使用线程同步工具, 我们会出现,线程之间会互相抢夺,临界区的资源,造成数据不符合我们预期的结果, 后面再说解决办法,那么我们怎么帮助实现原子性 1 屏蔽中断,不让线程之间切换,让它完成再切换 2 底层硬…

栈与递归的关系

定义 特点 函数调用过程 具体实现过程与状态 小结 拓展 递归的分解 典型案例

第三弹:JavaScript 学习记录

目录 1.1. 了解 1.1.1. 为什么学习JavaScript 1.1.2. JavaScript简介 1.1.3. JavaScript / ECMAScript 1.1.4. JavaScript使用方式 1.1.5. JavaScript输出 1.1.6. JavaScript语句 1.1.7. JavaScript注释 1.1.8. JavaScript变量及常量 1.1.9. JavaScript数据类型 1.1.…

C++实战演练---负载均衡在线oj项目预热

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 前言 学习准备了快一年时间&#xff0c;心心念念的实战演练终于可以开始了&#xff0c;话不多说&#xff0c;直接进入主题…

React、React Router 和 Redux 常用Hooks 总结,提升您的开发效率!

Hooks 是 React 16.8 中引入的一种新特性&#xff0c;它使得函数组件可以使用 state 和其他 React 特性&#xff0c;从而大大提高了函数组件的灵活性和功能性。下面分别总结React、React Router 、Redux中常用的Hooks。 常用Hooks速记 React Hooks useState&#xff1a;用于…

ssm088基于JAVA的汽车售票网站abo+vue

汽车售票网站的设计与实现 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对汽车售票信息管理混乱&#xff0c;出错率…

基于stm32的USB模拟UART的尝试F429

目录 基于stm32的USB模拟UART的尝试F429实验目的场景使用原理图USBX 组件移植USBX实现虚拟串口配置USB移植USBX源码工程中添加对应源码修改usb_otg.c创建 USBX 任务添加使用串口的代码上机现象本文中使用的测试工程 基于stm32的USB模拟UART的尝试F429 本文目标&#xff1a;基于…