Pytorch机器学习(一)——FashionMNIST分类实现
文章目录
前言
知道机器学习的,应该都对FashionMNIST数据集不陌生,这里就不对此数据集展开介绍了,直接从代码层面一步步的讲解,如何搭建自己第一个神经网络。
本文只是一个简单的对神经网络的搭建有个基础的认识,过细的知识就不讲了。
一、代码
1.引入库
import argparse
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torchvision.transforms import ToTensor
from torch.utils.data import DataLoader
from matplotlib import pyplot as plt
from torchsummary import summary
2.加载数据集
加载数据集主要有以下两步,调用datasets下载数据集,调用DataLoader去创建minibatch
2.1调用datasets
#加载数据集
transform=transforms.Compose([
transforms.ToTensor(),
#target_transform = Lambda(lambda y: torch.zeros(
#10, dtype=torch.float).scatter_(dim=0, index=torch.tensor(y), value=1))
])
training_data = datasets.FashionMNIST(
root="data",
train=True,
download=True,
transform=transform
)
test_data = datasets.FashionMNIST(
root="data",
train=False,
download=True,
transform=transform
)
其中数据集是官方Pytorch自带的,给初学者有很大的帮助。
datasets里面还有很多常用的数据集
__all__ = ('LSUN', 'LSUNClass',
'ImageFolder', 'DatasetFolder', 'FakeData',
'CocoCaptions', 'CocoDetection',
'CIFAR10', 'CIFAR100', 'EMNIST', 'FashionMNIST', 'QMNIST',
'MNIST', 'KMNIST', 'STL10', 'SVHN', 'PhotoTour', 'SEMEION',
'Omniglot', 'SBU', 'Flickr8k', 'Flickr30k',
'VOCSegmentation', 'VOCDetection', 'Cityscapes', 'ImageNet',
'Caltech101', 'Caltech256', 'CelebA', 'WIDERFace', 'SBDataset',
'VisionDataset', 'USPS', 'Kinetics400', 'HMDB51', 'UCF101',
'Places365')
而在里面可以设计的几个参数也是比较清楚的
参数 | 含义 |
---|---|
root | 数据集存放地址 |
train | 是否设置为训练集 |
download | 是否选择从指定网站下载,如果检测到root处包含数据集,则不下载 |
transform | 是否对数据集中的数据和标签进行变化 |
其中transform可以设置对数据或者标签进行操作
transforms.ToTensor():将dataset类中__getitem__()方法内读入的PIL或CV的图像数据转换为torch.FloatTensor
而下面这段代码即可以将label原来的标签,假如说是9,变成独热编码(one-hot)
target_transform = Lambda(lambda y: torch.zeros(
10, dtype=torch.float).scatter_(dim=0, index=torch.tensor(y), value=1)
将feature变成tensor后,我们可以打印一下其属性和其代表的东西看看,有个直观的了解
feature,label = training_data[0]
print(feature.shape)
print(label)
plt.figure()
plt.imshow(feature.squeeze(),cmap='gray') #1*28*28格式需要压缩成28*28形式
plt.title(label)
plt.show()
输出如下
torch.Size([1, 28, 28])
9
2.2 调用DataLoader构建minibatch
我们得到基础的数据集后,我们需要对数据集进行一些简单的处理,其中pytorch也给了我们很方便的方法,就是构建一个DataLoader的对象
train_loader = DataLoader(training_data,batch_size=64,shuffle=True)
test_loader = DataLoader(test_data, batch_size=64,shuffle=True)
其中这里面参数较多,挑两个经常使用的
参数 | 含义 |
---|---|
dataset | 数据集 |
batch_size | 每次训练batch中样本的个数 |
shuffle | 在每个epoch开始的时候,是否对数据进行重新排序 |
其中DataLoader只可用迭代的方法访问
for batch_index,(feature,label) in enumerate(train_loader):
print(batch_index)
print(feature.shape)
print(label.shape)
输出
0
torch.Size([10, 1, 28, 28])
torch.Size([10])
可以看到我们的数据已经被打包起来了,成为一个个batch。
我们也可以看看这个时候是否被打乱了
for batch_index,(feature,label) in enumerate(train_loader):
plt.figure()
for i in range(9):
plt.subplot(3,3,i+1)
plt.imshow(feature[i].squeeze(),cmap="gray")
plt.title(label[i].item())
plt.axis("off")
plt.show()
输出
至此我们就算是处理好我们的数据集了。
3.定义模型
定义模型也是十分简单的,我们只需要定义两部分即可,一部分是自己构建的子模块,另一部分是我们用子模块构建的前向计算图forward
这里就不细说这个模型是什么了,因为对于向FashionMNIST这种简单的数据集来说,随便构建一个模型,基本都能跑的85%的准确率,读者可以自行构建动手试试。
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 64, 3,padding=1)
self.conv2 = nn.Conv2d(64, 64, 3,padding=1)
self.conv3 = nn.Conv2d(64,128,3,padding=1)
self.conv4 = nn.Conv2d(128,128,3,padding=1)
self.dropout1 = nn.Dropout(0.25)
self.fc1 = nn.Linear(128*7*7, 512)
self.fc2 = nn.Linear(512, 10)
self.bn1 = nn.BatchNorm2d(64)
self.bn2 = nn.BatchNorm2d(128)
self.relu = nn.ReLU(inplace=True)
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x = F.max_pool2d(x, 2)
x = self.bn1(x)
x = self.relu(x)
x = self.conv3(x)
x = self.conv4(x)
x = F.max_pool2d(x, 2)
x = self.bn2(x)
x = self.relu(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = self.relu(x)
x = self.dropout1(x)
x = self.fc2(x)
#output = F.softmax(x)
return x
我们定义完模型后,可以去实例化我们的模型,并告诉电脑,这个模型将使用GPU进行计算
device = "cuda" if torch.cuda.is_available() else "cpu"
model = Net().to(device)
我们实例化模型后,我们可以通过一些方法去看里面到底有什么,比如想看看参数
for para in model.parameters():
print(para)
输出
Parameter containing:
tensor([[[[-2.1682e-01, -1.7200e-01, 6.2761e-02],
[-5.9057e-02, -2.9793e-01, 2.4450e-01],
[ 3.0849e-01, -2.5328e-01, -3.0286e-01]]],
[[[-3.2513e-01, -2.2943e-01, -2.9522e-02],
[ 1.2432e-02, 2.9955e-01, 6.0680e-02],
[-2.2615e-01, 2.4802e-01, -3.3225e-01]]],
.....
可以看到我们初始的参数都是随机的,准确的说是符合正态分布的。
我们也可以用summary来看网络的整体结构
summary(model,(1,28,28))
输出如下
----------------------------------------------------------------
Layer (type) Output Shape Param #
================================================================
Conv2d-1 [-1, 64, 28, 28] 640
Conv2d-2 [-1, 64, 28, 28] 36,928
BatchNorm2d-3 [-1, 64, 14, 14] 128
ReLU-4 [-1, 64, 14, 14] 0
Conv2d-5 [-1, 128, 14, 14] 73,856
Conv2d-6 [-1, 128, 14, 14] 147,584
BatchNorm2d-7 [-1, 128, 7, 7] 256
ReLU-8 [-1, 128, 7, 7] 0
Linear-9 [-1, 512] 3,211,776
ReLU-10 [-1, 512] 0
Dropout-11 [-1, 512] 0
Linear-12 [-1, 10] 5,130
================================================================
Total params: 3,476,298
Trainable params: 3,476,298
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.00
Forward/backward pass size (MB): 1.45
Params size (MB): 13.26
Estimated Total Size (MB): 14.71
----------------------------------------------------------------
4.开始训练模型
训练模型实质上也十分简单,一是使用网络去计算我们的输出,二是将网络的输出与输入使用损失函数进行评价,三是反向传播计算梯度,四是用优化器进行参数更新。
def train(args, model, device, train_loader, loss_fn, optimizer, epoch):
model.train() #打开batch normalize和dropout进行更新
for batch_idx, (data, target) in enumerate(train_loader): #取出数据
data, target = data.to(device), target.to(device) #放到GPU中计算
optimizer.zero_grad() #清空上一个batch中的梯度
output = model(data) #计算网络输出结果
pred = output.argmax(dim=1) #对网络输出结果进行argmax计算,选出当前确认值
loss = loss_fn(output,target) #计算损失函数
loss.backward() #反向传播计算梯度
optimizer.step() #使用优化器根据梯度更新参数
if batch_idx % 100 == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
4.对模型训练结果进行评估
训练完模型后,我们可以用test训练集对模型进行评估
def test(model, device,loss_fn, test_loader):
model.eval() #关闭batch normalize和dropout防止更新
total_loss = 0
correct = 0
with torch.no_grad(): #关闭梯度更新
for data, target in test_loader: #载入训练集
data, target = data.to(device), target.to(device) #放入GPU中计算
output = model(data) #计算网络计算结果
total_loss +=loss_fn(output, target).item() #计算损失
pred = output.argmax(dim=1, keepdim=True) #得到网络计算出最可能的结果
correct += pred.eq(target.view_as(pred)).sum().item() #判断是否正常
Average_loss = total_loss / len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
Average_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
5.对模型进行多次训练
我们需要对模型进行多次训练已达到比较高的准确率
#设置模型,损失函数,优化器等
model = Net().to(device)
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
loss_fn = nn.CrossEntropyLoss()
#scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)
#训练epoch
for epoch in range(1, args.epochs + 1):
train(args, model, device, train_loader, loss_fn, optimizer, epoch)
test(model, device, loss_fn,test_loader)
6.保存模型
#保存模型
torch.save(model.state_dict(), "mnist_cnn.pt")
总结
总的来说搭建一个神经网络就包含以上步骤,这个文章出的比较简略,因为只是相当于是一个对神经网络的初认识,后面会更新比较详细的文章。