pytorch 實戰二 CNN手寫數字識別

系列文章目錄


文章目錄

  • 系列文章目錄
  • 前言
  • 一、torchvision.datasets
    • 1. 數據下載
    • 2. 數據分批次傳入
  • 二、網絡
    • 1. 網絡搭建
    • 2. 訓練
    • 3.測試
  • 完整代碼
  • 三、保存模型與推理(inference)
    • 模型保存
    • 推理
    • 鳴謝


前言

??手寫數字識別,就是要根據手寫的數字0~9,然后我們搭建網絡,進行智能識別。說句實話,中草藥識別啊,等等。都可以用在本科的畢業設計論文中,妥妥的加分項,而且還不難。最離譜的是我有一個研究生的哥們兒,也用這個當做畢業設計論文,準確率高達99%。笑死我了,哈哈哈,我只能說,水深啊
?? 在實戰一中,我們已經詳細講解了 torch 完成一個神經網絡搭建的過程,在以后我將直接給出代碼,講解額外的庫函數。

一、torchvision.datasets

1. 數據下載

??本文使用的手寫數字的識別就在 torchvision.datasets 中,我們可以直接調用。在手寫數據中,有 6 w張訓練集和 1 w 張測試集。
dowload = True, 只能運行一次,之后改成False, transform.Tensor ,就是把數據轉化成我們可以使用的 Tensor 數據類型。
代碼加載數據:

# data
train_data = datasets.MNIST(root='./datasets',train = True,transform = transforms.ToTensor(),download=True)

但是這個下載可能有點慢?這里給出一個百度鏈接:

https://pan.baidu.com/s/1ia3vFA73hEtWK9qU-O-4iQ?pwd=mnis

2. 數據分批次傳入

??在第一個章節中,我們把數據一次加載到訓練集和測試集中,但是深度學習的照片可能很大,我們這里要用一種新的方法:

# batchSize
train_loader = data_util.DataLoader(dataset=train_data,batch_size=64,shuffle=True)test_loader = data_util.DataLoader(dataset=test_data,batch_size=64,shuffle=True)

dataset = train_data 這個參數是傳入要分割的數據集,batch_size 分塊的批次,shuffle 數據是否需要打亂,那必須的啊。所以把這個當做模版來,用的時候就 CV。

二、網絡

1. 網絡搭建

??在實戰一中,我們是直接定義算子,然后組合在一起。在實戰二中,我們玩點不一樣的,使用序列定義算子。

class CNN(torch.nn.Module):def __init__(self):super(CNN, self).__init__()# 定義第一個卷積操作self.conv = torch.nn.Sequential(# 參數的具體含義我們在卷積神經網絡中介紹torch.nn.Conv2d(1, 32, kernel_size=(5,5),padding = 2),# 第一個參數是灰度,第二個參數是輸出通道,第三個參數是卷積核torch.nn.BatchNorm2d(32),torch.nn.ReLU(),torch.nn.MaxPool2d(2))self.fc = torch.nn.Linear(14*14*32,10) # 第一個是特征圖整體的數量,第二個是Y的種類def forward(self, x):out = self.conv(x) # 第一個卷積out = out.view(out.size()[0],-1) # 拉成一個一維的向量out = self.fc(out) # 線性運算return out # 返回最后的輸出
cnn = CNN()

??class 用于構建我們的網絡,第一個函數除了繼承父類的初始化操作,還要定義我們的算子。卷積操作,用一個序列torch.nn.Sequential,在里面定義了一個 2d 卷積、BatchNorm等等,這些參數的配置我們在卷積神經網絡會細講。此處我們需要了解編程的結構。網絡中定義了一個前向傳播算子,經過卷積,拉直,輸出的線性化。

2. 訓練

cnn = cnn.cuda()
# loss
loss_fn = torch.nn.CrossEntropyLoss() # 交叉熵的損失函數# optimizer
optimizer = torch.optim.Adam(cnn.parameters(),lr=0.01)# 采用Adam 對學習率不是特別敏感
# training
for epoch in range(10):# 這里我們把整個數據集過 10 遍for i, (images, labels) in enumerate(train_loader):images = images.cuda() # 圖片放到cuda上面labels = labels.cuda()# 定義輸出結構outputs = cnn(images)loss = loss_fn(outputs,labels) # loss 傳入輸出的結果optimizer.zero_grad() # 梯度優化loss.backward() # 反向傳播optimizer.step()print("Epoch is:{} item is :{}/{} Loss is :{}".format(epoch+1,i,len(train_data)//64,loss.item()))

把網絡加載到cuda上面,使用顯卡跑數據。損失函數采用交叉熵,優化函數采用Adam,這可以減小學習率的影響。然后訓練10個輪次,每個輪次還有64個區塊,我們都要訓練完全。訓練過程每個batch:

  1. 把數據加載到 cuda 上面
  2. 網絡上跑一邊獲得第一個輸出,輸出在net中已經處理了。
  3. 計算損失函數
  4. 梯度優化器
  5. 損失函數反向傳播
  6. 優化器保存參數

圖片:

圖 1 訓練結果
我們從訓練結果可以看出,epoch 有10輪,其中有937次區塊訓練。損失函數萬分之二,非常好了。

3.測試

??測試我們在每個epoch測試一次(每輪測試),注意代碼的位置。用測試集來跑模型,然后計算出準確率。

# testloss_test = 0accuracy = 0for i, (images, labels) in enumerate(test_loader):images = images.cuda()  # 圖片放到cuda上面labels = labels.cuda()# 定義輸出結構outputs = cnn(images)# 我們使用交叉熵計算,label 的維度是[batchsize],每個樣本的標簽樣本是一個值# outputs = batches * cls_num  cls_num 概率分布也就是種類10 個loss_test += loss_fn(outputs, labels)  # loss 傳入輸出的結果,對text_loss 進行一個累加,最后求平均值_, predicted = outputs.max(dim=1)   # 獲得最大的概率作為我們的預測值accuracy += (predicted ==labels).sum().item() # 我們通過相同的樣本進行統計求和,通過item獲取這個值accuracy = accuracy/len(test_data)loss_test=loss_test/(len(test_data)//64) # 除以batch的個數print("測試:\nEpoch is:{} accuracy is :{} loss_test is :{}".format(epoch+1,accuracy,loss_test.item()))

??測試我們定義在每輪測試一次。先定義損失函數的值和準確率為零,我們把他們的每個batch求和,然后求得平均值,這是我們測試經常做的。注意預測, _, predicted = outputs.max(dim=1) 我們取得最大的預測結果,_ 表示忽略索引,只保留預測的標簽。accuracy += (predicted ==labels).sum().item() 把預測值和真實值進行比較,再求和,再取得數據,加到 accuracy (每個batch的輪次)。.item() 將單元素張量轉換為Python標量,有利于計算。最后計算求平均值,準確率除以整個測試集,而loss_test 除以batch的數量,因為損失函數這一個batch只有一個,準確率統計了這個batch的所有正確樣本。

圖片:

圖 2 測試

??從數據來看,準確率 98%,loss 也在0.06 左右,基本是個好模型了。

完整代碼

記得修改數據集的路徑:

import torch
import torchvision.datasets as datasets
import torchvision.transforms as transforms
import torch.utils.data as data_util
# data
train_data = datasets.MNIST(root='datasets',train = True,transform = transforms.ToTensor(),download=True)
test_data = datasets.MNIST(root='datasets',train = False,transform = transforms.ToTensor(),download=False)# batchSize
train_loader = data_util.DataLoader(dataset=train_data,batch_size=64,shuffle=True)test_loader = data_util.DataLoader(dataset=test_data,batch_size=64,shuffle=True)
# net
class CNN(torch.nn.Module):def __init__(self):super(CNN, self).__init__()# 定義第一個卷積操作self.conv = torch.nn.Sequential(# 參數的具體含義我們在卷積神經網絡中介紹torch.nn.Conv2d(1, 32, kernel_size=(5,5),padding=2),# 第一個參數是灰度,第二個參數是輸出通道,第三個參數是卷積核torch.nn.BatchNorm2d(32),torch.nn.ReLU(),torch.nn.MaxPool2d(2))self.fc = torch.nn.Linear(14*14*32,10) # 第一個是特征圖整體的數量,第二個是Y的種類def forward(self, x):out = self.conv(x) # 第一個卷積out = out.view(out.size()[0],-1) # 拉成一個一維的向量out = self.fc(out) # 線性運算return out # 返回最后的輸出
cnn = CNN()
cnn = cnn.cuda()
# loss
loss_fn = torch.nn.CrossEntropyLoss() # 交叉熵的損失函數# optimizer
optimizer = torch.optim.Adam(cnn.parameters(),lr=0.01)# 采用Adam 對學習率不是特別敏感
# training
for epoch in range(10):# 這里我們把整個數據集過 10 遍for i, (images, labels) in enumerate(train_loader):images = images.cuda() # 圖片放到cuda上面labels = labels.cuda()# 定義輸出結構outputs = cnn(images)loss = loss_fn(outputs,labels) # loss 傳入輸出的結果optimizer.zero_grad() # 梯度優化loss.backward() # 反向傳播optimizer.step()# print("訓練:\nEpoch is:{}  Loss is :{}".format(epoch+1,loss.item()))
# testloss_test = 0accuracy = 0for i, (images, labels) in enumerate(test_loader):images = images.cuda()  # 圖片放到cuda上面labels = labels.cuda()# 定義輸出結構outputs = cnn(images)# 我們使用交叉熵計算,label 的維度是[batchsize],每個樣本的標簽樣本是一個值# outputs = batches * cls_num  cls_num 概率分布也就是種類10 個loss_test += loss_fn(outputs, labels)  # loss 傳入輸出的結果,對text_loss 進行一個累加,最后求平均值_, predicted = outputs.max(dim=1)   # 獲得最大的概率作為我們的預測值accuracy += (predicted ==labels).sum().item() # 我們通過相同的樣本進行統計求和,通過item獲取這個值accuracy = accuracy/len(test_data)loss_test=loss_test/(len(test_data)//64) # 除以batch的個數# .item() 將單元素張量轉換為Python標量# print("測試:\nEpoch is:{} accuracy is :{} loss_test is :{}".format(epoch+1,accuracy,loss_test.item()))# 保存模型
torch.save(cnn,"model/cnn.pkl")

三、保存模型與推理(inference)

模型保存

# 保存模型
torch.save(cnn,"model/cnn.pkl")

推理

import torch
import torchvision.datasets as datasets
import torchvision.transforms as transforms
import torch.utils.data as data_util# net
class CNN(torch.nn.Module):def __init__(self):super(CNN, self).__init__()# 定義第一個卷積操作self.conv = torch.nn.Sequential(# 參數的具體含義我們在卷積神經網絡中介紹torch.nn.Conv2d(1, 32, kernel_size=(5,5),padding=2),# 第一個參數是灰度,第二個參數是輸出通道,第三個參數是卷積核torch.nn.BatchNorm2d(32),torch.nn.ReLU(),torch.nn.MaxPool2d(2))self.fc = torch.nn.Linear(14*14*32,10) # 第一個是特征圖整體的數量,第二個是Y的種類def forward(self, x):out = self.conv(x) # 第一個卷積out = out.view(out.size()[0],-1) # 拉成一個一維的向量out = self.fc(out) # 線性運算return out # 返回最后的輸出test_data = datasets.MNIST(root='../datasets',train = False,transform = transforms.ToTensor(),download=False)# batchSize
test_loader = data_util.DataLoader(dataset=test_data,batch_size=64,shuffle=True)
cnn = torch.load("cnn.pkl")
cnn = cnn.cuda()
# loss
loss_fn = torch.nn.CrossEntropyLoss() # 交叉熵的損失函數# optimizer
optimizer = torch.optim.Adam(cnn.parameters(),lr=0.01)# 采用Adam 對學習率不是特別敏感
# training
for epoch in range(10):# 這里我們把整個數據集過 10 遍
# testloss_test = 0accuracy = 0for i, (images, labels) in enumerate(test_loader):images = images.cuda()  # 圖片放到cuda上面labels = labels.cuda()# 定義輸出結構outputs = cnn(images)# 我們使用交叉熵計算,label 的維度是[batchsize],每個樣本的標簽樣本是一個值# outputs = batches * cls_num  cls_num 概率分布也就是種類10 個loss_test += loss_fn(outputs, labels)  # loss 傳入輸出的結果,對text_loss 進行一個累加,最后求平均值_, predicted = outputs.max(dim=1)   # 獲得最大的概率作為我們的預測值accuracy += (predicted ==labels).sum().item() # 我們通過相同的樣本進行統計求和,通過item獲取這個值accuracy = accuracy/len(test_data)loss_test=loss_test/(len(test_data)//64) # 除以batch的個數# .item() 將單元素張量轉換為Python標量# print("測試:\nEpoch is:{} accuracy is :{} loss_test is :{}".format(epoch+1,accuracy,loss_test.item()))
print("accuracy:",accuracy)

??模型推理的數據集一定要找準確,模型調用的路徑也要準確。我們可以直接復制代碼到新建的推理 python 文件中:

  1. 刪除所有與訓練有關的數據
  2. 刪除模型的保存部分
    3.模型初始化改成調用:cnn = torch.load("cnn.pkl")
    4.修改打印參數

運行結果:

圖 3 模型推理結果

鳴謝

??為了感謝我弟對我的照顧,我也沒有什么可以報答的,如果這篇文章有助于大家搭建一個神經網絡,有助于大家的pytorch編程,還請大家來我弟的小店逛一逛,增加一點訪問量,小店才開也不容易,點擊一下這里 ,謝謝大家~。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/84980.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/84980.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/84980.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[Godot] C#讀取CSV表格創建雙層字典實現本地化

最近研究了一下本地化,給大家用簡單易懂的方式說明我是怎么實現的,使用CSV表格填寫翻譯,然后在Godot中讀取為字典 表格填寫 首先,我們表格可以按照下面這種格式填寫 idzhenjaruesdefrapple蘋果appleリンゴяблокоmanzanaA…

Spark 之 Subquery

各類 Subquery src/main/scala/org/apache/spark/sql/catalyst/expressions/predicates.scala /*** Evaluates to `true` if `values` are returned in `query`s result set.*/ case class InSubquery(values: Seq[Expression], query: ListQuery)extends Predicate with Une…

3.1.3_棧的鏈式存儲實現

知識總覽: 鏈棧定義: 頭插法建立單鏈表: 每次要插入一個元素的時候,總是把該元素插在頭節點之后的位置,如果規定只能在單鏈表的鏈頭一端進行操作即為進棧操作 每次刪除一個元素的時候,規定只能在單鏈表…

華為OD機試_2025 B卷_字符串重新排列(Python,100分)(附詳細解題思路)

題目描述 給定一個字符串s,s包括以空格分隔的若干個單詞,請對s進行如下處理后輸出: 1、單詞內部調整:對每個單詞字母重新按字典序排序 2、單詞間順序調整: 1)統計每個單詞出現的次數,并按次數降…

http的緩存問題

一句話概括:瀏覽器請求資源的時候,會首先檢查本地是否有緩存,減少向服務器請求的次數 一、緩存類型: 1. 強緩存(本地緩存):直接讀本地,不發請求 控制方式: ① Cache-C…

【網絡安全】SRC漏洞挖掘思路/手法分享

文章目錄 Tip1Tip2Tip3Tip4Tip5Tip6Tip7Tip8Tip9Tip10Tip11Tip12Tip13Tip14Tip15Tip16Tip17Tip18Tip19Tip20Tip21Tip22Tip23Tip24Tip25Tip26Tip27Tip28Tip29Tip30Tip1 “復制該主機所有 URL”:包含該主機上的所有接口等資源。 “復制此主機里的鏈接”:包括該主機加載的第三…

「Linux中Shell命令」Shell常見命令

知識點及案例解析 1. who 命令 功能:顯示當前登錄系統的用戶信息,包括用戶名、終端、登錄時間、IP等。 案例: who輸出示例: root tty1 2025-06-13 19:42 root pts/0 2025-06-13 19:45 (192.168.226.1)解析: 顯示兩個用戶登錄信息: 第一列(用…

StampedLock入門教程

文章目錄 一、理解“戳” (Stamp)二、為什么 StampedLock 能提高讀性能?秘密在于“樂觀讀”StampedLock性能對比性能對比結果圖 總結 StampedLock完整演示代碼對代碼的疑問之處問題一:為什么 demonstrateOptimisticReadFailure 中寫線程能修改成功&#…

基于云計算的振動弦分析:諧波可視化與波動方程參數理解-AI云計算數值分析和代碼驗證

振動弦方程是一個基礎的偏微分方程,它描述了彈性弦的橫向振動。其應用范圍廣泛,不僅可用于模擬樂器和一般的波動現象,更是數學物理以及深奧的弦理論中的重要基石。 ??AI云計算數值分析和代碼驗證 振動弦方程是描述固定兩端彈性弦橫向振動的…

Qt .pro配置gcc相關命令(三):-W1、-L、-rpath和-rpath-link

目錄 1.Linux 動態庫相關知識 1.1.動態庫查找路徑 1.2.查看程序依賴的動態庫 1.3.修改動態庫查找路徑的方法 1.4.動態鏈接器緩存管理 2.-Wl參數 3.-L選項(編譯時路徑) 4.-rpath參數(運行時路徑) 5.-rpath-link 參數 6.常見問題與解決方案 7.總…

Hoppscotch

官方地址 xixiaxiazxiaxix下載 ? Hoppscotch Hoppscotch 是一款輕量級、基于 Web 的 API 開發套件,其核心功能和特點如下: 核心功能3 交互式 API 測試:允許用戶實時發送請求并查看響應,方便記錄 API 行為,在記錄響…

RabbitMQ 知識詳解(Java版)

RabbitMQ 知識詳解(Java版) RabbitMQ 是一個開源的消息代理,實現了高級消息隊列協議(AMQP)。它用于在分布式系統中實現應用解耦、異步通信和流量削峰。 核心概念 生產者(Producer):發送消息的應用消費者(…

Flink task、Operator 和 UDF 之間的關系

要真正駕馭 Flink 并構建出高效、穩定、可擴展的流處理應用,僅僅停留在 API 的表面使用是遠遠不夠的。深入理解其內部的運行機制,洞悉數據從代碼到分布式執行的完整生命周期,以及明晰各個核心組件之間錯綜復雜而又協同工作的關系,…

Veeam Backup Replication系統的安裝與使用

Veeam Backup & Replication系統安裝與使用 系統簡介 核心功能 備份與恢復:專注于虛擬化環境(如VMware和Hyper-V)的備份與恢復,支持物理服務器和云環境。快速恢復:提供即時恢復功能,可在幾分鐘內恢復…

十四、【ESP32全棧開發指南:搭建輕量級HTTP服務器】

一、HTTP協議基礎 HTTP(Hyper Text Transfer Protocol)作為互聯網基礎協議,采用請求-響應模型工作: 1.1 HTTP請求組成 GET /uri?query1value1 HTTP/1.1 // 請求行(方法URI協議版本) Host: example…

java中LinkedList和ArrayList的區別和聯系?

我們被要求解釋Java中LinkedList和ArrayList的區別和聯系。下面將分別從實現原理、性能特點、使用場景等方面進行詳細說明,并總結它們的相同點和不同點。 # 一、聯系(共同點) 1. 都實現了List接口,因此具有List接口的所有方法&…

明遠智睿SD2351核心板:邊緣計算時代的工業級核心引擎深度解析

在工業4.0與物聯網深度融合的背景下,邊緣計算設備正從單一功能模塊向高集成度、智能化平臺演進。明遠智睿推出的SD2351核心板,憑借其異構計算架構、工業級接口資源和全棧技術生態,重新定義了邊緣計算設備的性能邊界。本文將從技術架構、場景適…

Flask 動態模塊注冊

目錄 1. 項目概述2. 項目結構3. 核心組件解析3.1 動態模塊注冊系統 (api/__init__.py)3.2 應用程序入口 (setup_demo.py) 4. 模塊開發指南4.1 標準模塊 (*_app.py)4.2 SDK模塊 (sdk/*.py) 5. URL路徑規則6. 如何使用6.1 啟動應用6.2 添加新模塊 7. 工作原理 1. 項目概述 這個項…

JVM 內存、JMM內存與集群機器節點內存的聯系

目錄 1、JVM 內存 1.1、分配機制 1.2、jvm模型位置 1.3、字節碼內存塊 2、JMM內存 2.1、JMM模型 2.2、工作流程圖 1、工作內存與主內存的交互 2. 多線程下的主內存與堆內存交互 2.3、 主內存與工作內存的同步方案 1、volatile 2、synchronized 3、final 3、內存使…

學習昇騰開發的第一天--環境配置

1、昇騰社區官網:昇騰社區官網-昇騰萬里 讓智能無所不及 2、產品-->選擇開發者套件-->點擊制卡工具的下載:資源-Atlas 200I DK A2-昇騰社區 3、如果制卡工具不能使用在線制卡,可以下載鏡像到本地使用本地制卡:Linux系統制…