MNIST是一個非常有名的手寫數字識別數據集,在很多資料中,這個數據集都會被用作深度學習的入門案例。
MNIST數據集是NIST數據集的一個子集,它包含了60000張圖片作爲訓練數據,10000張圖片作爲測試數據。在MNIST數據集中的每一張圖片都代表了0~9中的一個數字。圖片的大小都爲28x28,且數字都會出現在圖片的正中間。
數字圖片及其像素矩陣:(MNIST數據集中圖片的像素大小爲28x28,爲了更清楚的展示,圖中顯示的是14x14矩陣)
MNIST提供了四個文件:
train-images-idx3-ubyte.gz (訓練圖像數據60000個)
train-labels-idx1-ubyte.gz (訓練圖像數據標籤60000個)
t10k-images-idx3-ubyte.gz (測試圖像數據10000個)
t10k-labels-idx1-ubyte.gz (測試圖像數據標籤10000個)
PyTorch和TensorFlow都內置mnist數據集。
多分類問題,loss函數使用一個更加複雜的函數,叫交叉熵。
softmax
sigmoid函數可以將任何一個值轉化到0~1之間,對於一個二分類問題,這樣就足夠了,如果不屬於第一類,那麼必定屬於第二類,所以只需要用一個值來表示其屬於其中一類概率,但是對於多分類問題,需要知道其屬於每一類的概率,這個時候需要softmax函數。
MNIST
import numpy as np
import torch
from torchvision.datasets import mnist#導入pytorch內置的mnist數據集
from torch import nn
from torch.autograd import Variable
#使用內置函數下載mnist數據集
train_set=mnist.MNIST('/data',train=True,download=True)
test_set=mnist.MNIST('/data',train=False,download=True)
#查看其中的一個數據集
a_data,a_label=train_set[0]
print(a_data)
print(a_label)
下載完後的文件:
#這裏讀入的數據是PIL庫中的格式,可以非常方便地將其轉化爲numpy array
a_data=np.array(a_data,dtype='float32')
print(a_data.shape)
#我們可以看到這種圖片的大小是28X28
print(a_data)
完整代碼:
import numpy as np
import torch
from torchvision.datasets import mnist#導入pytorch內置的mnist數據集
from torch import nn
from torch.autograd import Variable
import matplotlib.pyplot as plt
#我們可以將數組展示出來,裏面的0就表示黑色,255表示白色
#對於神經網絡,我們第一層的輸入就是28*28=784,所以必須將得到的數據做一個變換
#使用reshape將他們拉平成一個一維向量
def data_tf(x):
x=np.array(x,dtype='float32')/255
x=(x-0.5)/0.5#標準化
x=x.reshape((-1,))#拉平
x=torch.from_numpy(x)
return x
train_set=mnist.MNIST('data',train=True,transform=data_tf,download=True)#重新載入數據集,申明定義的數據變換
test_set=mnist.MNIST('data',train=False,transform=data_tf,download=True)
a,a_label=train_set[0]
print(a.shape)
print(a_label)
#使用pytorch自帶的DataLoader定義一個數據迭代器
from torch.utils.data import DataLoader
train_data=DataLoader(train_set,batch_size=64,shuffle=True)
test_data=DataLoader(test_set,batch_size=128,shuffle=False)
#使用這樣的數據迭代器是非常有必要的,如果數據量太大,就無法一次將他們全部讀入內存,所以需要使用Python迭代器,每次生成一個批次的數據
a,a_label=next(iter(train_data))
print(a.shape)
print(a_label.shape)
#使用Sequential定義4層神經網絡
net=nn.Sequential(
nn.Linear(784,400),
nn.ReLU(),
nn.Linear(400,200),
nn.ReLU(),
nn.Linear(200,100),
nn.ReLU(),
nn.Linear(100,10)
)
#交叉熵在pyTorch中已經內置了,交叉熵的數值穩定性更差,所以內置的函數
#幫我們解決了這個問題
#定義loss函數
criterion=nn.CrossEntropyLoss()
optimizer=torch.optim.SGD(net.parameters(),1e-1)#使用隨機梯度下降,學習率0.1
#開始訓練
losses=[]
acces=[]
eval_losses=[]
eval_acces=[]
for e in range(20):
train_loss=0
train_acc=0
net.train()
for im,label in train_data:
im=Variable(im)
label=Variable(label)
#前向傳播
out=net(im)
loss=criterion(out,label)
#反向傳播
optimizer.zero_grad()
loss.backward()
optimizer.step()
#記錄誤差
train_loss+=loss.item()
#計算分類的準確率
_,pred=out.max(1)
num_correct=(pred==label).sum().item()
acc=num_correct/im.shape[0]
train_acc+=acc
losses.append(train_loss/len(train_data))
acces.append(train_acc/len(train_data))
#在測試集上檢驗效果
eval_loss=0
eval_acc=0
net.eval()#將模型改爲預測模式
for im,label in test_data:
im=Variable(im)
label=Variable(label)
out=net(im)
loss=criterion(out,label)
#記錄誤差
eval_loss+=loss.item()
#記錄準確率
_,pred=out.max(1)
num_correct=(pred==label).sum().item()
acc=num_correct/im.shape[0]
eval_acc+=acc
eval_losses.append(eval_loss/len(test_data))
eval_acces.append(eval_acc/len(test_data))
print('epoch:{},Train Loss:{:.6f},Train Acc:{:.6f},Eval Loss:{:.6f},Eval ACC:{:.6f}'
.format(e,train_loss/len(train_data),train_acc/len(train_data),
eval_loss/len(test_data),eval_acc/len(test_data)))
#畫出loss曲線和準確率曲線
plt.title("train loss")
plt.plot(np.arange(len(losses)),losses)
plt.show()
plt.plot(np.arange(len(acces)),acces)
plt.title('train acc')
plt.show()
plt.plot(np.arange(len(eval_losses)),eval_losses)
plt.title('test loss')
plt.show()
plt.plot(np.arange(len(eval_acces)),eval_acces)
plt.title('test acc')
plt.show()
運行結果: