用代码处理数据样本的活一般比较脏。 我们希望解耦读取数据和训练的过程。
PyTorch提供了两个库:torch.utils.data.DataLoader
和torch.utils.data.Dataset
允许预加载数据集以及自己的数据。
Dataset
存储样本和相应的标签并支持索引,而DataLoader
把数据集做成iterable
方便迭代。
加载数据集
包的依赖关系:
import numpy as np
from torchvision import datasets, transforms
from torch.utils.data import DataLoader, Dataset
import torch
import gzip, os
import torch.nn.functional as F
import torch.nn as nn
import torch.optim as optim
Pytorch自带了一些数据集(它们已经继承了Dataset
类)
从 TorchVision 加载Fashion-MNIST数据集的示例。
- Fashion-MNIST是Zalando的商品图片数据集,其中包含60,000个训练示例和10,000个测试示例。 每个示例包括一个28×28灰度图像和一个来自10个类别之一的关联标签。
torchvision
包含一些常用的数据集、模型、转换函数等等。版本0.5.0包括图片分类、语义切分、目标识别、实例分割、关键点检测、视频分类等工具
import torch
from torch.utils.data import Dataset
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda
import matplotlib.pyplot as plt
training_data = datasets.FashionMNIST(
root="data", # server端的目录名
train=True, # 训练集还是测试集
download=True, # 从server端下载
transform=ToTensor() # 图像预处理,转换成torch的数据格式
)
test_data = datasets.FashionMNIST(
root="data",
train=False,
download=True,
transform=ToTensor()
)
然而,似乎是由于gfw的原因,带有download=True的东西现在似乎不太容易下载了…
ConnectionRefusedError: [WinError 10061] 由于目标计算机积极拒绝,无法连接。
于是我们退而求其次,使用已经下载好的本地数据集MNIST:
前面说了torch.Dataset
可以用来存储样本和标签并提供索引,torch.DataLoader
为负责迭代。下面就定制一下自己的Dataset和DataLoader:
class DealDataset(Dataset):
def __init__(self, folder, data_name, label_name, transform = None):
(train_set, train_labels) = load_data(folder, data_name, label_name)
self.train_set = train_set
self.train_labels = train_labels
self.transform = transform
def __getitem__(self, index):
img,target = self.train_set[index],int(self.train_labels[index])
if self.transform is not None:
img = self.transform(img)
return img, target
def __len__(self):
return len(self.train_set)
# 其中load_data定义如下:
def load_data(data_folder, data_name, label_name):
with gzip.open(os.path.join(data_folder,label_name), 'rb') as lbpath:
y_train = np.frombuffer(lbpath.read(), np.uint8, offset=8)
with gzip.open(os.path.join(data_folder,data_name), 'rb') as imgpath:
x_train = np.frombuffer(imgpath.read(), np.uint8, offset=16).reshape(len(y_train), 28, 28)
return (x_train, y_train)
定制Dataset需要覆写三个方法:
__init__
:进行类的初始化__getitem__
:进行索引__len__
:返回数据集的长度
上述代码在__init__
中实现了从MNIST目录中读取所有数据(包括训练集和测试集)并且对数据类型进行适当转化(幸好MNIST数据集不大)
trainDataset = DealDataset('.\\mnist_dataset', "train-images-idx3-ubyte.gz","train-labels-idx1-ubyte.gz",transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
]))
testDataset = DealDataset('.\\mnist_dataset', "t10k-images-idx3-ubyte.gz","t10k-labels-idx1-ubyte.gz",transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
]))
在训练模型时,我们通常希望小批量传递样本,防止数据集太大,并且在每个时期重新整理数据以减少模型的过拟合,用Python多进程来加快数据检索(retrieve)的速度。这时便可以使用Dataloader。
接下来是对一些参数的初始化以及使用DataLoader对Dataset进行简易包装:
batch_size = 20
learning_rate = .01
epochs = 50
train_loader = DataLoader(trainDataset, batch_size, shuffle=True)
test_loader = DataLoader(testDataset, batch_size, shuffle=True)
下面构建一个简易的三层神经网络模型:
class MLP(nn.Module):
def __init__(self):
super(MLP, self).__init__()
self.model = nn.Sequential(
nn.Linear(784, 200),
nn.ReLU(inplace=True),
nn.Linear(200, 200),
nn.ReLU(inplace=True),
nn.Linear(200, 10),
nn.ReLU(inplace=True)
)
def forward(self, x):
x = self.model(x)
return x
接下来便开始训练了:
net = MLP()
optimizer = optim.SGD(net.parameters(), lr = learning_rate)
criterion = nn.CrossEntropyLoss()
i = 0
for epoch in range(epochs):
for batch_idx, (data, target) in enumerate(train_loader):
data = data.view(-1, 28*28)
data, target = data, target
logits = net(data)
loss = criterion(logits, target)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if batch_idx % 100 == 0:
print('Train Epoch:{} [{}/{} ({:.0f}%)]\tLoss:{:.6f}'.format( epoch,batch_idx*len(data),len(train_loader.dataset),100.*batch_idx/len(train_loader),loss.item()))
test_loss = 0
correct = 0
for data, target in test_loader:
data = data.view(-1, 28*28)
data, target = data, target
logits = net(data)
test_loss += criterion(logits, target).item()
pred = logits.argmax(dim=1)
correct += pred.eq(target).float().sum().item()
test_loss /= len(test_loader.dataset)
i = i+1
if i % 1000 == 0:
print('\nTest set:Average loss:{:.4f},Accuracy:{}/{} ({:.0f}%)\n'.format(test_loss,correct,
len(test_loader.dataset),
100.*correct/len(test_loader.dataset)))
这里因为计算机版本的问题,所以没有使用cuda加速。