pytorch實現對Fashion-MNIST數據集進行圖像分類
導入所需模塊:
import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import time
import sys
對數據集的操作(讀取數據集):
由於像素值為0到255的整數,所以剛好是uint8所能表示的范圍,包括transforms.ToTensor()在內的一些關於圖片的函數就默認輸入的是uint8型,若不是,可能不會報錯但可能得不到想要的結果。所以,如果用像素值(0-255整數)表示圖片數據,那么一律將其類型設置成uint8,避免不必要的bug
通過torchvision的torchvision.datasets來下載這個數據集。第一次調用時數據集目錄不存在,會自動從網上獲取數據。
mnist_train = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST', train=True, download=True, transform=transforms.ToTensor())
mnist_test = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST', train=False, download=True, transform=transforms.ToTensor())
獲取標簽:
def get_fashion_mnist_labels(labels):
text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
return [text_labels[int(i)] for i in labels]
定義畫圖函數:
def use_svg_display():
# 用矢量圖顯示
display.set_matplotlib_formats('svg')
def set_figsize(figsize=(3.5, 2.5)):
use_svg_display()
# 設置圖的尺寸
plt.rcParams['figure.figsize'] = figsize
def show_fashion_mnist(images, labels):
use_svg_display()
# 這里的_表示我們忽略(不使用)的變量
_, figs = plt.subplots(1, len(images), figsize=(12, 12))
for f, img, lbl in zip(figs, images, labels):
f.imshow(img.view((28, 28)).numpy())
f.set_title(lbl)
f.axes.get_xaxis().set_visible(False)
f.axes.get_yaxis().set_visible(False)
plt.show()
展示數據集中的前10個樣本:
X, y = [], []
for i in range(10):
X.append(mnist_train[i][0])
y.append(mnist_train[i][1])
show_fashion_mnist(X, get_fashion_mnist_labels(y))
PyTorch的DataLoader允許使用多進程來加速數據讀取
通過參數num_workers來設置4個進程讀取數據:
batch_size = 256
if sys.platform.startswith('win'):
num_workers = 0 # 0表示不用額外的進程來加速讀取數據
else:
num_workers = 4
train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)
初始化模型參數:
num_inputs = 784 # 784個樣本
num_outputs = 10 # 分為10類
W = torch.tensor(np.random.normal(0, 0.01, (num_inputs, num_outputs)), dtype=torch.float)
b = torch.zeros(num_outputs, dtype=torch.float)
# 模型參數梯度回調
W.requires_grad_(requires_grad=True)
b.requires_grad_(requires_grad=True)
定義softmax運算:
softmax運算會先通過exp函數對每個元素做指數運算,再對exp矩陣同行元素求和,最后令矩陣每行各元素與該行元素之和相除。這樣一來,最終得到的矩陣每行元素和為1且非負。因此,該矩陣每行都是合法的概率分布。softmax運算的輸出矩陣中的任意一行元素代表了一個樣本在各個輸出類別上的預測概率。
def softmax(X):
#對於隨機輸入,每個元素變成了非負數,且每一行和為1
X_exp = X.exp()
partition = X_exp.sum(dim=1, keepdim=True) # 按行求和
return X_exp / partition # 這里應用了廣播機制,會自動對partition進行擴展到與X_exp尺度相同
定義模型:
def net(X): # 定義模型
return softmax(torch.mm(X.view((-1, num_inputs)), W) + b)
def cross_entropy(y_hat, y): # 交叉熵損失函數
return - torch.log(y_hat.gather(1, y.view(-1, 1)))
def accuracy(y_hat, y): # 定義准確率計算
return (y_hat.argmax(dim=1) == y).float().mean().item()
訓練模型:
num_epochs, lr = 5, 0.1
def sgd(params, lr, batch_size): # 隨機梯度下降算法
for param in params:
param.data -= lr * param.grad / batch_size # 注意這里更改param時用的param.data
def evaluate_accuracy(data_iter, net):
acc_sum, n = 0.0, 0
for X, y in data_iter:
acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
n += y.shape[0]
return acc_sum / n
def train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size,
params=None, lr=None, optimizer=None):
for epoch in range(num_epochs):
train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
for X, y in train_iter:
y_hat = net(X)
l = loss(y_hat, y).sum()
# 梯度清零
if optimizer is not None:
optimizer.zero_grad() # 這里我們沒有用到優化器,所以直接對參數進行梯度清零
elif params is not None and params[0].grad is not None:
for param in params:
param.grad.data.zero_()
l.backward()
if optimizer is None:
sgd(params, lr, batch_size)
else:
optimizer.step() # 簡潔實現將用到優化器這里
train_l_sum += l.item()
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
n += y.shape[0]
test_acc = evaluate_accuracy(test_iter, net)
print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'
% (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc))
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, batch_size, [W, b], lr)
注意,這里的每一個train_iter是以batch_size個樣本為實例的,所以每一個train_iter包含多個(tensor, type)組合。
訓練完畢進行預測:
X, y = iter(test_iter).next()
true_labels = get_fashion_mnist_labels(y.numpy())
pred_labels = get_fashion_mnist_labels(net(X).argmax(dim=1).numpy())
titles = [true + '\n' + pred for true, pred in zip(true_labels, pred_labels)]
show_fashion_mnist(X[0:9], titles[0:9])