从0开始深度学习(9)——softmax回归的逐步实现

文章使用Fashion-MNIST数据集,做一次分类识别任务

Fashion-MNIST中包含的10个类别,分别为:

t-shirt(T恤)、trouser(裤子)、pullover(套衫)、dress(连衣裙)、coat(外套)

sandal(凉鞋)、shirt(衬衫)、sneaker(运动鞋)、bag(包)、ankle boot(短靴)

0 图像数据

0.1 读取展示数据

python 复制代码
import torch
import torchvision
from torch.utils import data
from torchvision import transforms
import matplotlib.pyplot as plt

# 下载数据集 ,60,000 个训练样本和 10,000 个测试样本,每个样本包含一张28*28的灰度图和一个标签

trans = transforms.ToTensor()
mnist_train = torchvision.datasets.FashionMNIST(
    root="D/DL_Data/Fashion-MNIST", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.FashionMNIST(
    root="D/DL_Data/Fashion-MNIST", train=False, transform=trans, download=True)
    
print("test:",len(mnist_test))
print("train:",len(mnist_train))

# 获取第一个样本的图像和标签
image, label = mnist_train[0]
print("图像的形状:", image.shape)
print("标签:", label)

0.2 可视化图像

python 复制代码
# 可视化
def show_img():
    class_names = [
    'T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
    'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot'
    ]
    # 可视化前5张图片
    fig, axes = plt.subplots(1, 10, figsize=(15, 3))

    for i in range(10):
        # 获取第 i 个样本的图像和标签
        image, label = mnist_train[i]
    
        # 将图像从 Tensor 转换回 numpy 数组,并移除通道维度
        image_np = image.squeeze().numpy()
    
        # 在子图中显示图像
        axes[i].imshow(image_np, cmap='gray')
        axes[i].set_title(f'Label: {class_names[label]}')
        axes[i].axis('off')  # 关闭坐标轴

    plt.tight_layout()
    plt.show()
    
show_img()

0.3 整合为数据加载模块

python 复制代码
def load_data_fashion_mnist(batch_size, resize=None):  #@save
    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()))
                            
train_iter, test_iter = load_data_fashion_mnist(256, resize=28)

1 初始化参数模型

我们选择把 28 ∗ 28 28*28 28∗28的图片展开成 1 ∗ 784 1*784 1∗784的向量,认为每个像素位置都是一个特征,所以输入是784维 ,输出是10个类别标签,所以输出是10维

因为softmaxhi回归类似于线性回归,所以权重 w w w应该是 784 ∗ 10 784*10 784∗10 的矩阵,偏置是 1 ∗ 10 1*10 1∗10 的行向量,接下来如同线性回归中一样,使用正太分布初始化权重,偏置初始化为0:

python 复制代码
num_inputs = 784
num_outputs = 10

W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
b = torch.zeros(num_outputs, requires_grad=True)

2 定义softmax操作

回顾一下softmax的公式:

由三个步骤组成:

  1. 对每个项目求幂
  2. 将每一行求和(小批量样本中,每个样本是一行),得到每个样本的规范化常数。
  3. 将每一行除以其规范化常数,确保结果的和为1。
python 复制代码
# 定义softmax操作
def softmax(x):
    x_exp=torch.exp(x)
    x_exp_sum=x_exp.sum(1,keepdim=True)
    return x_exp/x_exp_sum

3 定义模型

python 复制代码
# 定义模型
def net(x):
    x = x.reshape(-1, w.shape[0])  # 将图片重塑为 [batch_size, 784]
    temp = torch.matmul(x, w)
    temp = temp + b
    return softmax(temp)

4 定义损失函数

使用从0开始深度学习(8)------softmax回归提到的交叉熵损失函数

python 复制代码
# 定义损失函数
def cross_entropy(y_hat, y): # 预测值、真实值
    return - torch.log(y_hat[range(len(y_hat)), y]) # 计算负对数似然

cross_entropy(y_hat, y)

5 分类精度

分类精度即正确预测数量与总预测数量之比。

python 复制代码
def compute_accuracy(y_hat, y):  # 预测值、真实值
    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  # 将预测值 y_hat 与真实标签 y 进行比较,生成一个布尔张量 cmp
    return float(cmp.type(y.dtype).sum())

# 计算在指定数据集上模型的准确率
def evaluate_accuracy(net, data_iter):  
    if isinstance(net, torch.nn.Module):
        net.eval()  # 将模型设置为评估模式
    metric = Accumulator(2)  # 累加多个变量的总和。这里初始化了一个包含两个元素的累加器,分别用来存储正确预测的数量和总的预测数量。
    with torch.no_grad():
        for X, y in data_iter:
            metric.add(compute_accuracy(net(X), y), y.numel())
    return metric[0] / metric[1]

class Accumulator:  #@save
    """在n个变量上累加"""
    def __init__(self, n):
        self.data = [0.0] * n

    def 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]
        
# 评估模型
accuracy = evaluate_accuracy(net, test_iter)
print(f"Test Accuracy: {accuracy:.4f}")

6 定义优化器

python 复制代码
# 定义优化器
def sgd(params, lr, batch_size):
    with torch.no_grad():
        for param in params:
            param -= lr * param.grad / batch_size
            param.grad.zero_()

7 训练

python 复制代码
# 训练模型
def train_epoch(net, train_iter, loss, updater):
    if isinstance(net, torch.nn.Module):
        net.train()  # 将模型设置为训练模式
    metric = Accumulator(3)  # 训练损失总和、训练准确度总和、样本数
    for X, y in train_iter:
        y_hat = net(X)
        l = loss(y_hat, y).mean()
        if isinstance(updater, torch.optim.Optimizer):
            updater.zero_grad()
            l.backward()
            updater.step()
        else:
            l.backward()
            updater([w, b], lr, batch_size)
        metric.add(float(l) * y.numel(), compute_accuracy(y_hat, y), y.numel())
    return metric[0] / metric[2], metric[1] / metric[2]

def train(net, train_iter, test_iter, loss, num_epochs, updater):
    for epoch in range(num_epochs):
        train_metrics = train_epoch(net, train_iter, loss, updater)
        test_acc = evaluate_accuracy(net, test_iter)
        print(f'Epoch {epoch + 1}: Train Loss {train_metrics[0]:.3f}, Train Acc {train_metrics[1]:.3f}, Test Acc {test_acc:.3f}')

# 训练模型
updater = lambda params, lr, batch_size: sgd(params, lr, batch_size)
train(net, train_iter, test_iter, cross_entropy, num_epochs, updater)

8 预测

python 复制代码
# 定义 Fashion-MNIST 标签的文本描述
def get_fashion_mnist_labels(labels):
    text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
                   'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
    return [text_labels[int(i)] for i in labels]

# 预测并显示结果
def predict(net, test_iter, n=6):
    for X, y in test_iter:
        break  # 只取一个批次的数据
    trues = get_fashion_mnist_labels(y)
    preds = get_fashion_mnist_labels(net(X).argmax(axis=1))
    titles = [true + '\n' + pred for true, pred in zip(trues, preds)]
    n = min(n, X.shape[0])
    fig, axs = plt.subplots(1, n, figsize=(12, 3))
    for i in range(n):
        axs[i].imshow(X[i].permute(1, 2, 0).squeeze().numpy(), cmap='gray')
        axs[i].set_title(titles[i])
        axs[i].axis('off')
    plt.show()

# 调用预测函数
predict(net, test_iter, n=6)
相关推荐
B站计算机毕业设计超人38 分钟前
计算机毕业设计PySpark+Hadoop中国城市交通分析与预测 Python交通预测 Python交通可视化 客流量预测 交通大数据 机器学习 深度学习
大数据·人工智能·爬虫·python·机器学习·课程设计·数据可视化
学术头条42 分钟前
清华、智谱团队:探索 RLHF 的 scaling laws
人工智能·深度学习·算法·机器学习·语言模型·计算语言学
18号房客1 小时前
一个简单的机器学习实战例程,使用Scikit-Learn库来完成一个常见的分类任务——**鸢尾花数据集(Iris Dataset)**的分类
人工智能·深度学习·神经网络·机器学习·语言模型·自然语言处理·sklearn
feifeikon1 小时前
机器学习DAY3 : 线性回归与最小二乘法与sklearn实现 (线性回归完)
人工智能·机器学习·线性回归
游客5201 小时前
opencv中的常用的100个API
图像处理·人工智能·python·opencv·计算机视觉
古希腊掌管学习的神1 小时前
[机器学习]sklearn入门指南(2)
人工智能·机器学习·sklearn
Ven%1 小时前
如何在防火墙上指定ip访问服务器上任何端口呢
linux·服务器·网络·深度学习·tcp/ip
凡人的AI工具箱1 小时前
每天40分玩转Django:Django国际化
数据库·人工智能·后端·python·django·sqlite
IT猿手1 小时前
最新高性能多目标优化算法:多目标麋鹿优化算法(MOEHO)求解TP1-TP10及工程应用---盘式制动器设计,提供完整MATLAB代码
开发语言·深度学习·算法·机器学习·matlab·多目标算法
咸鱼桨2 小时前
《庐山派从入门到...》PWM板载蜂鸣器
人工智能·windows·python·k230·庐山派