项目介绍
用mlp实现图像分类(暂时不用CNN)
采用贯序模型而不是创捷nn.Model的派生类(这样做的好处是不用写__init__函数和forward函数。
数据集介绍:
FashionMNIST(时尚MNIST)是一个经典的计算机视觉数据集,用于图像分类任务。它是由 Zalando Research 创建的,旨在替代传统的MNIST数据集,以更贴近实际场景中的图像分类问题。
FashionMNIST数据集包含了60,000个用于训练的图像样本和10,000个用于测试的图像样本,总共包括10个类别。每个样本都是灰度图像,分辨率为28x28像素。每个像素的值介于0到255之间,表示像素的灰度强度。
数据集中的图像样本代表了不同种类的时尚商品,包括衣服、鞋子、手袋等。以下是FashionMNIST数据集中的类别标签和对应的类别名称:
- T-shirt/top(T恤/上衣)
- Trouser(裤子)
- Pullover(套头衫)
- Dress(裙子)
- Coat(外套)
- Sandal(凉鞋)
- Shirt(衬衫)
- Sneaker(运动鞋)
- Bag(包)
- Ankle boot(短靴)
FashionMNIST数据集的设计灵感来自于MNIST数据集,但相对于MNIST,FashionMNIST更具挑战性,更适合用于评估和比较不同的图像分类算法和模型。
由于其简单的图像和类别标签结构,FashionMNIST数据集成为了深度学习和计算机视觉领域中常用的基准数据集之一。它可以用于训练和评估各种图像分类算法、卷积神经网络(CNN)等模型,并作为学术研究和教学的基础。
程序实现
1、导包
from torch.utils.data import Dataset, DataLoader
import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
device = "cuda" if torch.cuda.is_available() else "cpu"
from torchvision import datasets
data_folder = '~/data/FMNIST' #数据的下载路径
fmnist = datasets.FashionMNIST(data_folder, download=True, train=True)
#data_folder: 这是一个字符串,表示数据集的存储位置。它指定了数据集的根文件夹路径。
#download=True: 这是一个可选的参数,用于指定是否下载FashionMNIST数据集。如果数据集尚未下载,设置为True将自动下载数据集。如果数据集已经下载,设置为False将跳过下载步骤。
#train=True: 这是一个可选的参数,用于指定是否加载FashionMNIST的训练集。当设置为True时,创建的数据集对象将包含FashionMNIST的训练集。当设置为False时,将加载测试集。
#torch.manual_seed(0)用于使用相同值初始化网络模型,方便模型复现
tr_images = fmnist.data
#数据集中的输入
tr_targets = fmnist.targets
#与数据集中的输入对应的输出
2、数据处理
#定义数据集类,派生自Dataset类,Dataset类是在机器学习和深度学习中常用的一个抽象类,用于表示数据集。
#Dataset类提供了一种通用的接口和功能,用于组织、存储和预处理数据集。它为机器学习和深度学习任务提供了一种方便和灵活的方式来处理数据。
class FMNISTDataset(Dataset):
def __init__(self, x, y):
x = x.float()
x = x.view(-1,28*28)
self.x, self.y = x, y
def __getitem__(self, ix):
x, y = self.x[ix], self.y[ix]
return x.to(device), y.to(device)
def __len__(self):
return len(self.x)
def get_data():
train = FMNISTDataset(tr_images, tr_targets)
trn_dl = DataLoader(train, batch_size=32, shuffle=True)
#DataLoader是用于批量加载和处理数据的类。它封装了一个Dataset对象,并提供了数据的批量加载、并行处理、顺序打乱等功能。
#通过DataLoader,可以从Dataset中按照指定的批次大小加载数据,并且可以使用多个工作线程并行地加载和处理数据。
return trn_dl
3、模型
from torch.optim import SGD
#这个SGD不是理论定义上的挑一个样本计算损失更新参数,而是按batch计算损失函数并更新(Pytorch并没有直接定义GD,MBGD都是通过SGD实现的)
#这里采用Sequential的方式定理网络结构,而不是继承nn.model的方式定义网络结构,这样做的优点是不用定义__init__函数,和forward方法
def get_model():
model = nn.Sequential(
nn.Linear(28 * 28, 1000),
nn.ReLU(),
nn.Linear(1000, 10)
).to(device)
loss_fn = nn.CrossEntropyLoss()#分离问题采用交叉熵损失函数
optimizer = SGD(model.parameters(), lr=1e-2)#优化器设置为SGD,指定学习率未0.01
return model, loss_fn, optimizer#一般模型的函数都返回模型本身,损失函数,优化器,其实就对应着机器学习(深度学习也一个意思)三要素:模型、策略、算法
4、训练
def train_batch(x, y, model, opt, loss_fn):
model.train()
#model.train()方法用于将模型设置为训练模式,启用梯度计算、启用 Dropout 层和 Batch Normalization 层的训练行为。这是深度学习训练的重要步骤之一,用于指定模型在训练阶段的行为,并进行参数更新和优化。
prediction = model(x)
#计算预测值
batch_loss = loss_fn(prediction, y)
#计算损失
batch_loss.backward()
# based on the forward pass in `model(x)` compute all the gradients of
# 'model.parameters()'
optimizer.step()
# apply new-weights = f(old-weights, old-weight-gradients) where
# "f" is the optimizer
optimizer.zero_grad()
# Flush gradients memory for next batch of calculations
# 至于为啥要清零由pytorch计算梯度的机制决定
return batch_loss.item()
#batch_loss是一个tensor,item()取其值
5、测试
@torch.no_grad()
#这个修饰器用于省去梯度计算,在测试时不用计算梯度
def accuracy(x, y, model):
model.eval() # <- let's wait till we get to dropout section
#model.eval()用于将模型设置为评估模式,以确保一致的行为和计算结果。在进行模型评估、验证和推理时,通常会在调用model.eval()之前使用。
# get the prediction matrix for a tensor of `x` images
prediction = model(x)
# compute if the location of maximum in each row coincides
# with ground truth
max_values, argmaxes = prediction.max(-1)
is_correct = argmaxes == y
return is_correct.cpu().numpy().tolist()
6、串起来
trn_dl = get_data()
model, loss_fn, optimizer = get_model()
losses, accuracies = [], []
for epoch in range(5):
print(epoch)
epoch_losses, epoch_accuracies = [], []
#for i in trn_dl:
# print(i)
for ix, batch in enumerate(iter(trn_dl)):
#trn_dl是Dataloder的一个实例,他是一个迭代器对象,其返回形式是<torch.utils.data.dataloader.DataLoader object at 0x7b4969b2afb0>
#iter()用于创建一个迭代器对象,事实上,这里将iter()省略也完全可以运行,其返回形式是<torch.utils.data.dataloader._SingleProcessDataLoaderIter object at 0x7bf037a93c70>
#enumerate返回的是一个枚举对象,相较于迭代器对象,它多了index,其返回形式是<enumerate object at 0x7bf037aae2c0>
#最后的数是对象在内存中的地址
x, y = batch
#取出一个batch中对应的输入输出
batch_loss = train_batch(x, y, model, optimizer, loss_fn)
epoch_losses.append(batch_loss)
#epoch_losses存batch_loss
epoch_loss = np.array(epoch_losses).mean()
for ix, batch in enumerate(iter(trn_dl)):
x, y = batch
is_correct = accuracy(x, y, model)
epoch_accuracies.extend(is_correct)
epoch_accuracy = np.mean(epoch_accuracies)
losses.append(epoch_loss)
#losses存epoch_loss
accuracies.append(epoch_accuracy)
#预测准确度存轮准确度
7、可视化
epochs = np.arange(5)+1
plt.figure(figsize=(20,5))
plt.subplot(121)
plt.title('Loss value over increasing epochs')
plt.plot(epochs, losses, label='Training Loss')
plt.legend()
plt.subplot(122)
plt.title('Accuracy value over increasing epochs')
plt.plot(epochs, accuracies, label='Training Accuracy')
plt.gca().set_yticklabels(['{:.0f}%'.format(x*100) for x in plt.gca().get_yticks()])
plt.legend()
#plt.show()#pycharm中加上,jupyter、colab不用加