CNN結(jié)構(gòu)
CNN(卷積神經(jīng)網(wǎng)絡(luò))主要包括卷積層、池化層和全連接層。輸入數(shù)據(jù)經(jīng)過多個(gè)卷積層和池化層提取圖片信息后,最后經(jīng)過若干個(gè)全連接層獲得最終的輸出。CNN的實(shí)現(xiàn)主要包括以下步驟:
- 數(shù)據(jù)加載與預(yù)處理
- 模型搭建
- 定義損失函數(shù)、優(yōu)化器
- 模型訓(xùn)練
- 模型測(cè)試
以下基于Pytorch框架搭建一個(gè)CNN神經(jīng)網(wǎng)絡(luò)實(shí)現(xiàn)手寫數(shù)字識(shí)別。
CNN實(shí)現(xiàn)
此處使用MNIST數(shù)據(jù)集,包含60000個(gè)訓(xùn)練樣本和10000個(gè)測(cè)試樣本。分為圖片和標(biāo)簽,每張圖片是一個(gè) 28 × 28 28 \times 28 28×28 的像素矩陣,標(biāo)簽是0~9一共10種數(shù)字。每個(gè)樣本的格式為[data, label]。
1. 導(dǎo)入相關(guān)庫
import numpy as np
import torch
from torch import nn
from torchvision import datasets, transforms,utils
from PIL import Image
import matplotlib.pyplot as plt
2. 數(shù)據(jù)加載與預(yù)處理
# 定義超參數(shù)
batch_size = 128 # 每個(gè)批次(batch)的樣本數(shù)
# 對(duì)輸入的數(shù)據(jù)進(jìn)行標(biāo)準(zhǔn)化處理
# transforms.ToTensor() 將圖像數(shù)據(jù)轉(zhuǎn)換為 PyTorch 中的張量(tensor)格式,并將像素值縮放到 0-1 的范圍內(nèi)。
# 這是因?yàn)樯窠?jīng)網(wǎng)絡(luò)需要的輸入數(shù)據(jù)必須是張量格式,并且需要進(jìn)行歸一化處理,以提高模型的訓(xùn)練效果。
# transforms.Normalize(mean=[0.5],std=[0.5]) 將圖像像素值進(jìn)行標(biāo)準(zhǔn)化處理,使其均值為 0,標(biāo)準(zhǔn)差為 1。
# 輸入數(shù)據(jù)進(jìn)行標(biāo)準(zhǔn)化處理可以提高模型的魯棒性和穩(wěn)定性,減少模型訓(xùn)練過程中的梯度爆炸和消失問題。
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize(mean=[0.5],std=[0.5])])
# 加載MNIST數(shù)據(jù)集
train_dataset = torchvision.datasets.MNIST(root='./data',
train=True,
transform=transform,
download=True)
test_dataset = torchvision.datasets.MNIST(root='./data',
train=False,
transform=transform,
download=True)
# 創(chuàng)建數(shù)據(jù)加載器(用于將數(shù)據(jù)分次放進(jìn)模型進(jìn)行訓(xùn)練)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=batch_size,
shuffle=True, # 裝載過程中隨機(jī)亂序
num_workers=2) # 表示2個(gè)子進(jìn)程加載數(shù)據(jù)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
batch_size=batch_size,
shuffle=False,
num_workers=2)
加載完數(shù)據(jù)后,可以得到60000個(gè)訓(xùn)練樣本和10000個(gè)測(cè)試樣本
print(len(train_dataset))
print(len(test_dataset))
以及469個(gè)訓(xùn)練批次和79測(cè)試批次
# batch=128
# train_loader=60000/128 = 469 個(gè)batch
# test_loader=10000/128=79 個(gè)batch
print(len(train_loader))
print(len(test_loader))
打印前5個(gè)手寫數(shù)字樣本看看
for i in range(0,5):
oneimg,label = train_dataset[i]
grid = utils.make_grid(oneimg)
grid = grid.numpy().transpose(1,2,0)
std = [0.5]
mean = [0.5]
grid = grid * std + mean
# 可視化圖像
plt.subplot(1, 5, i+1)
plt.imshow(grid)
plt.axis('off')
plt.show()
這里用了 make_grid() 函數(shù)將多張圖像拼接成一張網(wǎng)格圖像,并調(diào)整了網(wǎng)格圖像的形狀,使得它可以直接作為 imshow() 函數(shù)的輸入。這種方式可以在一張圖中同時(shí)顯示多張圖像,比單獨(dú)顯示每張圖像更加方便,常用于可視化深度學(xué)習(xí)中的卷積神經(jīng)網(wǎng)絡(luò)(CNN)中的特征圖、卷積核等信息。
在 PyTorch 中,默認(rèn)的圖像張量格式是 (channel, height, width),即通道維度在第一個(gè)維度。 torchvision.transforms.ToTensor() 函數(shù)會(huì)將 PIL 圖像對(duì)象轉(zhuǎn)換為 PyTorch 張量,并將通道維度放在第一個(gè)維度。因此,當(dāng)我們使用 ToTensor() 函數(shù)加載圖像數(shù)據(jù)時(shí),得到的 PyTorch 張量的格式就是 (channel, height, width)。代碼中的 oneimg.numpy().transpose(1,2,0) 就是將 PyTorch 張量 oneimg 轉(zhuǎn)換為 NumPy 數(shù)組,然后通過 transpose 函數(shù)將圖像數(shù)組中的通道維度從第一個(gè)維度(channel-first)調(diào)整為最后一個(gè)維度(channel-last),即將 (channel, height, width) 調(diào)整為 (height, width, channel),以便于 Matplotlib 庫正確處理通道信息。
2. 模型搭建
我們將使用Pytorch構(gòu)建一個(gè)如下圖所示的CNN,包含兩個(gè)卷積層,和全連接層,并使用Relu作為激活函數(shù)。
接下來看以下不同層的參數(shù)。
卷積層: Connv2d
- in_channels ——輸入數(shù)據(jù)的通道數(shù)目
- out_channels ——卷積產(chǎn)生的通道數(shù)目
- kernel_size ——卷積核的尺寸
- stride——步長(zhǎng)
- padding——輸入數(shù)據(jù)的邊緣填充0的層數(shù)
池化層: MaxPool2d
- kernel_siez ——池化核大小
- stride——步長(zhǎng)
- padding——輸入數(shù)據(jù)的邊緣填充0的層數(shù)
全連接層: Linear
- in_features:輸入特征數(shù)
- out_features:輸出特征數(shù)
代碼實(shí)現(xiàn)如下:
class CNN(nn.Module):
# 定義網(wǎng)絡(luò)結(jié)構(gòu)
def __init__(self):
super(CNN, self).__init__()
# 圖片是灰度圖片,只有一個(gè)通道
self.conv1 = nn.Conv2d(in_channels=1, out_channels=16,
kernel_size=5, stride=1, padding=2)
self.relu1 = nn.ReLU()
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv2 = nn.Conv2d(in_channels=16, out_channels=32,
kernel_size=5, stride=1, padding=2)
self.relu2 = nn.ReLU()
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
self.fc1 = nn.Linear(in_features=7*7*32, out_features=256)
self.relu3 = nn.ReLU()
self.fc2 = nn.Linear(in_features=256, out_features=10)
# 定義前向傳播過程的計(jì)算函數(shù)
def forward(self, x):
# 第一層卷積、激活函數(shù)和池化
x = self.conv1(x)
x = self.relu1(x)
x = self.pool1(x)
# 第二層卷積、激活函數(shù)和池化
x = self.conv2(x)
x = self.relu2(x)
x = self.pool2(x)
# 將數(shù)據(jù)平展成一維
x = x.view(-1, 7*7*32)
# 第一層全連接層
x = self.fc1(x)
x = self.relu3(x)
# 第二層全連接層
x = self.fc2(x)
return x
定義損失函數(shù)和優(yōu)化函數(shù)
import torch.optim as optim
learning_rate = 0.001 # 學(xué)習(xí)率
# 定義損失函數(shù),計(jì)算模型的輸出與目標(biāo)標(biāo)簽之間的交叉熵?fù)p失
criterion = nn.CrossEntropyLoss()
# 訓(xùn)練過程通常采用反向傳播來更新模型參數(shù),這里使用的是SDG(隨機(jī)梯度下降)優(yōu)化器
# momentum 表示動(dòng)量因子,可以加速優(yōu)化過程并提高模型的泛化性能。
optimizer = optim.SGD(net.parameters(), lr=learning_rate, momentum=0.9)
#也可以選擇Adam優(yōu)化方法
# optimizer = torch.optim.Adam(net.parameters(),lr=1e-2)
3. 模型訓(xùn)練
model = CNN() # 實(shí)例化CNN模型
num_epochs = 10 # 定義迭代次數(shù)
# 如果可用的話使用 GPU 進(jìn)行訓(xùn)練,否則使用 CPU 進(jìn)行訓(xùn)練。
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 將神經(jīng)網(wǎng)絡(luò)模型 net 移動(dòng)到指定的設(shè)備上。
model = model.to(device)
total_step = len(train_loader)
for epoch in range(num_epochs):
for i, (images,labels) in enumerate(train_loader):
images=images.to(device)
labels=labels.to(device)
optimizer.zero_grad() # 清空上一個(gè)batch的梯度信息
# 將輸入數(shù)據(jù) inputs 喂入神經(jīng)網(wǎng)絡(luò)模型 net 中進(jìn)行前向計(jì)算,得到模型的輸出結(jié)果 outputs。
outputs=model(images)
# 使用交叉熵?fù)p失函數(shù) criterion 計(jì)算模型輸出 outputs 與標(biāo)簽數(shù)據(jù) labels 之間的損失值 loss。
loss=criterion(outputs,labels)
# 使用反向傳播算法計(jì)算模型參數(shù)的梯度信息,并使用優(yōu)化器 optimizer 對(duì)模型參數(shù)進(jìn)行更新。
loss.backward()
# 更新梯度
optimizer.step()
# 輸出訓(xùn)練結(jié)果
if (i+1) % 100 == 0:
print ('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'.format(epoch+1, num_epochs, i+1, total_step, loss.item()))
print('Finished Training')
保存模型
# 模型保存
PATH = './mnist_net.pth'
torch.save(model.state_dict(), PATH)
4. 模型測(cè)試
# 測(cè)試CNN模型
with torch.no_grad(): # 進(jìn)行評(píng)測(cè)的時(shí)候網(wǎng)絡(luò)不更新梯度
correct = 0
total = 0
for images, labels in test_loader:
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0
correct += (predicted == labels).sum().item()
print('Accuracy of the network on the 10000 test images: {} %'.format(100 * correct / total))
這里訓(xùn)練的模型準(zhǔn)確率達(dá)到了98%,非常高,如果還想繼續(xù)提高模型準(zhǔn)確率,可以調(diào)整迭代次數(shù)、學(xué)習(xí)率等參數(shù)或者修改CNN網(wǎng)絡(luò)結(jié)構(gòu)實(shí)現(xiàn)。
可視化檢驗(yàn)一個(gè)批次測(cè)試數(shù)據(jù)的準(zhǔn)確性
# 將 test_loader 轉(zhuǎn)換為一個(gè)可迭代對(duì)象 dataiter
dataiter = iter(test_loader)
# 使用 next(dataiter) 獲取 test_loader 中的下一個(gè) batch 的圖像數(shù)據(jù)和標(biāo)簽數(shù)據(jù)
images, labels = next(dataiter)
# print images
test_img = utils.make_grid(images)
test_img = test_img.numpy().transpose(1,2,0)
std = [0.5]
mean = [0.5]
test_img = test_img*std+0.5
plt.imshow(test_img)
plt.show()
plt.savefig('./mnist_net.png')
print('GroundTruth: ', ' '.join('%d' % labels[j] for j in range(128)))
文章來源:http://www.zghlxwxcb.cn/news/detail-438972.html
參考來源:
使用Pytorch框架的CNN網(wǎng)絡(luò)實(shí)現(xiàn)手寫數(shù)字(MNIST)識(shí)別
PyTorch初探MNIST數(shù)據(jù)集文章來源地址http://www.zghlxwxcb.cn/news/detail-438972.html
到了這里,關(guān)于CNN實(shí)現(xiàn)手寫數(shù)字識(shí)別(Pytorch)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!