Softmax回归(分类问题)学习记录

使用Softmax与交叉熵损失实现简单的MNIST手写数字数据集分类问题。

Softmax

 Softmax 是一种常用于多分类任务的激活函数,它将一个包含任意实数的向量(通常称为 logits)转换为一个概率分布:输出的每个元素都在 0 到 1 之间,且所有元素之和为 1。

在深度学习中,Softmax 通常接在神经网络最后一层,配合交叉熵损失(CrossEntropyLoss)使用。

值得注意的是,PyTorch 的 nn.CrossEntropyLoss 内部已自动应用了 Softmax 的数值稳定版本(LogSoftmax + NLLLoss),因此模型输出应直接返回原始 logits,无需显式添加 Softmax 层。

交叉熵损失

交叉熵损失(Cross-Entropy Loss)是多分类任务中最常用的损失函数,用于衡量模型预测的概率分布与真实标签(通常为 one-hot 编码或类别索引)之间的差异。

在 PyTorch 中,nn.CrossEntropyLoss 接收原始 logits(未经过 Softmax 的网络输出)和整数形式的真实标签,内部自动通过数值稳定的 LogSoftmax 计算负对数似然,从而避免直接计算 Softmax 可能带来的数值溢出问题。交叉熵越小 ,表示预测分布越接近真实分布,模型性能越好。

代码

完整代码如下:

python 复制代码
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import os


data_root = r'D:\pycode\write\Minist1\w1'

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])

train_dataset = datasets.MNIST(root=data_root, train=True, download=False, transform=transform)
test_dataset = datasets.MNIST(root=data_root, train=False, download=False, transform=transform)

batch_size = 256
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)


class SoftmaxRegression(nn.Module):
    def __init__(self, input_dim=28*28, num_classes=10):
        super(SoftmaxRegression, self).__init__()
        self.linear = nn.Linear(input_dim, num_classes)

    def forward(self, x):
        x = x.view(x.size(0), -1)
        return self.linear(x)  # logits


def train(model, loader, criterion, optimizer, device):
    model.train()
    total_loss = 0
    correct = 0
    total = 0
    for data, target in loader:
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()

        total_loss += loss.item()
        pred = output.argmax(dim=1)
        correct += pred.eq(target).sum().item()
        total += target.size(0)
    return total_loss / len(loader), correct / total

def test(model, loader, criterion, device):
    model.eval()
    total_loss = 0
    correct = 0
    total = 0
    with torch.no_grad():
        for data, target in loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            loss = criterion(output, target)
            total_loss += loss.item()
            pred = output.argmax(dim=1)
            correct += pred.eq(target).sum().item()
            total += target.size(0)
    return total_loss / len(loader), correct / total


if __name__ == "__main__":
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    print(f"Using device: {device}")

    model = SoftmaxRegression().to(device)
    criterion = nn.CrossEntropyLoss()
    optimizer = torch.optim.SGD(model.parameters(), lr=0.1)

    epochs = 10
    for epoch in range(1, epochs + 1):
        train_loss, train_acc = train(model, train_loader, criterion, optimizer, device)
        test_loss, test_acc = test(model, test_loader, criterion, device)
        print(f"Epoch {epoch:2d} | "
              f"Train Loss: {train_loss:.4f}, Acc: {train_acc:.4f} | "
              f"Test Loss: {test_loss:.4f}, Acc: {test_acc:.4f}")

    print("\n训练完成!")

导入库

python 复制代码
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import os

torch:PyTorch 核心库,提供张量计算和自动微分。

nn:神经网络模块,包含层(如 Linear)、损失函数等。

DataLoader:用于批量加载数据,支持打乱、多线程等。

datasets, transforms:torchvision 提供的标准数据集(如 MNIST)和数据预处理工具。

os:用于路径操作。

数据加载与预处理

python 复制代码
data_root = r'D:\pycode\write\Minist1\w1'

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])

train_dataset = datasets.MNIST(root=data_root, train=True, download=False, transform=transform)
test_dataset = datasets.MNIST(root=data_root, train=False, download=False, transform=transform)

batch_size = 256
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

data_root:指定 MNIST 数据集的父目录。PyTorch 会在该目录下寻找 MNIST/raw/ 中的 .gz 文件。

transforms.Compose:

ToTensor():将 PIL 图像(0~255)转为 FloatTensor(0.0~1.0),并调整维度为 C, H, W。

Normalize(mean, std):对每个像素做标准化:(x - mean) / std。MNIST 的全局均值 ≈ 0.1307,标准差 ≈ 0.3081。

datasets.MNIST:

train=True/False:分别加载训练集(6万张)和测试集(1万张)。

download=False:不联网下载,使用本地数据。

DataLoader:

batch_size=256:每次输入 256 张图片。

shuffle=True(仅训练集):打乱顺序,提升泛化能力;测试集无需打乱。

模型定义 ------ Softmax 回归

python 复制代码
class SoftmaxRegression(nn.Module):
    def __init__(self, input_dim=28*28, num_classes=10):
        super(SoftmaxRegression, self).__init__()
        self.linear = nn.Linear(input_dim, num_classes)

    def forward(self, x):
        x = x.view(x.size(0), -1)  # [B, 1, 28, 28] → [B, 784]
        return self.linear(x)      # 输出 logits,形状 [B, 10]

这是一个没有隐藏层的线性分类器,即 Softmax 回归。

view(x.size(0), -1):将每张 28×28 的图像展平为 784 维向量。

nn.Linear(784, 10):学习一个权重矩阵W和偏置b,输出 logits。

注:不加 Softmax,因为 CrossEntropyLoss 内部会处理,直接返回 logits 即可。

训练函数

python 复制代码
def train(model, loader, criterion, optimizer, device):
    model.train()  # 启用训练模式(如 Dropout、BatchNorm)
    total_loss = 0
    correct = 0
    total = 0
    for data, target in loader:
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()      # 清空梯度
        output = model(data)       # 前向传播
        loss = criterion(output, target)  # 计算损失
        loss.backward()            # 反向传播
        optimizer.step()           # 更新参数

        total_loss += loss.item()
        pred = output.argmax(dim=1)           # 预测类别
        correct += pred.eq(target).sum().item()  # 统计正确数
        total += target.size(0)
    return total_loss / len(loader), correct / total

测试函数

python 复制代码
def test(model, loader, criterion, device):
    model.eval()  # 启用评估模式
    total_loss = 0
    correct = 0
    total = 0
    with torch.no_grad():  # 禁用梯度计算,节省内存、加速
        for data, target in loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            loss = criterion(output, target)
            total_loss += loss.item()
            pred = output.argmax(dim=1)
            correct += pred.eq(target).sum().item()
            total += target.size(0)
    return total_loss / len(loader), correct / total

主程序 ------ 训练循环

python 复制代码
if __name__ == "__main__":
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    print(f"Using device: {device}")

    model = SoftmaxRegression().to(device)
    criterion = nn.CrossEntropyLoss()
    optimizer = torch.optim.SGD(model.parameters(), lr=0.1)

    epochs = 10
    for epoch in range(1, epochs + 1):
        train_loss, train_acc = train(model, train_loader, criterion, optimizer, device)
        test_loss, test_acc = test(model, test_loader, criterion, device)
        print(f"Epoch {epoch:2d} | "
              f"Train Loss: {train_loss:.4f}, Acc: {train_acc:.4f} | "
              f"Test Loss: {test_loss:.4f}, Acc: {test_acc:.4f}")

    print("\n训练完成!")

测试/训练损失

训练损失(Training Loss) 是模型在训练数据上预测结果与真实标签之间的平均误差,反映了模型对已见数据的拟合程度。训练过程中,优化器通过反向传播不断调整参数以最小化该损失,因此训练损失通常随着训练轮次增加而逐渐下降。

测试损失(Test Loss) (测试误差、泛化误差)则是模型在未参与训练的测试集上的平均误差,用于评估模型的泛化能力------即对新数据的适应能力。

理想的训练状态是训练损失和测试损失都较低且接近;若测试损失明显高于训练损失,往往说明模型过拟合,记住了训练数据的细节却未能学到通用规律。

相关推荐
LaughingZhu6 天前
Product Hunt 每日热榜 | 2026-09-19
人工智能·深度学习·神经网络·搜索引擎·百度
猎头南楼6 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
高洁016 天前
未来三年,AI 落地的五个确定性判断一
人工智能·深度学习·机器学习·transformer·知识图谱
`流年づ6 天前
人工智能学习笔记 - 补充
人工智能·笔记·深度学习·学习
成为深度学习高手6 天前
CrossLinear:即插即用的跨相关嵌入,让线性模型也能用好外生变量
人工智能·python·深度学习·数据挖掘
科技苑6 天前
如何打造一个高效的全栖内容制作生态系统?
深度学习
江屿风6 天前
【认识深度学习】【深度学习分类与计算机视觉任务要点解析】流食般投喂
大数据·人工智能·深度学习·计算机视觉·目标跟踪·自然语言处理·语音识别
童园管理札记6 天前
从政策驱动到课堂落地:2026年“人工智能+教育”全景解读与技术实践指南
人工智能·python·深度学习·职场和发展·学习方法
Ivanqhz6 天前
SVD++算法
java·服务器·网络·深度学习·神经网络