PyTorch
使用自己的数据集
from __future__ import print_function
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.autograd.variable as Variable
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
保存 CIFAR10
为图片
CIFAR10
数据集说明
- 包含有
data_batch_1
,…,data_batch_5
,test_batch
- 每一个batch文件包括一个字典,字典的元素是:
data
:一个尺寸为 10000×3072,数据格式为uint8
的numpy array
,每一行数据存储了一张 32×32 彩色图片的数据,前 1024 位是图像的红色通道数据,接着是绿色通道和蓝色通道。label
:一个包含 10000 个 0-9 数字的列表,对应data
里每张图片的标签。
读取数据集并保存
重复执行会清空csv文件
from matplotlib.image import imsave
import numpy as np
import csv
import os
def unpickle(file):
import pickle
with open(file, 'rb') as fo:
dict = pickle.load(fo, encoding='bytes')
return dict
path = './data/cifar10/cifar-10-batches-py/'
root = './data/cifar10/cifar10/'
header = ['label', 'index']
rows = []
for i in range(5):
file = path + 'data_batch_' + str(i+1)
Xtr = unpickle(file)
print('正在处理训练集:', i)
for j in range(10000): # 处理10k张图片
img = np.reshape(Xtr[b'data'][j], (3, 32, 32)) # R:1024,G:1024,B:1024
img = img.transpose(1, 2, 0) # 转置成为图片的格式(H,W,C)
picName = './data/cifar10/cifar10/train/' + \
str(int(i*10000+j)) + '.jpg'
if not os.path.exists(picName):
imsave(picName, img)
rows.append((Xtr[b'labels'][j], j))
with open(root+'train/train.csv', 'w', encoding='utf-8', newline='') as f:
w = csv.writer(f)
w.writerow(header)
w.writerows(rows)
rows = []
Xte = unpickle(path+'test_batch')
print('正在处理测试集: 0')
for j in range(10000): # 处理10k张图片
img = np.reshape(Xte[b'data'][j], (3, 32, 32))
img = img.transpose(1, 2, 0) # 转置成为图片的格式(H,W,C)
picName = './data/cifar10/cifar10/test/' + \
str(j) + '.jpg'
if not os.path.exists(picName):
imsave(picName, img)
rows.append((Xte[b'labels'][j], j))
with open(root+'test/test.csv', 'w', encoding='utf-8', newline='') as f:
w = csv.writer(f)
w.writerow(header)
w.writerows(rows)
print('处理完毕')
正在处理训练集: 0
正在处理训练集: 1
正在处理训练集: 2
正在处理训练集: 3
正在处理训练集: 4
显示一张图片
from matplotlib.pyplot import imshow
# show a single image
img = np.reshape(Xtr[b'data'][0], (3, 32, 32)).transpose(1, 2, 0)
imshow(img)
<matplotlib.image.AxesImage at 0x18d183d2400>
定义自己的类
定义自己的数据集类,主要是在继承(torch.utils.data.Dataset
)后修改初始化(__init__(self)
)和读取图片(__getitem__(self, index)
)的函数(和 __len__(self)
)
- 训练集每张图片的命名规则为“number.jpg”,
train.csv
标记了各个图片的类别 - 测试集每张图片的命名规则为“number.jpg”,
rain.csv
标记了各个图片的类别
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from torch.utils.data import Dataset
# import csv
# 数据保存在 ./data/cifar10/cifar10/train(test)/
# root = './data/cifar10/cifar10/
class myDataset(Dataset):
def __init__(self, root, train=True, transform=None):
'''
Args:
root : 根目录
transform: 数据处理方式
'''
if train:
self.root = root + 'train/'
data = pd.read_csv(root + 'train/train.csv')
else:
self.root = root + 'test/'
data = pd.read_csv(root + 'test/test.csv')
self.imgs = data['index'].values
self.labels = data['label'].values
if transform:
self.transform = transform
def __getitem__(self, index):
target = self.labels[index]
image = plt.imread(self.root+str(int(index))+'.jpg')
if self.transform is not None:
image = self.transform(image)
return image, target
def __len__(self):
return len(self.imgs)
使用自己的类
from torch.utils.data import Dataset, DataLoader
batch_size = 4
epoch_size = 3
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
trainset = myDataset(root='./data/cifar10/cifar10/',
train=True,
transform=transform)
trainloader = torch.utils.data.DataLoader(trainset,
batch_size=batch_size,
shuffle=True,
num_workers=0)
testset = myDataset(root='./data/cifar10/cifar10/',
train=False,
transform=transform)
testloader = torch.utils.data.DataLoader(testset,
batch_size=batch_size,
shuffle=False,
num_workers=0)
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
BUG
当 shuffle 设置为 True 时,num_sample 报错
原因
因为之前的csv文件被清空导致数据为空
def imshow(img):
img = img / 2 + 0.5 # 去标准化
npimg = img.numpy() # 将torch.FloatTensor 转换为numpy
# plt.axis("off") # 不显示坐标尺寸
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('%11s' % classes[labels[j]] for j in range(batch_size)))
plane dog truck frog
torchvision.datasets.ImageFolder
保存/加载模型
保存模型
# 模型保存
torch.save(net.state_dict(), './model/learn0.pt') # .pt or .pth
定义网络
# 网络结构
class Net(nn.Module): # 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
加载模型
model = Net().to('cuda')
model.load_state_dict(torch.load('./model/learn0.pt'))
model.eval()
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)
)
model.load_state_dict(torch.load(‘./model/learn0.pt’))
IncompatibleKeys(missing_keys=[], unexpected_keys=[])
猜测是加载模型正确,没有遗漏和错误。
测试模型
# 加载测试集
test_set = torchvision.datasets.CIFAR10(
root='./data/cifar10',
train=False,
transform=transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5),
(0.5, 0.5, 0.5))])
)
testloader = torch.utils.data.DataLoader(
test_set,
batch_size=4,
shuffle=True,
num_workers=2
)
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
# 测试
dataiter = iter(testloader)
images, labels = dataiter.next()
def imshow(img):
img = img / 2 + 0.5 # 去标准化
npimg = img.numpy() # 将torch.FloatTensor 转换为numpy
# plt.axis("off") # 不显示坐标尺寸
plt.imshow(np.transpose(npimg, (1, 2, 0))) # 进行转置
plt.show() # 显示图片
# print images
print('GroundTruth:')
imshow(torchvision.utils.make_grid(images))
print(' '.join('%11s' % classes[labels[j]] for j in range(4)))
# 测试一次
outputs = model(images.to('cuda'))
_, predicted = torch.max(outputs, 1)
print('Predicted:\n', ' '.join('%11s' % classes[predicted[j]]
for j in range(4)))
GroundTruth:
dog deer dog bird
Predicted:
cat car cat bird
测试自己的图片
网络输入尺寸是 32x32
,因此需要把自己的图片进行预处理,之后才可以进行测试
需要注意的是,如果使用 plt.imread()
读入图片,返回的是 np.ndarray
类型,此时需要用 transforms.ToPILImage()
转化为 PIL
类型
采用 PIL.Image
(Pillow for Python3.x) 则无需进行转化
img_path = './data/cifar10/mytest/car-1.jpg' # √
# img_path = './data/cifar10/mytest/dog-1.jpg' # ×
# # 采用 plt.imread() 读入图片
# img = plt.imread(img_path)
# print('图片尺寸:',np.array(img).shape,type(img))
# transform = transforms.Compose(
# [transforms.ToPILImage(),
# transforms.Resize([32,32]),
# transforms.ToTensor(),
# transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
# 采用 PIL.Image 读取图片
from PIL import Image
img = Image.open(img_path)
print('未 transform 的图片尺寸:',np.array(img).shape,' 图片类型:',type(img))
plt.imshow(np.array(img))
plt.show()
transform = transforms.Compose(
[transforms.Resize([32,32]),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
img = transform(img).reshape(-1,3,32,32)
print('已 transpose 的图片尺寸:',np.array(img).shape,' 图片类型:',type(img))
# plt.imshow(img)
imshow(torchvision.utils.make_grid(img))
output = model(img.to('cuda'))
_, predicted = torch.max(output, 1)
print('模型输出为:',classes[predicted])
未 transform 的图片尺寸: (435, 728, 3) 图片类型: <class 'PIL.JpegImagePlugin.JpegImageFile'>
已 transpose 的图片尺寸: (1, 3, 32, 32) 图片类型: <class 'torch.Tensor'>
模型输出为: car
!pip install Pillow
Looking in indexes: https://pypi.doubanio.com/simple/
Requirement already satisfied: Pillow in d:\office\office\python\python3.6.2\lib\site-packages (4.3.0)
Requirement already satisfied: olefile in d:\office\office\python\python3.6.2\lib\site-packages (from Pillow) (0.44)
格式转换
# !jupyter nbconvert --to html --template full learn.ipynb
# !jupyter nbconvert --to markdown learn.ipynb
!jupyter nbconvert --to html --template full learn-1.ipynb
!jupyter nbconvert --to markdown learn-1.ipynb
[NbConvertApp] Converting notebook learn-1.ipynb to html
[NbConvertApp] Writing 335545 bytes to learn-1.html
[NbConvertApp] Converting notebook learn-1.ipynb to markdown
[NbConvertApp] Support files will be in learn-1_files\
[NbConvertApp] Making directory learn-1_files
[NbConvertApp] Making directory learn-1_files
[NbConvertApp] Writing 8365 bytes to learn-1.md