使用PyTorch手寫數字識別 - 神經網絡簡介

人工智能 數據庫 Python 湃紳Python 2019-04-10

在本文中,我們將討論神經網絡,並嘗試構建手寫數字分類器。我們將使用PyTorch來實現我們的任務。

使用PyTorch手寫數字識別 - 神經網絡簡介

本文的唯一先決條件是有關Python語法的基本知識。

第1步 - 瞭解數據集

作為數據科學家,最關鍵的任務是收集完美的數據集並徹底瞭解它。對於這個項目,我們將使用流行的MNIST數據庫。它是70000個手寫數字的集合,分別分為60000個圖像的訓練集和10000個圖像的測試集。

使用PyTorch手寫數字識別 - 神經網絡簡介

清理數據是最重要的任務。幸運的是,對於我們來說,PyTorch提供了一個簡單的實現,可以使用幾行代碼下載已清理且已準備好的數據。在開始之前,我們需要導入所有必要的庫。

import numpy as np
import torch
import torchvision
import matplotlib.pyplot as plt
from time import time
from torchvision import datasets, transforms
from torch import nn, optim
使用PyTorch手寫數字識別 - 神經網絡簡介

在下載數據之前,讓我們在導入數據之前我們需要對數據執行的一些轉換。換句話說,您可以將其視為對圖像執行某種自定義編輯,以便所有圖像具有相同的尺寸和屬性。我們使用torchvision.transforms做到這一點

transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,)),
])
使用PyTorch手寫數字識別 - 神經網絡簡介

  1. transforms.ToTensor()  - 將圖像轉換為系統可以理解的數字。它將圖像分為三個顏色通道(單獨的圖像):紅色,綠色和藍色。然後它將每個圖像的像素轉換為0到255之間顏色的亮度。然後將這些值縮小到0到1之間的範圍。圖像現在是一個 Torch Tensor
  2. transforms.Normalize() - 以分別作為兩個參數的平均值和標準偏差對張量進行標準化。

現在我們下載了數據集,將它們隨機分配並轉換每個數據集。我們下載數據集並將它們加載到DataLoaderDataLoader結合了數據集和採樣器,並在數據集上提供單進程或多進程迭代器。

trainset = datasets.MNIST('PATH_TO_STORE_TRAINSET', download=True, train=True, transform=transform)
valset = datasets.MNIST('PATH_TO_STORE_TESTSET', download=True, train=False, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)
valloader = torch.utils.data.DataLoader(valset, batch_size=64, shuffle=True)
使用PyTorch手寫數字識別 - 神經網絡簡介

在一行中,batch大小是我們想要一次讀取的圖像的數量。

第2步 - 更好地瞭解數據集

在這個階段,我們將對我們的圖像和張量做一些探索性的數據分析。讓我們看看圖片和標籤的形狀。

dataiter = iter(trainloader)
images, labels = dataiter.next()
print(images.shape)
print(labels.shape)
使用PyTorch手寫數字識別 - 神經網絡簡介

你會發現圖像的形狀torch.Size([64,1,28,28]),這表明每個batch中有64個圖像,每個圖像的尺寸為28 x 28像素。同樣,標籤的形狀為torch.Size([64]),64張圖像分別對應有64個標籤。

讓我們顯示來自訓練集的一個圖像。

plt.imshow(images[0].numpy().squeeze(), cmap='gray_r');
使用PyTorch手寫數字識別 - 神經網絡簡介

我們來顯示更多圖像,這將讓我們瞭解數據集的外觀。

figure = plt.figure()
num_of_images = 60
for index in range(1, num_of_images + 1):
plt.subplot(6, 10, index)
plt.axis('off')
plt.imshow(images[index].numpy().squeeze(), cmap='gray_r')
使用PyTorch手寫數字識別 - 神經網絡簡介

這將生成一個隨機順序的圖像網格。現在,是時候開始定義我們將要使用的神經網絡了。

第3步 - 構建神經網絡

我們將構建以下神經網絡,因為您可以看到它包含一個輸入層(第一層),一個由十個神經元和兩個隱藏層組成的輸出層。

使用PyTorch手寫數字識別 - 神經網絡簡介

PyTorch的torch.nn模塊允許我們非常簡單地構建上述網絡。看下面的代碼:

input_size = 784
hidden_sizes = [128, 64]
output_size = 10
model = nn.Sequential(nn.Linear(input_size, hidden_sizes[0]),
nn.ReLU(),
nn.Linear(hidden_sizes[0], hidden_sizes[1]),
nn.ReLU(),
nn.Linear(hidden_sizes[1], output_size),
nn.LogSoftmax(dim=1))
print(model)
使用PyTorch手寫數字識別 - 神經網絡簡介

nn.Sequencial在網絡中封裝層。有三個線性層具有ReLU激活。輸出層是具有LogSoftmax激活的線性層,因為這是一個分類問題。

從技術上講,LogSoftmax函數是Softmax函數的對數,它看起來如下所示。

使用PyTorch手寫數字識別 - 神經網絡簡介

接下來,我們定義負對數似然損失。用C類訓練分類問題很有用。LogSoftmax()NLLLoss()一起作為交叉熵損失,如上面的網絡架構圖所示。

此外,你一定想知道我們為什麼在第一層有784個單元。這是因為我們在將每個圖像發送到神經網絡之前將其展平。(28 x 28 = 784)

criterion = nn.NLLLoss()
images, labels = next(iter(trainloader))
images = images.view(images.shape[0], -1)
logps = model(images) #log probabilities
loss = criterion(logps, labels) #calculate the NLL loss
使用PyTorch手寫數字識別 - 神經網絡簡介

我們將在後續文章中討論更多有關神經網絡,激活函數等的內容。

第4步 - 調整權重

神經網絡通過在可用數據上多次迭代來學習。學習術語是指調整網絡權重以最小化損失。讓我們來看看它是如何工作的。

print('Before backward pass: \n', model[0].weight.grad)
loss.backward()
print('After backward pass: \n', model[0].weight.grad)
使用PyTorch手寫數字識別 - 神經網絡簡介

在反向傳播之前,將模型權重設置為默認的none值。我們調用backward()函數來更新權重。

Before backward pass: 
None
After backward pass:
tensor([[-0.0003, -0.0003, -0.0003, ..., -0.0003, -0.0003, -0.0003],
[ 0.0008, 0.0008, 0.0008, ..., 0.0008, 0.0008, 0.0008],
[-0.0037, -0.0037, -0.0037, ..., -0.0037, -0.0037, -0.0037],
...,
[-0.0005, -0.0005, -0.0005, ..., -0.0005, -0.0005, -0.0005],
[ 0.0043, 0.0043, 0.0043, ..., 0.0043, 0.0043, 0.0043],
[-0.0006, -0.0006, -0.0006, ..., -0.0006, -0.0006, -0.0006]])
使用PyTorch手寫數字識別 - 神經網絡簡介

第5步 - 核心訓練流程

神經網絡遍歷訓練集並更新權重。我們利用PyTorch提供的torch.optim模塊來優化模型,執行梯度下降並通過反向傳播更新權重。因此,在每個週期(我們迭代訓練集的次數),我們將看到訓練損失逐漸減少。

optimizer = optim.SGD(model.parameters(), lr=0.003, momentum=0.9)
time0 = time()
epochs = 15
for e in range(epochs):
running_loss = 0
for images, labels in trainloader:
# Flatten MNIST images into a 784 long vector
images = images.view(images.shape[0], -1)

# Training pass
optimizer.zero_grad()

output = model(images)
loss = criterion(output, labels)

#This is where the model learns by backpropagating
loss.backward()

#And optimizes its weights here
optimizer.step()

running_loss += loss.item()
else:
print("Epoch {} - Training loss: {}".format(e, running_loss/len(trainloader)))
print("\nTraining Time (in minutes) =",(time()-time0)/60)
使用PyTorch手寫數字識別 - 神經網絡簡介

這可能需要一些時間來執行,並且因系統而異。

第6步 - 測試和評估

該模型已準備就緒,但我們必須先對其進行評估。我創建了一個實用函數view_classify()來顯示預測的圖像和類概率。

我將圖像傳遞給我們之前創建的驗證集中的訓練模型,以查看模型的工作原理。

images, labels = next(iter(valloader))
img = images[0].view(1, 784)
with torch.no_grad():
logps = model(img)
ps = torch.exp(logps)
probab = list(ps.numpy()[0])
print("Predicted Digit =", probab.index(max(probab)))
view_classify(img.view(1, 28, 28), ps)
使用PyTorch手寫數字識別 - 神經網絡簡介

使用PyTorch手寫數字識別 - 神經網絡簡介

預測結果

現在我們使用for循環遍歷驗證集並計算正確預測的總數。這就是我們計算精度的方法。

correct_count, all_count = 0, 0
for images,labels in valloader:
for i in range(len(labels)):
img = images[i].view(1, 784)
with torch.no_grad():
logps = model(img)

ps = torch.exp(logps)
probab = list(ps.numpy()[0])
pred_label = probab.index(max(probab))
true_label = labels.numpy()[i]
if(true_label == pred_label):
correct_count += 1
all_count += 1
print("Number Of Images Tested =", all_count)
print("\nModel Accuracy =", (correct_count/all_count))
使用PyTorch手寫數字識別 - 神經網絡簡介

我們現在看看結果。

Number Of Images Tested = 10000
Model Accuracy = 0.9751

我們的準確率達到了97.5%。我們獲得如此高精度的原因是因為我們的數據集很乾淨,有各種排列良好的圖像,而且數量很多。這使得我們的模型能夠很好地識別大量看不見的數字。

第7步 - 保存模型

現在我們完成了所有工作,我們不想失去經過訓練的模型。我們不想每次使用它時都要訓練它。為此,我們將保存模型。當我們將來需要它時,我們可以加載它並直接使用它而無需進一步訓練。

torch.save(model, './my_mnist_model.pt') 

第一個參數是模型對象,第二個參數是路徑。PyTorch模型通常使用.pt或.pth擴展名保存。

結論

我希望您喜歡構建神經網絡,訓練它,測試它並最終保存它的過程。除了建立一個很棒的項目,你肯定已經掌握了一些概念並學到了新的東西。

我們製作了GPU版本與CPU版本,如果有需要可以私信我。

相關推薦

推薦中...