PyTorch基础教程学习笔记(八):训练一个分类器
数据应该怎么办呢?
通常来说,当必须处理图像、文本、音频或视频数据时,可以使用python标准库将数据加载到numpy数组里。然后将这个数组转化成torch.*Tensor
。
- 对于图片,有Pillow,OpenCV等包可以使用
- 对于音频,有scipy和librosa等包可以使用
- 对于文本,不管是原生python的或者是基于Cython的文本,可以使用NLTK和SpaCy
特别对于视觉方面,我们创建了一个包,名字叫torchvision
,其中包含了针对Imagenet、CIFAR10、MNIST等常用数据集的数据加载器(data loaders),还有对图片数据变形的操作,即torchvision.datasets
和torch.utils.data.DataLoader
。
这提供了极大的便利,可以避免编写样板代码。
Pytorch——计算机视觉工具包:torchvision
torchvision
独立于Pytorch,需通过pip install torchvision
安装。
torchvision 主要包含以下三部分:
- models : 提供深度学习中各种经典的网络结构以及训练好的模型,包括Alex Net, VGG系列、ResNet系列、Inception系列等;
- datasets:提供常用的数据集加载,设计上都是继承torch.utils.data.Dataset,主要包括MMIST、CIFAR10/100、ImageNet、COCO等;
- transforms: 提供常用的数据预处理操作,主要包括对Tensor及PIL Image对象的操作。详细介绍:pytorch中的torchvision.transforms模块详解
在这个教程中,将使用CIFAR10数据集,它有如下的分类:“飞机”,“汽车”,“鸟”,“猫”,“鹿”,“狗”,“青蛙”,“马”,“船”,“卡车”等。在CIFAR-10里面的图片数据大小是3x32x32,即三通道彩色图,图片大小是32x32像素。
训练一个图片分类器
按顺序做以下步骤:
- 通过
torchvision
加载CIFAR10里面的训练和测试数据集,并对数据进行标准化 - 定义卷积神经网络
- 定义损失函数
- 利用训练数据训练网络
- 利用测试数据测试网络
加载并标准化CIFAR10
使用torchvision
, 很简单.
import torch
import torchvision
import torchvision.transforms as transforms
torchvision.transforms是pytorch中的图像预处理包,包含了很多种对图像数据进行变换的函数,这些都是在我们进行图像数据读入步骤中必不可少的。
torchvision
的输出是PILImage, 范围[0,1]. 我们将其转化为归一化到[-1, 1]范围的Tensor
.
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])trainset = torchvision.datasets.CIFAR10(root='./data', train=True,download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,shuffle=True, num_workers=2)testset = torchvision.datasets.CIFAR10(root='./data', train=False,download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,shuffle=False, num_workers=2)classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')
transforms.Compose(transforms) 方法是将多种变换组合在一起。
transforms.ToTensor() 将PILImage转变为torch.FloatTensor的数据形式;
ToTensor()能够把灰度范围从0-255变换到0-1之间,而后面的transform.Normalize()则把0-1变换到(-1,1).具体地说,对每个通道而言,Normalize执行以下操作:
image=(image-mean)/std
其中mean和std分别通过(0.5,0.5,0.5)和(0.5,0.5,0.5)进行指定。原来的0-1最小值0则变成(0-0.5)/0.5=-1,而最大值1则变成(1-0.5)/0.5=1.
注意,多种组合变换有一定的先后顺序,处理PILImage的变换方法(大多数方法)都需要放在ToTensor方法之前,而处理tensor的方法(比如Normalize方法)就要放在ToTensor方法之后。
运行Pytorch tutorial代码报错:BrokenPipeError: [Errno 32] Broken pipe
源代码地址: Training a classifier (CIFAR10)
该问题的产生是由于windows下多线程的问题,和DataLoader类有关,具体细节点这里Fix memory leak when using multiple workers on Windows。
解决方案:
修改调用torch.utils.data.DataLoader()函数时的 num_workers 参数。该参数官方API解释如下:
- num_workers (int, optional) – how many subprocesses to use for data loading. 0
means that the data will be loaded in the main process. (default: 0)该参数是指在进行数据集加载时,启用的线程数目。截止当前2018年5月9日11:15:52,如官方未解决该BUG,则可以通过修改num_works参数为 0 ,只启用一个主进程加载数据集,避免在windows使用多线程即可。
相关:
- Python图像库PIL的类Image及其方法介绍
- pytorch中的torchvision.transforms模块详解
数据集读取相关有专门的笔记博客:
- 待记录
- 待记录
- 待记录
Out:
Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data/cifar-10-python.tar.gz
Extracting ./data/cifar-10-python.tar.gz to ./data
Files already downloaded and verified
让我们看看训练数据什么样.
import matplotlib.pyplot as plt
import numpy as np# functions to show an imagedef imshow(img):img = img / 2 + 0.5 # unnormalizenpimg = img.numpy()plt.imshow(np.transpose(npimg, (1, 2, 0)))plt.show()# get some random training images
dataiter = iter(trainloader)
images, labels = dataiter.next()# show images
imshow(torchvision.utils.make_grid(images))
# print labels
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
../../_images/sphx_glr_cifar10_tutorial_001.png
- plt.imshow(np.transpose(npimg, (1, 2, 0)))。因为在plt.imshow在现实的时候输入的是(imagesize,imagesize,channels),而def imshow(img,text,should_save=False)中,参数img的格式为(channels,imagesize,imagesize),这两者的格式不一致,我们需要调用一次np.transpose函数,即np.transpose(npimg,(1,2,0)),将npimg的数据格式由(channels,imagesize,imagesize)转化为(imagesize,imagesize,channels),进行格式的转换后方可进行显示。
ist、tuple等都是可迭代对象,我们可以通过iter()函数获取这些可迭代对象的迭代器。然后我们可以对获取到的迭代器不断使⽤next()函数来获取下⼀条数据。iter()函数实际上就是调⽤了可迭代对象的 __iter__ ⽅法。
>>> li = [11, 22, 33, 44, 55]
>>> li_iter = iter(li)
>>> next(li_iter) 11
>>> next(li_iter) 22
>>> next(li_iter) 33
>>> next(li_iter) 44
>>> next(li_iter) 55
>>> next(li_iter)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
注意,当我们已经迭代完最后⼀个数据之后,再次调⽤next()函数会抛出 StopIteration的异常,来告诉我们所有数据都已迭代完成,不⽤再执⾏ next()函数了。
Out:
bird horse cat frog
这里的4是batch_size决定的
Define a Convolutional Neural Network 定义一个卷积神经网络
将之前定义的网络拷贝过来, 修改为接收3通道图片(之前的接收单通道).
import torch.nn as nn
import torch.nn.functional as Fclass Net(nn.Module):def __init__(self):super(Net, self).__init__()self.conv1 = nn.Conv2d(3, 6, 5)self.pool = nn.MaxPool2d(2, 2)self.conv2 = nn.Conv2d(6, 16, 5)self.fc1 = nn.Linear(16 * 5 * 5, 120)self.fc2 = nn.Linear(120, 84)self.fc3 = nn.Linear(84, 10)def forward(self, x):x = self.pool(F.relu(self.conv1(x)))x = self.pool(F.relu(self.conv2(x)))x = x.view(-1, 16 * 5 * 5)x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))x = self.fc3(x)return xnet = Net()
Define a Loss function and optimizer 定义一个loss函数和优化器
用分类交叉熵和带动量(momentum)的SGD.
import torch.optim as optimcriterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
Train the network 训练网络
有意思的地方来了. 我们简单地在数据迭代器(data iterator)上循环遍历, 将输入喂给网络并优化.
for epoch in range(2): # 在数据集上迭代多次running_loss = 0.0for i, data in enumerate(trainloader, 0):# get the inputs; data is a list of [inputs, labels]inputs, labels = data# zero the parameter gradientsoptimizer.zero_grad()# forward + backward + optimizeoutputs = net(Variable(inputs))loss = criterion(outputs, labels)loss.backward()optimizer.step()# print statisticsrunning_loss += loss.item()if i % 2000 == 1999: # print every 2000 mini-batchesprint('[%d, %5d] loss: %.3f' %(epoch + 1, i + 1, running_loss / 2000))running_loss = 0.0print('Finished Training')
- enumerate第二个参数,用于指定索引起始值,如:
list1 = ["这", "是", "一个", "测试"] for index, item in enumerate(list1, 1):print index, item >>> 1 这 2 是 3 一个 4 测试
out:
[1, 2000] loss: 2.205
[1, 4000] loss: 1.811
[1, 6000] loss: 1.636
[1, 8000] loss: 1.531
[1, 10000] loss: 1.466
[2, 2000] loss: 1.412
[2, 4000] loss: 1.376
[2, 6000] loss: 1.343
[2, 8000] loss: 1.301
[2, 10000] loss: 1.290
Finished Training
保存训练好的模型:
PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)
这里有更多的保存模型的细节.
Test the network on the test data 用测试数据测试网络
我们已经训练了两轮了, 我们看看网络是否学到东西了.
我们将模型输出的结果和真实结果(ground-truth)作比较. 如果预测是正确的, 我们将这个sample加到正确预测的list中.
dataiter = iter(testloader)
images, labels = dataiter.next()
# print images
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))
然后, 重新载入保存的模型(注意: 保存和重新载入模型不是必要的, 我们这里只是为了说明怎样做):
net = Net()
net.load_state_dict(torch.load(PATH))
好, 我们来看看模型分类结果如何:
outputs = net(Variable(images)) # 注意这里的images是我们从上面获得的那四张图片,所以首先要转化成variable
输出是10类的置信度(energy). 某一类的置信度越高, 网络认为图片是此类的可能性越大. 所以, 我们来获取最高的置信度:
_, predicted = torch.max(outputs.data, 1)
# 这个 _ , predicted是python的一种常用的写法,表示后面的函数其实会返回两个值
# 但是我们对第一个值不感兴趣,就写个_在那里,把它赋值给_就好,我们只关心第二个值predicted
# 比如 _ ,a = 1,2 这中赋值语句在python中是可以通过的,你只关心后面的等式中的第二个位置的值是多少print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]for j in range(4)))
这里用到了torch.max( ), 它是属于Tensor的一个方法:
注意到注释中第一句话,是说返回返回输入Tensor中每行的最大值,并转换成指定的dim(维度),
torch.max(outputs.data, 1) ,返回一个tuple (元组),元组的第一个元素是image data,即是最大的 值,第二个元素是label, 即是最大的值 的 索引.
我们只需要label(最大值的索引),所以就会有 _ , predicted这样的赋值语句,表示忽略第一个返回值,把它赋值给 _, 就是舍弃它的意思;
第二个参数1,是 the dimension to reduce 而不是去这个dimension上面找最大
所以这里dim=1,基于我们的a是 4行 x 4列 这么一个维度,所以指的是 消除列这个维度.
如果dim=0,它其实是在返回每列的最大值,
所以一定不要搞混!这里的dim是指的 the dimension to reduce!并不是在the dimension上去返回最大值。
用 torch.argmax()这个函数更直观更好理解一些
Out:
Predicted: cat ship ship ship
结果还不错, 我们来看看整个数据集上的表现:
correct = 0
total = 0
with torch.no_grad():for data in testloader:images, labels = dataoutputs = net(Variable(images))_, predicted = torch.max(outputs.data, 1)total += labels.size(0)correct += (predicted == labels).sum().item()print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total))
Out:
Accuracy of the network on the 10000 test images: 53 %
看起来比随机猜测要好. 随机猜测正确率是10%. 看来网络确实学到了东西.
看看哪些类表现好, 哪些类表现不好:
class_correct = list(0. for i in range(10))# 定义一个存储每类中测试正确的个数的 列表,初始化为0
class_total = list(0. for i in range(10))# 定义一个存储每类中测试总数的个数的 列表,初始化为0
with torch.no_grad():for data in testloader:images, labels = dataoutputs = net(Variable(images))_, predicted = torch.max(outputs.data, 1)c = (predicted == labels).squeeze()for i in range(4):label = labels[i]class_correct[label] += c[i].item()class_total[label] += 1for i in range(10):print('Accuracy of %5s : %2d %%' % (classes[i], 100 * class_correct[i] / class_total[i]))
Out:
Accuracy of plane : 57 %
Accuracy of car : 69 %
Accuracy of bird : 41 %
Accuracy of cat : 16 %
Accuracy of deer : 41 %
Accuracy of dog : 61 %
Accuracy of frog : 63 %
Accuracy of horse : 76 %
Accuracy of ship : 71 %
Accuracy of truck : 40 %
好了, 接下来做什么呢?
怎样在GPU上跑神经网络呢?
Training on GPU GPU上训练
正如将Tensor
转到GPU上一样, 你可以将网络转到GPU上.
如果CUDA有效的话, 将device定义为第一个可用的cuda设备.
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")# Assuming that we are on a CUDA machine, this should print a CUDA device:print(device)
cuda:0
剩下的部分, 我们认为device就是CUDA device.
然后这些方法将会递归地遍历我们的模型部件(module)并将他们的参数和buffer转为CUDA tensor:
net.to(device)
记住, 你必须要在每一步中将输入图像和目标输出都转到GPU上:
inputs, labels = data[0].to(device), data[1].to(device)
Training on multiple GPUs 在多GPU上训练.
如果你想用多块GPU训练, 请看: Data Parallelism.
PyTorch基础教程学习笔记(八):训练一个分类器相关推荐
- 黑马程序员最新版JavaWeb基础教程-学习笔记
da@黑马程序员最新版JavaWeb基础教程-学习笔记 day06-HTML&CSS HTML HTML(HyperTest Markup Language):超文本标记语言 是一门语言,所有 ...
- linux磁盘符变化autofs,Linux基础教程学习笔记之Autofs自动挂载
Linux基础教程学习笔记之Autofs自动挂载 Autofs自动挂载: yum -y install autofs vim /etc/auto.master 在文件中添加下面行 /home/gue ...
- 网络存储 linux 访问,Linux基础教程学习笔记28——使用Samba访问网络存储
Linux基础教程学习笔记28--使用Samba访问网络存储 SMB用于Windows和类Linux系统直接的文件共享 安装samba client包: [root@linuxidc~]# yum i ...
- Dynamic Quantization PyTorch官方教程学习笔记
诸神缄默不语-个人CSDN博文目录 本文是PyTorch的教程Dynamic Quantization - PyTorch Tutorials 1.11.0+cu102 documentation的学 ...
- Python基础教程学习笔记:第一章 基础知识
Python基础教程 第二版 学习笔记 1.python的每一个语句的后面可以添加分号也可以不添加分号:在一行有多条语句的时候,必须使用分号加以区分 2.查看Python版本号,在Dos窗口中输入&q ...
- python机器学习基础教程-学习笔记(一)
了解 scikit-learn 及其用法是很重要的,但还有其他一些库也可以改善你的编程体验. scikit-learn 是基于 NumPy 和 SciPy 科学计算库的.此外,我们还会用到 panda ...
- 【莫烦Python】Python 基础教程——学习笔记
文章目录 本笔记基于p1-p29[莫烦Python]Python 基础教程 大家可以根据代码内容和注释进行学习. 安装 我的:python3.8+anaconda+VS code print() pr ...
- 训练softmax分类器实例_吴恩达深度学习笔记(56)-训练一个 Softmax 分类器
训练一个 Softmax 分类器(Training a Softmax classifier) 上一个笔记中我们学习了Softmax层和Softmax激活函数,在这个笔记中,你将更深入地了解Softm ...
- python自学教程读书导图-python机器学习基础教程读书笔记八(全书总结)
全书总结 现在你知道了如何使用重要的机器学习算法进行监督学习和无监督学习,这让你可以解 决很多种机器学习问题.在带你探索机器学习提供的所有可能性之前,我们希望给你一 些最后的建议.一些额外的资源,并提 ...
最新文章
- .net中6个重要的基础概念:Stack, heap, Value types, reference types, boxing and Unboxing.
- 机器学习入门-决策树算法
- TLS 改变密码标准协议(Change Cipher Spec Protocol) 就是加密传输中每隔一段时间必须改变其加解密参数的协议...
- Asp.net MVC 中Controller返回值类型ActionResult
- linux编译ffmpeg.exe,linux下编译FFmpeg
- 实习成长之路——SpringBean一:BeanDefinition元信息有什么?除了Bean名称和类型,还有那些Bean的元信息值得关注?
- linux c语言 模拟键盘输入
- 数据库查询函数count搭配casewhen使用
- oppoa5降级教程_OPPO A5官方原版固件rom系统刷机包升级包下载A.12版
- 第25章 串行FLASH文件系统FatFs
- 【问题记录】pip 安装报错 Failed to establish a new connection
- TypeScript学习
- 计算机组成原理--数制与编码(校验码,CRC,汉明码详解)
- 三极管集电极和基集短接等效成二极管
- 时序分析基本概念介绍ILM
- 古诗词-飞火在线工具
- 软件工程课堂作业(十六)——找“1”的个数
- 联发科MT5597 4K数字电视芯片处理器介绍
- oracle8i substr,Oracle中INSTR和SUBSTR的用法
- 转:CentOS 7 安装Nginx