<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='./data', train=True, download=True, transform=transform),
batch_size=batch_size, shuffle=True)
test_iter = torch.utils.data.DataLoader(
torchvision.datasets.FashionMNIST(
root='./data', 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]。
---