从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)
相关推荐
lynn-fish7 分钟前
机器人对人工智能未来发展的影响
人工智能·机器人
旗晟机器人10 分钟前
A4-C四驱高防变电站巡检机器人
大数据·人工智能·安全·机器人
weixi_kelaile52014 分钟前
ai智能语音电销机器人可以做哪些事情?
java·linux·服务器·人工智能·机器人·云计算·腾讯云
雪兽软件5 小时前
人工智能和大数据如何改变企业?
大数据·人工智能
UMS攸信技术7 小时前
汽车电子行业数字化转型的实践与探索——以盈趣汽车电子为例
人工智能·汽车
ws2019077 小时前
聚焦汽车智能化与电动化︱AUTO TECH 2025 华南展,以展带会,已全面启动,与您相约11月广州!
大数据·人工智能·汽车
堇舟8 小时前
斯皮尔曼相关(Spearman correlation)系数
人工智能·算法·机器学习
爱写代码的小朋友8 小时前
使用 OpenCV 进行人脸检测
人工智能·opencv·计算机视觉
Cici_ovo9 小时前
摄像头点击器常见问题——摄像头视窗打开慢
人工智能·单片机·嵌入式硬件·物联网·计算机视觉·硬件工程