pytoch关于图像数据的部分
-
一般情况下处理图像、文本、音频和视频数据时,可以使用标准的Python包来加载数据到一个numpy数组中。 然后把这个数组转换成
torch.*Tensor
。-
图像可以使用 Pillow, OpenCV
-
音频可以使用 scipy, librosa
-
文本可以使用原始Python和Cython来加载,或者使用 NLTK或 SpaCy 处理
-
-
特别的,对于图像任务,创建了一个包
torchvision
,它包含了处理一些基本图像数据集的方法。这些数据集包括 Imagenet, CIFAR10, MNIST 等。除了数据加载以外,torchvision
还包含了图像转换器,torchvision.datasets
和torch.utils.data.DataLoader
。 -
本文使用CIFAR10数据集,它有如下10个类别 :‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’。CIFAR-10的图像都是 3x32x32大小的,即,3颜色通道,32x32像素。
训练一个图像分类器
-
- 使用
torchvision
加载和归一化CIFAR10训练集和测试集 - 定义一个卷积神经网络
- 定义损失函数
- 在训练集上训练网络
- 在测试集上测试网络
- 使用
-
读取和归一化 CIFAR10,使用
torchvision
可以非常容易地加载CIFAR10。-
import torch import torchvision import torchvision.transforms as transforms 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') print(trainset,len(trainset),testset,len(testset))
-
Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data\cifar-10-python.tar.gz 100.0% Extracting ./data\cifar-10-python.tar.gz to ./data Files already downloaded and verified Dataset CIFAR10 Number of datapoints: 50000 Root location: ./data Split: Train StandardTransform Transform: Compose( ToTensor() Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5)) ) 50000 Dataset CIFAR10 Number of datapoints: 10000 Root location: ./data Split: Test StandardTransform Transform: Compose( ToTensor() Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5)) ) 10000
-
-
展示一些训练图像。
-
import matplotlib.pyplot as plt import numpy as np # 展示图像的函数 def imshow(img): img = img / 2 + 0.5 # unnormalize npimg = img.numpy() plt.imshow(np.transpose(npimg, (1, 2, 0))) # 获取随机数据 dataiter = iter(trainloader) images, labels = next(dataiter) # 展示图像 imshow(torchvision.utils.make_grid(images)) # 显示图像标签 print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
-
定义一个卷积神经网络
-
将上文的LeNet5模型修改一下输出接收的图像为3通道的RGB图像
-
import torch.nn as nn import torch.nn.functional as F class 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 x net = Net() print(net)
-
Net( (conv1): Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1)) (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1)) (fc1): Linear(in_features=400, out_features=120, bias=True) (fc2): Linear(in_features=120, out_features=84, bias=True) (fc3): Linear(in_features=84, out_features=10, bias=True) )
定义损失函数和优化器
-
使用交叉熵作为损失函数,使用带动量的随机梯度下降。
-
import torch.optim as optim criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) print(criterion) print(optimizer)
-
CrossEntropyLoss() SGD ( Parameter Group 0 dampening: 0 differentiable: False foreach: None lr: 0.001 maximize: False momentum: 0.9 nesterov: False weight_decay: 0 )
训练深度学习模型
-
只需在数据迭代器上循环,将数据输入给网络,并优化。
-
for epoch in range(10): # 多批次循环 running_loss = 0.0 for i, data in enumerate(trainloader, 0): # 获取输入 inputs, labels = data # 梯度置0 optimizer.zero_grad() # 正向传播,反向传播,优化 outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() # 打印状态信息 running_loss += loss.item() if i % 2000 == 1999: # 每2000批次打印一次 print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000)) running_loss = 0.0 print('Finished Training')
-
[1, 2000] loss: 2.199 [1, 4000] loss: 1.879 [1, 6000] loss: 1.682 [1, 8000] loss: 1.567 [1, 10000] loss: 1.533 [1, 12000] loss: 1.460 [2, 2000] loss: 1.366 [2, 4000] loss: 1.390 [2, 6000] loss: 1.335 [2, 8000] loss: 1.300 [2, 10000] loss: 1.273 [2, 12000] loss: 1.248 [3, 2000] loss: 1.199 [3, 4000] loss: 1.177 [3, 6000] loss: 1.157 [3, 8000] loss: 1.161 [3, 10000] loss: 1.158 [3, 12000] loss: 1.124 [4, 2000] loss: 1.075 [4, 4000] loss: 1.056 [4, 6000] loss: 1.064 [4, 8000] loss: 1.073 [4, 10000] loss: 1.077 [4, 12000] loss: 1.072 [5, 2000] loss: 0.982 [5, 4000] loss: 0.979 [5, 6000] loss: 1.005 [5, 8000] loss: 1.013 [5, 10000] loss: 1.022 [5, 12000] loss: 0.996 [6, 2000] loss: 0.924 [6, 4000] loss: 0.947 [6, 6000] loss: 0.957 [6, 8000] loss: 0.934 [6, 10000] loss: 0.965 [6, 12000] loss: 0.960 [7, 2000] loss: 0.864 [7, 4000] loss: 0.896 [7, 6000] loss: 0.907 [7, 8000] loss: 0.904 [7, 10000] loss: 0.916 [7, 12000] loss: 0.936 [8, 2000] loss: 0.835 [8, 4000] loss: 0.847 [8, 6000] loss: 0.881 [8, 8000] loss: 0.885 [8, 10000] loss: 0.875 [8, 12000] loss: 0.871 [9, 2000] loss: 0.747 [9, 4000] loss: 0.830 [9, 6000] loss: 0.817 [9, 8000] loss: 0.874 [9, 10000] loss: 0.857 [9, 12000] loss: 0.851 [10, 2000] loss: 0.747 [10, 4000] loss: 0.788 [10, 6000] loss: 0.821 [10, 8000] loss: 0.801 [10, 10000] loss: 0.827 [10, 12000] loss: 0.850 Finished Training
在测试集上测试网络
-
在整个训练集上进行了10次训练,但是我们需要检查网络是否从数据集中学习到有用的东西。 通过预测神经网络输出的类别标签与实际情况标签进行对比来进行检测。 如果预测正确,我们把该样本添加到正确预测列表。 第一步,显示测试集中的图片并熟悉图片内容。
-
dataiter = iter(testloader) images, labels =next(dataiter) # 显示图片 imshow(torchvision.utils.make_grid(images)) print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))
-
outputs = net(images) _, predicted = torch.max(outputs, 1) imshow(torchvision.utils.make_grid(images)) print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]for j in range(4)))
-
Predicted: frog car ship plane
-
接下来让看看网络在整个测试集上的结果如何。
-
correct = 0 total = 0 with torch.no_grad(): for data in testloader: images, labels = data outputs = net(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))
-
Accuracy of the network on the 10000 test images: 63 %
-
结果看起来不错,至少比随机选择要好,随机选择的正确率为10%。 似乎网络学习到了一些东西。在识别哪一个类的时候好,哪一个不好呢?
-
class_correct = list(0. for i in range(10)) class_total = list(0. for i in range(10)) with torch.no_grad(): for data in testloader: images, labels = data outputs = net(images) _, predicted = torch.max(outputs, 1) c = (predicted == labels).squeeze() for i in range(4): label = labels[i] class_correct[label] += c[i].item() class_total[label] += 1 for i in range(10): print('Accuracy of %5s : %2d %%' % ( classes[i], 100 * class_correct[i] / class_total[i]))
-
Accuracy of plane : 69 % Accuracy of car : 74 % Accuracy of bird : 44 % Accuracy of cat : 37 % Accuracy of deer : 62 % Accuracy of dog : 55 % Accuracy of frog : 70 % Accuracy of horse : 69 % Accuracy of ship : 76 % Accuracy of truck : 78 %
在GPU上训练
-
把一个神经网络移动到GPU上训练就像把一个Tensor转换GPU上一样简单。并且这个操作会递归遍历有所模块,并将其参数和缓冲区转换为CUDA张量。
-
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # 确认我们的电脑支持CUDA,然后显示CUDA信息: print(device)
-
cpu
-
所以本机cuda不可用,后面的实验就不做了。Multi-GPU Examples — PyTorch Tutorials 1.13.1+cu117 documentation
PyTorch 基础 :数据的加载和预处理
-
PyTorch通过torch.utils.data对一般常用的数据加载进行了封装,可以很容易地实现多线程数据预读和批量加载。 并且torchvision已经预先实现了常用图像数据集,包括前面使用过的CIFAR-10,ImageNet、COCO、MNIST、LSUN等数据集,可通过torchvision.datasets方便的调用
-
Dataset
-
Dataset是一个抽象类,为了能够方便的读取,需要将要使用的数据包装为Dataset类。 自定义的Dataset需要继承它并且实现两个成员方法:
-
__getitem__()
该方法定义用索引(0
到len(self)
)获取一条数据或一个样本
-
__len__()
该方法返回数据集的总长度
-
-
下面我们使用kaggle上的一个竞赛bluebook for bulldozers自定义一个数据集,为了方便介绍,我们使用里面的数据字典来做说明(因为条数少)
-
from torch.utils.data import Dataset import pandas as pd #定义一个数据集 class BulldozerDataset(Dataset): """ 数据集演示 """ def __init__(self, csv_file): """实现初始化方法,在初始化的时候将数据读载入""" self.df=pd.read_csv(csv_file) def __len__(self): ''' 返回df的长度''' return len(self.df) def __getitem__(self, idx): '''根据 idx 返回一行数据''' return self.df.iloc[idx].SalePrice ds_demo= BulldozerDataset('./bluebook-for-bulldozers/median_benchmark.csv') print(ds_demo,len(ds_demo)) #用索引可以直接访问对应的数据,对应 __getitem__ 方法 print(ds_demo[0])o,len(ds_demo))
-
<__main__.BulldozerDataset object at 0x00000271406178E0> 11573 24000.0
-
自定义的数据集已经创建好了,下面我们使用官方提供的数据载入器,读取数据
-
Dataloader
-
DataLoader为我们提供了对Dataset的读取操作,常用参数有:batch_size(每个batch的大小)、 shuffle(是否进行shuffle操作)、 num_workers(加载数据的时候使用几个子进程)。下面做一个简单的操作
-
import torch dl = torch.utils.data.DataLoader(ds_demo, batch_size=10, shuffle=True, num_workers=0) idata=iter(dl) print(next(idata))
-
tensor([24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000.], dtype=torch.float64)
-
常见的用法是使用for循环对其进行遍历
-
for i, data in enumerate(dl): print(i,data) if i > 5: break
-
0 tensor([24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000.], dtype=torch.float64) 1 tensor([24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000.], dtype=torch.float64) 2 tensor([24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000.], dtype=torch.float64) 3 tensor([24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000.], dtype=torch.float64) 4 tensor([24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000.], dtype=torch.float64) 5 tensor([24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000.], dtype=torch.float64) 6 tensor([24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000., 24000.], dtype=torch.float64)
-
已经可以通过dataset定义数据集,并使用Datalorder载入和遍历数据集,除了这些以外,PyTorch还提供能torcvision的计算机视觉扩展包,里面封装了
torchvision 包
-
torchvision 是PyTorch中专门用来处理图像的库,PyTorch官网的安装教程中最后的pip install torchvision 就是安装这个包。
-
torchvision.datasets 可以理解为PyTorch团队自定义的dataset,这些dataset帮我们提前处理好了很多的图片数据集,我们拿来就可以直接使用: - MNIST - COCO - Captions - Detection - LSUN - ImageFolder - Imagenet-12 - CIFAR - STL10 - SVHN - PhotoTour 我们可以直接使用,示例如下:
-
import torchvision.datasets as datasets trainset = datasets.MNIST(root='./data', # 表示 MNIST 数据的加载的目录 train=True, # 表示是否加载数据库的训练集,false的时候加载测试集 download=True, # 表示是否自动下载 MNIST 数据集 transform=None) # 表示是否需要对数据进行预处理,none为不进行预处理
-
torchvision不仅提供了常用图片数据集,还提供了训练好的模型,可以加载之后,直接使用,或者在进行迁移学习 torchvision.models模块的 子模块中包含以下模型结构。 - AlexNet - VGG - ResNet - SqueezeNet - DenseNet
-
#我们直接可以使用训练好的模型,当然这个与datasets相同,都是需要从服务器下载的 import torchvision.models as models resnet18 = models.resnet18(pretrained=True)
-
transforms 模块提供了一般的图像转换操作类,用作数据处理和数据增强
-
from torchvision import transforms as transforms transform = transforms.Compose([ transforms.RandomCrop(32, padding=4), #先四周填充0,在把图像随机裁剪成32*32 transforms.RandomHorizontalFlip(), #图像一半的概率翻转,一半的概率不翻转 transforms.RandomRotation((-45,45)), #随机旋转 transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.229, 0.224, 0.225)), #R,G,B每层的归一化用到的均值和方差 ])
-
(0.485, 0.456, 0.406), (0.2023, 0.1994, 0.2010) 这几个数字是官方的这个帖子有详细的说明: https://discuss.pytorch.org/t/normalization-in-the-mnist-example/457/21 这些都是根据ImageNet训练的归一化参数,可以直接使用,我们认为这个是固定值就可以.