zoukankan      html  css  js  c++  java
  • 如何入门Pytorch之四:搭建神经网络训练MNIST

           上一节我们学习了Pytorch优化网络的基本方法,本节我们将以MNIST数据集为例,通过搭建一个完整的神经网络,来加深对Pytorch的理解。

    一、数据集

           MNIST是一个非常经典的数据集,下载链接:http://yann.lecun.com/exdb/mnist/

          下载下来的文件如下:

    该手写数字数据库具有60,000个示例的训练集和10,000个示例的测试集。它是NIST提供的更大集合的子集。数字已经过尺寸标准化,并以固定尺寸的图像为中心。

    手写数字识别是一个比较简单的任务,它是一个10分类问题,(0-9),之所以选这个数据集,是因为识别难度低,计算量小,数据容易获得。

    二、模型搭建

        1、网络节点的确定

        对于不同的目的,网络的选择也是不一样的。一般来说,网络容量和数据集大小是对应的。一个小型数据集也只需要一个小型的网络。

    这里有一个经验值:

          1)model_size=sqrt(in_size*out_size)

          2)model_size=log(in_size)

          3)  model_size=sqrt(in_size*out_size)

          model_size:网络的节点量

          in_size:输入的节点量

          out_size输出的节点量

         2、导入pytorch包

    import torch
    import torchvision
    import trochvision import datasets
    import trochvision import transforms
    from torch.autograd import Variable

        3、获取训练集和测试集

    #root用于指定数据集下载后的存放路径
    #transform用于指定导入数据集需要对数据进行变换操作
    #train指定在数据集下载后需要载入哪部分数据,true为训练集,false为测试集
    data_train=datasets.MNIST(root="./data/",transform=transform,train=True,download=True) data_test=datasets.MNIST(root='./data/',transform=transform,train=False)

        4、数据预览和装载

    #数据装载,可以理解为对图片的处理
    #处理完成后,将图片送给模型训练,装载就是打包的过程
    #dataset 用于指定载入的数据集名称
    #batch_size设置了每个包的图片数据数据个数
    #shuffle 装载过程将数据随机打乱并打包
    data_loader_train=torch.utils.data.DataLoader(dataset=data_train,batch_size=64,shuffle=True)
    data_loader_test=torch.utils.data.DataLoader(dataset=data_test,batch_size=64,shuffle=True)

     装载后,可以选取其中一个批次数据进行预览

    images,labels=next(iter(data_loader_train))
    img=torchvision.utils.make_grid(images)  #batch_size,channel,height,weight
    
    img=img.numpy().transpose(1,2,0) #height,weight,channel
    std=[0.5,0.5,0.5]
    mean=[0.5,0.5,0.5]
    img=img*std+mean
    print(labels)
    
    cv2.show('win',img)
    key_pressed=cv2.waitKey(0)

    5、模型搭建和参数优化

    #卷积神经网络CNN结构一般包含几层:
    输入层:数据输入
    卷积层:卷积核进行特征提取和特征映射   torch.nn.Conv2d
    激励层:卷积也是线性,所以要增加非线性映射 torch.nn.ReLU
    池化层:进行下采样,对特征图稀疏处理,去除噪声 torch.nn.MaxPool2d
    全连接层:torch.nn.Linear
    输出层:输出结果 
    
    import torch.nn as nn
    class Model(nn.Module):
        def __init__(self):
            self.conv1=nn.Sequential(
                nn.Conv2d(1,64,kernel_size=3,stride=1,padding=1),
                nn.ReLU(),
                nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1),
                nn.ReLU(),
                nn.Maxpool2d(stride=2,kernel_size=2))
            self.dense=nn.Sequential(
                nn.Linear(14*14*128,1024),
                nn.ReLU(),
                nn.Dropout(p=0.5),
                nn.Linear(1024,10))
    
        def forward(self,x):
             net=self.conv1(x)
             net=net.view(-1,14*14*128)
             net=self.dense(net)
             return net
    

           由于MNIST分类任务较简单,所以,网络结构只使用了两个卷积层,一个最大池化层和两个全连接。

           在前向传播forward函数中,首先,经过self.conv1卷积处理,然后使用view(-1,14*14*128)对参数实现扁平化,来满足紧接的全连接。

            搭建完模型后,就可以开始进行模型训练和对参数进行优化了。

    #模型实例化
    #定义损失函数和优化函数
    #损失函数使用交叉熵
    #优化函数使用自适应优化算法
    model=Model()
    cost=torch.nn.CrossEntropyLoss()
    optimizer=torch.optim.Adam(model.parameters())

    可以打印搭建好的网络结构

    print(model)
    显示如下:
    Model(
      (conv1): Sequential(
        (0): Conv2d(1, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        (1): ReLU()
        (2): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        (3): ReLU()
        (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
      )
      (dense): Sequential(
        (0): Linear(in_features=25088, out_features=1024, bias=True)
        (1): ReLU()
        (2): Dropout(p=0.5)
        (3): Linear(in_features=1024, out_features=10, bias=True)
      )
    )
    n_epochs = 10
     
    for epoch in range(n_epochs):
        total_loss = 0.0
        total_corrrect = 0
        print("Epoch  {}/{}".format(epoch, n_epochs))
        print("-"*10)
        for data in data_loader_train:
            X_train , y_train = data
            X_train,y_train = Variable(X_train),Variable(y_train)
            outputs = model(X_train)
            _,pred = torch.max(outputs.data,1)
            optimizer.zero_grad()
            loss = cost(outputs,y_train)
     
            loss.backward()
            optimizer.step()
            total_loss += loss.data[0]
            total_corrrect += torch.sum(pred == y_train.data)
        testing_correct = 0
        for data in data_loader_train:
            X_test,y_test = data
            X_test,y_test = Variable(X_test),Variable(y_test)
            outputs = model(X_test)
            _,pred = torch.max(X_test)
            testing_correct += torch.sum(pred == y_test.data)
     
        print("Loss is {:.4f}, Train Accuracy is:{:.4f}%,Test Accuracy is:{:.4f}"
              .format(total_loss/len(data_train),100*total_corrrect/len(data_train),
                      100*testing_correct/len(data_test)))

    总的训练周期为10次,使用print显示训练过程细节。且在每轮训练完成后,使用测试集验证模型泛化能力,并计算准确率。

    6、完整训练代码

    import torch
    import torch.nn as nn
    import torchvision.datasets as normal_datasets
    import torchvision.transforms as transforms
    from torch.autograd import Variable
     
    num_epochs = 5
    batch_size = 100
    learning_rate = 0.001
     
     
    # 将数据处理成Variable, 如果有GPU, 可以转成cuda形式
    def get_variable(x):
        x = Variable(x)
        return x.cuda() if torch.cuda.is_available() else x
     
     
    # 从torchvision.datasets中加载一些常用数据集
    train_dataset = normal_datasets.MNIST(
        root='./mnist/',  # 数据集保存路径
        train=True,  # 是否作为训练集
        transform=transforms.ToTensor(),  # 数据如何处理, 可以自己自定义
        download=True)  # 路径下没有的话, 可以下载
     
    # 见数据加载器和batch
    test_dataset = normal_datasets.MNIST(root='./mnist/',
                                         train=False,
                                         transform=transforms.ToTensor())
     
    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)
     
     
    # 两层卷积
    class CNN(nn.Module):
        def __init__(self):
            super(CNN, self).__init__()
            # 使用序列工具快速构建
            self.conv1 = nn.Sequential(
                nn.Conv2d(1, 16, kernel_size=5, padding=2),
                nn.BatchNorm2d(16),
                nn.ReLU(),
                nn.MaxPool2d(2))
            self.conv2 = nn.Sequential(
                nn.Conv2d(16, 32, kernel_size=5, padding=2),
                nn.BatchNorm2d(32),
                nn.ReLU(),
                nn.MaxPool2d(2))
            self.fc = nn.Linear(7 * 7 * 32, 10)
     
        def forward(self, x):
            out = self.conv1(x)
            out = self.conv2(out)
            out = out.view(out.size(0), -1)  # reshape
            out = self.fc(out)
            return out
     
     
    cnn = CNN()
    if torch.cuda.is_available():
        cnn = cnn.cuda()
     
    # 选择损失函数和优化方法
    loss_func = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(cnn.parameters(), lr=learning_rate)
     
    for epoch in range(num_epochs):
        for i, (images, labels) in enumerate(train_loader):
            images = get_variable(images)
            labels = get_variable(labels)
     
            outputs = cnn(images)
            loss = loss_func(outputs, labels)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
     
            if (i + 1) % 100 == 0:
                print('Epoch [%d/%d], Iter [%d/%d] Loss: %.4f'
                      % (epoch + 1, num_epochs, i + 1, len(train_dataset) // batch_size, loss.item()))
     
     
    # Save the Trained Model
    torch.save(cnn.state_dict(), 'cnn.pkl')

    7、模型验证

          为了验证训练好的模型效果,我们选取一部分测试集中的图片,看看与真实值的偏差,并对结果可视化。

    import matplotlib.pyplot as plt
    import cv2
    import torch
    import torchvision
    from torchvision import datasets
    from torchvision import transforms
    from torch.autograd import Variable
     
    transform = transforms.Compose([transforms.ToTensor(),
                                    transforms.Normalize(mean=[0.5,0.5,0.5],std=[0.5,0.5,0.5])])
     
     
    data_test = datasets.MNIST(root="./data/",
                               transform = transform,
                                train = False)
     
     
    data_loader_test = torch.utils.data.DataLoader(dataset =data_test,
                                                    batch_size = 4,
                                                    shuffle = True)
     
     
    #模型搭建和参数优化
    # 在顺利完成数据装载后,我们可以开始编写卷积神经网络模型的搭建和参数优化的代码
    #卷积层使用torch.nn.Conv2d类来搭建
    # 激活层使用torch.nn.ReLU 类方法来搭建
    # 池化层使用torch.nn.MaxPool2d类方法来搭建
    # 全连接层使用 torch.nn.Linear 类方法来搭建
     
    class Model(torch.nn.Module):
        def __init__(self):
            super(Model,self).__init__()
            self.conv1 = torch.nn.Sequential(
                torch.nn.Conv2d(1,64,kernel_size=3,stride=1,padding=1),
                torch.nn.ReLU(),
                torch.nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1),
                torch.nn.ReLU(),
                torch.nn.MaxPool2d(stride=2,kernel_size=2))
     
            self.dense = torch.nn.Sequential(
                torch.nn.Linear(14*14*128,1024),
                torch.nn.ReLU(),
                torch.nn.Dropout(p = 0.5),
                torch.nn.Linear(1024,10)
            )
     
        # 我们通过继承torch.nn.Modeule来构造网络,因为手写数字
        # 识别比较简单,我们只是用了两个卷积层,一个最大池化层,两个全连接层。
        # 在向前传播过程中进行x.view(-1, 14 * 14 * 128)
        # 对参数实现扁平化。最后通过自己self.dense定义的全连接层进行最后的分类
        def forward(self, x):
            x = self.conv1(x)
            x = x.view(-1,14*14*128)
            x = self.dense(x)
            return x
     
     
    # 在编写完搭建卷积神经网络模型的代码后,我们可以对模型进行训练和参数进行优化了
    # 首先 定义在训练之前使用哪种损失函数和优化函数
    # 下面定义了计算损失值的损失函数使用的是交叉熵
    # 优化函数使用的额是Adam自适应优化算法
    model = Model()
    # 将所有的模型参数移动到GPU上
    if torch.cuda.is_available():
        model.cuda()
    cost = torch.nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters())
    # print(model)
     
    # 卷积神经网络模型进行模型训练和参数优化的代码
    n_epochs = 5
     
    X_test,y_test = next(iter(data_loader_test))
    inputs = Variable(X_test)
    pred = model(inputs)
    _,pred = torch.max(pred,1)
     
    print("Predict Label is:",(i for i in pred))
    print("Real Label is :",[i for i in y_test])
     
    img = torchvision.utils.make_grid(X_test)
    img = img.numpy().transpose(1,2,0)
     
    std = [0.5,0.5,0.5]
    mean = [0.5,0.5,0.5]
    img = img*std +mean
    cv2.imshow('win',img)
    key_pressed=cv2.waitKey(0)

    附原文完整代码:

    #_*_coding:utf-8_*_
    import matplotlib.pyplot as plt
    import numpy as np
    import cv2
    import time
    import torch
    # torchvision包的主要功能是实现数据的处理,导入和预览等
    import torchvision
    from torchvision import datasets
    from torchvision import transforms
    from torch.autograd import Variable
     
    start_time = time.time()
    # 对数据进行载入及有相应变换,将Compose看成一种容器,他能对多种数据变换进行组合
    # 传入的参数是一个列表,列表中的元素就是对载入的数据进行的各种变换操作
    transform = transforms.Compose([transforms.ToTensor(),
                                    transforms.Normalize(mean=[0.5,0.5,0.5],std=[0.5,0.5,0.5])])
     
     
    # 首先获取手写数字的训练集和测试集
    # root 用于指定数据集在下载之后的存放路径
    # transform 用于指定导入数据集需要对数据进行那种变化操作
    # train是指定在数据集下载完成后需要载入那部分数据,
    # 如果设置为True 则说明载入的是该数据集的训练集部分
    # 如果设置为FALSE 则说明载入的是该数据集的测试集部分
    data_train = datasets.MNIST(root="./data/",
                               transform = transform,
                                train = True,
                                download = True)
     
    data_test = datasets.MNIST(root="./data/",
                               transform = transform,
                                train = False)
     
     
    #数据预览和数据装载
    # 下面对数据进行装载,我们可以将数据的载入理解为对图片的处理,
    # 在处理完成后,我们就需要将这些图片打包好送给我们的模型进行训练 了  而装载就是这个打包的过程
    # dataset 参数用于指定我们载入的数据集名称
    # batch_size参数设置了每个包中的图片数据个数
    #  在装载的过程会将数据随机打乱顺序并进打包
    data_loader_train = torch.utils.data.DataLoader(dataset =data_train,
                                                    batch_size = 64,
                                                    shuffle = True)
    data_loader_test = torch.utils.data.DataLoader(dataset =data_test,
                                                    batch_size = 64,
                                                    shuffle = True)
     
    # 在装载完成后,我们可以选取其中一个批次的数据进行预览
    images,labels = next(iter(data_loader_train))
    img = torchvision.utils.make_grid(images)
     
    img = img.numpy().transpose(1,2,0)
     
    std = [0.5,0.5,0.5]
    mean = [0.5,0.5,0.5]
     
    img = img*std +mean
    # print(labels)
    print([labels[i] for i in range(64)])
    # 由于matplotlab中的展示图片无法显示,所以现在使用OpenCV中显示图片
    # plt.imshow(img)
    # cv2.imshow('win',img)
    # key_pressed=cv2.waitKey(0)
     
    #模型搭建和参数优化
    # 在顺利完成数据装载后,我们可以开始编写卷积神经网络模型的搭建和参数优化的代码
    #卷积层使用torch.nn.Conv2d类来搭建
    # 激活层使用torch.nn.ReLU 类方法来搭建
    # 池化层使用torch.nn.MaxPool2d类方法来搭建
    # 全连接层使用 torch.nn.Linear 类方法来搭建
     
    class Model(torch.nn.Module):
        def __init__(self):
            super(Model,self).__init__()
            self.conv1 = torch.nn.Sequential(
                torch.nn.Conv2d(1,64,kernel_size=3,stride=1,padding=1),
                torch.nn.ReLU(),
                torch.nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1),
                torch.nn.ReLU(),
                torch.nn.MaxPool2d(stride=2,kernel_size=2))
     
            self.dense = torch.nn.Sequential(
                torch.nn.Linear(14*14*128,1024),
                torch.nn.ReLU(),
                torch.nn.Dropout(p = 0.5),
                torch.nn.Linear(1024,10)
            )
     
        # 我们通过继承torch.nn.Modeule来构造网络,因为手写数字
        # 识别比较简单,我们只是用了两个卷积层,一个最大池化层,两个全连接层。
        # 在向前传播过程中进行x.view(-1, 14 * 14 * 128)
        # 对参数实现扁平化。最后通过自己self.dense定义的全连接层进行最后的分类
        def forward(self, x):
            x = self.conv1(x)
            x = x.view(-1,14*14*128)
            x = self.dense(x)
            return x
     
     
    # 在编写完搭建卷积神经网络模型的代码后,我们可以对模型进行训练和参数进行优化了
    # 首先 定义在训练之前使用哪种损失函数和优化函数
    # 下面定义了计算损失值的损失函数使用的是交叉熵
    # 优化函数使用的额是Adam自适应优化算法
    model = Model()
    # 将所有的模型参数移动到GPU上
    if torch.cuda.is_available():
        model.cuda()
    cost = torch.nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters())
    # print(model)
     
    # 卷积神经网络模型进行模型训练和参数优化的代码
    n_epochs = 5
     
    for epoch in range(n_epochs):
        running_loss = 0.0
        running_correct = 0
        print("Epoch  {}/{}".format(epoch, n_epochs))
        print("-"*10)
        for data in data_loader_train:
            X_train , y_train = data
            # 有GPU加下面这行,没有不用加
            # X_train, y_train = X_train.cuda(), y_train.cuda()
            X_train , y_train = Variable(X_train),Variable(y_train)
            # print(y_train)
            outputs = model(X_train)
            # print(outputs)
            _,pred = torch.max(outputs.data,1)
            optimizer.zero_grad()
            loss = cost(outputs,y_train)
     
            loss.backward()
            optimizer.step()
            # running_loss += loss.data[0]
            running_loss += loss.item()
            running_correct += torch.sum(pred == y_train.data)
            # print("ok")
            # print("**************%s"%running_corrrect)
     
        print("train ok ")
        testing_correct = 0
        for data in data_loader_test:
            X_test,y_test = data
            # 有GPU加下面这行,没有不用加
            # X_test, y_test = X_test.cuda(), y_test.cuda()
            X_test,y_test = Variable(X_test),Variable(y_test)
            outputs = model(X_test)
            _, pred = torch.max(outputs,1)
            testing_correct += torch.sum(pred == y_test.data)
            # print(testing_correct)
     
        print( "Loss is :{:.4f},Train Accuracy is:{:.4f}%,Test Accuracy is:{:.4f}".format(
                     running_loss / len(data_train),100 * running_correct / len(data_train),
                     100 * testing_correct / len(data_test)))
     
     
    stop_time = time.time()
    print("time is %s" %(stop_time-start_time))

    上一篇:

        如何入门Pytorch之三:如何优化神经网络

    下一篇:

       未完待续。。。

  • 相关阅读:
    22(1).模型融合---Random Forest
    Tesseract 模块
    非线性问题的三种处理方法
    jupyter 快捷键
    回归评价指标---MSE、RMSE、MAE、R-Squared
    理解JavaScript中的事件处理
    jquery事件重复绑定解决办法
    IE6 IE7 IE8(Q) 不支持 JSON 对象
    浏览器事件机制与自定义事件的实现
    浏览器加载和渲染html的顺序
  • 原文地址:https://www.cnblogs.com/jimchen1218/p/13660800.html
Copyright © 2011-2022 走看看