PyTorch實現手寫數字識別的示例代碼
加載手寫數字的數據
組成訓練集和測試集,這裡已經下載好瞭,所以download為False
import torchvision # 是否支持gpu運算 # device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') # print(device) # print(torch.cuda.is_available()) # 加載訓練集的數據 使用torchvision自帶的MNIST數據集 train_dataset = torchvision.datasets.MNIST(root='./data1', train=True, transform=torchvision.transforms.ToTensor(), download=False ) # 加載測試集的數據 創建測試集 test_dataset = torchvision.datasets.MNIST(root='./data1', train=False, transform=torchvision.transforms.ToTensor(), download=False )
數據加載器(分批加載)
# 加載數據的批次 一批有多少條數據 batch_size = 100 # 創建數據加載器shuffle為True 加載時打亂 train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True ) test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=True ) # 數據加載器生成的對象轉為迭代器 examples = iter(test_loader) # 使用next方法獲取到一批次的數據 example_data, example_targets = examples.next() # 遍歷獲取到6條數據 展示觀察一下 for i in range(6): plt.subplot(2, 3, i + 1) plt.imshow(example_data[i][0], cmap='gray') # 查看圖片的大小 方便建立模型時輸入的大小 print(example_data[i][0].shape) plt.show()
建立模型
建立模型之前定義輸入大小和分類類別輸出大小
通過上邊查看圖片的大小為28*28*1,所以輸入大小為784
數字識別隻有0~9所以為10個類別的多分類問題
input_size = 784 num_classes = 10
創建模型類
class NeuralNet(torch.nn.Module): def __init__(self, n_input_size, hidden_size, n_num_classes): """ 神經網絡類初始化 :param n_input_size: 輸入 :param hidden_size: 隱藏層 :param n_num_classes: 輸出 """ # 調用父類__init__方法 super(NeuralNet, self).__init__() self.input_size = input_size # 第一層線性模型 傳入輸入層和隱藏層 self.l1 = torch.nn.Linear(n_input_size, hidden_size) # relu激活函數層 self.relu = torch.nn.ReLU() # 第二層線性模型 傳入隱藏層和輸出層 self.l2 = torch.nn.Linear(hidden_size, n_num_classes) def forward(self, x): """ 重寫正向傳播函數 獲取到預測值 :param x: 數據 :return: 預測值 """ # 線性模型 out = self.l1(x) # 激活函數 out = self.relu(out) # 線性模型2 out = self.l2(out) # 返回預測值 return out # 獲取到gpu設備 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') # 創建模型並把模型放到當前支持的gpu設備中 model = NeuralNet(input_size, 500, num_classes).to(device) print(model)
- 可以看出模型一共三層
- 輸入層(節點數量和圖小大小相同)
- 隱藏層(節點數為500)
- 輸出層(輸出節點數量為10
0~9
)
定義損失函數和優化器
- 因為是多分類問題,所以使用交叉熵函數的多分類損失函數
- 因為傳統的梯度下降存在一定缺陷,比如學習速率一直不變,所以使用PyTorch中梯度下降的優化算法Adam算法
# 定義學習率 learning_rate = 0.01 # 損失函數 criterion = torch.nn.CrossEntropyLoss() # 定義優化器 參數1為模型的參數 lr為學習率 optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
模型訓練
訓練步驟:
- 通過模型類正向傳播獲取到預測結果
- 通過損失函數傳入預測結果和真實值計算損失
- 通過反向傳播獲取梯度
- 通過梯度下降更新模型參數的權重
- 梯度清空,防止下次梯度累加
- 循環,降低損失為我們想要的結果(提高模型精度)
# 定義訓練的次數 num_epochs = 10 # 訓練集數據的總長度 total_steps = len(train_loader) # 遍歷訓練次數 for epoch in range(num_epochs): # 每次從數據加載器中取出一批數據 每批次100條 for i, (images, labels) in enumerate(train_loader): # 把圖片降維到一維數組 加載到gpu images = images.reshape(-1, 28 * 28).to(device) # 真實值加載到gpu labels = labels.to(device) # 正向傳播 獲取到預測值 outputs = model(images) # 通過損失函數獲取到損失值 loss_val = criterion(outputs, labels) # 清空梯度 optimizer.zero_grad() # 進行反向傳播 loss_val.backward() # 梯度下降更新參數 optimizer.step() # 打印每次訓練的損失值 if i % 100 == 0: print(f'Loss:{loss_val.item():.4f}') print('訓練完成') # 訓練完之後保存模型 torch.save(model.state_dict(), './last.pt')
- 損失值很明顯的在收斂
- 生成瞭pt模型文件
測試集抽取數據,查看預測結果
# 把測試集的數據加載器轉為生成器 examples = iter(test_loader) # next()方法獲取一批數據 example_data, example_targets = examples.next() # 拿出前三條 for i in range(3): # 畫圖展示 plt.subplot(1, 3, i + 1) plt.imshow(example_data[i][0], cmap='gray') plt.show() images = example_data # 圖片將為加載到GPU images = images.reshape(-1, 28 * 28).to(device) # 正向傳播獲取預測結果 outputs = model(images) # 打印結果 detach()方法結果不會計算梯度更新 轉為numpy print(f'真實結果:{example_targets[0:3].detach().numpy()}') # 預測完的結果為10個數字的概率 使用argmax()根據行歸一化並求自變量的概率最大值 print(f'預測結果:{np.argmax(outputs[0:3].cpu().detach().numpy(), axis=1)}')
計算模型精度
# 用測試集的數據,校驗模型的準確率 with torch.no_grad(): n_correct = 0 n_samples = 0 # 取出測試集數據 for images, labels in test_loader: # 和訓練代碼一致 images = images.reshape(-1, 28 * 28).to(device) labels = labels.to(device) outputs = model(images) # 返1 最大值 返2 索引 0每列最大值 1每行最大值 _, predicted = torch.max(outputs.data, 1) n_samples += labels.size(0) n_correct += (predicted == labels).sum().item() # 計算模型精度 acc = 100.0 * n_correct / n_samples print(f"準確率:{acc}%")
自己手寫數字進行預測
import cv2 import numpy as np import torch from 手寫數字神經網絡結構 import NeuralNet # 獲取到gpu設備 device = torch.device('cuda') # 加載保存好的模型 input_size = 784 num_classes = 10 model = NeuralNet(input_size, 500, num_classes) # 因為保存模型時在GPU所以要指定map_location='cuda:0' model.load_state_dict(torch.load('./last.pt', map_location='cuda:0')) # 加載到gpu上 model.to(device) # 局域內不計算梯度 with torch.no_grad(): # cv2讀取圖片 灰度方式 images = cv2.imread('./number_four.png', cv2.IMREAD_GRAYSCALE) # 使用大津算法進行二值化處理 並反轉 ret, thresh_img = cv2.threshold(images, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU) # 展示處理過後的圖片 cv2.imshow('png1', thresh_img) cv2.waitKey() # 圖片降維 把拍的圖片降維到和訓練時的圖片大小一樣 my_image = cv2.resize(thresh_img, (28, 28)) # 轉為numpy my_image = np.array(my_image, np.float32) # 轉為torch的張量 my_image = torch.from_numpy(my_image) # 降維 my_image = my_image.reshape(-1, 28 * 28).to(device) # 正向傳播獲取預測值 outputs = model(my_image) # 取出預測結果 pred = np.argmax(outputs.cpu().detach().numpy(), axis=1) print(f'預測結果為:{pred[0]}')
到此這篇關於PyTorch實現手寫數字識別的示例代碼的文章就介紹到這瞭,更多相關PyTorch 手寫數字識別內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Pytorch實現圖像識別之數字識別(附詳細註釋)
- 深入理解Pytorch微調torchvision模型
- PyTorch 遷移學習實戰
- 詳解pytorch的多GPU訓練的兩種方式
- PyTorch 遷移學習實踐(幾分鐘即可訓練好自己的模型)