《动手学深度学习(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=warea⋅area+wage⋅age+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^=w⊤x+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=1∑nl(i)(w,b)=n1i=1∑n21(w⊤x(i)+b−y(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):在每次需要计算更新的时候随机抽取一小批样本。
批量大小(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πσ21exp(−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寻找使得观测到的数据概率(即“似然”)最大化的参数值。
在高斯噪声的假设下,最小化均方误差等价于对线性模型的极大似然估计。
最小化均方误差(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=1∑n(yi−Xiβ)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 ) 最小。
极大似然估计(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=1∏n2πσ1exp(−2σ2(yi−Xiβ)2)
,其中 ( σ \sigma σ) 是高斯噪声的标准差。- 极大似然估计的目标是找到参数 ( β \beta β ),以最大化似然函数 ( $L(\beta) $)。
等价性:
- 当我们取似然函数 ( L ( β ) L(\beta) L(β) ) 的对数(称为对数似然函数),并对其进行最大化,我们实际上是在最小化 ( y i − X i β ) 2 (y_i - X_i\beta)^2 (yi−Xiβ)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_()
训练
训练过程: 正向传播->计算损失->反向传播->参数更新
- 在每次迭代(epoch)中,我们读取一小批量训练样本,并通过我们的模型来获得一组预测(正向传播)。
- 计算完损失后,我们开始反向传播[计算每个参数(权重和偏差)的偏导数(梯度)],存储每个参数的梯度。
- 调用优化算法
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.data
和bias.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个可能的输出类别
向量形式表达为 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]=j∑−P(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)=i∑−P(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=1∑qyjlogy^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)j−yj
模型预测和评估
精度(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
原因:没有安装最新版本的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步骤:
- 对每个项求幂(使用
exp
); - 对每一行求和(小批量中每个样本是一行),得到每个样本的规范化常数;
- 将每一行除以其规范化常数,确保结果的和为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)
预测
对图像进行分类预测
#在测试集预测少量数据
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像素的灰度图。参数1
和n
指定每行显示1个图像,总共显示n
个图像。titles[0:n]
为每个图像提供一个标题。
输出:实际标签(文本输出的第一行)模型预测(文本输出的第二行)
训练过程
- 读取数据
- 定义模型和损失函数
- 使用优化算法训练模型
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)
Q:为什么测试精度会在一段时间后降低?
A:模型过拟合,可以加入正则化如dropout
相关文章:

《动手学深度学习(Pytorch版)》Task03:线性神经网络——4.29打卡
《动手学深度学习(Pytorch版)》Task03:线性神经网络 线性回归基本元素线性模型损失函数随机梯度下降 正态分布与平方损失 线性回归的从零开始实现读取数据集初始化模型参数定义模型定义损失函数定义优化算法训练 线性回归的简洁实现读取数据集…...
机器学习(二) ----------K近邻算法(KNN)+特征预处理+交叉验证网格搜索
目录 1 核心思想 1.1样本相似性 1.2欧氏距离(Euclidean Distance) 1.3其他距离 1.3.1 曼哈顿距离(Manhattan Distance) 1.3.2 切比雪夫距离(Chebyshev distance) 1.3.3 闵式距离(也称为闵…...
This error originates from a subprocess, and is likely not a problem with pip.
Preparing metadata (setup.py) ... errorerror: subprocess-exited-with-error python setup.py egg_info did not run successfully.│ exit code: 1╰─> [63 lines of output]WARNING: The repository located at mirrors.aliyun.com is not a trusted or secure host a…...

Python中关于子类约束的开发规范
Python中关于子类约束的开发规范 我们知道,在java和C#中有一种接口的类型,用来约束实现该接口的类,必须要定义接口中指定的方法 而在python中,我们可以基于父类子类异常来仿照着实现这个功能 class Base:def func():raise NotI…...

Isaac Sim 4 键盘控制小车前进方向(学习笔记5.8.2)
写的乱糟糟,主要是这两周忘了记录了...吭哧吭哧往下搞,突然想起来要留档,先大致写一个,后面再往里添加和修改吧,再不写就全忘了 有一个一直没解决的问题: 在保存文件时出现问题:isaac sim mism…...

「Python绘图」绘制太极图
python 绘制太极 一、预期结果 二、核心代码 import turtlepen turtle.Turtle()print("开始绘制太极")radius 100 pen.color("black", "black") pen.begin_fill() pen.circle(radius/2, 180) pen.circle(radius, 180) pen.left(180) pen.circ…...

解决html2canvas生成图片慢的问题
// 主要看那个点击事件就行 <divclass"textBox-right-board-group"v-for"item in screenList":key"item.id"><!-- 获取不同分辨率下的屏幕的展示的文字大小DPI: fontSize: getFontSize(item.resolutionRatio), --><di…...

模型智能体开发之metagpt-多智能体实践
参考: metagpt环境配置参考模型智能体开发之metagpt-单智能体实践 需求分析 之前有过单智能体的测试case,但是现实生活场景是很复杂的,所以单智能体远远不能满足我们的诉求,所以仍然还需要了解多智能体的实现。通过多个role对动…...

Java | Leetcode Java题解之第67题二进制求和
题目: 题解: class Solution {public String addBinary(String a, String b) {StringBuffer ans new StringBuffer();int n Math.max(a.length(), b.length()), carry 0;for (int i 0; i < n; i) {carry i < a.length() ? (a.charAt(a.leng…...
考过PMP之后,为什么建议学CSPM?
在项目管理领域,PMP证书和CSPM证书都是非常重要的认证,那么CSPM到底是什么?含金量如何?为什么建议学习CSPM?今天,我们一起来了解CSPM! CSPM是什么? CSPM中文全称:项目管理专业人员…...

智能合约是什么?搭建与解析
智能合约是一种基于区块链技术的自动化执行合约,它通过编程语言编写,并在区块链网络上部署运行。智能合约是区块链技术的重要组成部分,它使得去中心化应用(DApp)的开发变得更加便捷和高效。本文将从智能合约的搭建、原…...

windows下安装最新的nginx
1、进入官网下载地址 https://nginx.org/en/download.html#/ 2、点击这里最新的版本下载 3、(不要直接运行解压的nginx.exe),应这样操作WindowsR,输入CMD, 4、查看一下自己解压后的位置,我的是在E盘 5、输入对应的W…...

【深耕 Python】Data Science with Python 数据科学(19)书402页练习题:模型准确率对比研究、KMeans算法的一点探讨
写在前面 关于数据科学环境的建立,可以参考我的博客: 【深耕 Python】Data Science with Python 数据科学(1)环境搭建 往期数据科学博文一览: 【深耕 Python】Data Science with Python 数据科学(2&…...

汽车品牌区域营销方案
领克汽车粤海区域营销方案-36P 活动策划信息: 方案页码:36页 文件格式:PPT 方案简介: 车市反弹形势明显,领克销量呈现稳健上涨趋势 品牌 未来市场可观,应 持续扩大品牌声量,保持市场占有优…...
matlab 中在3维坐标系中绘制一个点的X,Y,Z坐标,除了mesh还有什么函数?使用格式与mesh都有什么区别?
在MATLAB中,除了mesh函数之外,还有其他一些函数可以用来在三维坐标系中绘制点或曲面。以下是一些常用的函数及其与mesh函数的区别: 函数名描述与mesh的区别plot3在三维坐标系中绘制线或点仅限于线或点的绘制,不生成网格scatter3在…...

如何在六个月内学会任何一门外语(ted转述)
/仅作学习和参考,勿作他用/ a question : how can you speed up learning? 学得快,减少在学校时间 结果去研究心理学惹 spend less time at school. if you learn really fast , you donot need to go to school at all. school got in the way of …...

前端 Android App 上架详细流程 (Android App)
1、准备上架所需要的材料 先在需要上架的官方网站注册账号。提前把手机号,名字,身份证等等材料准备好,完成开发者实名认证;软著是必要的,提前准备好,软著申请时间比较长大概需要1-2周时间才能下来…...

MySQL学习笔记11——数据备份 范式 ER模型
数据备份 & 范式 & ER模型 一、数据备份1、如何进行数据备份(1)备份数据库中的表(2)备份数据库(3)备份整个数据库服务器 2、如何进行数据恢复3、如何导出和导入表里的数据(1)…...

软件测试基础理论复习
什么是软件? 软件是计算机系统中与硬件相互依存的另一部分, 软件包括程序文档 什么是软件测试? (1)软件测试是在现有软件(程序文档)中寻找缺陷的过程; (2࿰…...

【UnityRPG游戏制作】Unity_RPG项目_玩家逻辑相关
👨💻个人主页:元宇宙-秩沅 👨💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨💻 本文由 秩沅 原创 👨💻 收录于专栏:就业…...

关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...

select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...