多維度分類問題中,最重要的部份是一部份是透過類神經網路進行圖形的分類;PyTorch 也提供了圖形方面相當多的資源以及資料,其中包括著名的「Fashion-MNIST」問題的圖形資料。
「Fashion-MNIST」問題提供了7,0000 個服飾的灰階圖形資料,每個服飾圖形由「28*28」的像素所組成;其中60,000 為訓練資料而10,000組則為測試資料;所有的圖形共分成 9 組不同的服飾類別。
在 PyTorch 中要使「Fashion-MNIST」資料以及相關的圖形處理工具,首先要先安裝「torchvision」程式庫。由於我們已經透過 nVidia 提供的 wheel 檔安裝過「PyTorch」了,所以只要裝原來的 wheel 檔複製到工作目錄,然後對照「 nVidia PyTorch 軟體工具安裝網頁」確認適當的版本,再下安裝指令就可以了。
由於我們安裝的 PyTorch 版本是 2.0,所以安裝指令指定「torchvision==0.15」。
pip3 install ‘torchvision==0.15’ *.whl
接下來可以寫程式來使用這組資料,分別將訓練資料及測試資料放在「train_data」及「test_data」這兩個變數;在第一次讀取資料的程式執行時,它會在本機端建立一個「data/FashionMNIST」的資料夾,將資料放在本機端;讀取的資料形態可以直接指定為「tensor」方便接下來類神經網路的訓練使用。
import torchvision
from torchvision import datasets
from torchvision.transforms import ToTensor
train_data=datasets.FashionMNIST(root="data",
train=True,
download=True,
transform=ToTensor(),
target_transform=None)
test_data=datasets.FashionMNIST(root="data",
train=False,
download=True,
transform=ToTensor())
print('train data count:',len(train_data))
print('test data count:',len(test_data))
這一部份與之前的多維分類問題的類神經網路的作法是一樣的。
import torch
from torch import nn
#-----------------------
# creat neural network class
class classNeural(nn.Module):
def __init__(self,n_input,n_hidden,n_output):
super().__init__()
self.n_input=n_input
self.n_hidden=n_hidden
self.n_output=n_output
#--------
self.layer1=nn.Linear(n_input,n_hidden)
self.layer2=nn.Linear(n_hidden,n_hidden)
self.layer3=nn.Linear(n_hidden,n_output)
self.active=nn.Sigmoid()
#--------
def forward(self,x):
x=self.active(self.layer1(x))
x=self.active(self.layer2(x))
return self.layer3(x)
#-----------------------
# assign CUDA device
device=torch.device('cpu')
if(torch.cuda.is_available()):
device=torch.device('cuda')
因為「Fashion-MINST」的圖形資料相對於之前的數值資料,資料量相對地比較大。每一個灰階圖形是由「28*28」的圖形點 (pixel) 所形成,每個圖形點的數值由 0 到 255 來形成;在初步的影像分類學習上,我們先使用最前面的 16 組圖形來作分類,如程式中的「Images」及「labels」變數。圖形的 pixel 資料可以使用「train_data.data」變數來取得;圖形的分類結果則以整數的方式,放在「train_data.targets」;分類的標註文字則放在「train_data.classes」文字陣列中。
import torch
from torch import nn
import matplotlib.pyplot as plt
import numpy as np
#-----------
print(train_data.data[0].shape)
print('max:',np.max(train_data.data[0].numpy()))
print('min:',np.min(train_data.data[0].numpy()))
#-----------
Images=train_data.data[0:16]
labels=train_data.targets[0:16].numpy()
#-----------------------
fig=plt.figure(figsize=(9,9))
rows,cols=4,4
for i in range(rows*cols):
fig.add_subplot(rows,cols,i+1)
plt.imshow(Images[i],cmap='gray')
plt.title(train_data.classes[labels[i]])
plt.axis(False)
plt.show()
#-----------------
由於圖形資料是一個二維的 pixel 所形成的矩陣,所以在進入類神經網路訓練之前,要將二維矩陣轉換成一維的資料。這時候我們可以使用「torchvision」提供的「flatten()」函數,將訓練資料由16個二維變數轉換成16個一維變數;同時將訓練資料成正規化。
import torch
from torch import nn
import numpy as np
# setup training data
Pixels=Images.flatten(start_dim=1,end_dim=2)
nData,nPixel=Pixels.shape
X_train=((Pixels-255/2.0)/(255)).to(device)
y_train=(labels-np.mean(labels))/(np.max(labels)-np.min(labels))
Y_train=torch.tensor(y_train.astype('float32')).unsqueeze(dim=1).to(device)
經過改變資料維度的正規化訓練,與多維分類問題同樣地進入類神經網路進行訓練。
#-------------
# create neural network model
imgNeural=classNeural(nPixel,28,1).to(device)
torch.manual_seed(13)
loss_fn=nn.MSELoss() # MSE
optimizer=torch.optim.AdamW(imgNeural.parameters(),lr=0.01)
#-------------
# training neural network
import time
for epoche in range(500):
imgNeural.train()
Y_pred=imgNeural(X_train)
loss=loss_fn(Y_pred,Y_train)
optimizer.zero_grad()
loss.backward()
optimizer.step()
類神經訓練完之後,將原來的 16 組訓練資料丟進類神經網路預測結果;初步可以看到,類神經網路可以輕易地學習完成這 16 組圖形分類資料;這也說明了類神經網路具備圖形分類學習的基本能力。不過一般的圖形分類問題,資料量都相當地龐大,會需要更多的資料處理工具才能完成。
#------------
# prediction
imgNeural.eval()
with torch.inference_mode():
Y_pred=imgNeural(X_train)
y_pred=Y_pred.squeeze(dim=1).to('cpu').numpy()
label_pred=np.round(y_pred*(np.max(labels)-np.min(labels))+np.mean(labels))
#------------
# plot
plt.scatter(range(16),labels,color='red',facecolor='none',label='Target')
plt.scatter(range(16),label_pred,color='blue',marker='x',label='Predition')
plt.xlabel('Sample #')
plt.ylabel('Classes')
showTick=np.arange(0,nData,1)
plt.xticks(ticks=showTick,labels=(showTick+1))
plt.yticks(ticks=range(len(train_data.classes)),labels=train_data.classes,fontsize=8)
plt.legend()
plt.grid()
plt.show()
當然也可以亂數選取圖形來分類,進行確認。
import random
random.seed(13)
fig=plt.figure(figsize=(9,9))
rows,cols=4,4
for i in range(rows*cols):
ix=random.randint(0,15)
#---------- handle input tensor
ImgInput=(train_data.data[ix]-255/2.0)/255
PixelInput=ImgInput.flatten().unsqueeze(dim=0).to(device)
#---------- neural network prediction
imgNeural.eval()
with torch.inference_mode():
LabelOutput=imgNeural(PixelInput)
#---------- handle output tensor
labelOutput=LabelOutput.squeeze(dim=1).to('cpu').numpy()
labelPred=np.round(labelOutput*(np.max(labels)-np.min(labels))+np.mean(labels))
#---------- transfer output into label text
titlePred=train_data.classes[int(labelPred)]
titleTarget=train_data.classes[train_data.targets[ix]]
#---------- plot
fig.add_subplot(rows,cols,i+1)
plt.imshow(ImgInput.to('cpu'),cmap='gray')
strTitle=titleTarget+'->'+titlePred
plt.title(strTitle)
plt.axis(False)
plt.show()