文章目录

  • 1.MNIST
  • 2.数据预处理
  • 2.1相关包
  • 2.2数据载入和预处理
  • 3.网络结构
  • 4.优化器、损失函数、网络训练以及可视化分析
  • 4.1定义优化器
  • 4.2网络训练
  • 4.3可视化分析
  • 5.测试
  • 6.改进


1.MNIST

MNIST下载地址:http://yann.lecun.com/exdb/mnist/

下载pytorch放那个文件夹_下载pytorch放那个文件夹


下载好了之后,请建好如下目录(其中data的名字你可以随便改,其他不要变):

data_path = 'D:\lbq\lang\pythoncode\data'

下载pytorch放那个文件夹_神经网络_02


也就是说:在官网上下载好的4个文件放在raw目录下。

2.数据预处理

2.1相关包
import torch 
from torch.utils import data # 获取迭代数据
from torch.autograd import Variable # 获取变量
import torchvision
from torchvision.datasets import mnist # 获取数据集和数据预处理
import matplotlib.pyplot as plt
2.2数据载入和预处理
# 数据集的预处理
data_tf = torchvision.transforms.Compose(
    [
        torchvision.transforms.ToTensor(),
        torchvision.transforms.Normalize([0.5],[0.5])
    ]
)

data_path = r'D:\lbq\lang\pythoncode\data'
# 获取数据集
train_data = mnist.MNIST(data_path,train=True,transform=None,download=True)
test_data = mnist.MNIST(data_path,train=False,transform=None,download=True)

解释:

  1. 其中data_path后面的r表示防止后面的字符串转义。
  2. transform=None是为了先演示图片,否则转换之后就变成了向量,不是图片了。

此时我们发现,其在raw目录自动解压了4个gz文件,并在processed目录下面生成了两个文件。

/raw/

下载pytorch放那个文件夹_人工智能_03


/processed/

下载pytorch放那个文件夹_下载pytorch放那个文件夹_04

其他一些参数解释如下:

下载pytorch放那个文件夹_下载pytorch放那个文件夹_05


该网址为:https://pypi.org/project/torchvision/0.1.8/#mnist

下载pytorch放那个文件夹_机器学习_06

一些操作:

1.展示训练集的第0和第1张图片:

下载pytorch放那个文件夹_神经网络_07


可以看到,train_data[0]是一个元组,第一项是图片[0],第二项5是标签[1]。

下载pytorch放那个文件夹_下载pytorch放那个文件夹_08

下载pytorch放那个文件夹_人工智能_09

2.查看总共有多少张图片:

下载pytorch放那个文件夹_机器学习_10


不过图片并不能被计算机识别,要转化为向量,所以用到transform

# 数据集的预处理
data_tf = torchvision.transforms.Compose(
    [
        torchvision.transforms.ToTensor(),
        torchvision.transforms.Normalize([0.5],[0.5])
    ]
)

data_path = r'D:\lbq\lang\pythoncode\data'
# 获取数据集
train_data = mnist.MNIST(data_path,train=True,transform=data_tf,download=True)
test_data = mnist.MNIST(data_path,train=False,transform=data_tf,download=True)

下载pytorch放那个文件夹_神经网络_11


这个时候已经不能查看图片了,不过其仍然是元组,第一项是一个向量,第二项是一个标签。

下载pytorch放那个文件夹_机器学习_12


下载pytorch放那个文件夹_人工智能_13


我们发现,一张图片用(1,28,28)表示,1表示图片只有一个channel(通道),因为这个是黑白照片。彩色有3个。后面的28*28代表像素,可以想象为一个平面。由于我们前面标准化了,所以每个数值都比较小,不然可能是(0,255)之间的数。

batch_size=32
train_loader = data.DataLoader(train_data,batch_size=batch_size,shuffle=True,pin_memory=True)
test_loader = data.DataLoader(test_data,batch_size=batch_size)

帮助:

  1. DataLoader可以处理DataSet类型的东西,而train_data就是这个类型,之前图片已经展示过了。
  2. DataLoader有什么用?其叫做数据加载器,结合了数据集和取样器(之前train_data只是一个数据集),并且可以提供多个线程处理数据集。在训练模型时使用到此函数,用来把训练数据分成多个小组,此函数每次抛出一组(batch_size)数据。直至把所有的数据都抛出。
  3. shuffle是洗牌的意思,每个epoch就洗一次,打乱训练数据的顺序。这个很多人都不明白,我看了都是说的是错的。注意,一个epoch包括很多组(batch_size)。比如1,3,4为训练数据,batch_size是1,那么第一次shuffle一下,随机变成341,那么根据batch_size,喂给模型训练的顺序是3,4,1。喂完之后,到第2个epoch,又重新shuffle,随便变成413,那么根据batch_size,喂给模型训练的顺序是4,1,3。以此类推。
  4. pin_memory据说可以使得训练速度更快,随便你吧。

3.网络结构


下载pytorch放那个文件夹_人工智能_14

图1 卷积层


下载pytorch放那个文件夹_深度学习_15

图2 全连接层

代码实现:

#导入相关的包
import torch.nn as nn#网络结构
import torch.nn.functional as F
import torch.optim as optim#优化器
class CNN(nn.Module):
    def __init__(self):
        super(CNN,self).__init__()
        self.conv1=nn.Conv2d(1,20,5,1)
        self.conv2=nn.Conv2d(20,50,5,1)
        self.fc1=nn.Linear(4*4*50,500)
        self.fc2=nn.Linear(500,10)
    def forward(self,x):
        #x是一个batch_size的数据
        #x:1*28*28
        x=F.relu(self.conv1(x))
        #20*24*24
        x=F.max_pool2d(x,2,2)
        #20*12*12
        x=F.relu(self.conv2(x))
        #50*8*8
        x=F.max_pool2d(x,2,2)
        #50*4*4
        x=x.view(-1,50*4*4)
        #压扁成了行向量,(1,50*4*4)
        x=F.relu(self.fc1(x))
        #(1,500)
        x=self.fc2(x)
        #(1,10)
        return F.log_softmax(x,dim=1)

帮助:

  1. Conv2d是什么?max_pool2d又是什么?见:简要解释什么是Conv1d,Conv2d,Conv3d
  2. Conv2d(20,50,5,1)指的就是输入输入图片的通道是20个,我们卷积核的大小是(20,5,5),相当于是一个立方体,对着输入(也是立方体)进行卷积,然后移动,移动步长是1。这样的卷积核话只能生成一个通道的特征图对吧?没事,我们有50个卷积核,可以并行卷积,那么就得到了50个通道的特征图,也就是说,输出是50通道的。
  3. 为什么用log_softmax()?见:(详细全面)softmax和log_softmax的联系和区别,NLLLOSS和CrossEntropyLoss的联系和区别
  4. view是用来转换维度的,和numpy中的reshape一样。

4.优化器、损失函数、网络训练以及可视化分析

优化器:SGD(随机梯度下降)
损失函数:NLLLOSS(交叉熵损失函数的最新版,见。)

4.1定义优化器
lr=0.01#学习率
momentum=0.5
device=torch.device("cuda" if torch.cuda.is_available() else "cpu" )
model=CNN().to(device)
optimizer=optim.SGD(model.parameters(),lr=lr,momentum=momentum)
4.2网络训练
def train(model,device,train_loader,optimizer,epoch,losses):
    model.train()
    for idx,(t_data,t_target) in enumerate(train_loader):
        t_data,t_target=t_data.to(device),t_target.to(device)
        pred=model(t_data)#batch_size*10
        loss=F.nll_loss(pred,t_target)
        
        #SGD
        optimizer.zero_grad()#将上一步的梯度清0
        loss.backward()#重新计算梯度
        optimizer.step()#更新参数
        if idx%100==0:
            print("epoch:{},iteration:{},loss:{}".format(epoch,idx,loss.item()))
            losses.append(loss.item()) #每100批数据采样一次loss,记录下来,用来画图可视化分析。

训练

num_epochs=2
losses=[]#记录起来用来画图的,可以画出损失随着迭代次数而下降。
from time import *
begin_time=time()#测试我们的模型训练要花多久。
for epoch in range(num_epochs):
    train(model,device,train_loader,optimizer,epoch,losses)
end_time=time()

训练结果:

下载pytorch放那个文件夹_人工智能_16


模型训练所花时间为22.9秒:

下载pytorch放那个文件夹_下载pytorch放那个文件夹_17

4.3可视化分析
import matplotlib.pyplot as plt
len_l=len(losses)
x=[i for i in range(len_l)]
figure=plt.figure(figsize=(20,8),dpi=80)
plt.plot(x,losses)
plt.show()

下载pytorch放那个文件夹_神经网络_18


可以发现,loss在前期下降的很快,后面基本就稳定了。

上面的代码基本上是套路,除了输出语句和参数之外,能改的东西很少。

5.测试

def test(model,device,test_loader):
    model.eval()
    correct=0#预测对了几个。
    with torch.no_grad():
        for idx,(t_data,t_target) in enumerate(test_loader):
            t_data,t_target=t_data.to(device),t_target.to(device)
            pred=model(t_data)#batch_size*10
            pred_class=pred.argmax(dim=1)#batch_size*10->batch_size*1
			correct+=pred_class.eq(t_target.view_as(pred_class)).sum().item()
    acc=correct/len(test_data)
    print("accuracy:{},".format(acc))

帮助:

  1. tensor才可以eq,返回的是true,false.numpy也可以,使用a==b
  2. view_as主要是怕一个是行向量,一个是列向量,不同形状不可以eq。
  3. .item()是用来将只有一个元素的tensor转化为一个数值的,即把维度去掉。例如tensor([[[0]]]),使用.item()就变成0。
  4. 这里len(test_loader)是指的是有多少批数据,len(test_data)是有多少张图片。也就是说:len(test_loader)=len(test_data)/batch_size上取整。

测试:

test(model,device,test_loader)

测试结果:

下载pytorch放那个文件夹_深度学习_19

6.改进

最后给出改进方向,也就是说如果你还要提升准确率或者调参什么的,我应该怎么做?

  1. 网络结构,比如增加网络层数。
  2. 优化器,上面是SGD,但是还有很多其他的,比如Adam,见https://pytorch.org/docs/stable/generated/torch.optim.Adam.html?highlight=adam#torch.optim.Adam。而且参数几乎一样,直接换就行。
  3. 学习率lr,比如调低或者调高。
  4. 批处理batch_size大小
  5. 更换损失函数,别换交叉熵,因为上面就是交叉熵的最新版,还有其他的,比如平方损失。nll_loss对应位置换成mse_loss,参数也几乎一样,保持不变。见https://pytorch.org/docs/stable/generated/torch.nn.functional.mse_loss.html?highlight=mse#torch.nn.functional.mse_loss。
  6. 增加训练趟数num_epochs或者减少。
  7. 池化操作pooling,使用平均值AvgPool2d,参数也几乎和MaxPool2d一摸一样,见https://pytorch.org/docs/stable/generated/torch.nn.AvgPool2d.html?highlight=pool#torch.nn.AvgPool2d。
  8. 模型集成,比如多训练几个模型,然后使用投票的方法决定测试图片属于哪个类别。