手寫數字是機器學習中非常經典的案例,本文將通過pytorch框架,利用神經網絡來實現手寫數字識別
pytorch中提供了手寫數字的數據集,我們可以直接從pytorch中下載
MNIST中包含70000張手寫數字圖像:60000張用于訓練,10000張用于測試
圖像是灰度的,28x28像素
下載數據集
import torch
from torchvision import datasets#封裝了許多與圖像相關的模型,數據集
from torchvision.transforms import ToTensor#數據類型轉換,將其他類型數據轉換為tensor張量'''下載訓練數據集(包含訓練圖片+標簽)'''
training_data = datasets.MNIST( #導入數據集root="data",#下載的數據集到哪個路徑train=True,#讀取訓練集download=True,#如果已經下載,就不用再下載transform=ToTensor(),#數據類型轉換
) '''下載測試數據集(包含訓練圖片+標簽) '''
test_data = datasets.MNIST(#導入數據集root="data",#下載的數據集到哪個路徑train=False,#讀取測試集download=True,#如果已經下載,就不用再下載transform=ToTensor(),#數據類型轉換
)
數據可視化,展示手寫數字
from matplotlib import pyplot as plt
figure=plt.figure()
for i in range(9):img,label=training_data[i+59000]figure.add_subplot(3,3,i+1)plt.title(label)plt.axis('off')plt.imshow(img.squeeze(),cmap='gray')a=img.squeeze()
plt.show()
得到結果如下
?打包數據
from torch.utils.data import DataLoader #數據包管理工具,打包數據train_dataloader=DataLoader(training_data,batch_size=64)#64張圖片為一個包
test_dataloader=DataLoader(test_data,batch_size=64)
判斷當前設備是否支持GPU
device='cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
print(f'using {device} device')
構建神經網絡模型
from torch import nn #導入神經網絡模塊class NeuralNetwork(nn.Module):def __init__(self):super().__init__()self.flatten=nn.Flatten()self.hidden1=nn.Linear(28*28,128)self.hidden2=nn.Linear(128,256)self.out=nn.Linear(256,10)def forward(self,x):#前向傳播x=self.flatten(x)x=self.hidden1(x)x=torch.relu(x)#引入非線性變換,使神經網絡能夠學習復雜的非線性關系,增強表達能力。x=self.hidden2(x)x=torch.relu(x)x=self.out(x)return x
返回的x結果大致如圖所示?
模型傳入GPU
model=NeuralNetwork().to(device)
print(model)
?損失函數
loss_fn = nn.CrossEntropyLoss() #創建交叉熵損失函數對象,因為手寫字識別中一共有10個數字,輸出會有10個結果
?優化器,用于在訓練神經網絡時更新模型參數,目的是??在神經網絡訓練過程中,自動調整模型的參數(權重和偏置),以最小化損失函數??。
optimizer=torch.optim.Adam(model.parameters(),lr=0.01)#獲取模型中所有需要訓練的參數
?模型訓練
def train(dataloader,model,loss_fn,optimizer):model.train()batch_size_num=1for X,y in dataloader:X,y=X.to(device),y.to(device)pred=model.forward(X)#自動初始化 w權值loss=loss_fn(pred,y) #通過交叉熵損失函數計算損失值loss#更新模型參數以最小化損失函數。optimizer.zero_grad()#梯度值清零loss.backward()#反向傳播計算得到每個參數的梯度值optimizer.step() #根據梯度更新網絡參數loss_value=loss.item()if batch_size_num%100==0:print(f'loss:{loss_value:7f}[number:{batch_size_num}]')batch_size_num+=1epochs=10for i in range(epochs):print(f'第{i}次訓練')train(train_dataloader, model, loss_fn, optimizer)
模型測試
def test(dataloader, model, loss_fn):size = len(dataloader.dataset)num_batches = len(dataloader)model.eval()#進入到模型的測試狀態,所有的卷積核權重被設為只讀模式test_loss, correct = 0, 0with torch.no_grad(): #一個上下文管理器,關閉梯度計算。當你確認不會調用Tensor.backward()的時候。這可以減少計算所用內存消耗。for X, y in dataloader:X, y = X.to(device), y.to(device)pred = model.forward(X)test_loss += loss_fn(pred, y).item() #correct += (pred.argmax(1) == y).type(torch.float).sum().item()#a = (pred.argmax(1) == y) #b = (pred.argmax(1) == y).type(torch.float)test_loss /= num_batchescorrect /= sizeprint(f"Test result: \n Accuracy: {(100*correct)}%, Avg loss: {test_loss}")test(test_dataloader,model,loss_fn)
得到結果如圖