Softmax回歸多分類網絡(PyTorch實現)
雖然說深度學習的教程已經爛大街了,基礎理論也比較容易掌握,但是真正讓自己去實現的時候還是有一些坑。一方面教程不會涉及太多具體的工程問題,另一方面啃PyTorch的英文文檔還是有點麻煩。記錄一下,就當是作業報告了。
獲取數據集
首先導入所需要的包:
import torch
import torch.nn as nn
import torch.utils.data as Data
import numpy as np
import matplotlib.pyplot as plt
所用的數據集共有500條,輸入特征feature_n = 8
,輸出類別lables_n = 4
。取前兩行數據看看數據的格式:
x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | class label |
---|---|---|---|---|---|---|---|---|
0.4812 | 0.7790 | 0.8904 | 0.7361 | 0.9552 | 0.2119 | 0.7992 | 0.2409 | 4 |
0.4472 | 0.5985 | 0.7859 | 0.5035 | 0.6912 | 0.4038 | 0.0787 | 0.2301 | 1 |
我們將前400條數據作為訓練集,后100條數據作為測試集。
定義一個函數用於從文件中獲取此數據集:
def get_data(filename):
dataset = np.loadtxt(filename)
x_train = dataset[0:400,0:8]
raw_y_train = dataset[0:400,8:]
x_test = dataset[400:,0:8]
raw_y_test = dataset[400:,8:]
y_train = np.zeros((400,4),dtype = np.int)
y_test = np.zeros((100,4),dtype = np.int)
y_train = raw_y_train - 1
y_test = raw_y_test - 1
#for i in range(400):
# y_train[i,int(raw_y_train[i])-1]=1
#for j in range(100):
# y_test[j,int(raw_y_test[j])-1]=1
return x_train,y_train,x_test,y_test
從原理上講Softmax分類器的輸出個數應與class label的數量相同,理想情況下正確的那一項為1,其他為0。那么class label為4時,理想預測值應為[0, 0, 0, 1] ,而數據集中給出的值為[4] 。如果是用NumPy手動搭建網絡和寫Softmax函數,那么就需要把y_train
處理成二維矩陣。
但是對於PyTorch中提供的損失函數torch.nn.CrossEntropyLoss()
來說,數據格式稍有不同。torch.nn.CrossEntropyLoss()
第一個參數y_pred
為batch_size * labels_n大小的矩陣,第二個參數y
是一維的batch_size大小的向量,且數據范圍為[0, labels_n -1],所以要在y_train
的基礎上減1,把標簽1/2/3/4變成0/1/2/3 。
設置批訓練
讀取進來的數據集還是ndarray格式的,我們先將它們轉化為tensor:
x_train,y_train,x_test,y_test = get_data('dataset.txt')
x_train = torch.from_numpy(x_train).type(torch.FloatTensor)
y_train = torch.from_numpy(y_train).type(torch.LongTensor)
注意對轉換后的數據格式有要求。這也是損失函數torch.nn.CrossEntropyLoss()
的要求,預測概率必須為float,正確標簽必須為long。
之后建立訓練集並加載,測試集同理 :
train_set = Data.TensorDataset(x_train,y_train)
train_loader = Data.DataLoader(
dataset=train_set,
batch_size=BATCH_SIZE,
shuffle=True
)
在《動手學習深度學習》書中采用了自己手寫的迭代器進行批訓練的數據迭代。我個人覺得PyTorch提供的DataLoader工具更加簡單好用。
定義模型
采用PyTorch提供的快速搭建法進行搭建神經網絡,包含隱藏層和輸出層共2層。需要注意一點,損失函數torch.nn.CrossEntropyLoss()
中已經包含了Softmax函數,所以我們的神經網絡直接線性輸出即可。
net = nn.Sequential(
nn.Linear(8,50),
nn.ReLU(),
nn.Linear(50,4)
)
之后定義一些訓練模型的函數和參數:
EPOCH = 5000
BATCH_SIZE = 100
LR = 0.01
LOSS_FUNC = nn.CrossEntropyLoss()
OPTIMIZER = torch.optim.SGD(net.parameters(), lr=LR)
神經網絡建立后本身會對參數w、b進行初始化。因為我也不知道應該初始化為多少比較合適,所以在此不進行顯式的初始化而采用其默認的。
訓練模型
for epoch in range(1,EPOCH+1):
loss_sum = 0.0
for step,(x,y) in enumerate(train_loader):
y_pred = net(x)
y = y.squeeze() #修正標簽格式
loss = LOSS_FUNC(y_pred,y)
loss_sum += loss
OPTIMIZER.zero_grad()
loss.backward()
OPTIMIZER.step()
print("epoch: %d, loss: %f" %(epoch,loss_sum/BATCH_SIZE))
之前說過損失函數對數據格式有要求。我們的標簽y
實際上還是二維矩陣,大小為batch_size * 1,所以在計算損失函數前需要進行降維。
測試模型
acc_sum = 0.0
acc_sum += (net(x_test).argmax(dim=1) == y_test.squeeze()).sum()
print("test accuracy: %f" %(acc_sum/100))
用測試集進行測試可知准確率能達到93%。