[020] 深度学习--学习笔记(1)-- Pytorch实现手写数字识别(GPU运算)

0、写在前面

① 提前安装好Ubuntu,显卡驱动,CUDA,CUDNN,Anaconda,pytorch,tensorflow。

② 手写数字图片数据集为MNIST,网上可下载,或者程序运行时会自动下载。

③ 我使用VS coda作为编译器撰写程序,一开始会报与Pytorch相关的错误(’torch’ has no member 'xxx’),这是因为在VS coda没有添加pylint的路径。解决办法参考这篇博客

1、Pytorch实现手写数字识别,Python代码及详细注释

# 引入Pytorch,在Python中使用torch表示
import torch
# 从torch中引入网络层
from torch import nn
# 从torch中引入优化器(求解器)
from torch import optim
# 引入torchvision处理常用的数据集
from torchvision import datasets, transforms

####################
## 第1步 加载数据集 ##
####################

# load dataset 加载数据集
# torch提供的数据加载函数
# 加载 datasets.MNIST 这个数据集
# 数据集存放路径为'db/mnist'
# train=True,是否开展训练:是
# 图片变换,transforms是torchvision提供的图片处理函数,包括放大,扰动等。此处要变换图片大小.Resize();改变图片数据格式为矢量.ToTensor(),因为图片加载后是标量numpy
# 如果没有这个数据集,就载这个数据集download=True
# batch_size可以理解为多线程,即一次加载128张图片
# shuffle=True,随机化,将图片打散
train_loader = torch.utils.data.DataLoader(datasets.MNIST('db/mnist', train=True,
                                                          transform=transforms.Compose([
                                                              transforms.Resize(
                                                                  28, 28),
                                                              transforms.ToTensor(),
                                                          ]), download=True),
                                           batch_size=128, shuffle=True
                                           )

# 将上面的train_loader复制粘贴下来,作为测试集test_loader。但也要做一些修改。
# 区分数据集是用于训练还是测试,从train = True/False可以判断
test_loader = torch.utils.data.DataLoader(datasets.MNIST('db/mnist', train=False,
                                                         transform=transforms.Compose([
                                                             transforms.Resize(
                                                                 28, 28),
                                                             transforms.ToTensor(),
                                                         ]), download=True),
                                          batch_size=128, shuffle=True
                                          )


#####################
## 第2步 新建网络模型 ##
#####################

# LeNet-5是一种用于手写体字符识别的非常高效的卷积神经网络。此处使用super()函数继承父类Lenet5
# 定义类Lenet5,Lenet5只需要定义好初始化__init__和foward函数
class Lenet5(nn.Module):
    def __init__(self):
        super(Lenet5, self).__init__()
        # 通过Squential构造网络模型:将网络层和激活函数结合起来,输出激活后的网络节点
        self.model = nn.Sequential(
            # 第一层使用全连接层Linear,全连接层的输入与输出都是二维张量,一般形状为[batch_size, size],此处为[b, 784]
            nn.Linear(28*28, 512),
            # 每一层节点的输入与上层输出都具备某种函数关系,这里的ReLU()函数就是定义这个函数形式,称为“激活函数”
            nn.ReLU(),
            # 第二层
            nn.Linear(512, 256),
            nn.ReLU(),
            # 第三层,输出为10个节点,对应0~9这10个数
            nn.Linear(256, 10),
            # 每张图片经过运算后,最后一层10个节点的数值或大或小,此时再连接一个把一个Softmax层,将数值归一化为(0,1)之间且和为1的值
            nn.Softmax()
        )

    # forward定义程序怎样前向计算:上面定义了网络的结构,这里定义这个结构的使用方式
    def forward(self, x):
        # 变换数据的维度:[b, 1, 28, 28] => [b, 784]
        x = x.view(x.size(0), 28*28)
        # [b,784] => [b,10]
        pred = self.model(x)
        return pred


def main():
    # 设置计算设备为GPU,如果不做此修改,默认的计算设备为CPU,速度会很慢。使用GPU加速的前提是先安装好GPU驱动,CUDA,Anoconda
    device = torch.device('cuda')
    # x:images,[128,1,28,28],图片有128张,每张尺寸都是28x28,由于是黑白二值化图片,所以色彩维度为1。如果是彩色RGB图片,这里就是3。
    # label:图片标签,就是每张图片到底是什么数字,都以标签的形式赋予其属性
    x, label = iter(train_loader).next()
    print(x.shape, label.shape)
    # print('x:', x)
    # print('label:', label)

    # 定义一个评价函数criteon,它是损失函数定义的拟合结果和真实结果之间的差异,作为优化的目标
    criteon = nn.CrossEntropyLoss().to(device)
    model = Lenet5().to(device)
    # 定义一个优化器SGD(Stochastic Gradient Descent 随机梯度下降法)
    optimizer = optim.SGD(model.parameters(), lr=1e-3)  # 0.001

    # 数据集跑100遍
    for epoch in range(100):
        # enumerate函数用于在循环中得到计数,利用它可以同时获得索引、步数和值
        # x是每张图片的数据,label是图片的标签属性
        for step, (x, label) in enumerate(train_loader):
            x, label = x.to(device), label.to(device)
            # 将x值送到模型中,[b, 1, 28, 28] => [b, 10]
            pred = model(x)
            # pred是一张图片分别为0~9的概率,形式为[0.1, 0.8, 0.05, ......]
            # 我们希望预测值pred与这张图片的标签label越接近越好,此处使用loss变量得到二者的相差量
            loss = criteon(pred, label)

            # 先将梯度值清零
            optimizer.zero_grad()
            # 相差量loss使用backward计算出梯度gradient
            loss.backward()
            # 使用梯度值gradient更新模型参数,或者说函数各系数的权重
            # a = a - lr * grad_a
            optimizer.step()

        # loss是矢量tensor()形式,使用tensor().item()可将矢量转换为numpy标量
        print(epoch, loss.item())


# Python不同于C/C++,程序执行并不需要主程序main(),而是文件自上而下的执行。
# 但很多Python程序中都有if __name__ == "__main__"
# 这段代码的主要作用主要是让该python文件既可以独立运行,也可以当做模块导入到其他文件。
# 当导入到其他的脚本文件的时候,此时__name__的名字其实是导入模块的名字,不是’__main__’, main代码里面的就不执行了。
if __name__ == "__main__":
    main()

2、运行结果

① 我的机器大概3秒跑一遍数据集,我让程序跑100遍,从打印的过程参数可看到,随着跑的遍数增加,loss值略有波动,但整体趋于下降,波动是随机梯度下降法SGB的弊端之一。

② 调用nvida-smi,可看到Conda中的python正在调用GPU运算。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值