前言
在深度學(xué)習(xí)的世界中,圖像分類任務(wù)是一個(gè)經(jīng)典的問題,它涉及到識別給定圖像中的對象類別。CIFAR-10數(shù)據(jù)集是一個(gè)常用的基準(zhǔn)數(shù)據(jù)集,包含了10個(gè)類別的60000張32x32彩色圖像。在本博客中,我們將探討如何使用PyTorch框架創(chuàng)建一個(gè)簡單的卷積神經(jīng)網(wǎng)絡(luò)(CNN)來對CIFAR-10數(shù)據(jù)集中的圖像進(jìn)行分類。
在下一篇博客中,我們將嘗試不斷優(yōu)化模型結(jié)構(gòu)和訓(xùn)練過程,以達(dá)到更高的準(zhǔn)確率和性能。
引用
關(guān)于卷積神經(jīng)網(wǎng)絡(luò)的原理,感興趣的請參閱我的另一篇博客,里面只使用numpy和基礎(chǔ)函數(shù)組建了一個(gè)卷積神經(jīng)網(wǎng)絡(luò)模型,并完成訓(xùn)練和測試
【手搓深度學(xué)習(xí)算法】從頭創(chuàng)建卷積神經(jīng)網(wǎng)絡(luò)
背景
卷積神經(jīng)網(wǎng)絡(luò)是深度學(xué)習(xí)中用于圖像識別和分類的一種強(qiáng)大工具。它們能夠自動從圖像中提取特征,并通過一系列卷積層、池化層和全連接層來學(xué)習(xí)圖像的復(fù)雜模式。
CIFAR-10數(shù)據(jù)集包含了飛機(jī)、汽車、鳥類、貓、鹿、狗、青蛙、馬、船和卡車等10個(gè)類別的圖像。每個(gè)類別有6000張圖像,其中50000張用于訓(xùn)練,10000張用于測試。
代碼解析
我們的目標(biāo)是構(gòu)建一個(gè)能夠處理CIFAR-10數(shù)據(jù)集的CNN模型。以下是我們的模型結(jié)構(gòu)和數(shù)據(jù)處理流程的簡要概述:
數(shù)據(jù)預(yù)處理
我們首先定義了unpickle
函數(shù)來加載CIFAR-10數(shù)據(jù)集的批次文件。read_data
函數(shù)用于讀取數(shù)據(jù),將其轉(zhuǎn)換為適合卷積網(wǎng)絡(luò)輸入的格式,并進(jìn)行歸一化處理。我們還提供了一個(gè)選項(xiàng)來將圖像轉(zhuǎn)換為灰度。
def unpickle(file):
import pickle
with open(file, 'rb') as fo:
dict = pickle.load(fo, encoding='bytes')
return dict
def read_data(file_path, gray = False, percent = 0, normalize = True):
data_src = unpickle(file_path)
np_data = np.array(data_src["data".encode()]).astype("float32")
np_labels = np.array(data_src["labels".encode()]).astype("float32").reshape(-1,1)
single_data_length = 32*32
image_ret = None
if (gray):
np_data = (np_data[:, :single_data_length] + np_data[:, single_data_length:(2*single_data_length)] + np_data[:, 2*single_data_length : 3*single_data_length])/3
image_ret = np_data.reshape(len(np_data),32,32)
else:
image_ret = np_data.reshape(len(np_data),32,32,3)
if(normalize):
mean = np.mean(np_data)
std = np.std(np_data)
np_data = (np_data - mean) / std
if (percent != 0):
np_data = np_data[:int(len(np_data)*percent)]
np_labels = np_labels[:int(len(np_labels)*percent)]
image_ret = image_ret[:int(len(image_ret)*percent)]
num_classes = len(np.unique(np_labels))
np_data, np_labels = convert_to_conv_input(np_data, np_labels)
return np_data, np_labels, num_classes, image_ret
網(wǎng)絡(luò)結(jié)構(gòu)
Conv
類定義了我們的CNN模型,它包含一個(gè)卷積層、一個(gè)最大池化層、一個(gè)ReLU激活函數(shù)和一個(gè)全連接層。在forward
方法中,我們指定了數(shù)據(jù)通過網(wǎng)絡(luò)的流程。
class Conv(th.nn.Module):
def __init__(self, *args, **kwargs) -> None:
super(Conv, self).__init__()
self.conv = th.nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3)
self.pool = th.nn.MaxPool2d(kernel_size=2,stride=2)
self.relu = th.nn.ReLU()
self.linear = th.nn.Linear(16*15*15, 10)
self.softmax = th.nn.Softmax(dim=1)
def forward(self, x):
x = self.conv(x) #32,16,30,30
x = self.pool(x) #32,16,15,15
x = self.relu(x)
x = x.view(x.size(0), -1)
x = self.linear(x)
return x
# 在predict函數(shù)中,額外調(diào)用了softmax,將線性層的10個(gè)特征值轉(zhuǎn)化為概率,在前向傳播中不用是因?yàn)閜ytorch中交叉熵函數(shù)自帶了softmax
def predict(self,x):
x = self.forward(x)
x = self.softmax(x)
return x
卷積層、池化層、線性層的輸入特征數(shù)量的計(jì)算方法
線性層的輸入特征個(gè)數(shù)取決于前面層的輸出。
具體來說,線性層的輸入特征個(gè)數(shù)是卷積層和池化層處理后的輸出特征圖的總元素?cái)?shù)量。
卷積層定義如下:
self.conv = th.nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3)
這里,in_channels=3
表示輸入圖像有3個(gè)顏色通道(RGB),out_channels=16
表示卷積層將輸出16個(gè)特征圖。
接下來是池化層:
self.pool = th.nn.MaxPool2d(kernel_size=2, stride=2)
kernel_size=2
,表示池化窗口的大小是2x2。stride=2
表示池化操作的步長是2。
為了計(jì)算線性層的輸入特征個(gè)數(shù),我們需要知道卷積層和池化層之后的輸出特征圖的大小。這可以通過計(jì)算公式得到,或者通過在實(shí)際數(shù)據(jù)上運(yùn)行網(wǎng)絡(luò)的前向傳播來確定。
計(jì)算公式如下:
對于卷積層,輸出特征圖的大小可以通過以下公式計(jì)算:
H_out = (H_in + 2 * padding - dilation * (kernel_size - 1) - 1) / stride + 1
W_out = (W_in + 2 * padding - dilation * (kernel_size - 1) - 1) / stride + 1
對于池化層,輸出特征圖的大小也可以通過類似的公式計(jì)算。
由于沒有指定padding
和dilation
,查看函數(shù)定義可知它們的默認(rèn)值分別是0和1。因此,如果輸入圖像的大小是32x32,卷積層之后的大小將是:
H_out = (32 - 1 * (3 - 1) - 1) / 1 + 1 = 30
W_out = (32 - 1 * (3 - 1) - 1) / 1 + 1 = 30
因此,卷積層的輸出將有16個(gè)30x30的特征圖。
然后,池化層將這些特征圖的大小減半(因?yàn)?code>kernel_size=2和stride=2
),所以輸出將是16個(gè)15x15的特征圖。
最后,線性層的輸入特征個(gè)數(shù)將是這些特征圖的總元素?cái)?shù)量:
num_features = out_channels * H_out_pool * W_out_pool = 16 * 15 * 15 = 3600
因此,線性層的正確定義應(yīng)該是:
self.linear = th.nn.Linear(3600, num_classes)
訓(xùn)練過程
在main
函數(shù)中,我們初始化了模型、損失函數(shù)和優(yōu)化器。我們使用隨機(jī)梯度下降(SGD)作為優(yōu)化算法,并設(shè)置了學(xué)習(xí)率。接著,我們進(jìn)入了訓(xùn)練循環(huán),其中包括前向傳播、損失計(jì)算、反向傳播和權(quán)重更新。
loss_function = th.nn.CrossEntropyLoss()
optimizer = th.optim.SGD(conv_model.parameters(), lr = lr)
測試和評估
訓(xùn)練完成后,我們使用訓(xùn)練好的模型對測試數(shù)據(jù)進(jìn)行評估,并計(jì)算準(zhǔn)確率。我們還提供了一個(gè)predict
方法,它在給定輸入數(shù)據(jù)后返回模型的預(yù)測概率。
def predict(self,x):
x = self.forward(x)
x = self.softmax(x)
return x
softmax激活函數(shù)
Softmax 激活函數(shù)是一種廣泛使用的函數(shù),它將一個(gè)實(shí)數(shù)向量轉(zhuǎn)換為概率分布。在深度學(xué)習(xí)中,它常常用于多類別分類問題的輸出層。
Softmax 函數(shù)的定義如下:
softmax ( z ) i = e z i ∑ j e z j \text{softmax}(z)_i = \frac{e^{z_i}}{\sum_{j} e^{z_j}} softmax(z)i?=∑j?ezj?ezi??
其中 z z z 是輸入向量, z i z_i zi? 是 z z z 的第 i i i 個(gè)元素, softmax ( z ) i \text{softmax}(z)_i softmax(z)i? 是輸出向量的第 i i i 個(gè)元素。
Softmax 函數(shù)的主要特性是它的輸出是一個(gè)概率分布,即所有輸出元素的值都在 ( 0 , 1 ) (0, 1) (0,1) 區(qū)間內(nèi),且所有輸出元素的值之和為 1。這使得 Softmax 函數(shù)非常適合用于表示概率。
Softmax 函數(shù)的一個(gè)重要性質(zhì)是它是連續(xù)的,且其導(dǎo)數(shù)容易計(jì)算。這使得 Softmax 函數(shù)在深度學(xué)習(xí)中的反向傳播過程中非常有用。
Softmax 函數(shù)的導(dǎo)數(shù)如下:
? ? z i softmax ( z ) i = softmax ( z ) i ( 1 ? softmax ( z ) i ) \frac{\partial}{\partial z_i}\text{softmax}(z)_i = \text{softmax}(z)_i(1 - \text{softmax}(z)_i) ?zi???softmax(z)i?=softmax(z)i?(1?softmax(z)i?)
這個(gè)導(dǎo)數(shù)表達(dá)式表明,對于 Softmax 函數(shù)的輸出 y i y_i yi?,其對輸入 z i z_i zi? 的導(dǎo)數(shù)等于 y i ( 1 ? y i ) y_i(1 - y_i) yi?(1?yi?)。這個(gè)導(dǎo)數(shù)表達(dá)式在反向傳播過程中非常有用,因?yàn)樗梢灾苯佑糜谟?jì)算梯度。
訓(xùn)練過程中沒有使用softmax層,是應(yīng)為torch的交叉熵?fù)p失函數(shù)已經(jīng)包含了softmax的操作,如果疊加使用,可能得到錯(cuò)誤的結(jié)果。
運(yùn)行結(jié)果
作為一個(gè)簡單的卷積模型,在測試集上得到了60%的準(zhǔn)確率
完整代碼
本文不提供完整代碼,因?yàn)殡S著我的微調(diào)優(yōu)化過程,已經(jīng)沒有這個(gè)版本的基線代碼了,想要最終代碼的歡迎閱讀下一篇博客 “記一次卷積網(wǎng)絡(luò)調(diào)優(yōu)的過程”文章來源:http://www.zghlxwxcb.cn/news/detail-819989.html
注意點(diǎn)
- 數(shù)據(jù)預(yù)處理:確保數(shù)據(jù)被正確地加載和歸一化,這對模型的訓(xùn)練效果至關(guān)重要。
- 模型結(jié)構(gòu):模型的層數(shù)和參數(shù)需要根據(jù)任務(wù)的復(fù)雜性來調(diào)整。過于簡單的模型可能無法捕捉到數(shù)據(jù)中的復(fù)雜特征,而過于復(fù)雜的模型可能會導(dǎo)致過擬合。
- 損失函數(shù):我們使用交叉熵?fù)p失函數(shù),它適用于多類別分類問題。
- 優(yōu)化器:在每次迭代前,記得清除累積的梯度,以避免錯(cuò)誤的梯度更新。
可能的優(yōu)化點(diǎn)
- 學(xué)習(xí)率調(diào)整:可以嘗試使用學(xué)習(xí)率調(diào)度器來在訓(xùn)練過程中調(diào)整學(xué)習(xí)率,以改善模型的收斂速度和性能。
- 權(quán)重初始化:嘗試不同的權(quán)重初始化方法,以幫助模型更快地收斂。
- 正則化技術(shù):使用如Dropout、L2正則化等技術(shù)來減少過擬合。
- 數(shù)據(jù)增強(qiáng):通過對訓(xùn)練圖像進(jìn)行隨機(jī)變換(如旋轉(zhuǎn)、縮放、裁剪等),可以增加模型的泛化能力。
- 更深的網(wǎng)絡(luò):考慮增加更多的卷積層和池化層來提取更復(fù)雜的特征。
- 批量歸一化:在卷積層之后添加批量歸一化層,以穩(wěn)定訓(xùn)練過程并加速收斂。
結(jié)論
通過本博客,我們展示了如何使用PyTorch框架構(gòu)建一個(gè)簡單的CNN模型,并在CIFAR-10數(shù)據(jù)集上進(jìn)行訓(xùn)練和測試。雖然我們的模型結(jié)構(gòu)相對簡單,但它為理解深度學(xué)習(xí)和圖像分類提供了一個(gè)很好的起點(diǎn)。在下一篇博客中,我們將嘗試不斷優(yōu)化模型結(jié)構(gòu)和訓(xùn)練過程,以達(dá)到更高的準(zhǔn)確率和性能。文章來源地址http://www.zghlxwxcb.cn/news/detail-819989.html
到了這里,關(guān)于【PyTorch】使用PyTorch創(chuàng)建卷積神經(jīng)網(wǎng)絡(luò)并在CIFAR-10數(shù)據(jù)集上進(jìn)行分類的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!