[深度学习实战]基于PyTorch的深度学习实战(下)[Mnist手写数字图像识别]

本文涉及的产品
图像搜索,7款服务类型 1个月
简介: 笔记

一、前言


 首先感谢所有点开本文的朋友们!基于PyTorch的深度学习实战可能要告一段落了。本想着再写几篇关于PyTorch神经网络深度学习的文章来着,可无奈项目时间紧任务重,要求短时间内出图并做好参数拟合。所以只得转战Matlab编程,框架旧就旧吧,无所谓了 (能出结果就行) 。

 往期回顾:

[深度学习实战]基于PyTorch的深度学习实战(上)[变量、求导、损失函数、优化器]


[深度学习实战]基于PyTorch的深度学习实战(中)[线性回归、numpy矩阵的保存、模型的保存和导入、卷积层、池化层]


二、Mnist手写数字图像识别


 一个典型的神经网络的训练过程大致分为以下几个步骤:

 (1)首先定义神经网络的结构,并且定义各层的权重参数的规模和初始值。

 (2)然后将输入数据分成多个批次输入神经网络。

 (3)将输入数据通过整个网络进行计算。

 (4)每次迭代根据计算结果和真实结果的差值计算损失。

 (5)根据损失对权重参数进行反向求导传播。

 (6)更新权重值,更新过程使用下面的公式:

 weight = weight + learning_rate * gradient。其中 weight 是上一次的权重值,learning_rate 是学习步长,gradient 是求导值。

 下面我们还是以经典的图像识别卷积网络作为例子来学习 pytorch 的用法。选择一个较小的数据集进行训练,这里我们选择 mnist 手写识别数据集,该数据集是 0-9 个数字的训练数据和测试数据,训练数据 60000 张图片,测试数据 10000 张图片。每张图片是 28×28 像素大小的单通道黑白图片,这样读到内存数据组就是 28 * 28 * 1 大小。这里的 28 * 28 是宽度和高度的像素数量,1 是图像通道数据(如果是彩色图像就是 3 道路,内存数据为 28 * 28 * 3 大小)。

 我们先看看样本图像的模样:

20.png

 好,下一步我们构建一个简单的卷积神经网络模型,这个模型的输入是图片,输出是 0-9 的数字,这是一个典型的监督式分类神经网络。


2.1 加载数据

 这里我们不打算下载原始的图像文件然后通过 opencv 等图像库读取到数组,而是直接下载中间数据。当然读者也可以下载原始图像文件从头开始装载数据,这样对整个模型会有更深刻的体会。

 我们用 mnist 数据集作例子,下载方法有两种:


2.1.1 下载地址

 http://yann.lecun.com/exdb/mnist/。一共四个文件:

 train-images-idx3-ubyte.gz: training set images (9912422 bytes)

 train-labels-idx1-ubyte.gz: training set labels (28881 bytes)

 t10k-images-idx3-ubyte.gz: test set images (1648877 bytes)

 t10k-labels-idx1-ubyte.gz: test set labels (4542 bytes)

 这些文件中是已经处理过的数组数据,通过numpy的相关方法读取到训练数据集和测试数据集数组中。注意调用 load_mnist 方法之前先解压上述四个文件。


from __future__ import print_function
import os
import struct
import numpy as np
def load_mnist(path, kind='train'):
labels_path = os.path.join(path, '%s-labels.idx1-ubyte' % kind)
images_path = os.path.join(path, '%s-images.idx3-ubyte' % kind)
with open(labels_path, 'rb') as lbpath:
magic, n = struct.unpack('>II', lbpath.read(8))
labels = np.fromfile(lbpath, dtype=np.uint8)
with open(images_path, 'rb') as imgpath:
magic, num, rows, cols = struct.unpack(">IIII", imgpath.read(16))
images = np.fromfile(imgpath, dtype=np.uint8).reshape(len(labels), 784)
return images, labels
X_train, y_train = load_mnist('./data', kind='train')
print('Rows: %d, columns: %d' % (X_train.shape[0], X_train.shape[1]))
X_test, y_test = load_mnist('./data', kind='t10k')
print('Rows: %d, columns: %d' % (X_test.shape[0], X_test.shape[1]))


 显示结果:


D:\AI>python mnist_cnn.py
Rows: 60000, columns: 784
Rows: 10000, columns: 784


 我们看到训练数据的行数是 60000,表示 60000 张图片,列是 784,表示 28281=784 个像素,测试数据是 10000 张图片,在后面构建卷积模型时,要先将 784 个像素的列 reshape 成 28281 维度。

 例如:


image1 = X_train[1]
image1 = image1.astype('float32')
image1 = image1.reshape(28,28,1)


 我们还可以将这些图像数组导出为 jpg 文件,比如下面的代码:


import cv2
cv2.imwrite('1.jpg',image1)


 当前目录下的 1.jpg 文件都是我们导出的图像文件了。

 图像的显示:


import cv2
import numpy as np
img = cv2.imread("C:\lena.jpg")
cv2.imshow("lena",img)
cv2.waitKey(10000)


2.1.2 用 numpy 读取 mnist.npz

 可以直接从亚马逊下载文件:

 https://s3.amazonaws.com/imgdatasets/mnist.npz

 Mnist.npz 是一个 numpy 数组文件。如果下载的是 mnist.npz.gz 文件,则用 gunzip mnist.npz.gz 先解压成 mnist.npz,然后再处理。也可以调用 keras 的方法来下载:


from keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()


 通过 mnist.load_data() 下载的 mnist.npz 会放在当前用户的 .keras 目录中。路径名称:

 ~/.keras/datasets/mnist.npz

 然后调用 numpy 来加载 mnist.npz。

 示例代码:


import numpy as np
class mnist_data(object):
def load_npz(self,path):
f = np.load(path)
for i in f:
print i
x_train = f['trainInps']
y_train = f['trainTargs']
x_test = f['testInps']
y_test = f['testTargs']
f.close()
return (x_train, y_train), (x_test, y_test)
a = mnist_data()
(x_train, y_train), (x_test, y_test) = a.load_npz('D:/AI/torch/data/mnist.npz')
print ("train rows:%d,test rows:%d"% (x_train.shape[0], x_test.shape[0]))
print("x_train shape",x_train.shape)
print("y_train shape",y_train.shape )


 结果:

21.png

 一共 60000 张训练图片,10000 张测试图片。训练图像的大小 784 个像素,也就是 1 通道的 28*28 的手写图片。标签是长度为 10 的 (0,1) 向量,表示 0-9 是个数字。例如数字 1 就表示为 [0,1,0,0,0,0,0,0,0,0]。

 让我们打印出第一张图片的截图和标签。代码如下:

tt=x_train[1]
tt=tt.astype('float32')
image = tt.reshape(28,28,1)
cv2.imwrite("001.png",image)
print("tt label:",y_train[1])

22.png

 标签为 [0,0,0,1,0,0,0,0,0,0]。


2.2 定义卷积模型

 这一步我们定义自己的卷积模型,对于 28 * 28 的数组,我们定义 (5,5) 的卷积核大小比较合适,卷积核的大小可根据图像大小灵活设置,比如 (3,3),(5,5),(9,9)等。一般卷积核的大小是奇数。

 输入数据首先连接 1 个卷积层加 1 个池化层,conv1 和 pool1,假设我们定义 conv1 的神经元数目为 10,卷积核大小 (5,5),定义 pool1 的大小 (2,2),意思将 2 * 2区域共 4 个像素统计为 1 个像素,这样这层数据量减少 4 倍。这时候输出图像大小为 (28-5+1)/2=12。输出数据维度(10,12,12)。

 接着再来一次卷积池化,连接 1 个卷积层加 1 个池化层,conv2 和 pool2,假设我们定义 conv2 的神经元数目为 20,卷积核大小 (5,5),定义pool2的大小 (2,2),意思将 2*2 区域共 4 个像素统计为 1 个像素,这样这层数据量减少 4 倍。这时候输出图像大小为 (12-5+1)/2=4。输出数据维度 (20,4,4)。

 然后接一个 dropout 层,设置 dropout=0.2,随机抛弃部分数据。

 最后连续接两个全连接层,第一个全连接层 dense1 输入维度 20 * 4 * 4,输出 320,第二个全连接层 dense1 输入维度 60,输出 10。

 模型定义的 pytorch 代码如下:


class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
self.conv2_drop = nn.Dropout2d()
self.fc1 = nn.Linear(320, 60)
self.fc2 = nn.Linear(60, 10)
def forward(self, x):
x = F.relu(F.max_pool2d(self.conv1(x), 2))
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
x = x.view(-1, 320)
x = F.relu(self.fc1(x))
x = F.dropout(x, training=self.training)
x = self.fc2(x)
return F.log_softmax(x)
model = Net()
print(model)


 注解:nn.Conv2d(1, 10, kernel_size=5) 是指将 1 通道的图像数据的输入卷积成 10 个神经元,这 1 个通道跟 10 个神经元都建立连接,然后神经网络对每个连接计算出不同的权重值,某个神经元上的权重值对原图卷积来提取该神经元负责的特征。这个过程是神经网络自动计算得出的。

 那么 nn.Conv2d(10, 20, kernel_size=5) 是指将 10 通道的图像数据的输入卷积成 20 个神经元,同样的,这 10 个通道会和 20 个神经元的每一个建立连接,那么10 个通道如何卷积到 1 个通道呢?一般是取 10 个通道的平均值作为最后的结果。

 执行,将卷积模型的结构打印出来:


[root@iZ25ix41uc3Z ai]# python mnist_torch.py
Net (
(conv1): Conv2d(1, 10, kernel_size=(5, 5), stride=(1, 1))
(conv2): Conv2d(10, 20, kernel_size=(5, 5), stride=(1, 1))
(conv2_drop): Dropout2d (p=0.5)
(fc1): Linear (320 -> 50)
(fc2): Linear (50 -> 10)
)


2.3 开始训练

 数据准备好,模型建立好,下面根据神经网络的三部曲,就是选择损失函数和梯度算法对模型进行训练了。

 损失函数通俗讲就是计算模型的计算结果和真实结果之间差异性的函数,典型的如距离的平方和再开平方,对于图像分类来说,我们取的损失函数是:


F.log_softmax(x)


 在神经网络的训练过程中,我们使用 loss.backward() 来反向传递修正,反向修正就是根据计算和真实结果的差值(就是损失)来反向逆传播修正各层的权重参数,修正之后的结果保存在 .grad 中,因此每轮迭代执行 loss.backward() 的时候要先对 .grad 清零。


model.zero_grad()
print('conv1.bias.grad before backward')
print(model.conv1.bias.grad)
loss.backward()
print('conv1.bias.grad after backward')
print(model.conv1.bias.grad)


 优化算法一般是指迭代更新权重参数的梯度算法,这里我们选择随机梯度算法 SGD。

 SGD 的算法如下:


weight = weight - learning_rate * gradient


 可以用一段简单的 python 脚本模拟这个 SGD 的过程:


learning_rate = 0.01
for f in model.parameters():
f.data.sub_(f.grad.data * learning_rate)


 pytorch 中含有其他的优化算法,如 Nesterov-SGD, Adam, RMSProp 等。

 它们的用法和 SGD 基本类似,这里就不一一介绍了。

 训练代码如下:


import torch.optim as optim
input = Variable(torch.randn(1, 1, 32, 32))
out = model(input)
print(out)
# create your optimizer
optimizer = optim.SGD(model.parameters(), lr=0.01)
# in your training loop:
optimizer.zero_grad() # zero the gradient buffers
output = model(input)
loss = criterion(output, target)
loss.backward()
optimizer.step() # Does the update


2.4 完整代码

 最后的代码如下所示,这里我们做了一点修改,每次迭代完会将模型参数保存到文件,下次再次执行会自动加载上次迭代后的数据。整个完整的代码如下:


'''
Trains a simple convnet on the MNIST dataset.
'''
from __future__ import print_function
import os
import struct
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable
def load_mnist(path, kind='train'):
"""Load MNIST data from `path`"""
labels_path = os.path.join(path, '%s-labels.idx1-ubyte' % kind)
images_path = os.path.join(path, '%s-images.idx3-ubyte' % kind)
with open(labels_path, 'rb') as lbpath:
magic, n = struct.unpack('>II', lbpath.read(8))
labels = np.fromfile(lbpath, dtype=np.uint8)
with open(images_path, 'rb') as imgpath:
magic, num, rows, cols = struct.unpack(">IIII", imgpath.read(16))
images = np.fromfile(imgpath, dtype=np.uint8).reshape(len(labels), 784)
return images, labels
X_train, y_train = load_mnist('./data', kind='train')
print("shape:",X_train.shape)
print('Rows: %d, columns: %d' % (X_train.shape[0], X_train.shape[1]))
X_test, y_test = load_mnist('./data', kind='t10k')
print('Rows: %d, columns: %d' % (X_test.shape[0], X_test.shape[1]))
batch_size = 100
num_classes = 10
epochs = 2
# input image dimensions
img_rows, img_cols = 28, 28
x_train= X_train
x_test=X_test
if 'channels_first' == 'channels_first':
x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
input_shape = (1, img_rows, img_cols)
else:
x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
num_samples=x_train.shape[0]
print("num_samples:",num_samples)
'''
build torch model
'''
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
self.conv2_drop = nn.Dropout2d()
self.fc1 = nn.Linear(320, 50)
self.fc2 = nn.Linear(50, 10)
def forward(self, x):
x = F.relu(F.max_pool2d(self.conv1(x), 2))
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
x = x.view(-1, 320)
x = F.relu(self.fc1(x))
x = F.dropout(x, training=self.training)
x = self.fc2(x)
return F.log_softmax(x)
model = Net()
if os.path.exists('mnist_torch.pkl'):
model = torch.load('mnist_torch.pkl')
print(model)
'''
trainning
'''
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
#loss=torch.nn.CrossEntropyLoss(size_average=True)
def train(epoch,x_train,y_train):
num_batchs = num_samples/ batch_size
model.train()
for k in range(num_batchs):
start,end = k*batch_size,(k+1)*batch_size
data, target = Variable(x_train[start:end],requires_grad=False), Variable(y_train[optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if k % 10 == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, k * len(data), num_samples,
100. * k / num_samples, loss.data[0]))
torch.save(model, 'mnist_torch.pkl')
'''
evaludate
'''
def test(epoch):
model.eval()
test_loss = 0
correct = 0
if 2>1:
data, target = Variable(x_test, volatile=True), Variable(y_test)
output = model(data)
test_loss += F.nll_loss(output, target).data[0]
pred = output.data.max(1)[1] # get the index of the max log-probability
correct += pred.eq(target.data).cpu().sum()
test_loss = test_loss
test_loss /= len(x_test) # loss function already averages over batch size
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(x_test),
100. * correct / len(x_test)))
x_train=torch.from_numpy(x_train).float()
x_test=torch.from_numpy(x_test).float()
y_train=torch.from_numpy(y_train).long()
y_test=torch.from_numpy(y_test).long()
for epoch in range(1,epochs):
train(epoch,x_train,y_train)
test(epoch)


2.5 验证结果

 2 次迭代后:Test set: Average loss: 0.3419, Accuracy: 9140/10000 (91%)

 3 次迭代后:Test set: Average loss: 0.2362, Accuracy: 9379/10000 (94%)

 4 次迭代后:Test set: Average loss: 0.2210, Accuracy: 9460/10000 (95%)

 5 次迭代后:Test set: Average loss: 0.1789, Accuracy: 9532/10000 (95%)

 顺便说一句,pytorch 的速度比 keras 确实要快很多,每次迭代几乎 1 分钟内就完成了,确实很不错!


2.6 修改参数

 我们把卷积层的神经元个数重新设置一下,第一层卷积加到 32 个,第二层卷积神经元加到 64 个。则新的模型为下面的组织:


{
(conv1): Conv2d(1, 32, kernel_size=(5, 5), stride=(1, 1))
(conv2): Conv2d(32, 64, kernel_size=(5, 5), stride=(1, 1))
(conv2_drop): Dropout2d (p=0.5)
(fc1): Linear (1024 -> 100)
(fc2): Linear (100 -> 10)
)


 然后同样的步骤我们跑一遍看看结果如何,这次明显慢了很多,但看的出来准确度也提高了一些:

 第 1 次迭代:Test set: Average loss: 0.3159, Accuracy: 9038/10000 (90%)

 第 2 次迭代:Test set: Average loss: 0.1742, Accuracy: 9456/10000 (95%)

 第 3 次迭代:Test set: Average loss: 0.1234, Accuracy: 9608/10000 (96%)

 第 4 次迭代:Test set: Average loss: 0.1009, Accuracy: 9694/10000 (97%)


三、后记


 至此,我们已经完成了机器学习中的深度学习的大部分实用知识点。其实,我这里都是演示已经跑通的程序,而在修改程序配置环境等等这些“绕弯路”的经历才更能锻炼人。基于PyTorch的深度学习实战还会有个补充篇放出,补充RNN和LSTM原理相关,作为《基于PyTorch的深度学习实战》这个方向的最后一篇。稍后见!


相关文章
|
8天前
|
机器学习/深度学习 人工智能 算法
【宠物识别系统】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+图像识别
宠物识别系统,本系统使用Python作为主要开发语言,基于TensorFlow搭建卷积神经网络算法,并收集了37种常见的猫狗宠物种类数据集【'阿比西尼亚猫(Abyssinian)', '孟加拉猫(Bengal)', '暹罗猫(Birman)', '孟买猫(Bombay)', '英国短毛猫(British Shorthair)', '埃及猫(Egyptian Mau)', '缅因猫(Maine Coon)', '波斯猫(Persian)', '布偶猫(Ragdoll)', '俄罗斯蓝猫(Russian Blue)', '暹罗猫(Siamese)', '斯芬克斯猫(Sphynx)', '美国斗牛犬
69 29
【宠物识别系统】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+图像识别
|
8天前
|
机器学习/深度学习 自动驾驶 算法
深度学习在图像识别中的应用
本文将探讨深度学习技术在图像识别领域的应用。我们将介绍深度学习的基本原理,以及如何利用这些原理进行图像识别。我们将通过一个简单的代码示例来演示如何使用深度学习模型进行图像分类。最后,我们将讨论深度学习在图像识别领域的未来发展趋势和挑战。
|
8天前
|
机器学习/深度学习 数据采集 算法
深度学习在图像识别中的应用与挑战
本文探讨了深度学习技术在图像识别领域的应用,重点分析了卷积神经网络(CNN)的基本原理、优势以及面临的主要挑战。通过案例研究,展示了深度学习如何提高图像识别的准确性和效率,同时指出了数据质量、模型泛化能力和计算资源等关键因素对性能的影响。
|
8天前
|
机器学习/深度学习 计算机视觉
深度学习在图像识别中的应用与挑战
本文深入探讨了深度学习技术在图像识别领域的应用及其面临的挑战。通过分析深度学习模型如卷积神经网络(CNN)的工作原理,我们揭示了这些模型如何有效地处理和识别图像数据。同时,文章也指出了当前深度学习在图像识别中遇到的一些主要问题,包括过拟合、数据集偏差和模型解释性等,为读者提供了对这一领域全面而深入的理解。
|
9天前
|
机器学习/深度学习 计算机视觉
深度学习在图像识别中的应用与挑战
本文深入探讨了深度学习技术在图像识别领域的应用,分析了当前主流的深度学习模型及其在图像识别中的优势和面临的挑战。通过具体案例分析,揭示了深度学习如何推动图像识别技术的边界,并讨论了未来可能的发展方向。
23 4
|
8天前
|
机器学习/深度学习 人工智能 算法
深度学习在图像识别中的革命性应用####
本文不采用传统摘要形式,直接以一段引人入胜的事实开头:想象一下,一台机器能够比人类更快速、更准确地识别出图片中的对象,这不再是科幻电影的情节,而是深度学习技术在图像识别领域带来的现实变革。通过构建复杂的神经网络模型,特别是卷积神经网络(CNN),计算机能够从海量数据中学习到丰富的视觉特征,从而实现对图像内容的高效理解和分类。本文将深入探讨深度学习如何改变图像识别的游戏规则,以及这一技术背后的原理、关键挑战与未来趋势。 ####
27 1
|
8天前
|
机器学习/深度学习 传感器 边缘计算
基于深度学习的图像识别技术在自动驾驶中的应用####
随着人工智能技术的飞速发展,深度学习已成为推动自动驾驶技术突破的关键力量之一。本文深入探讨了深度学习算法,特别是卷积神经网络(CNN)在图像识别领域的创新应用,以及这些技术如何被集成到自动驾驶汽车的视觉系统中,实现对复杂道路环境的实时感知与理解,从而提升驾驶的安全性和效率。通过分析当前技术的最前沿进展、面临的挑战及未来趋势,本文旨在为读者提供一个全面而深入的视角,理解深度学习如何塑造自动驾驶的未来。 ####
37 1
|
9天前
|
机器学习/深度学习 计算机视觉
深度学习在图像识别中的突破与挑战####
本文深入探讨了深度学习技术在图像识别领域的最新进展,重点分析了卷积神经网络(CNN)的架构创新、生成对抗网络(GANs)的应用拓展以及自监督学习策略的兴起。通过综述近年来的研究成果,本文揭示了深度学习如何不断推动图像识别技术的边界,同时指出了当前面临的主要挑战,包括数据偏差、模型泛化能力及计算资源依赖等问题,并展望了未来的发展方向。 ####
28 2
|
10天前
|
机器学习/深度学习 传感器 人工智能
深度学习在图像识别中的应用与挑战
本文探讨了深度学习技术在图像识别领域的应用,分析了其在提高图像处理效率、准确性方面的贡献,同时指出了面临的数据依赖性、模型泛化能力不足等挑战。通过具体案例分析,文章展示了深度学习如何推动图像识别技术的发展,并对未来可能的研究方向进行了展望。
38 3
|
11天前
|
机器学习/深度学习 人工智能 编解码
深度学习在图像识别中的革命性进展###
近年来,深度学习技术在图像识别领域取得了显著成就,极大地推动了人工智能的发展。本文探讨了深度学习模型如何通过模拟人类视觉系统来提高图像识别的准确性和效率,并分析了几种主流的深度学习架构及其在实际应用中的表现。此外,还讨论了当前面临的挑战及未来可能的发展方向。 ###
30 4