訂閱
糾錯(cuò)
加入自媒體

在PyTorch中使用卷積神經(jīng)網(wǎng)絡(luò)建立圖像分類模型

概述

在PyTorch中構(gòu)建自己的卷積神經(jīng)網(wǎng)絡(luò)(CNN)的實(shí)踐教程

我們將研究一個(gè)圖像分類問題——CNN的一個(gè)經(jīng)典和廣泛使用的應(yīng)用

我們將以實(shí)用的格式介紹深度學(xué)習(xí)概念

介紹

我被神經(jīng)網(wǎng)絡(luò)的力量和能力所吸引。在機(jī)器學(xué)習(xí)和深度學(xué)習(xí)領(lǐng)域,幾乎每一次突破都以神經(jīng)網(wǎng)絡(luò)模型為核心。

這在計(jì)算機(jī)視覺領(lǐng)域尤為普遍。無論是簡單的圖像分類還是更高級的東西(如對象檢測),神經(jīng)網(wǎng)絡(luò)開辟了處理圖像數(shù)據(jù)的可能性。簡而言之,對于像我這樣的數(shù)據(jù)科學(xué)家來說,這是一座金礦!

當(dāng)我們使用深度學(xué)習(xí)來解決一個(gè)圖像分類問題時(shí),簡單的神經(jīng)網(wǎng)絡(luò)總是一個(gè)好的起點(diǎn)。但是,它們確實(shí)有局限性,而且模型的性能在達(dá)到一定程度后無法得到改善。

這就是卷積神經(jīng)網(wǎng)絡(luò)(CNNs)改變了競爭環(huán)境的地方。它們在計(jì)算機(jī)視覺應(yīng)用中無處不在。老實(shí)說,我覺得每一個(gè)計(jì)算機(jī)視覺愛好者都應(yīng)該可以很快學(xué)會(huì)這個(gè)概念。

我將向你介紹使用流行的PyTorch框架進(jìn)行深度學(xué)習(xí)的新概念。在本文中,我們將了解卷積神經(jīng)網(wǎng)絡(luò)是如何工作的,以及它如何幫助我們改進(jìn)模型的性能。我們還將研究在PyTorch中CNNs的實(shí)現(xiàn)。

目錄

1.簡要介紹PyTorch、張量和NumPy

2.為什么選擇卷積神經(jīng)網(wǎng)絡(luò)(CNNs)?

3.識別服裝問題

4.使用PyTorch實(shí)現(xiàn)CNNs

一、簡要介紹PyTorch、張量和NumPy

讓我們快速回顧一下第一篇文章中涉及的內(nèi)容。我們討論了PyTorch和張量的基礎(chǔ)知識,還討論了PyTorch與NumPy的相似之處。

PyTorch是一個(gè)基于python的庫,提供了以下功能:

用于創(chuàng)建可序列化和可優(yōu)化模型的TorchScript

以分布式訓(xùn)練進(jìn)行并行化計(jì)算

動(dòng)態(tài)計(jì)算圖,等等

PyTorch中的張量類似于NumPy的n維數(shù)組,也可以與gpu一起使用。在這些張量上執(zhí)行操作幾乎與在NumPy數(shù)組上執(zhí)行操作類似。這使得PyTorch非常易于使用和學(xué)習(xí)。

在本系列的第1部分中,我們構(gòu)建了一個(gè)簡單的神經(jīng)網(wǎng)絡(luò)來解決一個(gè)案例研究。使用我們的簡單模型,我們在測試集中獲得了大約65%的基準(zhǔn)準(zhǔn)確度。現(xiàn)在,我們將嘗試使用卷積神經(jīng)網(wǎng)絡(luò)來提高這個(gè)準(zhǔn)確度。

二、為什么選擇卷積神經(jīng)網(wǎng)絡(luò)(CNNs)?

在我們進(jìn)入實(shí)現(xiàn)部分之前,讓我們快速地看看為什么我們首先需要CNNs,以及它們是如何工作的。

我們可以將卷積神經(jīng)網(wǎng)絡(luò)(CNNs)看作是幫助從圖像中提取特征的特征提取器。

在一個(gè)簡單的神經(jīng)網(wǎng)絡(luò)中,我們把一個(gè)三維圖像轉(zhuǎn)換成一維圖像,對吧?讓我們看一個(gè)例子來理解這一點(diǎn):

你能認(rèn)出上面的圖像嗎?這似乎說不通,F(xiàn)在,讓我們看看下面的圖片:

我們現(xiàn)在可以很容易地說,這是一只狗。如果我告訴你這兩個(gè)圖像是一樣的呢?相信我,他們是一樣的!唯一的區(qū)別是第一個(gè)圖像是一維的,而第二個(gè)圖像是相同圖像的二維表示。

空間定位

人工神經(jīng)網(wǎng)絡(luò)也會(huì)丟失圖像的空間方向。讓我們再舉個(gè)例子來理解一下:

你能分辨出這兩幅圖像的區(qū)別嗎?至少我不能。由于這是一個(gè)一維的表示,因此很難確定它們之間的區(qū)別,F(xiàn)在,讓我們看看這些圖像的二維表示:

在這里,圖像某些定位已經(jīng)改變,但我們無法通過查看一維表示來識別它。

這就是人工神經(jīng)網(wǎng)絡(luò)的問題——它們失去了空間定位。

大量參數(shù)

神經(jīng)網(wǎng)絡(luò)的另一個(gè)問題是參數(shù)太多。假設(shè)我們的圖像大小是28*28*3 -所以這里的參數(shù)是2352。如果我們有一個(gè)大小為224*224*3的圖像呢?這里的參數(shù)數(shù)量為150,528。

這些參數(shù)只會(huì)隨著隱藏層的增加而增加。因此,使用人工神經(jīng)網(wǎng)絡(luò)的兩個(gè)主要缺點(diǎn)是:

1.丟失圖像的空間方向

2.參數(shù)的數(shù)量急劇增加

那么我們?nèi)绾翁幚磉@個(gè)問題呢?如何在保持空間方向的同時(shí)減少可學(xué)習(xí)參數(shù)?

這就是卷積神經(jīng)網(wǎng)絡(luò)真正有用的地方。CNNs有助于從圖像中提取特征,這可能有助于對圖像中的目標(biāo)進(jìn)行分類。它首先從圖像中提取低維特征(如邊緣),然后提取一些高維特征(如形狀)。

我們使用濾波器從圖像中提取特征,并使用池技術(shù)來減少可學(xué)習(xí)參數(shù)的數(shù)量。

在本文中,我們不會(huì)深入討論這些主題的細(xì)節(jié)。如果你希望了解濾波器如何幫助提取特征和池的工作方式,我強(qiáng)烈建議你從頭開始學(xué)習(xí)卷積神經(jīng)網(wǎng)絡(luò)的全面教程。

三、理解問題陳述:識別服裝

理論部分已經(jīng)鋪墊完了,開始寫代碼吧。我們將討論與第一篇文章相同的問題陳述。這是因?yàn)槲覀兛梢灾苯訉⑽覀兊腃NN模型的性能與我們在那里建立的簡單神經(jīng)網(wǎng)絡(luò)進(jìn)行比較。

你可以從這里下載“識別”Apparels問題的數(shù)據(jù)集。

https://datahack.a(chǎn)nalyticsvidhya.com/contest/practice-problem-identify-the-apparels/?utm_source=blog&utm_medium=building-image-classification-models-cnn-pytorch

讓我快速總結(jié)一下問題陳述。我們的任務(wù)是通過觀察各種服裝形象來識別服裝的類型。我們總共有10個(gè)類可以對服裝的圖像進(jìn)行分類:

Label

Description

0

T-shirt/top

1

Trouser

2

Pullover

3

Dress

4

Coat

5

Sandal

6

Shirt

7

Sneaker

8

Bag

9

Ankle boot

數(shù)據(jù)集共包含70,000張圖像。其中60000張屬于訓(xùn)練集,其余10000張屬于測試集。所有的圖像都是大。28*28)的灰度圖像。數(shù)據(jù)集包含兩個(gè)文件夾,—一個(gè)用于訓(xùn)練集,另一個(gè)用于測試集。每個(gè)文件夾中都有一個(gè).csv文件,該文件具有圖像的id和相應(yīng)的標(biāo)簽;

準(zhǔn)備好開始了嗎?我們將首先導(dǎo)入所需的庫:

# 導(dǎo)入庫
import pandas as pd
import numpy as np
# 讀取與展示圖片
from skimage.io import imread
import matplotlib.pyplot as plt
%matplotlib inline
# 創(chuàng)建驗(yàn)證集
from sklearn.model_selection import train_test_split
# 評估模型
from sklearn.metrics import accuracy_score
from tqdm import tqdm
# Pytorch的相關(guān)庫
import torch
from torch.a(chǎn)utograd import Variable
from torch.nn import Linear, ReLU, CrossEntropyLoss, Sequential, Conv2d, MaxPool2d, Module, Softmax, BatchNorm2d, Dropout
from torch.optim import Adam, SGD

加載數(shù)據(jù)集

現(xiàn)在,讓我們加載數(shù)據(jù)集,包括訓(xùn)練,測試樣本:

# 加載數(shù)據(jù)集
train = pd.read_csv('train_LbELtWX/train.csv')
test = pd.read_csv('test_ScVgIM0/test.csv')
sample_submission = pd.read_csv('sample_submission_I5njJSF.csv')
train.head()

該訓(xùn)練文件包含每個(gè)圖像的id及其對應(yīng)的標(biāo)簽

另一方面,測試文件只有id,我們必須預(yù)測它們對應(yīng)的標(biāo)簽

樣例提交文件將告訴我們預(yù)測的格式

我們將一個(gè)接一個(gè)地讀取所有圖像,并將它們堆疊成一個(gè)數(shù)組。我們還將圖像的像素值除以255,使圖像的像素值在[0,1]范圍內(nèi)。這一步有助于優(yōu)化模型的性能。

讓我們來加載圖像:

# 加載訓(xùn)練圖像
train_img = []
for img_name in tqdm(train['id']):
   # 定義圖像路徑
   image_path = 'train_LbELtWX/train/' + str(img_name) + '.png'
   # 讀取圖片
   img = imread(image_path, as_gray=True)
   # 歸一化像素值
   img /= 255.0
   # 轉(zhuǎn)換為浮點(diǎn)數(shù)
   img = img.a(chǎn)stype('float32')
   # 添加到列表
   train_img.a(chǎn)ppend(img)
# 轉(zhuǎn)換為numpy數(shù)組
train_x = np.a(chǎn)rray(train_img)
# 定義目標(biāo)
train_y = train['label'].values
train_x.shape

如你所見,我們在訓(xùn)練集中有60,000張大。28,28)的圖像。由于圖像是灰度格式的,我們只有一個(gè)單一通道,因此形狀為(28,28)。

現(xiàn)在讓我們研究數(shù)據(jù)和可視化一些圖像:

# 可視化圖片
i = 0
plt.figure(figsize=(10,10))
plt.subplot(221), plt.imshow(train_x[i], cmap='gray')
plt.subplot(222), plt.imshow(train_x[i+25], cmap='gray')
plt.subplot(223), plt.imshow(train_x[i+50], cmap='gray')
plt.subplot(224), plt.imshow(train_x[i+75], cmap='gray')

以下是來自數(shù)據(jù)集的一些示例。我鼓勵(lì)你去探索更多,想象其他的圖像。接下來,我們將把圖像分成訓(xùn)練集和驗(yàn)證集。

創(chuàng)建驗(yàn)證集并對圖像進(jìn)行預(yù)處理

# 創(chuàng)建驗(yàn)證集
train_x, val_x, train_y, val_y = train_test_split(train_x, train_y, test_size = 0.1)
(train_x.shape, train_y.shape), (val_x.shape, val_y.shape)

我們在驗(yàn)證集中保留了10%的數(shù)據(jù),在訓(xùn)練集中保留了10%的數(shù)據(jù)。接下來將圖片和目標(biāo)轉(zhuǎn)換成torch格式:

# 轉(zhuǎn)換為torch張量
train_x = train_x.reshape(54000, 1, 28, 28)
train_x  = torch.from_numpy(train_x)
# 轉(zhuǎn)換為torch張量
train_y = train_y.a(chǎn)stype(int);
train_y = torch.from_numpy(train_y)
# 訓(xùn)練集形狀
train_x.shape, train_y.shape

同樣,我們將轉(zhuǎn)換驗(yàn)證圖像:

# 轉(zhuǎn)換為torch張量
val_x = val_x.reshape(6000, 1, 28, 28)
val_x  = torch.from_numpy(val_x)
# 轉(zhuǎn)換為torch張量
val_y = val_y.a(chǎn)stype(int);
val_y = torch.from_numpy(val_y)
# 驗(yàn)證集形狀
val_x.shape, val_y.shape

我們的數(shù)據(jù)現(xiàn)在已經(jīng)準(zhǔn)備好了。最后,是時(shí)候創(chuàng)建我們的CNN模型了!

四、使用PyTorch實(shí)現(xiàn)CNNs

我們將使用一個(gè)非常簡單的CNN架構(gòu),只有兩個(gè)卷積層來提取圖像的特征。然后,我們將使用一個(gè)完全連接的Dense層將這些特征分類到各自的類別中。

讓我們定義一下架構(gòu):

class Net(Module):  
   def __init__(self):
       super(Net, self).__init__()
       self.cnn_layers = Sequential(
           # 定義2D卷積層
           Conv2d(1, 4, kernel_size=3, stride=1, padding=1),
           BatchNorm2d(4),
           ReLU(inplace=True),
           MaxPool2d(kernel_size=2, stride=2),
           # 定義另一個(gè)2D卷積層
           Conv2d(4, 4, kernel_size=3, stride=1, padding=1),
           BatchNorm2d(4),
           ReLU(inplace=True),
           MaxPool2d(kernel_size=2, stride=2),
       )
       self.linear_layers = Sequential(
           Linear(4 * 7 * 7, 10)
       )
   # 前項(xiàng)傳播
   def forward(self, x):
       x = self.cnn_layers(x)
       x = x.view(x.size(0), -1)
       x = self.linear_layers(x)
       return x

現(xiàn)在我們調(diào)用這個(gè)模型,定義優(yōu)化器和模型的損失函數(shù):

# 定義模型
model = Net()
# 定義優(yōu)化器
optimizer = Adam(model.parameters(), lr=0.07)
# 定義loss函數(shù)
criterion = CrossEntropyLoss()
# 檢查GPU是否可用
if torch.cuda.is_available():
   model = model.cuda()
   criterion = criterion.cuda()
print(model)

這是模型的架構(gòu)。我們有兩個(gè)卷積層和一個(gè)線性層。接下來,我們將定義一個(gè)函數(shù)來訓(xùn)練模型:

def train(epoch):
   model.train()
   tr_loss = 0
   # 獲取訓(xùn)練集
   x_train, y_train = Variable(train_x), Variable(train_y)
   # 獲取驗(yàn)證集
   x_val, y_val = Variable(val_x), Variable(val_y)
   # 轉(zhuǎn)換為GPU格式
   if torch.cuda.is_available():
       x_train = x_train.cuda()
       y_train = y_train.cuda()
       x_val = x_val.cuda()
       y_val = y_val.cuda()
   # 清除梯度
   optimizer.zero_grad()
   # 預(yù)測訓(xùn)練與驗(yàn)證集
   output_train = model(x_train)
   output_val = model(x_val)
   # 計(jì)算訓(xùn)練集與驗(yàn)證集損失
   loss_train = criterion(output_train, y_train)
   loss_val = criterion(output_val, y_val)
   train_losses.a(chǎn)ppend(loss_train)
   val_losses.a(chǎn)ppend(loss_val)
   # 更新權(quán)重
   loss_train.backward()
   optimizer.step()
   tr_loss = loss_train.item()
   if epoch%2 == 0:
       # 輸出驗(yàn)證集loss
       print('Epoch : ',epoch+1, ' ', 'loss :', loss_val)

最后,我們將對模型進(jìn)行25個(gè)epoch的訓(xùn)練,并存儲(chǔ)訓(xùn)練和驗(yàn)證損失:

# 定義輪數(shù)
n_epochs = 25
# 空列表存儲(chǔ)訓(xùn)練集損失
train_losses = []
# 空列表存儲(chǔ)驗(yàn)證集損失
val_losses = []
# 訓(xùn)練模型
for epoch in range(n_epochs):
   train(epoch)

可以看出,隨著epoch的增加,驗(yàn)證損失逐漸減小。讓我們通過繪圖來可視化訓(xùn)練和驗(yàn)證的損失:

# 畫出loss曲線
plt.plot(train_losses, label='Training loss')
plt.plot(val_losses, label='Validation loss')
plt.legend()
plt.show()

啊,我喜歡想象的力量。我們可以清楚地看到,訓(xùn)練和驗(yàn)證損失是同步的。這是一個(gè)好跡象,因?yàn)槟P驮隍?yàn)證集上進(jìn)行了很好的泛化。

讓我們在訓(xùn)練和驗(yàn)證集上檢查模型的準(zhǔn)確性:

# 訓(xùn)練集預(yù)測
with torch.no_grad():
   output = model(train_x.cuda())
softmax = torch.exp(output).cpu()
prob = list(softmax.numpy())
predictions = np.a(chǎn)rgmax(prob, axis=1)
# 訓(xùn)練集精度
accuracy_score(train_y, predictions)

訓(xùn)練集的準(zhǔn)確率約為72%,相當(dāng)不錯(cuò)。讓我們檢查驗(yàn)證集的準(zhǔn)確性:

# 驗(yàn)證集預(yù)測
with torch.no_grad():
   output = model(val_x.cuda())
softmax = torch.exp(output).cpu()
prob = list(softmax.numpy())
predictions = np.a(chǎn)rgmax(prob, axis=1)
# 驗(yàn)證集精度
accuracy_score(val_y, predictions)

正如我們看到的損失,準(zhǔn)確度也是同步的-我們在驗(yàn)證集得到了72%的準(zhǔn)確度。

為測試集生成預(yù)測

最后是時(shí)候?yàn)闇y試集生成預(yù)測了。我們將加載測試集中的所有圖像,執(zhí)行與訓(xùn)練集相同的預(yù)處理步驟,最后生成預(yù)測。

所以,讓我們開始加載測試圖像:

# 載入測試圖
test_img = []
for img_name in tqdm(test['id']):
   # 定義圖片路徑
   image_path = 'test_ScVgIM0/test/' + str(img_name) + '.png'
   # 讀取圖片
   img = imread(image_path, as_gray=True)
   # 歸一化像素
   img /= 255.0
   # 轉(zhuǎn)換為浮點(diǎn)數(shù)
   img = img.a(chǎn)stype('float32')
   # 添加到列表
   test_img.a(chǎn)ppend(img)
# 轉(zhuǎn)換為numpy數(shù)組
test_x = np.a(chǎn)rray(test_img)
test_x.shape

現(xiàn)在,我們將對這些圖像進(jìn)行預(yù)處理步驟,類似于我們之前對訓(xùn)練圖像所做的:

# 轉(zhuǎn)換為torch格式
test_x = test_x.reshape(10000, 1, 28, 28)
test_x  = torch.from_numpy(test_x)
test_x.shape

最后,我們將生成對測試集的預(yù)測:

# 生成測試集預(yù)測
with torch.no_grad():
   output = model(test_x.cuda())
softmax = torch.exp(output).cpu()
prob = list(softmax.numpy())
predictions = np.a(chǎn)rgmax(prob, axis=1)

用預(yù)測替換樣本提交文件中的標(biāo)簽,最后保存文件并提交到排行榜:

# 用預(yù)測替換
sample_submission['label'] = predictions
sample_submission.head()

# 保存文件
sample_submission.to_csv('submission.csv', index=False)

你將在當(dāng)前目錄中看到一個(gè)名為submission.csv的文件。你只需要把它上傳到問題頁面的解決方案檢查器上,它就會(huì)生成分?jǐn)?shù)。鏈接:https://datahack.a(chǎn)nalyticsvidhya.com/contest/practice-problem-identify-the-apparels/?utm_source=blog&utm_medium=building-image-classification-models-cnn-pytorch

我們的CNN模型在測試集上給出了大約71%的準(zhǔn)確率,這與我們在上一篇文章中使用簡單的神經(jīng)網(wǎng)絡(luò)得到的65%的準(zhǔn)確率相比是一個(gè)很大的進(jìn)步。

結(jié)尾

在這篇文章中,我們研究了CNNs是如何從圖像中提取特征的。他們幫助我們將之前的神經(jīng)網(wǎng)絡(luò)模型的準(zhǔn)確率從65%提高到71%,這是一個(gè)重大的進(jìn)步。

你可以嘗試使用CNN模型的超參數(shù),并嘗試進(jìn)一步提高準(zhǔn)確性。要調(diào)優(yōu)的超參數(shù)可以是卷積層的數(shù)量、每個(gè)卷積層的濾波器數(shù)量、epoch的數(shù)量、全連接層的數(shù)量、每個(gè)全連接層的隱藏單元的數(shù)量等。

聲明: 本文由入駐維科號的作者撰寫,觀點(diǎn)僅代表作者本人,不代表OFweek立場。如有侵權(quán)或其他問題,請聯(lián)系舉報(bào)。

發(fā)表評論

0條評論,0人參與

請輸入評論內(nèi)容...

請輸入評論/評論長度6~500個(gè)字

您提交的評論過于頻繁,請輸入驗(yàn)證碼繼續(xù)

  • 看不清,點(diǎn)擊換一張  刷新

暫無評論

暫無評論

人工智能 獵頭職位 更多
掃碼關(guān)注公眾號
OFweek人工智能網(wǎng)
獲取更多精彩內(nèi)容
文章糾錯(cuò)
x
*文字標(biāo)題:
*糾錯(cuò)內(nèi)容:
聯(lián)系郵箱:
*驗(yàn) 證 碼:

粵公網(wǎng)安備 44030502002758號