Python+Pytorch實(shí)戰(zhàn)之彩色圖片識(shí)別
一、 前期準(zhǔn)備
1. 設(shè)置GPU
如果設(shè)備上支持GPU就使用GPU,否則使用CPU
import torch import torch.nn as nn import matplotlib.pyplot as plt import torchvision device = torch.device("cuda" if torch.cuda.is_available() else "cpu") device
device(type='cuda')
2. 導(dǎo)入數(shù)據(jù)
使用dataset下載MNIST數(shù)據(jù)集,并劃分好訓(xùn)練集與測(cè)試集
使用dataloader加載數(shù)據(jù),并設(shè)置好基本的batch_size
torchvision.datasets.MNIST詳解
torchvision.datasets是Pytorch自帶的一個(gè)數(shù)據(jù)庫,我們可以通過代碼在線下載數(shù)據(jù),這里使用的是torchvision.datasets中的MNIST數(shù)據(jù)集。
函數(shù)原型:
torchvision.datasets.MNIST(root, train=True, transform=None, target_transform=None, download=False)
參數(shù)說明:
- root (string) :數(shù)據(jù)地址
- train (string) :True = 訓(xùn)練集,F(xiàn)alse = 測(cè)試集
- download (bool,optional) : 如果為True,從互聯(lián)網(wǎng)上下載數(shù)據(jù)集,并把數(shù)據(jù)集放在root目錄下。
- transform (callable, optional ):這里的參數(shù)選擇一個(gè)你想要的數(shù)據(jù)轉(zhuǎn)化函數(shù),直接完成數(shù)據(jù)轉(zhuǎn)化
- target_transform (callable,optional) :接受目標(biāo)并對(duì)其進(jìn)行轉(zhuǎn)換的函數(shù)/轉(zhuǎn)換。
train_ds = torchvision.datasets.CIFAR10('data', train=True, transform=torchvision.transforms.ToTensor(), # 將數(shù)據(jù)類型轉(zhuǎn)化為Tensor download=True) test_ds = torchvision.datasets.CIFAR10('data', train=False, transform=torchvision.transforms.ToTensor(), # 將數(shù)據(jù)類型轉(zhuǎn)化為Tensor download=True)
Files already downloaded and verified Files already downloaded and verified
batch_size = 32 train_dl = torch.utils.data.DataLoader(train_ds, batch_size=batch_size, shuffle=True) test_dl = torch.utils.data.DataLoader(test_ds, batch_size=batch_size)
# 取一個(gè)批次查看數(shù)據(jù)格式 # 數(shù)據(jù)的shape為:[batch_size, channel, height, weight] # 其中batch_size為自己設(shè)定,channel,height和weight分別是圖片的通道數(shù),高度和寬度。 imgs, labels = next(iter(train_dl)) imgs.shape
torch.Size([32, 3, 32, 32])
3. 數(shù)據(jù)可視化
squeeze()函數(shù)的功能是從矩陣shape中,去掉維度為1的。例如一個(gè)矩陣是的shape是(5, 1),使用過這個(gè)函數(shù)后,結(jié)果為(5, )。
import numpy as np # 指定圖片大小,圖像大小為20寬、5高的繪圖(單位為英寸inch) plt.figure(figsize=(20, 5)) for i, imgs in enumerate(imgs[:20]): # 維度縮減 npimg = imgs.numpy().transpose((1, 2, 0)) # 將整個(gè)figure分成2行10列,繪制第i+1個(gè)子圖。 plt.subplot(2, 10, i+1) plt.imshow(npimg, cmap=plt.cm.binary) plt.axis('off')
二、構(gòu)建簡單的CNN網(wǎng)絡(luò)
對(duì)于一般的CNN網(wǎng)絡(luò)來說,都是由特征提取網(wǎng)絡(luò)和分類網(wǎng)絡(luò)構(gòu)成,其中特征提取網(wǎng)絡(luò)用于提取圖片的特征,分類網(wǎng)絡(luò)用于將圖片進(jìn)行分類。
1. torch.nn.Conv2d()詳解
函數(shù)原型:
torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode=‘zeros', device=None, dtype=None)
關(guān)鍵參數(shù)說明:
- in_channels ( int ) – 輸入圖像中的通道數(shù)
- out_channels ( int ) – 卷積產(chǎn)生的通道數(shù)
- kernel_size ( int or tuple ) – 卷積核的大小
- stride ( int or tuple , optional ) – 卷積的步幅。默認(rèn)值:1
- padding ( int , tuple或str , optional ) – 添加到輸入的所有四個(gè)邊的填充。默認(rèn)值:0
- padding_mode (字符串,可選) – ‘zeros’, ‘reflect’, ‘replicate’或’circular’. 默認(rèn):‘zeros’
2. torch.nn.Linear()詳解
函數(shù)原型:
torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None)
關(guān)鍵參數(shù)說明:
- in_features:每個(gè)輸入樣本的大小
- out_features:每個(gè)輸出樣本的大小
3. torch.nn.MaxPool2d()詳解
函數(shù)原型:
torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
關(guān)鍵參數(shù)說明:
- kernel_size:最大的窗口大小
- stride:窗口的步幅,默認(rèn)值為kernel_size
- padding:填充值,默認(rèn)為0
- dilation:控制窗口中元素步幅的參數(shù)
4. 關(guān)于卷積層、池化層的計(jì)算:
下面的網(wǎng)絡(luò)數(shù)據(jù)shape變化過程為:
3, 32, 32(輸入數(shù)據(jù))
-> 64, 30, 30(經(jīng)過卷積層1)-> 64, 15, 15(經(jīng)過池化層1)
-> 64, 13, 13(經(jīng)過卷積層2)-> 64, 6, 6(經(jīng)過池化層2)
-> 128, 4, 4(經(jīng)過卷積層3) -> 128, 2, 2(經(jīng)過池化層3)
-> 512 -> 256 -> num_classes(10)
import torch.nn.functional as F num_classes = 10 # 圖片的類別數(shù) class Model(nn.Module): def __init__(self): super().__init__() # 特征提取網(wǎng)絡(luò) self.conv1 = nn.Conv2d(3, 64, kernel_size=3) # 第一層卷積,卷積核大小為3*3 self.pool1 = nn.MaxPool2d(kernel_size=2) # 設(shè)置池化層,池化核大小為2*2 self.conv2 = nn.Conv2d(64, 64, kernel_size=3) # 第二層卷積,卷積核大小為3*3 self.pool2 = nn.MaxPool2d(kernel_size=2) self.conv3 = nn.Conv2d(64, 128, kernel_size=3) # 第二層卷積,卷積核大小為3*3 self.pool3 = nn.MaxPool2d(kernel_size=2) # 分類網(wǎng)絡(luò) self.fc1 = nn.Linear(512, 256) self.fc2 = nn.Linear(256, num_classes) # 前向傳播 def forward(self, x): x = self.pool1(F.relu(self.conv1(x))) x = self.pool2(F.relu(self.conv2(x))) x = self.pool3(F.relu(self.conv3(x))) x = torch.flatten(x, start_dim=1) x = F.relu(self.fc1(x)) x = self.fc2(x) return x
加載并打印模型
from torchinfo import summary # 將模型轉(zhuǎn)移到GPU中(我們模型運(yùn)行均在GPU中進(jìn)行) model = Model().to(device) summary(model)
================================================================= Layer (type:depth-idx) Param # ================================================================= Model -- ├─Conv2d: 1-1 1,792 ├─MaxPool2d: 1-2 -- ├─Conv2d: 1-3 36,928 ├─MaxPool2d: 1-4 -- ├─Conv2d: 1-5 73,856 ├─MaxPool2d: 1-6 -- ├─Linear: 1-7 131,328 ├─Linear: 1-8 2,570 ================================================================= Total params: 246,474 Trainable params: 246,474 Non-trainable params: 0 =================================================================
三、 訓(xùn)練模型
1. 設(shè)置超參數(shù)
loss_fn = nn.CrossEntropyLoss() # 創(chuàng)建損失函數(shù) learn_rate = 1e-2 # 學(xué)習(xí)率 opt = torch.optim.SGD(model.parameters(),lr=learn_rate)
2. 編寫訓(xùn)練函數(shù)
1. optimizer.zero_grad()
函數(shù)會(huì)遍歷模型的所有參數(shù),通過內(nèi)置方法截?cái)喾聪騻鞑サ奶荻攘?,再將每個(gè)參數(shù)的梯度值設(shè)為0,即上一次的梯度記錄被清空。
2. loss.backward()
PyTorch的反向傳播(即tensor.backward())是通過autograd包來實(shí)現(xiàn)的,autograd包會(huì)根據(jù)tensor進(jìn)行過的數(shù)學(xué)運(yùn)算來自動(dòng)計(jì)算其對(duì)應(yīng)的梯度。
具體來說,torch.tensor是autograd包的基礎(chǔ)類,如果你設(shè)置tensor的requires_grads為True,就會(huì)開始跟蹤這個(gè)tensor上面的所有運(yùn)算,如果你做完運(yùn)算后使用tensor.backward(),所有的梯度就會(huì)自動(dòng)運(yùn)算,tensor的梯度將會(huì)累加到它的.grad屬性里面去。
更具體地說,損失函數(shù)loss是由模型的所有權(quán)重w經(jīng)過一系列運(yùn)算得到的,若某個(gè)w的requires_grads為True,則w的所有上層參數(shù)(后面層的權(quán)重w)的.grad_fn屬性中就保存了對(duì)應(yīng)的運(yùn)算,然后在使用loss.backward()后,會(huì)一層層的反向傳播計(jì)算每個(gè)w的梯度值,并保存到該w的.grad屬性中。
如果沒有進(jìn)行tensor.backward()的話,梯度值將會(huì)是None,因此loss.backward()要寫在optimizer.step()之前。
3. optimizer.step()
step()函數(shù)的作用是執(zhí)行一次優(yōu)化步驟,通過梯度下降法來更新參數(shù)的值。因?yàn)樘荻认陆凳腔谔荻鹊?,所以在?zhí)行optimizer.step()函數(shù)前應(yīng)先執(zhí)行l(wèi)oss.backward()函數(shù)來計(jì)算梯度。
注意:optimizer只負(fù)責(zé)通過梯度下降進(jìn)行優(yōu)化,而不負(fù)責(zé)產(chǎn)生梯度,梯度是tensor.backward()方法產(chǎn)生的。
# 訓(xùn)練循環(huán) def train(dataloader, model, loss_fn, optimizer): size = len(dataloader.dataset) # 訓(xùn)練集的大小,一共60000張圖片 num_batches = len(dataloader) # 批次數(shù)目,1875(60000/32) train_loss, train_acc = 0, 0 # 初始化訓(xùn)練損失和正確率 for X, y in dataloader: # 獲取圖片及其標(biāo)簽 X, y = X.to(device), y.to(device) # 計(jì)算預(yù)測(cè)誤差 pred = model(X) # 網(wǎng)絡(luò)輸出 loss = loss_fn(pred, y) # 計(jì)算網(wǎng)絡(luò)輸出和真實(shí)值之間的差距,targets為真實(shí)值,計(jì)算二者差值即為損失 # 反向傳播 optimizer.zero_grad() # grad屬性歸零 loss.backward() # 反向傳播 optimizer.step() # 每一步自動(dòng)更新 # 記錄acc與loss train_acc += (pred.argmax(1) == y).type(torch.float).sum().item() train_loss += loss.item() train_acc /= size train_loss /= num_batches return train_acc, train_loss
3. 編寫測(cè)試函數(shù)
測(cè)試函數(shù)和訓(xùn)練函數(shù)大致相同,但是由于不進(jìn)行梯度下降對(duì)網(wǎng)絡(luò)權(quán)重進(jìn)行更新,所以不需要傳入優(yōu)化器
def test (dataloader, model, loss_fn): size = len(dataloader.dataset) # 測(cè)試集的大小,一共10000張圖片 num_batches = len(dataloader) # 批次數(shù)目,313(10000/32=312.5,向上取整) test_loss, test_acc = 0, 0 # 當(dāng)不進(jìn)行訓(xùn)練時(shí),停止梯度更新,節(jié)省計(jì)算內(nèi)存消耗 with torch.no_grad(): for imgs, target in dataloader: imgs, target = imgs.to(device), target.to(device) # 計(jì)算loss target_pred = model(imgs) loss = loss_fn(target_pred, target) test_loss += loss.item() test_acc += (target_pred.argmax(1) == target).type(torch.float).sum().item() test_acc /= size test_loss /= num_batches return test_acc, test_loss
4. 正式訓(xùn)練
1. model.train()
model.train()的作用是啟用 Batch Normalization 和 Dropout。
如果模型中有BN層(Batch Normalization)和Dropout,需要在訓(xùn)練時(shí)添加model.train()。model.train()是保證BN層能夠用到每一批數(shù)據(jù)的均值和方差。對(duì)于Dropout,model.train()是隨機(jī)取一部分網(wǎng)絡(luò)連接來訓(xùn)練更新參數(shù)。
2. model.eval()
model.eval()的作用是不啟用 Batch Normalization 和 Dropout。
如果模型中有BN層(Batch Normalization)和Dropout,在測(cè)試時(shí)添加model.eval()。model.eval()是保證BN層能夠用全部訓(xùn)練數(shù)據(jù)的均值和方差,即測(cè)試過程中要保證BN層的均值和方差不變。對(duì)于Dropout,model.eval()是利用到了所有網(wǎng)絡(luò)連接,即不進(jìn)行隨機(jī)舍棄神經(jīng)元。
訓(xùn)練完train樣本后,生成的模型model要用來測(cè)試樣本。在model(test)之前,需要加上model.eval(),否則的話,有輸入數(shù)據(jù),即使不訓(xùn)練,它也會(huì)改變權(quán)值。這是model中含有BN層和Dropout所帶來的的性質(zhì)。
epochs = 10 train_loss = [] train_acc = [] test_loss = [] test_acc = [] for epoch in range(epochs): model.train() epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, opt) model.eval() epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn) train_acc.append(epoch_train_acc) train_loss.append(epoch_train_loss) test_acc.append(epoch_test_acc) test_loss.append(epoch_test_loss) template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%,Test_loss:{:.3f}') print(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss, epoch_test_acc*100, epoch_test_loss)) print('Done')
Epoch: 1, Train_acc:13.5%, Train_loss:2.280, Test_acc:19.8%,Test_loss:2.150
Epoch: 2, Train_acc:24.6%, Train_loss:2.022, Test_acc:29.0%,Test_loss:1.931
Epoch: 3, Train_acc:33.2%, Train_loss:1.811, Test_acc:36.9%,Test_loss:1.712
Epoch: 4, Train_acc:40.4%, Train_loss:1.637, Test_acc:40.8%,Test_loss:1.609
Epoch: 5, Train_acc:44.0%, Train_loss:1.535, Test_acc:46.4%,Test_loss:1.470
Epoch: 6, Train_acc:47.4%, Train_loss:1.449, Test_acc:47.4%,Test_loss:1.432
Epoch: 7, Train_acc:50.9%, Train_loss:1.365, Test_acc:53.1%,Test_loss:1.313
Epoch: 8, Train_acc:53.9%, Train_loss:1.289, Test_acc:55.2%,Test_loss:1.256
Epoch: 9, Train_acc:56.1%, Train_loss:1.226, Test_acc:50.4%,Test_loss:1.458
Epoch:10, Train_acc:58.4%, Train_loss:1.175, Test_acc:58.9%,Test_loss:1.156
Done
四、 結(jié)果可視化
到此這篇關(guān)于Python+Pytorch實(shí)戰(zhàn)之彩色圖片識(shí)別的文章就介紹到這了,更多相關(guān)Python Pytorch彩色圖片識(shí)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python 實(shí)現(xiàn)使用dict 創(chuàng)建二維數(shù)據(jù)、DataFrame
下面小編就為大家分享一篇Python 實(shí)現(xiàn)使用dict 創(chuàng)建二維數(shù)據(jù)、DataFrame,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-04-04Python之使用adb shell命令啟動(dòng)應(yīng)用的方法詳解
今天小編就為大家分享一篇Python之使用adb shell命令啟動(dòng)應(yīng)用的方法詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-01-01python查看包版本、更新單個(gè)包、卸載單個(gè)包的操作方法
這篇文章主要介紹了python查看包版本、更新單個(gè)包、卸載單個(gè)包,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-12-12