PyTorch簡單手寫數字識別的實現(xiàn)過程
具體流程:
① 導入相應的包,下載訓練集和測試集對應需要的圖像數據。
②進行圖像數據的變換,使圖像數據轉化成pytorch可識別并計算的張量數據類型
③數據預覽測試和數據裝載
④模型搭建和參數優(yōu)化
⑤總代碼
⑥測試
一、包導入及所需數據的下載
torchvision包的主要功能是實現(xiàn)數據的處理、導入、預覽等,所以如果需要對計算機視覺的相關問題進行處理,就可以借用在torchvision包中提供的大量的類來完成相應的工作。
代碼的開始部分有這兩個:
import torch from torchvision import datasets, transforms # torchvision包的主要功能是實現(xiàn)數據的處理、導入和預覽等
torchvision.datasets:實現(xiàn)對數據集的訓練集和測試集的下載,只需使用torchvision再加上需要下載的數據集的名稱就可以了,比如本例的MNIST
下載數據集的代碼如下:
data_train = datasets.MNIST(
transform=transform,
root="./data/",
train=True,
download=True
)
data_test = datasets.MNIST(
root="./data/",
transform=transform,
train=True,
download=False
)
①root用于指定數據集在下載之后的存放路徑,這里存放在根目錄下的data文件夾
②transform用于指定導入數據集是需要對數據進行哪種變換操作
③train用于指定數據集下載完成后需要載入哪部分數據(如果設置為True,則說明載入的是該數據集的訓練集部分;如果設置為False,則說明載入的是該數據集的測試集部分)
關于數據集引入的改動
此處我對此進行了稍微地小改動,因為整個導入下載的數據集大約有6萬張圖片,這是一個極大的數據量,一臺配置正常的電腦程序運行的時間需求將會是巨大的,我當時大約跑了一上午(一臺正常配置的學生電腦),所以此處我將6萬張數據集的訓練集和測試集都只截取了前1000張用作訓練和測試,雖然說精度會降低,使得偏差較大,但是也足夠用了,在時間上會有極大的節(jié)省,代碼如下:
from torch.utils.data import random_split
data_train, _ = random_split(
dataset=data_train,
lengths=[1000, 59000],
generator=torch.Generator().manual_seed(0)
)
data_test, _ = random_split(
dataset=data_test,
lengths=[1000, 59000],
generator=torch.Generator().manual_seed(0)
)
我調用torch.utils.data import random_split函數對數據集進行了切割,使得數據量減少,提升了運行速率。
二、進行數據處理變換操作
在torch.transforms中提供了豐富的類對載入的數據進行變換。我們知道,在計算機視覺中處理的數據集有很大一部分是圖片類型的,而在PyTorch中實際進行計算的是Tensor數據類型的變量,所以我們首先需要解決的是數據類型轉換的問題
對數據進行載入及有相應變化的代碼如下:
transform = transforms.Compose( [transforms.ToTensor(), transforms.Normalize(mean=[0.5], std=[0.5])] )
我們可以將以上代碼中的torchvision.transforms.Compose類看成一種容器,它能夠同時對多種數據變換進行組合。傳入的參數是一個列表,列表中的元素就開始對載入的數據進行各種變換操作。例如本例:
①轉化數據類型為Tensor(張量)
②對均值(mean)和標準差(std)均為0.5的原始數據進行數據標準化變化
三、數據預覽測試和數據裝載
數據下載完成并載入之后,我們還需對數據進行裝載。
我們可以將數據的載入理解為對圖片的處理,在處理完成后,我們就需要將這些圖片打包好送給我們的模型進行訓練了,而裝載就是這個打包的過程
代碼片如下:
data_loader_train = torch.utils.data.DataLoader(dataset=data_train,
batch_size=4,
shuffle=True)
data_loader_test = torch.utils.data.DataLoader(dataset=data_test,
batch_size=4,
shuffle=True)
對數據的裝載使用的是torch.utils.data.DataLoader類,類中的參數:
①batch_size參數設置了每個包中的圖片數據個數,代碼中的值是4(此處如果電腦配置不是很高或者想讓程序跑的快一點的話可以稍微調低,原本為64,此處我將其調為4)
②dataset參數用于指定我們載入的數據集的名稱。 ③將shuffle參數設置為True,在裝載的過程中會將數據隨機打亂順序并進行打包。
在裝載完成后,我們可以選取其中一個批次的數據進行預覽。進行數據預覽的代碼如下:
images, labels = next(iter(data_loader_train)) img = torchvision.utils.make_grid(images) img = img.numpy().transpose(1, 2, 0) std = [0.5] mean = [0.5] img = img * std + mean print([labels[i] for i in range(4)]) plt.imshow(img) plt.show()
在以上代碼中使用了iter和next來獲取一個批次的圖片數據(images)和其對應的圖片標簽(abels)。
然后使用torchvision.utils中的make_grid類方法將一個批次的圖片構造成網格模式。
需要傳遞給torchvision.utils.make_grid的參數就是一個批次的裝載數據,每個批次的裝載數據都是4維的,維度的構成從前往后分別為batch_size、channel、height、weight,分別對應一個批次中的數據個數、每張圖片的色彩通道數、每張圖片的高度和寬度。
在通過torchvision.utils.make_grid之后,圖片的維度就變成了(channel,height,weight),這個批次的圖片全部被整合到了一起,所以在這個維度中對應的值也和之前不一樣了,但是色彩通道數保持不變。
若我們想使用Matplotlib將數據顯示成正常的圖片形式,則使用的數據首先必須是數組,其次這個數組的維度必須是(height、weight、channel),即色彩通道數在最后面。
所以我們要通過numpy和transpose完成原始數據類型的轉換和數據維度的交換,這樣才能夠使用Matplotlib繪制出正確的圖像。
在完成數據預覽的代碼中,我們先打印輸出了這個批次中的數據的全部標簽,然后才對這個批次中的所有圖片數據進行顯示。結果如下:

效果圖如下,可以看到,打印輸出的首先是4張圖片對應的標簽,然后是4張圖片的預覽效果

plt.show()的話如果是使用PyCham編譯的話一定要加上去,不然會出現(xiàn)顯示不出圖像的情況
plt.show()
四、模型搭建和參數優(yōu)化
在順利完成數據裝載之后,我們就可以開始編寫卷積神經網絡的搭建和參數優(yōu)化的代碼了。
卷積層使用torch.nn.Conv2d類方法來搭建;
激活層使用torch.nn.ReLU()類方法來搭建;
池化層使用torch.nn.MaxPool2d類方法來搭建;
全連接層使用torch.nn.Linear類方法來搭建
實現(xiàn)卷積神經網絡模型搭建的代碼如下:
class Model(torch.nn.Module):
def __init__(self):
super(Model, self).__init__()
self.conv1 = torch.nn.Sequential(
torch.nn.Conv2d(1, 64, kernel_size=3, stride=1, padding=1),
torch.nn.ReLU(),
torch.nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
torch.nn.ReLU(),
torch.nn.MaxPool2d(stride=2, kernel_size=2)
)
self.dense = torch.nn.Sequential(
torch.nn.Linear(14 * 14 * 128, 1024),
torch.nn.ReLU(),
torch.nn.Dropout(p=0.5),
torch.nn.Linear(1024, 10)
)
def forward(self, x):
x = self.conv1(x) # 卷積處理
x = x.view(-1, 14*14*128) # 對參數實行扁平化處理
x = self.dense(x)
return x
我們選擇搭建一個在結構層次上有所簡化的卷積神經網絡模型,在結構上使用了兩個卷積層:一個最大池化層和兩個全連接層
torch.nn.Conv2d():用于搭建卷積神經網絡的卷積層,主要的輸入參數有輸入通道數、輸出通道數、卷積核大小、卷積核移動步長和Padding值。其中,
輸入通道數的數據類型是整型,用于確定輸入數據的層數;
輸出通道數的數據類型也是整型,用于確定輸出數據的層數;
卷積核大小的數據類型是整型,用于確定卷積核的大?。?br /> 卷積核移動步長的數據類型是整型,用于確定卷積核每次滑動的步長;
Paddingde的數據類型是整型,值為0時代表不進行邊界像素的填充,如果值大于0,那么增加數字所對應的邊界像素層數。
torch.nn.MaxPool2d():用于實現(xiàn)卷積神經網絡中的最大池化層,主要的輸入參數時池化窗口的大小、池化窗口移動步長和Paddingde值。
同樣:
池化窗口大小的數據類型是整型,用于確定池化窗口的大小。
池化窗口步長的數據類型也是整型,用于確定池化窗口每次移動的步長。
Paddingde值和在torch.nn.Conv2d中定義的Paddingde值的用法和意義時一樣的。
torch.nn.Dropout():torch.nn.Dropout類用于防止卷積神經網絡在訓練的過程中發(fā)生過擬合,其工作原理簡單來說就是在模型訓練的過程中,以一定的隨機概率將卷積神經網絡模型的部分參數歸零,以達到減少相鄰兩層神經連接的目的。
代碼前向傳播forward函數中的內容:
首先,經過self.conv1進行卷積處理;然后進行x.view(-1 ,14 * 14 *128),對參數實現(xiàn)扁平化因為之后緊挨著就是全連接層,所以如果不進行扁平化處理,則全連接層的實際輸出的參數維度和其定義輸入的維度將不匹配,程序會報錯;最后,通過self.dense定義的全連接進行最后的分類。
在編輯完搭建卷積神經網絡模型的代碼之后,我們就可以開始對模型進行訓練和對參數進行優(yōu)化了。首先,定義在訓練之前使用哪種損失函數和優(yōu)化函數:
model = Model() cost = torch.nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters()) # 損失函數: 交叉熵 # 優(yōu)化函數: Adam自適應優(yōu)化算法,需要優(yōu)化的參數實在Model中生成的全部參數, #因為沒有定義學習速率的值,所以使用默認值
最后,卷積神經網絡模型進行模型訓練和參數優(yōu)化的代碼如下:
epochs_n = 5
for epoch in range(epochs_n):
running_loss = 0.0
running_correct = 0
print("Epoch{}/{}".format(epoch, epochs_n))
print("-" * 10)
for data in data_loader_train:
X_train, y_train = data
X_train, y_train = Variable(X_train), Variable(y_train)
outputs = model(X_train)
_,pred = torch.max(outputs.data, 1)
optimizer.zero_grad()
loss = cost(outputs, y_train)
loss.backward()
optimizer.step()
running_loss += loss.data
running_correct += torch.sum(pred == y_train.data)
testing_correct = 0
for data in data_loader_test:
X_test, y_test = data
X_test, y_test = Variable(X_test), Variable(y_test)
outputs = model(X_test)
_, pred = torch.max(outputs.data, 1)
testing_correct += torch.sum(pred == y_test.data)
print("Loss is:{:.4f},Train Accuracy is:{:.4f}%, Test Accuracy is:{:.4f}".format(running_loss / len(data_train),100 * running_correct / len(data_train),100 * testing_correct / len(data_test)))
關于模型搭建的改動
在此處我對上面模型進行了優(yōu)化改動,大大優(yōu)化了運行的時間,但是對應也減少了一些訓練精度。
原理就是,卷積層的運算量不會太大,但全連接層的運算量比較大,所以降低全連接的參數量,以及降低圖像特征圖的尺寸
class Model(torch.nn.Module):
def __init__(self):
super(Model, self).__init__()
self.conv1 = torch.nn.Sequential(
torch.nn.Conv2d(1, 64, kernel_size=3, stride=2, padding=1),
torch.nn.ReLU(),
torch.nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=1),
torch.nn.ReLU(),
# torch.nn.MaxPool2d(stride=2, kernel_size=2)
)
self.dense = torch.nn.Sequential(
# torch.nn.Linear(14 * 14 * 128, 1024),
torch.nn.Linear(7 * 7 * 128, 512),
torch.nn.ReLU(),
# torch.nn.Dropout(p=0.5),
torch.nn.Dropout(p=0.8),
torch.nn.Linear(512, 10)
)
def forward(self, x):
x = self.conv1(x) # 卷積處理
# x = x.view(-1, 14*14*128) # 對參數實行扁平化處理
x = x.view(-1, 7*7*128) # 對參數實行扁平化處理
x = self.dense(x)
return x
為了驗證我們訓練的模型是不是真的已如結果顯示的一樣準確,則最好的方法就是隨機選取一部分測試集中的圖片,用訓練好的模型進行預測,看看和真實值有多大偏差,并對結果進行可視化,測試的代碼如下:
X_test, y_test = next(iter(data_loader_test))
inputs = Variable(X_test)
pred = model(inputs)
_, pred = torch.max(pred,1)
print("Predict Label is:", [i for i in pred.data])
print("Real Label is:", [i for i in y_test])
img = torchvision.utils.make_grid(X_test)
img = img.numpy().transpose(1,2,0)
std = [0.5, 0.5, 0.5]
mean = [0.5, 0.5, 0.5]
img = img*std+mean
plt.imshow(img)
plt.show()
記得末尾一定加上plt.show()
用于測試的數據標簽結果輸出如下:


在輸出結果中
第1個結果是我們訓練好的模型的預測值,第2個結果是這4個測試數據的真實值。
對測試數據進行可視化,如下圖所示:

可以看到,在上圖可視化的這部分測試集圖片,模型的預測結果和真實結果是完全一致的。當然如果想選取更多的測試集進行可視化,則只需將batch_size設置的更大,但考慮對應程序的運行速度將會略微降低
總代碼:
import torch
import numpy
import torchvision
import matplotlib.pyplot as plt
from torchvision import datasets, transforms # torchvision包的主要功能是實現(xiàn)數據的處理、導入和預覽等
from torch.autograd import Variable
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize(mean=[0.5], std=[0.5])])
data_train = datasets.MNIST(
transform=transform,
root="./data/",
train=True,
download=True
)
data_test = datasets.MNIST(
root="./data/",
transform=transform,
train=True,
download=False
)
from torch.utils.data import random_split
data_train, _ = random_split(
dataset=data_train,
lengths=[1000, 59000],
generator=torch.Generator().manual_seed(0)
)
data_test, _ = random_split(
dataset=data_test,
lengths=[1000, 59000],
generator=torch.Generator().manual_seed(0)
)
data_loader_train = torch.utils.data.DataLoader(dataset=data_train,
batch_size=4,
shuffle=True)
data_loader_test = torch.utils.data.DataLoader(dataset=data_test,
batch_size=4,
shuffle=True)
# images, labels = next(iter(data_loader_train))
#
# img = torchvision.utils.make_grid(images)
# img = img.numpy().transpose(1, 2, 0)
#
# std = [0.5]
# mean = [0.5]
# img = img * std + mean
#
# print([labels[i] for i in range(64)])
# plt.imshow(img)
# plt.show()
# class Model(torch.nn.Module):
#
# def __init__(self):
# super(Model, self).__init__()
# self.conv1 = torch.nn.Sequential(
# torch.nn.Conv2d(1, 64, kernel_size=3, stride=1, padding=1),
# torch.nn.ReLU(),
# torch.nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
# torch.nn.ReLU(),
# torch.nn.MaxPool2d(stride=2, kernel_size=2)
# )
#
# self.dense = torch.nn.Sequential(
# torch.nn.Linear(14 * 14 * 128, 1024),
# torch.nn.ReLU(),
# torch.nn.Dropout(p=0.5),
# torch.nn.Linear(1024, 10)
# )
#
# def forward(self, x):
# x = self.conv1(x) # 卷積處理
# x = x.view(-1, 14*14*128) # 對參數實行扁平化處理
# x = self.dense(x)
# return x
class Model(torch.nn.Module):
def __init__(self):
super(Model, self).__init__()
self.conv1 = torch.nn.Sequential(
torch.nn.Conv2d(1, 64, kernel_size=3, stride=2, padding=1),
torch.nn.ReLU(),
torch.nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=1),
torch.nn.ReLU(),
# torch.nn.MaxPool2d(stride=2, kernel_size=2)
)
self.dense = torch.nn.Sequential(
# torch.nn.Linear(14 * 14 * 128, 1024),
torch.nn.Linear(7 * 7 * 128, 512),
torch.nn.ReLU(),
# torch.nn.Dropout(p=0.5),
torch.nn.Dropout(p=0.8),
torch.nn.Linear(512, 10)
)
def forward(self, x):
x = self.conv1(x) # 卷積處理
# x = x.view(-1, 14*14*128) # 對參數實行扁平化處理
x = x.view(-1, 7 * 7 * 128) # 對參數實行扁平化處理
x = self.dense(x)
return x
model = Model()
cost = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())
epochs_n = 5
for epoch in range(epochs_n):
running_loss = 0.0
running_correct = 0
print("Epoch{}/{}".format(epoch, epochs_n))
print("-" * 10)
for data in data_loader_train:
X_train, y_train = data
X_train, y_train = Variable(X_train), Variable(y_train)
outputs = model(X_train)
_, pred = torch.max(outputs.data, 1)
optimizer.zero_grad()
loss = cost(outputs, y_train)
loss.backward()
optimizer.step()
running_loss += loss.data
running_correct += torch.sum(pred == y_train.data)
testing_correct = 0
for data in data_loader_test:
X_test, y_test = data
X_test, y_test = Variable(X_test), Variable(y_test)
outputs = model(X_test)
_, pred = torch.max(outputs.data, 1)
testing_correct += torch.sum(pred == y_test.data)
print("Loss is:{:.4f},Train Accuracy is:{:.4f}%, Test Accuracy is:{:.4f}".format(running_loss / len(data_train),
100 * running_correct / len(
data_train),
100 * testing_correct / len(
data_test)))
X_test, y_test = next(iter(data_loader_test))
inputs = Variable(X_test)
pred = model(inputs)
_, pred = torch.max(pred, 1)
print("Predict Label is:", [i for i in pred.data])
print("Real Label is:", [i for i in y_test])
img = torchvision.utils.make_grid(X_test)
img = img.numpy().transpose(1, 2, 0)
std = [0.5, 0.5, 0.5]
mean = [0.5, 0.5, 0.5]
img = img * std + mean
plt.imshow(img)
plt.show()
測試
最后,關于這類代碼的運行時間的需求都是巨大的,所以短時間內出不來很正常,盡量別中途中斷程序,若你想檢測程序是否運行:
epochs_n = 5
for epoch in range(epochs_n):
running_loss = 0.0
running_correct = 0
print("Epoch{}/{}".format(epoch, epochs_n))
print("-" * 10)
iter = 0
for data in data_loader_train:
iter+=1
print(iter)
X_train, y_train = data
X_train, y_train = Variable(X_train), Variable(y_train)
outputs = model(X_train)
_, pred = torch.max(outputs.data, 1)
optimizer.zero_grad()
loss = cost(outputs, y_train)
loss.backward()
optimizer.step()
running_loss += loss.data
running_correct += torch.sum(pred == y_train.data)
testing_correct = 0
for data in data_loader_test:
X_test, y_test = data
X_test, y_test = Variable(X_test), Variable(y_test)
outputs = model(X_test)
_, pred = torch.max(outputs.data, 1)
testing_correct += torch.sum(pred == y_test.data)
print("Loss is:{:.4f},Train Accuracy is:{:.4f}%, Test Accuracy is:{:.4f}".format(running_loss / len(data_train),
100 * running_correct / len(
data_train),
100 * testing_correct / len(
data_test)))
你可以在此處加上一個int型的測試變量iter,通過觀察iter是否累加迭代來判斷程序是否繼續(xù)在運行


總結
到此這篇關于PyTorch簡單手寫數字識別的文章就介紹到這了,更多相關PyTorch手寫數字識別內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
詳解Python如何精確控制asyncio并發(fā)運行多個任務
這篇文章主要為大家詳細介紹了Python如何精確控制asyncio并發(fā)運行多個任務,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2023-10-10
PyQt4 treewidget 選擇改變顏色,并設置可編輯的方法
今天小編就為大家分享一篇PyQt4 treewidget 選擇改變顏色,并設置可編輯的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-06-06

