零基础-动手学深度学习-3.6softmax回归的从零开始实现

目录

一、初始化模型参数

二、定义softmax操作

三、定义模型

四、定义损失函数

五、分类精度

六、训练

七、一点补充


鄙人生医转码,道行浅薄,请多谅解~

感觉这章的内容超量,代码和详解都非常长,细嚼慢咽ing~

首先导入需要的库和上一章讲的训练和测试集MNIST(相比于原码我多加了一个库后面用)

import torch
import matplotlib.pyplot as plt
from IPython import display
from d2l import torch as d2l


batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

一、初始化模型参数

num_inputs = 784#输入一个拉长的向量
num_outputs = 10#模型输出维度为10
#拉长向量会损失很多空间信息,这就是卷积神经网络要考虑的事情了
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)#定义权重
b = torch.zeros(num_outputs, requires_grad=True)#偏移长度同理

这一步和线性回归操作一样,但是我们需要明确w,b矩阵的大小关系,不然容易搞混,比如说w为了在和x相乘后得出的数据与分类模型的类别数相同,我们要其列数为10也就是output

二、定义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)

回顾后我们来写softmax的代码实现:

def softmax(X):
    X_exp = torch.exp(X)#输入x中每个元素的指数函数
    partition = X_exp.sum(1, keepdim=True)#关键,计算出每个样本的所有类别分数指数和
    #参数 keepdim=True 表示在加和操作后保持维度,结果仍然是二维张量,这在后面的广播机制中起作用
    return X_exp / partition  # 这里应用了广播机制
    #由于 partition 是 [n, 1] 形状的张量,而 X_exp 是 [n, m] 形状的张量(n 是样本数量,m 是类别数量)
    #广播机制会自动将 partition 扩展为 [n, m] 的形状,使得每行的每个元素都被对应的 partition 值除

#我感觉这里有一个比较容易搞混的地方?
#就是为什么sum函数的维度是1而不是0是因为类别分数的归一化是针对每个样本单独进行的
#而不是将所有样本的类别分数一起归一化

这里也再次强调了输出矩阵的格式,我感觉我第一次看李沐的时候没有太关注代码和这些具体实现,二刷的时候就很懵,我现在浅薄的想法就是,输入的样本数作为行数,然后和w相乘后以类别数作为列数。

三、定义模型

定义模型就很简单了直接导入上面的softmax操作:

def net(X):
    return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)
#reshape 是为了确保输入的形状与权重矩阵 W 的形状兼容,从而进行正确的矩阵运算

这里确保了输入的形状兼容性,但我寻思之前我们写w的正态分布不是已经规定好了吗?不懂

四、定义损失函数

需要先理解一个格式:

#利用y作为y_hat概率中的索引
#y = torch.tensor([0, 2])
#y_hat = torch.tensor([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
#y_hat[[0, 1], y]

 然后再写交叉熵损失函数:

#实现交叉熵损失函数
def cross_entropy(y_hat, y):
    return - torch.log(y_hat[range(len(y_hat)), y])
#y_hat 是模型的输出概率分布,通常是经过 Softmax 处理后的张量,形状为 (num_samples, num_classes)
#y 是真实标签,通常是一个包含每个样本对应类别索引的一维张量,形状为 (num_samples,)
#这行代码通过索引选择 y_hat 中每个样本对应类别的预测概率。例如,如果 y 中的某个值是 2,
<think>我们根据用户需求,实现一个从零开始(不使用深度学习框架)的Softmax回归模型。Softmax回归用于多分类问题。我们将按照以下步骤进行: 1. 准备数据集(这里以Fashion-MNIST为例,但我们将使用PyTorch内置的数据集,然后从零处理) 2. 初始化模型参数(权重和偏置) 3. 定义Softmax操作 4. 定义模型 5. 定义损失函数(交叉熵损失) 6. 定义优化算法(小批量随机梯度下降) 7. 训练模型 8. 评估模型 注意:由于用户要求从零开始,我们将避免直接使用PyTorch的nn.Module等高级功能,而是用基础张量操作实现。 步骤详细说明: 1. 准备数据集: - 使用Fashion-MNIST数据集,包含10个类别的图像。 - 我们将图像展平为向量,因为Softmax回归是线性模型,处理的是向量输入。 - 数据归一化(可选,但通常有助于训练)。 2. 初始化参数: - 权重W:形状为(输入特征数, 输出类别数),初始化为随机数(如正态分布)。 - 偏置b:形状为(输出类别数,),初始化为0。 3. Softmax函数: - 对于任意一个样本的预测向量(一个类别的得分),通过指数函数和归一化转换为概率分布。 - 公式:$ \text{softmax}(\mathbf{o})_i = \frac{\exp(o_i)}{\sum_j \exp(o_j)} $ 4. 模型定义: - 线性部分:$ \mathbf{O} = \mathbf{X} \mathbf{W} + \mathbf{b} $ - 然后对O进行softmax得到预测概率。 5. 损失函数(交叉熵损失): - 对于每个样本,我们取预测概率中对应真实标签的那个类别的概率的负对数。 - 公式:$ l(\mathbf{y}, \hat{\mathbf{y}}) = -\sum_{j} y_j \log \hat{y}_j $,其中y是one-hot向量,但实际上我们通常直接使用类别索引。 - 实现时,我们使用交叉熵损失的高效实现:先取预测概率中真实标签对应位置的概率,然后取负对数。 6. 优化算法:小批量随机梯度下降(SGD) - 按照小批量计算梯度,然后更新参数。 7. 训练循环: - 迭代多个周期(epoch),每个周期中遍历整个训练集(分小批量)。 - 每个小批量:前向传播计算损失,反向传播计算梯度,更新参数。 8. 评估:在测试集上计算准确率。 下面我们使用PyTorch张量操作来实现,但注意不使用自动求导(autograd)的高级封装,而是手动计算梯度。但为了方便,我们仍然会使用PyTorch的张量,因为用户要求从零实现,但可以使用基础的张量运算。 注意:由于手动计算梯度,我们需要自己推导梯度公式。 梯度推导(用于交叉熵损失和Softmax): 设: - $\mathbf{X}$: 一个批量的输入,形状为(batch_size, num_inputs) - $\mathbf{W}$: 权重,形状为(num_inputs, num_outputs) - $\mathbf{b}$: 偏置,形状为(num_outputs,) - $\mathbf{O} = \mathbf{X}\mathbf{W} + \mathbf{b}$,形状为(batch_size, num_outputs) - $\hat{\mathbf{Y}} = \text{softmax}(\mathbf{O})$,形状为(batch_size, num_outputs) - 损失函数:交叉熵损失。设真实标签为$\mathbf{y}$(每个元素是类别索引,形状为(batch_size,)),则损失为: $L = \frac{1}{n} \sum_{i} l^{(i)}$,其中$l^{(i)} = -\log(\hat{y}_{i, y_i})$ 梯度计算: 根据链式法则,我们有: - $\frac{\partial l^{(i)}}{\partial o_j} = \hat{y}_j - \mathbf{1}_{j=y_i}$ (这个公式是交叉熵损失对未归一化分数$o_j$的梯度) 所以,对于整个批量,我们有一个矩阵$\mathbf{\delta}$,其中第i行第j列的元素为: $\delta_{ij} = \hat{y}_{ij} - \mathbf{1}_{j=y_i}$ 注意:这里$\mathbf{1}_{j=y_i}$是指示函数,当j等于真实标签$y_i$时为1,否则为0。 因此,损失L对$\mathbf{O}$的梯度为:$\frac{\partial L}{\partial \mathbf{O}} = \frac{1}{n} (\hat{\mathbf{Y}} - \mathbf{I}_y)$,其中$\mathbf{I}_y$是一个矩阵,其第i行只有$y_i$列为1,其余为0(即one-hot编码的矩阵)。 然后,根据$\mathbf{O} = \mathbf{X}\mathbf{W} + \mathbf{b}$,我们可以计算: - $\frac{\partial L}{\partial \mathbf{W}} = \mathbf{X}^T \frac{\partial L}{\partial \mathbf{O}}$ - $\frac{\partial L}{\partial \mathbf{b}} = \sum_{i} \frac{\partial L}{\partial \mathbf{O}}_i$(按行求和,即每列求和,得到偏置的梯度) 因此,反向传播步骤: 1. 计算输出梯度:grad_output = (1/batch_size) * (y_hat - one_hot_y) # 注意:这里one_hot_y是真实标签的one-hot编码 2. 计算W的梯度:grad_W = X.T @ grad_output 3. 计算b的梯度:grad_b = grad_output.sum(axis=0) 然后使用梯度下降更新参数: W = W - learning_rate * grad_W b = b - learning_rate * grad_b 代码实现步骤: 1. 导入必要的库 2. 加载Fashion-MNIST数据集 3. 数据预处理:展平图像,归一化,创建数据迭代器(小批量) 4. 初始化参数 5. 定义softmax函数 6. 定义模型(前向传播) 7. 定义交叉熵损失函数(注意:这里我们使用手动计算梯度,所以损失函数只需要计算损失值,而梯度则按上述推导的公式计算) 8. 定义训练函数(包含梯度计算和参数更新) 9. 训练模型 10. 评估模型 注意:由于手动实现梯度,我们不会使用autograd。 下面开始写代码:</think>### 从零实现 Softmax 回归模型的步骤与代码 Softmax 回归是一种用于多分类问题的线性模型。以下是完整的实现步骤和 PyTorch 代码实现: #### **步骤 1:准备数据集** - 加载并预处理数据集 - 将图像展平为特征向量 - 数据归一化(可选) #### **步骤 2:初始化模型参数** - 初始化权重矩阵 $W$ 和偏置向量 $b$ - $W$ 的形状:`(输入特征数, 输出类别数)` - $b$ 的形状:`(输出类别数,)` #### **步骤 3:定义 Softmax 函数** $$\text{softmax}(\mathbf{o})_i = \frac{\exp(o_i)}{\sum_j \exp(o_j)}$$ #### **步骤 4:定义网络模型** $$ \mathbf{O} = \mathbf{X}W + b $$ $$ \hat{\mathbf{Y}} = \text{softmax}(\mathbf{O}) $$ #### **步骤 5:定义交叉熵损失函数** $$ l(\mathbf{y}, \hat{\mathbf{y}}) = -\sum_{j=1}^k y_j \log \hat{y}_j $$ #### **步骤 6实现梯度计算** 梯度公式: $$\frac{\partial l}{\partial o_j} = \hat{y}_j - \mathbf{1}_{j=y}$$ #### **步骤 7:实现参数更新** 使用小批量随机梯度下降: $$ W \leftarrow W - \eta \frac{\partial l}{\partial W} $$ $$ b \leftarrow b - \eta \frac{\partial l}{\partial b} $$ #### **步骤 8:训练循环** 迭代执行: 1. 前向传播计算预测值 2. 计算损失 3. 反向传播计算梯度 4. 更新参数 --- ### **完整代码实现 (PyTorch)** ```python import torch import torchvision from torchvision import transforms # 步骤 1: 准备数据集 def load_data_fashion_mnist(batch_size): """加载Fashion-MNIST数据集""" transform = transforms.ToTensor() train_iter = torch.utils.data.DataLoader( torchvision.datasets.FashionMNIST( root=&#39;./data&#39;, train=True, download=True, transform=transform), batch_size=batch_size, shuffle=True) test_iter = torch.utils.data.DataLoader( torchvision.datasets.FashionMNIST( root=&#39;./data&#39;, train=False, download=True, transform=transform), batch_size=batch_size, shuffle=False) return train_iter, test_iter # 步骤 2: 初始化参数 def init_params(num_inputs, num_outputs): """初始化权重和偏置""" W = torch.normal(0, 0.01, (num_inputs, num_outputs), requires_grad=True) b = torch.zeros(num_outputs, requires_grad=True) return W, b # 步骤 3: 定义Softmax函数 def softmax(O): """计算softmax""" O_exp = torch.exp(O) partition = O_exp.sum(dim=1, keepdim=True) return O_exp / partition # 广播机制 # 步骤 4: 定义模型 def net(X, W, b): """Softmax回归模型""" return softmax(torch.matmul(X.reshape(-1, W.shape[0]), W) + b) # 步骤 5: 定义交叉熵损失 def cross_entropy(y_hat, y): """交叉熵损失""" return -torch.log(y_hat[range(len(y_hat)), y]) # 步骤 6: 梯度计算与参数更新 def sgd(params, lr, batch_size): """小批量随机梯度下降""" with torch.no_grad(): for param in params: param -= lr * param.grad / batch_size param.grad.zero_() # 步骤 7: 训练函数 def train(train_iter, test_iter, num_epochs, lr): # 初始化参数 num_inputs = 28 * 28 # 图像展平后的维度 num_outputs = 10 # 10个类别 W, b = init_params(num_inputs, num_outputs) for epoch in range(num_epochs): for X, y in train_iter: # 前向传播 y_hat = net(X, W, b) # 计算损失 loss = cross_entropy(y_hat, y).mean() # 反向传播 loss.backward() # 更新参数 (手动实现梯度下降) sgd([W, b], lr, batch_size=X.shape[0]) # 每个epoch评估准确率 acc = evaluate_accuracy(test_iter, W, b) print(f"Epoch {epoch+1}, Test Acc: {acc:.4f}") def evaluate_accuracy(data_iter, W, b): """评估模型准确率""" correct, total = 0, 0 for X, y in data_iter: y_hat = net(X, W, b) predicted = torch.argmax(y_hat, dim=1) correct += (predicted == y).sum().item() total += y.numel() return correct / total # 超参数设置 batch_size = 256 num_epochs = 10 learning_rate = 0.1 # 执行训练 train_iter, test_iter = load_data_fashion_mnist(batch_size) train(train_iter, test_iter, num_epochs, learning_rate) ``` ### **关键实现说明** 1. **数据预处理**:使用 `ToTensor()` 将图像转换为张量并归一化到 $[0,1]$ 区间 2. **参数初始化**:权重 $W$ 从正态分布采样,偏置 $b$ 初始化为零 3. **梯度计算**:通过 `loss.backward()` 自动计算梯度 4. **手动优化**:`sgd()` 函数显式实现参数更新逻辑 5. **评估指标**:使用分类准确率评估模型性能 ### **模型特点** - Softmax 回归是线性模型,决策边界是超平面 - 输出层将线性变换结果转化为概率分布 - 交叉熵损失衡量预测概率与真实标签的差异 通过这个实现,你可以理解 Softmax 回归的核心机制,后续可在此基础上扩展更复杂的神经网络模型[^1][^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值