全连接神经网络实现MNIST手写数字识别
? ? ? ? 在對全連接神經網絡的基本知識(全連接神經網絡詳解)學習之后,通過MNIST手寫數字識別這個小項目來學習如何實現全連接神經網絡。
MNIST數據集
? ? ? ? 對于深度學習的任何項目來說,數據集是其中最為關鍵的部分。MNIST數據集是美國國家標準與技術研究院收集整理的大型手寫數字數據庫,包含60,000個示例的訓練集以及10,000個示例的測試集。其中的圖像的尺寸為28*28。
對于MNIST數據集,可以通過torchvision中的datasets進行下載
root?(string):?表示數據集的根目錄,其中根目錄存在MNIST/processed/training.pt和MNIST/processed/test.pt的子目錄
train?(bool, optional):?如果為True,則從training.pt創建數據集,否則從test.pt創建數據集
download?(bool, optional):?如果為True,則從internet下載數據集并將其放入根目錄。如果數據集已下載,則不會再次下載
transform?(callable, optional):?接收PIL圖片并返回轉換后版本圖片的轉換函數
data_train = datasets.MNIST(root = "./data/",transform=transform,train = True,download = True)data_test = datasets.MNIST(root="./data/",transform = transform,train = False)?但是對于這樣下載的數據,并不能直接以可視化的形式展示,對于初學者來說不是很友好。所也這里采用MNIST數據集的圖片形式。(完整的代碼和數據會放在最后)
可以看到整個數據集分為訓練集和測試集,其中文件夾名作為數據的標簽。?
下面就開始制作數據集
from torch.utils.data import DataLoader,Dataset import os import cv2 import numpy as np""" 制作自定義的數據集 實現三個魔術方法: __init__:初始化數據集 __len__:返回數據集的長度 __getitem__:索引數據,在DtaLoader的循環之中會調用這個方法 """ class MNIST_Dataset(Dataset):def __init__(self,root,isTrain = True):#初始化列表,用于存儲數據集的路徑和標簽#直接存儲圖片會導致列表過大,所以存儲圖片的路徑self.dataset = []#判斷數據集是訓練集還是測試集if isTrain:folder = "TRAIN"else:folder = "TEST"#利用os模塊,用字符串拼接圖片的路徑for tag in os.listdir(root + "/" + folder):for file in os.listdir(root + "/" + folder + "/" + tag):path = root + "/" + folder + "/" + tag + "/" + file#存儲圖片的路徑和標簽self.dataset.append((path,tag))def __len__(self):#返回數據集的長度return len(self.dataset)def __getitem__(self, index):#在DataLoader循環時調用此方法,處理圖片#首先根據index來索引一張圖片的路徑和標簽data = self.dataset[index]#獲取圖像的路徑path = data[0]#獲取圖像的標簽tag = data[1]#利用opencv讀取圖片img = cv2.imread(path,0)#將圖像轉換為一維數組 由28*28變為784img = img.reshape(-1)#歸一化處理img = img / 255#制作one-hot標簽"""因為手寫數字識別是一個十分類的問題,全連接神經網絡最后輸出的是一個長度為10的一維數組,所以one-hot標簽是一個長度為10的一維數組,獲取的標簽(比如數字9)就在一維數組索引為9的位置置1"""tag_one_hot = np.zeros(10)tag_one_hot[int(tag)] = 1return np.float32(img), np.float32(tag_one_hot)if __name__ == '__main__':#初始化測試集dataset = MNIST_Dataset("MNIST_IMG",isTrain=False)#初始化數據加載器 batch_size=100:每一批次的數據量 shuffle=True:是否打亂數據集loader = DataLoader(dataset=dataset,batch_size=100,shuffle=True)for i,(img,tag) in enumerate(loader):print(len(dataset))#批次print(i)print(img)print(img.shape)print(tag)print(tag.shape)下面來看測試集的運行結果:
????????整個數據集的長度為9997,本來應該為10000,但是我的圖像數據中少了三張,在訓練集中是正常的60000張。
????????首先看到的是第一批次的數據,有100張圖片的數據,每張圖片都由28*28轉換為了784(全連接神經網絡輸入要求)。然后就是數據的one-hot標簽,比如第一個標簽就是代表數字8
?下面再來具體的說說one-hot標簽和歸一化
one-hot標簽:
一位有效編碼,用N位狀態寄存器來對N個狀態進行編碼,例如[0, 0, 1]、[1, 0, 0]、[0, 1, 0]
下面來看一個具體的例子:
對 jack 進行one-hot編碼
1、確定樣本數,特征數;
2、jack共4個字母,也就是4個樣本;
3、字母共26個,也就是26個特征。
歸一化 :
????????由于再進行深度學習時,大多數使用的數據都是圖片。而圖片的像素范圍是0-255,在進行計算時是不夠簡單的。所以要對數據進行一個歸一化。
?全連接神經網絡的設計與實現
?
import torch from torch import nn """ 創建自定義的數據集需要繼承torch下的nn.Module類 實現兩個方法: __init__:初始化全連接神經網絡 forward:前向傳播 """ class MNIST_Net(nn.Module):def __init__(self):#需要實現父類的__init__方法,不然會報錯super().__init__()#整個全連接神經網絡放在Sequential這個容器之中self.layer = nn.Sequential(#bias:設置是否存在偏移量,默認為有#使用Linear實現全連接神經網絡的一層#圖片的原尺寸為28*28,轉化為784,輸入層為784,輸出層為512nn.Linear(784,512),#使用ReLU激活函數進行激活nn.ReLU(),nn.Linear(512,256),nn.ReLU(),nn.Linear(256,128),nn.ReLU(),nn.Linear(128,64),nn.ReLU(),nn.Linear(64,32),nn.ReLU(),nn.Linear(32,16),nn.ReLU(),nn.Linear(16,10),#最后一層使用Softmax作為激活函數,返回十個概率值#NV結構:在進入網絡的數據集向上面測試數據集時,是一個批次的進入#需要將第二個維度的數據進行激活nn.Softmax(dim=1))#進行前向傳播返回結果def forward(self,x):return self.layer(x)if __name__ == '__main__':#初始化網絡net = MNIST_Net()#初始化數據test = torch.randn(1,784)print(test)print(test.shape)#前向傳播結果answer = net.forward(test)print(answer)print(answer.shape)進行測試:
?MNIST數據集手寫數字識別訓練
import torch from torch import optim, nnfrom MNIST_Net import MNIST_Net from MNIST_Dataset import MNIST_Dataset from torch.utils.data import DataLoader """ 創建訓練類,需要實現兩個方法 __init__:初始化 call:訓練過程 """ class MNIST_Train():def __init__(self):#加載訓練集self.dataset_train = MNIST_Dataset("MNIST_IMG",isTrain=True)#加載器self.dataloader_train = DataLoader(dataset=self.dataset_train,batch_size=600,shuffle=True)#加載測試集self.dataset_test = MNIST_Dataset("MNIST_IMG",isTrain=False)#加載器self.dataloader_test = DataLoader(dataset=self.dataset_test,batch_size=100,shuffle=True)#創建模型self.net = MNIST_Net()#將模型放到cuda上,如果沒有cuda的,就只能使用CPU來進行訓練了self.net.to("cuda")#創建模型優化器,后面的梯度清空,更新參數都由這個優化器自動完成self.opt =optim.Adam(self.net.parameters())#求損失:均方差損失函數self.loss = nn.MSELoss()#進行訓練def __call__(self):#訓練300輪for epoch in range(0,300):#每一批次的總損失sum_loss = 0for i,(img,tag) in enumerate(self.dataloader_train):#開啟訓練模式self.net.train()#將數據傳入cudaimg, tag = img.to("cuda"), tag.to("cuda")#前向傳播結果out = self.net.forward(img)# 求損失(均方差):out、tags是矩陣,不是標量# loss = torch.mean((out - tag) ** 2)loss = self.loss(out,tag)# 完成了一次訓練:清空梯度、自動求導、更新梯度self.opt.zero_grad()loss.backward()self.opt.step()# 求損失的和sum_loss = sum_loss + loss.item()print("訓練損失 {}".format(sum_loss/len(self.dataloader_train)))# 測試#測試得分的總和sum_score = 0# 測試損失總和test_sum_loss = 0# 開始測試for i, (img, tag) in enumerate(self.dataloader_test):# 開始測試模式self.net.eval()# 將數據傳入cudaimg, tag = img.to("cuda"), tag.to("cuda")# 把數據加載在網絡中test_out = self.net.forward(img)# 得到測試集的均方差損失# test_loss = torch.mean((tag - test_out) ** 2)test_loss = self.loss(test_out,tag)# 測試損失求和test_sum_loss = test_sum_loss + test_loss.item()# 10分類,各自概率,取最大值的索引,做one-hot編碼pre = torch.argmax(test_out, dim=1)# 取標簽結果:正確答案labels_tag = torch.argmax(tag, dim=1)# 計算得分score = torch.mean(torch.eq(pre, labels_tag).float())# 總分sum_score = sum_score + score# 求平均損失(每一輪)test_avg_loss = test_sum_loss / len(self.dataloader_test)# 求平均分(每一輪)test_avg_score = sum_score / len(self.dataloader_test)print("訓練輪次:", epoch, "測試損失", test_avg_loss, "測試得分:", test_avg_score)if __name__ == '__main__':train = MNIST_Train()train()?
項目代碼以及圖片資源
鏈接:https://pan.baidu.com/s/1ioNsdoAVvXNV9pVsPfwi4Q? 提取碼:gokg總結
以上是生活随笔為你收集整理的全连接神经网络实现MNIST手写数字识别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python实现人人网留言获取与一键删除
- 下一篇: 普通话测试软件字体怎么调整,新版普通话测