Pytorch实战1:FashionMNIST时装分类收获

任务简介

  • 这里的任务是对10个类别的“时装”图像进行分类,使用FashionMNIST数据集。
  • FashionMNIST中数据的若干样例图,其中每个小图对应一个样本。
  • FashionMNIST数据集中包含已经预先划分好的训练集和测试集,其中训练集共60,000张图像,测试集共10,000张图像。
  • 每张图像均为单通道黑白图像,大小为28*28pixel,分属10个类别。

导入必要的包

import os
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader

配置训练环境和超参

先要查看GPU是否可用

torch.cuda.is_available()
  • 输出True,就说明是好的

配置GPU,有两种方式

  • 方案1:使用os.environ 这种之后用xxx.cuda()
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'  # 这里可以是多块 0,1,2,3
  • 方案2:使用device,后续要使用GPU直接用xxx.to_device(device)
device = torch.device('cuda:1' if torch.cuda.is_available() else 'cpu')
  • 这边可以查一下单卡和多卡配置

数据的读入与加载

from torchvision import transforms
  • 1.图像预处理

    • Compose把多个步骤整合到一起:
    data_transform = transforms.Compose([
        transforms.ToPILImage(),
        transforms.Resize(img_size),
        transforms.ToTensor()
    ])
    
    • 解析:
    # 示例代码
    transform.ToTensor(),
    transform.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))
    
    • ToTensor()能够把灰度范围从0-255变换到0-1之间
    • 后面的transform.Normalize()则把0-1变换到(-1,1)
    • 解析:
      Normalize:image = (image - mean) / std
    • 其中mean和std分别通过(0.5,0.5,0.5)和(0.5,0.5,0.5)进行指定
    • 将ToTensor()转换的(0, 1)变成(0-0.5)/0.5 = -1 (1-0.5)/0.5=1
  • 2.读取数据

    • 先读入csv格式的数据:60000张训练集和10000张测试集,784个特征,1个标签
    • 再构建自己的Dataset:
      • 继承Dataset,重写__init____len____getitem__
			def __init__(self, df, transform=None):
        		self.df = df
        		# 图像转换
        		self.transform = transform
       		 	# 取df的所有行第1列到最后的内容,第0列是label,从后面开始才是图的内容
        		self.images = df.iloc[:,1:].values.astype(np.uint8)  # 专门的图像格式
        		self.labels = df.iloc[:,0].values
        	def __len__(self):
        		return len(self.images)
        	def __getitem__(self, idx):
		        # 一条一条的读
		        image = self.images[idx].reshape(28, 28, 1)  # 单一通道的
		        label = int(self.labels[idx])
		        # 对imgae和label进行处理
		        if self.transform is not None:
		            image = self.transform(image)
		        else:
		            # /255是归一化为0-1,方便处理
		            image = torch.tensor(image/255, dtype=torch.float)
		        	label = torch.tensor(label, dtype=torch.long)
		        return image, label
  • 3.实例化,并用DataLoader加载

    train_data = FMDataset(train_df, data_transform)
    test_data = FMDataset(test_df, data_transform)
    
    • 构建好Dataset后,就可以使用DataLoader来按批次读入数据
    • data_set:构建好的样本
    • batch_size:样本是按“批”读入的,batch_size就是每次读入的样本数 这里为256
    • num_workers:有多少个进程用于读取数据
    • shuffle:是否将读入的数据打乱
    • drop_last:对于样本最后一部分没有达到批次数(就是不满一个batch的数量)的样本,使其不再参与训练
    • pin_memory:锁页内存,设置为True,直接将内存的张量转义到GPU的显存速度会快,如果显存爆炸,就设置为False
train_loader = DataLoader(dataset=train_data, batch_size=batch_size, num_workers=num_workers, shuffle=True, drop_last=True)
test_loader = DataLoader(dataset=test_data, batch_size=batch_size, num_workers=num_workers, shuffle=False)

读取一张图片看看

import matplotlib.pyplot as plt
# next是获取下一个项目
# next(iterator[,default])
# iterator :要读取行的文件对象
# default :如果迭代器耗尽则返回此默认值。 如果没有给出此默认值,则抛出 StopIteration 异常

images, labels = next(iter(train_loader))
print(images.shape, labels.shape)
# 展示第0个数据,第二个0为color
plt.imshow(images[0][0], cmap='gray')
plt.show()

模型构建

  • 神经网络的构造:基于nn.Module

    • init, forward
  • 神经网络是通过“层定义+层顺序”的方式构建起来的

  • 神经网络常见层

    • nn.Conv2d, nn.MaxPool2d, nn.Linear, nn.ReLU,…
  • 卷积层

      1. 二维卷积nn.Conv2d(1, 32, 5)
      • in_channels:对于最初输入图片样本的通道数 in_channels

        • 取决于图片的类型这里数据是灰色的通道就是1个,所以就传入1;如果是RGB的图,3通道,这里就传3
      • out_channels:卷积完成之后,输出的通道数 out_channels 取决于过滤器的数量,

        • 这里的 out_channels 设置的就是过滤器的数目。
      • conv

      • 对于第二层或者更多层的卷积,此时的 in_channels 就是上一层的 out_channels , out_channels 还是取决于过滤器数目

      • kernel_size:卷积核设置为5*5

      1. 池化:nn.MaxPool2d(2, stride=2)
      • kernel_size :表示做最大池化的窗口大小,可以是单个值(单*单),也可以是tuple元组
      • stride :步长,可以是单个值(向右滑动2个窗口,向下滑动2个窗口),也可以是tuple元组
      • padding :填充,可以是单个值,也可以是tuple元组
      • dilation :控制窗口中元素步幅
      • return_indices :布尔类型,返回最大值位置索引
      • ceil_mode :布尔类型,为True,用向上取整的方法,计算输出形状;默认是向下取整。
      1. nn.Dropout(0.3)
      • Dropout是为了防止过拟合而设置的
      • Dropout顾名思义有丢掉的意思
      • nn.Dropout(p = 0.3) # 表示每个神经元有0.3的可能性不被激活
      • Dropout只能用在训练部分而不能用在测试部分
      • Dropout一般用在全连接神经网络映射层之后,如代码的nn.Linear(20, 30)之后
  • 全连接层 这里做了两次,最后一次输出10类,这里有10类

    • nn.Linear(6444, 512)
      • in_features:输入的神经元个数
      • out_features:输出神经元个数
      • bias=True:是否包含偏置
      • 其实Linear其实就是对输入X(n*i)执行了一个线性变换,即:
      • y = XW + b 其实W是模型要学习的参数,W的维度为i*o,b是o维的向量偏置,n微输入向量的行数
      • (例如,一次输入10个样本,即batch_size为10,则n=10),i为输入神经元的个数(例如样本的特征书为5,则i=5)
      • o为输出神经元的个数
      • 例如,定义线性层,我们的输入特征为5,所以in_feature=5,
      • 我们想让下一层的神经元个数为10,所以out_feature=10,则模型参数为:W(5 × 10)
      • 这里输入特征数为6444,输出特征数为512

损失函数

  • 损失函数常用的操作
    • backward()
  • 使用nn自带的CrossEntropy损失
  • PyTorch会自动把整型的label转为one-hot型,用于计算CE loss。这里需要确保label是从0开始的,同时模型不加softmax层(使用logits计算),所以PyTorch训练中各个部分不是独立的,需要通盘考虑

训练与测试(验证)

  • 各自封装成函数,方便后续调用

  • 关注两者的主要区别:

    • 模型状态设置
    • 是否需要初始化优化器
    • 是否需要将loss传回到网络
    • 是否需要每步更新optimizer
  • 训练与评估

    • 模型状态设置
      • model.train(), model.eval()
    • 训练流程:读取、转换、梯度清零、输入、计算损失、反向传播、参数更新
    • 验证流程:读取、 转换、输入、计算损失、计算指标
def train(epoch):
    # model.train()的作用是启用 Batch Normalization 和 Dropout
    # 如果模型中有BN层(Batch Normalization)和Dropout,需要在训练时添加model.train()。
    # model.train()是保证BN层能够用到每一批数据的均值和方差。
    # 对于Dropout,model.train()是随机取一部分网络连接来训练更新参数。
    model.train()
    # 如果模型中有BN层(Batch Normalization)和Dropout,在测试时添加model.eval()。
    # model.eval()是保证BN层能够用全部训练数据的均值和方差,即测试过程中要保证BN层的均值和方差不变。
    # 对于Dropout,model.eval()是利用到了所有网络连接,即不进行随机舍弃神经元。
    # model.eval()
    train_loss = 0
    for data, label in train_loader:   # for i, (data, label) in enumerate(train_loader):
        # data.cuda()就将其转换为GPU的张量类型
        data, label = data.cuda(), label.cuda()
        # 梯度清零
        optimizer.zero_grad()
        # 训练集上得到结果
        output = model(data)
        loss = criterion(output, label)
        loss.backward()
        optimizer.step()
        # data.size(0) data的第1维为为256 一个batch_size为256
        train_loss += loss.item() * data.size(0)
    train_loss = train_loss / len(train_loader.dataset)
    print('Epoch:{} \t Training Loss:{:.6f}'.format(epoch, train_loss))
        
def val(epoch):
    model.eval()
    val_loss = 0
    gt_labels = []
    pred_labels = []
    with torch.no_grad():
        for data, label in test_loader:
            data, label = data.cuda(), label.cuda()
            output = model(data)
            # 每一维列的最大值的下标
            # 因为这里是10个类别,要看他哪一个是1,得到那个类别
            preds = torch.argmax(output, 1)
            
            # 真实的标签
            # 一次传入256长得list到一个list里面
            # 这样pred_labels就是n*256
            gt_labels.append(label.cpu().data.numpy())
            # print(gt_labels.shape)
            # 算出来的标签
            # 一次传入256长得list到一个list里面
            # 这样pred_labels就是n*256
            pred_labels.append(preds.cpu().data.numpy())
            loss = criterion(output, label)
            val_loss += loss.item()*data.size(0)
    val_loss = val_loss / len(test_loader.dataset)
    # print(len(gt_labels), len(pred_labels))
    # 这里拼接成一维
    gt_labels, pred_labels = np.concatenate(gt_labels), np.concatenate(pred_labels)
    # print('gt_labels:', gt_labels, 'pred_labels:',pred_labels)
    acc = np.sum(gt_labels == pred_labels) / len(pred_labels)
    print('Epoch:{} \t Validation Loss:{:.6f}, ACC:{:6f}'.format(epoch, val_loss, acc))
for epoch in range(1, epochs+1):
    train(epoch)
    val(epoch)

源码地址

FashionMNIST时装分类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值