基于Fashion‐MNIST数据集和MNIST数据集的多层感知机(MLP)两个案例代码实现

前言

这篇文章是本人在学习 《动手学深度学习》这本书的 4.2. 多层感知机的从零开始实现4.3. 多层感知机的简洁实现这两节的记录。以上两节的多层感知机模型代码实现是基于Fashion‐MNIST数据集的,对此在文章的最后,添加了一个案例使用多层感知机模型实现MNIST数据集分类。

多层感知机的从零开始实现

1、导入所需要的库

import torch
from torch import nn
from d2l import torch as d2l

2、读取Fashion‐MNIST数据集

# 读取Fashion‐MNIST数据集
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

3、初始化模型参数

Fashion‐MNIST中的每个图像由28×28=784个灰度像素值组成。所有图像共分为10个类别。
忽略像素之间的空间结构,我们可以将每个图像视为具有784个输入特征10个类简单分类数据集
我们将实现一个具有单隐藏层的多层感知机,它包含256个隐藏单元
对于每一层我们都要记录一个权重矩阵和一个偏置向量。

num_inputs, num_outputs, num_hiddens = 784, 10, 256
W1 = nn.Parameter(torch.randn(
num_inputs, num_hiddens, requires_grad=True) * 0.01)
b1 = nn.Parameter(torch.zeros(num_hiddens, requires_grad=True))
W2 = nn.Parameter(torch.randn(
num_hiddens, num_outputs, requires_grad=True) * 0.01)
b2 = nn.Parameter(torch.zeros(num_outputs, requires_grad=True))
params = [W1, b1, W2, b2]

4、选择激活函数

为了确保我们对模型的细节了如指掌,我们将实现ReLU激活函数,而不是直接调用内置的relu函数。

def relu(X):
    a = torch.zeros_like(X)
    return torch.max(X, a)

5、定义模型

因为我们忽略了空间结构,所以我们使用reshape将每个二维图像转换为一个长度为num_inputs的向量。

def net(X):
    X = X.reshape((-1, num_inputs))
    H = relu(X@W1 + b1) # 这里“@”代表矩阵乘法
    return (H@W2 + b2)

6、定义损失函数

在这里我们直接使用高级API中的内置函数来计
算softmax和交叉熵损失。

loss = nn.CrossEntropyLoss()

7、训练

num_epochs, lr = 10, 0.1
updater = torch.optim.SGD(params, lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, updater)

8、模型评估

为了对学习到的模型进行评估,我们将在一些测试数据上应用这个模型。

d2l.predict_ch3(net, test_iter)

9、图像显示

# 图像显示
d2l.plt.show()

10、效果展示

在这里插入图片描述
在这里插入图片描述

完整代码

import torch
from torch import nn
from d2l import torch as d2l

# 读取Fashion‐MNIST数据集
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

# 初始化模型参数
num_inputs, num_outputs, num_hiddens = 784, 10, 256
W1 = nn.Parameter(torch.randn(
num_inputs, num_hiddens, requires_grad=True) * 0.01)
b1 = nn.Parameter(torch.zeros(num_hiddens, requires_grad=True))
W2 = nn.Parameter(torch.randn(
num_hiddens, num_outputs, requires_grad=True) * 0.01)
b2 = nn.Parameter(torch.zeros(num_outputs, requires_grad=True))
params = [W1, b1, W2, b2]

# 激活函数
def relu(X):
    a = torch.zeros_like(X)
    return torch.max(X, a)

# 定义模型
def net(X):
    X = X.reshape((-1, num_inputs))
    H = relu(X@W1 + b1) # 这里“@”代表矩阵乘法
    return (H@W2 + b2)

# 损失函数
loss = nn.CrossEntropyLoss()

# 训练
num_epochs, lr = 10, 0.1
updater = torch.optim.SGD(params, lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, updater)

# 模型评估
d2l.predict_ch3(net, test_iter)

# 图像显示
d2l.plt.show()

多层感知机的简洁实现

Fashion‐MNIST数据集介绍

Fashion‐MNIST是一个服装分类数据集,由10个类别的图像组成,每个类别由训练数据集(traindataset)中的6000张图像和测试数据集(testdataset)中的1000张图像组成。
因此,训练集测试集分别包含6000010000张图像。测试数据集不会用于训练,只用于评估模型性能。
每个输入图像的高度和宽度均为28像素。数据集由灰度图像组成,其通道数为1。

1、导入所需要的库

import torch
from torch import nn
from d2l import torch as d2l

2、读取Fashion‐MNIST数据集

# 读取Fashion‐MNIST数据集
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

3、定义模型

添加了2个全连接层。第一层是隐藏层,它包含256个隐藏单元,并使用了ReLU激活函数。第二层是输出层。

num_imputs = 784 # 输入特征个数
num_outputs = 10 # 输出类别个数
num_hiddens = 256 # 隐藏层的大小,即隐藏单元的个数,256个

"""
定义神经网络模型
nn.Flatten()即降维打击函数,将图片的多维度数据降维为1维
nn.Linear(num_imputs, num_hiddens)定义从输入层到隐藏层的线性模型
nn.ReLU()即激活函数,去除样本数据中的所有负数值
nn.Linear(num_hiddens, num_outputs)定义从隐藏层到输出层的线性模型
"""
net = nn.Sequential(nn.Flatten(),
                   nn.Linear(num_imputs, num_hiddens),
                   nn.ReLU(),
                   nn.Linear(num_hiddens,num_outputs))

4、初始化权重参数

#参数权重初始化函数,以正态分布初始化神经网络的参数
def init_weights(m):
    if type(m)==nn.Linear:
        nn.init.normal_(m.weight, std=0.01)

net.apply(init_weights)

5、定义损失函数

# 定义内置的交叉熵损失函数
loss = nn.CrossEntropyLoss()

6、定义优化算法

lr = 0.1

# 使用小批量梯度下降算法
trainer = torch.optim.SGD(net.parameters(), lr=lr)

7、训练

num_epochs = 10

# 训练多层感知机模型
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

8、模型评估

# 模型评估,在一些测试数据上应用这个模型
d2l.predict_ch3(net, test_iter)

9、图像显示

# 图像显示
d2l.plt.show()

10、效果展示

在这里插入图片描述
在这里插入图片描述

完整代码

import torch
from torch import nn
from d2l import torch as d2l

# 读取Fashion‐MNIST数据集
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

num_imputs = 784 # 输入特征个数
num_outputs = 10 # 输出类别个数
num_hiddens = 256 # 隐藏层的大小,即隐藏单元的个数,256个

"""
定义神经网络模型
nn.Flatten()即降维打击函数,将图片的多维度数据降维为1维
nn.Linear(num_imputs, num_hiddens)定义从输入层到隐藏层的线性模型
nn.ReLU()即激活函数,去除样本数据中的所有负数值
nn.Linear(num_hiddens, num_outputs)定义从隐藏层到输出层的线性模型
"""
net = nn.Sequential(nn.Flatten(),
                   nn.Linear(num_imputs, num_hiddens),
                   nn.ReLU(),
                   nn.Linear(num_hiddens,num_outputs))

#参数权重初始化函数,以正态分布初始化神经网络的参数
def init_weights(m):
    if type(m)==nn.Linear:
        nn.init.normal_(m.weight, std=0.01)

net.apply(init_weights)

# 定义内置的交叉熵损失函数
loss = nn.CrossEntropyLoss()

lr = 0.1

# 使用小批量梯度下降算法
trainer = torch.optim.SGD(net.parameters(), lr=lr)

num_epochs = 10

# 训练多层感知机模型
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

# 模型评估,在一些测试数据上应用这个模型
d2l.predict_ch3(net, test_iter)

# 图像显示
d2l.plt.show()

多层感知机实现MNIST数据集分类

MNIST数据集介绍

MNIST数据集共有70000张图像,其中训练集60000张测试集10000张。所有图像都是28×28灰度图像,每张图像包含一个手写数字(0 ~ 9)。

1、导入所需要的库

import torch
import torch.nn as nn
import torchvision.transforms as transforms
import torchvision.datasets as datasets
import matplotlib.pyplot as plt

2、加载MNIST数据集

# 加载MNIST数据集
train_dataset = datasets.MNIST(root='./dataset', train=True, transform=transforms.ToTensor(), download=True)
test_dataset = datasets.MNIST(root='./dataset', train=False, transform=transforms.ToTensor(), download=True)

参数说明如下:

  • root=‘./dataset’: 指定数据集的存储路径。在这里,数据集将被下载到当前目录下的dataset文件夹中。
  • train=True: 表明我们正在获取训练数据集。如果设置为False,则会获取测试数据集。
  • transform=transforms.ToTensor(): 这个参数定义了对数据进行的预处理操作。在这个例子中,我们使用transforms.ToTensor()将PIL图像或NumPy数组转换为PyTorch张量。这是因为PyTorch模型需要输入的是张量形式的数据。
  • download=True: 这个参数指示是否从互联网下载数据集。当设置为True时,如果本地没有数据集,那么程序会自动从网上下载并将其保存到指定的路径。

3、定义MLP模型

# 定义MLP模型
class MLP(nn.Module):
    def __init__(self, input_size, hidden_size, num_classes):
        super(MLP, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(hidden_size, num_classes)

    def forward(self, x):
        out = self.fc1(x)
        out = self.relu(out)
        out = self.fc2(out)
        return out

参数说明如下:
input_size:输入层的大小
hidden_size:隐藏层的大小
num_classes:输出层的大小

def _init_(self, input_size, hidden_size, num_classes): 这是MLP类的初始化方法,用于设置模型的参数
def forward(self, x): 这是MLP类的前向传播方法

在这个例子中,MLP模型有两个全连接层。在初始化函数_init_()中,我们定义了两个全连接层self.fc1和self.fc2,一个ReLU激活函数self.relu。
在前向传播函数forward()中,将输入x传递给第一个全连接层(fc1),然后通过ReLU激活函数,再传递给第二个全连接层(fc2),最后得到输出out

这段代码定义了一个简单的两层全连接神经网络,其中包含一个隐藏层(fc1)和一个输出层(fc2),以及一个ReLU激活函数。

4、设置超参数

# 设置超参数
input_size = 784
hidden_size = 500
num_classes = 10
num_epochs = 5
batch_size = 100
learning_rate = 0.001

参数说明如下:
input_size:输入图像的大小(28x28=784)
hidden_size:隐藏层的大小(隐藏层中神经元的数量)
num_classes:输出的类别数(这里是10个数字)
num_epochs:训练的轮数
batch_size:每批训练样本的数量
learning_rate:学习率

5、准备数据加载器

# 准备数据加载器,用于将数据分成小批次进行训练和测试
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)

参数说明如下:

  • dataset=train_dataset:指定要加载的数据集。
  • batch_size=batch_size:指定每批加载的数据量。
  • shuffle=True:指定是否在每个epoch开始时打乱数据。当设置为True时,数据在每个epoch开始时都会被打乱。
  • 在test_loader的数据加载器中,shuffle参数被设置为False,因为在测试阶段,我们通常不需要打乱数据。

6、实例化模型

model = MLP(input_size, hidden_size, num_classes)

7、定义损失函数和优化器

criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

8、训练

# 训练模型
total_step = len(train_loader)# 计算训练数据加载器的长度,也就是总的训练步数。每一步对应于一个批次的训练数据。
for epoch in range(num_epochs):
    for i, (images, labels) in enumerate(train_loader):
    	# 将图像数据重塑为一个二维的张量,其中每一行代表一个扁平化的图像。
        images = images.reshape(-1, 28 * 28)

        # 前向传播和计算损失
        outputs = model(images)
        loss = criterion(outputs, labels)

        # 反向传播和优化
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
		
		# 每100步打印一次当前的损失值
        if (i + 1) % 100 == 0:
            print(f'Epoch [{epoch + 1}/{num_epochs}], Step [{i + 1}/{total_step}], Loss: {loss.item()}')

这段代码实现了在每个训练周期中,进行前向传播、计算损失、反向传播和参数更新等步骤。

9、模型评估

# 在测试集上测试模型
model.eval() # 将模型设置为评估模式
with torch.no_grad():
    correct = 0 # 初始化正确预测的数量
    total = 0 # 初始化总预测的数量
    for images, labels in test_loader:
        images = images.reshape(-1, 28 * 28)
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0) # 累加总预测的数
        correct += (predicted == labels).sum().item() # 累加正确预测的数量

    print(f'该模型在10000张测试图像上的准确性: {100 * correct / total}%')

这段代码实现了在测试集上测试模型的过程,包括前向传播、计算预测结果和统计准确率等步骤。

  • _, predicted = torch.max(outputs.data, 1)代码解释:
    在Python中,下划线 _ 通常用作临时或不重要的变量的名称。
    torch.max 函数返回两个值:一个是最大值,另一个是最大值的索引。由于我们只关心预测的类别(即最大值的索引),所以我们使用 _ 来忽略最大值,然后将最大值的索引赋值给 predicted。
  • 举一个简单的例子:
import torch
# 假设我们有一个模型的输出outputs,它是一个包含三个元素的tensor,每个元素都是一个概率分布
outputs = torch.tensor([[0.1, 0.6, 0.3],
                        [0.3, 0.2, 0.5],
                        [0.4, 0.1, 0.5]])

# 使用torch.max函数来找出每个样本的概率最大的类别
# torch.max函数会返回两个值:第一个是所有元素中的最大值,第二个是最大值的索引。
_, predicted = torch.max(outputs.data, 1)

print(outputs.data)
print(predicted)

运行结果如下:

tensor([[0.1000, 0.6000, 0.3000],
        [0.3000, 0.2000, 0.5000],
        [0.4000, 0.1000, 0.5000]])
tensor([1, 2, 2])

在这个例子中,我们只关心最大值的索引,所以我们将第一个返回值赋给了_(这是一个惯例,用来表示我们不关心这个值),将第二个返回值赋给了predicted。
运行结果意味着对于第一个样本,最可能的类别是1;对于第二个样本,最可能的类别是2;对于第三个样本,最可能的类别是2。

10、效果展示

images, labels = next(iter(test_loader))
images = images[:5]
labels = labels[:5]
 
fig, axes = plt.subplots(1, 5, figsize=(10,2))
 
for i in range(5):
    image = images[i].numpy().squeeze()
    axes[i].imshow(image, cmap='gray')
    axes[i].axis('off')
    axes[i].set_title(f'Ground Truth: {labels[i]}')
 
plt.show()

我们对一些图像示例进行效果展示,代码运行结果如下:

Epoch [1/5], Step [100/600], Loss: 0.3604086637496948
Epoch [1/5], Step [200/600], Loss: 0.2783631682395935
Epoch [1/5], Step [300/600], Loss: 0.29356688261032104
Epoch [1/5], Step [400/600], Loss: 0.13155366480350494
Epoch [1/5], Step [500/600], Loss: 0.18544769287109375
Epoch [1/5], Step [600/600], Loss: 0.06006329879164696
Epoch [2/5], Step [100/600], Loss: 0.11900061368942261
Epoch [2/5], Step [200/600], Loss: 0.19561482965946198
Epoch [2/5], Step [300/600], Loss: 0.07113198935985565
Epoch [2/5], Step [400/600], Loss: 0.12459728121757507
Epoch [2/5], Step [500/600], Loss: 0.13040272891521454
Epoch [2/5], Step [600/600], Loss: 0.09031425416469574
Epoch [3/5], Step [100/600], Loss: 0.09412173926830292
Epoch [3/5], Step [200/600], Loss: 0.0353107750415802
Epoch [3/5], Step [300/600], Loss: 0.03482735529541969
Epoch [3/5], Step [400/600], Loss: 0.036286383867263794
Epoch [3/5], Step [500/600], Loss: 0.07363829016685486
Epoch [3/5], Step [600/600], Loss: 0.03800008445978165
Epoch [4/5], Step [100/600], Loss: 0.06861492246389389
Epoch [4/5], Step [200/600], Loss: 0.036878302693367004
Epoch [4/5], Step [300/600], Loss: 0.10551707446575165
Epoch [4/5], Step [400/600], Loss: 0.026761818677186966
Epoch [4/5], Step [500/600], Loss: 0.009054150432348251
Epoch [4/5], Step [600/600], Loss: 0.052843380719423294
Epoch [5/5], Step [100/600], Loss: 0.02340124174952507
Epoch [5/5], Step [200/600], Loss: 0.06188277155160904
Epoch [5/5], Step [300/600], Loss: 0.02213984914124012
Epoch [5/5], Step [400/600], Loss: 0.015961142256855965
Epoch [5/5], Step [500/600], Loss: 0.032785091549158096
Epoch [5/5], Step [600/600], Loss: 0.08630551397800446
该模型在10000张测试图像上的准确性: 97.79%

在这里插入图片描述

完整代码

import torch
import torch.nn as nn
import torchvision.transforms as transforms
import torchvision.datasets as datasets
import matplotlib.pyplot as plt

# 加载MNIST数据集
train_dataset = datasets.MNIST(root='./dataset', train=True, transform=transforms.ToTensor(), download=True)
test_dataset = datasets.MNIST(root='./dataset', train=False, transform=transforms.ToTensor(), download=True)

# 定义MLP模型
class MLP(nn.Module):
    def __init__(self, input_size, hidden_size, num_classes):
        super(MLP, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(hidden_size, num_classes)

    def forward(self, x):
        out = self.fc1(x)
        out = self.relu(out)
        out = self.fc2(out)
        return out

# 设置超参数
input_size = 784
hidden_size = 500
num_classes = 10
num_epochs = 5
batch_size = 100
learning_rate = 0.001

# 准备数据加载器
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)

# 实例化模型
model = MLP(input_size, hidden_size, num_classes)

# 定义损失函数
criterion = nn.CrossEntropyLoss()

# 定义优化器
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

# 训练模型
total_step = len(train_loader)
for epoch in range(num_epochs):
    for i, (images, labels) in enumerate(train_loader):
        images = images.reshape(-1, 28 * 28)

        # 前向传播和计算损失
        outputs = model(images)
        loss = criterion(outputs, labels)

        # 反向传播和优化
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if (i + 1) % 100 == 0:
            print(f'Epoch [{epoch + 1}/{num_epochs}], Step [{i + 1}/{total_step}], Loss: {loss.item()}')

# 在测试集上测试模型
model.eval()
with torch.no_grad():
    correct = 0
    total = 0
    for images, labels in test_loader:
        images = images.reshape(-1, 28 * 28)
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

    print(f'该模型在10000张测试图像上的准确性: {100 * correct / total}%')

# 显示图像示例
images, labels = next(iter(test_loader))
images = images[:20]
labels = labels[:20]

fig, axes = plt.subplots(4, 5, figsize=(10, 8))

for i in range(20):
    row = i // 5
    col = i % 5
    image = images[i].numpy().squeeze()
    axes[row, col].imshow(image, cmap='gray')
    axes[row, col].axis('off')
    axes[row, col].set_title(f'Ground Truth: {labels[i]}')

plt.show()
  • 24
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值