前言
? 圖像回歸主要是對全連接進行理解 而圖像分類任務主要是對卷積的過程進行理解 這一部分會介紹一些基礎的概念 卷積的過程(包括單通道和多通道) 理解一個卷積神經網絡工作的過程 以及常見的模型的類別和創新點?
圖像分類是什么
定義
? 圖像分類是指將輸入的圖像分配到一個或多個預定義的類別中的任務。簡單來說,就是讓計算機“看”一張圖片,然后告訴用戶這張圖片屬于什么類別。例如,判斷一張圖片是貓、狗還是汽車等。
與回歸任務的區別
-
回歸和分類的主要區別在于目標變量的類型(連續 vs. 離散)。
-
回歸關注的是預測具體的數值,而分類關注的是將數據劃分到不同的類別中。
-
回歸解決函數擬合的問題 而回歸任務注重決策邊界劃分的問題
訓練流程的區別?
圖像分類與圖像回歸輸出的對比
特性 | 圖像分類 | 圖像回歸 |
---|---|---|
輸出類型 | 類別標簽或概率分布 | 連續數值 |
輸出層激活函數 | Softmax(多類別)或Sigmoid(二分類) | 線性(無激活函數) |
損失函數 | 交叉熵損失 | 均方誤差(MSE)、平均絕對誤差(MAE) |
應用場景 | 判斷圖像內容(如貓/狗分類、疾病診斷) | 預測連續數值(如物體大小、位置、年齡) |
輸出示例 | “貓” 或 “貓的概率為0.8,狗的概率為0.2” | “寬度為120像素” 或 “[100, 150, 20 |
評價的區別
卷積神經網絡架構
一個典型的CNN結構通常按照以下順序堆疊:
-
輸入層:輸入圖像或特征圖。
-
卷積層 + 激活層(可選):提取局部特征。
-
池化層(可選):降低特征圖尺寸。
-
重復步驟2和3:通過多層卷積和池化提取更高級的特征。
-
歸一化層(可選):對特征進行歸一化處理。
-
Dropout層(可選):防止過擬合。
-
全連接層:整合特征,輸出最終結果。
-
輸出層:根據任務類型,輸出分類概率(Softmax)或回歸值。
卷積神經網絡(CNN)相關概念
1. 卷積運算
卷積運算是CNN的核心操作,用于從輸入數據中提取特征。它通過卷積核(濾波器)在輸入數據上滑動并計算點積,生成特征圖(Feature Map)。
2. 輸入通道
輸入通道是指輸入圖像的維度。例如:
-
對于灰度圖像,輸入通道為1。
-
對于RGB圖像,輸入通道為3(分別對應紅、綠、藍三個顏色通道)。
3. 輸出通道
輸出通道是指卷積層輸出的特征圖數量。每個卷積核會生成一個特征圖,因此輸出通道的數量等于卷積核的數量。
4. 卷積核(濾波器)
卷積核是一個小的矩陣,用于在輸入數據上滑動并計算卷積。卷積核的大小(如3×3、5×5)和數量決定了卷積層的復雜性和特征提取能力。
5. 特征圖(Feature Map)
特征圖是卷積運算的輸出,表示輸入數據在某個特定卷積核下的特征表示。每個特征圖對應一個卷積核。
6. Padding(填充)
填充是在輸入數據的邊界添加額外的零值像素,用于控制特征圖的大小。常見的填充方式包括:
-
無填充(Valid Padding):不添加任何填充,特征圖的大小會減小。
-
同態填充(Same Padding):添加填充使特征圖的大小與輸入數據相同。
控制輸出大小:填充可以靈活調整輸出特征圖的空間維度。
6.1 填充是什么
填充是指在輸入圖像或特征圖的邊緣添加額外的像素(通常為0),以控制卷積操作后的輸出尺寸。在每張圖的長寬上填充 左右上下都要填充
6.2填充的作用
-
保留邊界信息:在沒有填充的情況下,卷積操作會使輸出特征圖的尺寸逐漸減小,導致邊緣信息丟失。
-
保持輸入輸出尺寸一致:通過填充,可以使卷積后的特征圖與輸入圖像保持相同的尺寸。
6.3填充的方式
-
零填充(Zero Padding):在輸入圖像周圍添加零值像素。這是最常用的填充方式,因為它簡單且計算效率高。
-
邊緣復制填充(Edge Replication Padding):將輸入圖像邊緣的像素復制到填充區域。
-
鏡像填充(Mirror Padding):將輸入圖像邊緣的像素進行鏡像翻轉后填充。
7. 步長(Stride)
步長是卷積核在輸入數據上滑動的步數。步長為1表示卷積核每次移動一個像素;步長為2表示每次移動兩個像素。較大的步長會導致特征圖尺寸減小。
7.1 步長是什么
步長是指卷積核在輸入圖像上滑動的步數。步長越大,卷積核滑動越快,輸出特征圖的尺寸越小。
7.2 不同步長的作用
-
控制輸出尺寸:較大的步長可以快速減小特征圖的尺寸,減少計算量。
-
增加感受野:較大的步長可以使卷積核覆蓋更廣的區域,從而增加模型的感受野
8. 卷積過程計算
卷積過程是通過卷積核在輸入數據上滑動并計算點積來完成的。
9. 多通道卷積
在多通道輸入(如RGB圖像)中,卷積核會同時作用于所有通道,并將結果相加生成一個特征圖。例如,對于一個3×3×3的卷積核和一個5×5×3的輸入數據,卷積核會分別與每個通道進行卷積運算,然后將結果相加。
池化層(Pooling Layer)
1. 池化層是什么?
池化層用于降低特征圖的空間維度,減少計算量并提取重要特征。常見的池化操作包括:
-
最大池化(Max Pooling):取池化窗口內的最大值。
-
平均池化(Average Pooling):取池化窗口內的平均值。
2. 為什么使用池化層?
-
降低計算量:減少特征圖的尺寸。
-
提取重要特征:保留關鍵信息,去除冗余信息。
-
增強模型的平移不變性:對輸入數據的小范圍平移不敏感。
3. 池化層工作原理?
池化層通常在卷積層之后使用,窗口大小和步長是其主要參數。例如,一個2×2的最大池化層,步長為2,會將特征圖的尺寸減半。
歸一化技術
1. 歸一化是什么?
歸一化是將輸入數據或特征值調整到一個統一的范圍(如0到1或-1到1)。常見的歸一化技術包括:
-
Min-Max Scaling:將數據縮放到0到1之間。
-
Z-Score標準化:將數據轉換為均值為0、標準差為1的分布。
-
Batch Normalization:在訓練過程中對每個小批量數據進行歸一化,加速訓練并提高模型性能。
2. 為什么使用歸一化?
-
加速訓練:歸一化后的數據分布更均勻,有助于優化算法更快收斂。
-
提高模型性能:避免某些特征對模型的影響過大。
-
減少數值不穩定:避免梯度爆炸或消失問題。
3. 怎么辦?
-
數據預處理階段:對輸入圖像進行歸一化處理。
-
模型訓練階段:使用Batch Normalization等技術對中間層的特征進行歸一化。
損失函數與交叉熵損失
1. 損失函數的定義
損失函數(Loss Function)是機器學習和深度學習中用于衡量模型預測值與真實值之間差異的函數。它的目的是通過優化模型的參數,使損失值最小化,從而提高模型的預測準確性。
2. 交叉熵損失的定義
交叉熵損失(Cross-Entropy Loss)是深度學習中分類問題常用的損失函數,特別適用于多分類任務。它通過度量預測分布與真實分布之間的差異來衡量模型輸出的準確性。
3. 交叉熵損失的數學公式
-
二分類問題:假設真實標簽 y∈{0,1},預測概率為 y^?,則交叉熵損失公式為:
當真實類別 y=1 時,若模型預測 y^? 接近 1(正確預測),損失接近 0;若預測接近 0(錯誤預測),損失接近無窮大。
-
多分類問題:假設 K 為類別數,y 為真實類別(one-hot 編碼),y^?i? 為第 i 類的預測概率,則交叉熵損失公式為:
由于 one-hot 編碼中只有真實類別的 yi?=1,其余為 0,因此公式可以簡化為:
其中 c 是真實類別的索引。
4. 交叉熵損失與 Softmax 的關系
交叉熵損失通常與 Softmax 函數結合使用。Softmax 函數將模型輸出的 logits(未歸一化的分數)轉換為概率分布:
其中 zi? 是第 i 類的 logits。
5. 交叉熵損失的應用
交叉熵損失廣泛應用于分類任務,如圖像分類、文本分類等。它能夠對錯誤的高置信度預測施加較大的懲罰,從而促進模型學習更準確的概率分布。
交叉熵損失 是分類問題中的核心工具,用于衡量預測分布與真實分布之間的差異。
它通過 Softmax 函數將 logits 轉換為概率分布,然后計算損失。
常見網絡架構
AlexNet
AlexNet的創新點
-
ReLU激活函數:引入了修正線性單元(ReLU)激活函數,它能夠加速網絡的訓練并提高模型的泛化能力。
-
Dropout:使用了Dropout技術來防止過擬合,通過在訓練過程中隨機丟棄一部分神經元,使模型更加魯棒。
-
池化:通過池化層降低特征圖的維度,同時保留重要特征,增強模型的平移不變性。
-
歸一化:它可以讓模型關注數據的分布,而不受數據量綱的影響。 保持學習有效性, 緩解梯度消失和梯度爆炸。
實現
import torch.nn as nnclass myAlexNet(nn.Module):def __init__(self, out_dim):super(myAlexNet, self).__init__()self.conv1 = nn.Conv2d(3,64,11,4,2)self.pool1 = nn.MaxPool2d(3, 2)self.conv2 = nn.Conv2d(64,192,5,1,2)self.pool2 = nn.MaxPool2d(3, 2)self.conv3 = nn.Conv2d(192,384,3,1,1)self.conv4 = nn.Conv2d(384, 256, 3, 1, 1)self.conv5 = nn.Conv2d(256, 256, 3, 1, 1)self.pool3 = nn.MaxPool2d(3, 2)self.pool4 = nn.AdaptiveAvgPool2d(6)self.fc1 = nn.Linear(9216, 4096)self.fc2 = nn.Linear(4096, 4096)self.fc3 = nn.Linear(4096, out_dim)def forward(self,x):x =self.conv1(x)x = self.pool1(x)x =self.conv2(x)x = self.pool2(x)x =self.conv3(x)x =self.conv4(x)x = self.conv5(x)x = self.pool3(x)x = self.pool4(x)x = x.view(x.size()[0], -1) #拉直。 batchx = self.fc1(x)x = self.fc2(x)x = self.fc3(x)return xmodel = myAlexNet(1000)
VggNet
創新
用小卷積核代替大的卷積核
小卷積核能夠代替大卷積核的關鍵在于它們可以通過堆疊多層來達到相同的感受野,同時減少參數數量,增加網絡深度,提高模型的表達能力和計算效率。這種設計使得網絡更加輕量化,易于訓練和部署,同時保持或提高模型的性能。
模塊化設計
比如下面的兩次卷積 一次池化
實現
class vggLayer(nn.Module):#模塊化 def __init__(self,in_cha, mid_cha, out_cha):super(vggLayer, self).__init__()self.relu = nn.ReLU() # 定義激活函數self.pool = nn.MaxPool2d(2) # 定義池化方式self.conv1 = nn.Conv2d(in_cha, mid_cha, 3, 1, 1) # 定義卷積核self.conv2 = nn.Conv2d(mid_cha, out_cha, 3, 1, 1) # 定義卷積核def forward(self,x):x = self.conv1(x)x= self.relu(x)x = self.conv2(x)x = self.relu(x)x = self.pool(x)return xclass MyVgg(nn.Module):def __init__(self): # 定義結構super(MyVgg, self).__init__()self.layer1 = vggLayer(3, 64, 64)self.layer2 = vggLayer(64, 128, 128)self.layer3 = vggLayer(128, 256, 256)self.layer4 = vggLayer(256, 512, 512)self.layer5 = vggLayer(512, 512, 512)self.adapool = nn.AdaptiveAvgPool2d(7)self.relu = nn.ReLU()self.fc1 = nn.Linear(25088, 4096)self.fc2 = nn.Linear(4096, 4096)self.fc3 = nn.Linear(4096, 1000)def forward(self,x):x = self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)x = self.layer5(x)x = self.adapool(x)x = x.view(x.size()[0], -1)x = self.fc1(x)x = self.relu(x)x = self.fc2(x)x = self.relu(x)x = self.fc3(x)x = self.relu(x)return x
ResNet
創新點
沒問題,我們可以用更簡單的方式來解釋1x1卷積和殘差連接(Residual Connection),這樣即使是初學者也能理解。
1x1卷積
想象一下,你有一堆彩色的畫筆(代表圖像中的顏色通道),每個畫筆都有不同的顏色。1x1卷積就像是一個神奇的畫筆混合器,它不改變畫筆的數量(也就是圖像的寬度和高度),但可以改變畫筆的顏色組合(也就是改變圖像的通道數)。
-
為什么使用1x1卷積?
-
減少計算量:如果圖像有很多通道,1x1卷積可以減少通道的數量,從而減少后續計算的復雜度。
-
增加非線性:雖然1x1卷積本身是線性操作,但通常我們會在它后面加上一個非線性激活函數(比如ReLU),這樣可以增加模型的表達能力。
-
殘差連接(Residual Connection)
現在,想象你正在學習畫一幅畫。殘差連接就像是你有一個基礎的畫(輸入),然后你嘗試在這幅畫上添加更多的細節(通過神經網絡的層)。但是,有時候添加的細節太多,反而讓畫變得混亂。
-
什么是殘差連接?
-
直接連接:殘差連接允許你將基礎的畫(輸入)直接復制一份,然后和添加的細節(通過神經網絡層處理后的輸出)放在一起。這樣,即使添加的細節不完美,最終的畫也不會太差,因為基礎的畫還在。
-
解決梯度消失問題:在深層神經網絡中,信息從一層傳遞到另一層時可能會丟失(梯度消失)。殘差連接通過直接將輸入傳遞到后面的層,幫助信息更順暢地流動,從而解決這個問題。
-
-
為什么使用殘差連接?
-
更容易訓練深層網絡:殘差連接使得訓練非常深的神經網絡變得更容易,因為信息可以更容易地從一層傳遞到另一層。
-
提高性能:在很多情況下,使用殘差連接的網絡比沒有使用殘差連接的網絡表現得更好。
-
總結
-
1x1卷積:是一種特殊的卷積操作,可以改變圖像的通道數,但不改變圖像的寬度和高度。它常用于減少計算量和增加非線性。
-
殘差連接:是一種網絡結構設計,允許輸入直接跳過一些層,與后面的層的輸出相加。它有助于解決深層網絡訓練中的梯度消失問題,并提高網絡的性能。
希望這個解釋能幫助你更好地理解這兩個概念!如果你還有其他問題,隨時可以問。
實現
class Residual_block(nn.Module): #@savedef __init__(self, input_channels, out_channels, down_sample=False, strides=1):super().__init__()self.conv1 = nn.Conv2d(input_channels, out_channels,kernel_size=3, padding=1, stride=strides)self.conv2 = nn.Conv2d(out_channels, out_channels,kernel_size=3, padding=1, stride= 1)if input_channels != out_channels:self.conv3 = nn.Conv2d(input_channels, out_channels,kernel_size=1, stride=strides)else:self.conv3 = Noneself.bn1 = nn.BatchNorm2d(out_channels)self.bn2 = nn.BatchNorm2d(out_channels)self.relu = nn.ReLU()def forward(self, X):out = self.relu(self.bn1(self.conv1(X)))out= self.bn2(self.conv2(out))if self.conv3:X = self.conv3(X)out += Xreturn self.relu(out)class MyResNet18(nn.Module):def __init__(self):super(MyResNet18, self).__init__()self.conv1 = nn.Conv2d(3, 64, 7, 2, 3)self.bn1 = nn.BatchNorm2d(64)self.pool1 = nn.MaxPool2d(3, stride=2, padding=1)self.relu = nn.ReLU()self.layer1 = nn.Sequential(Residual_block(64, 64),Residual_block(64, 64))self.layer2 = nn.Sequential(Residual_block(64, 128, strides=2),Residual_block(128, 128))self.layer3 = nn.Sequential(Residual_block(128, 256, strides=2),Residual_block(256, 256))self.layer4 = nn.Sequential(Residual_block(256, 512, strides=2),Residual_block(512, 512))self.flatten = nn.Flatten()self.adv_pool = nn.AdaptiveAvgPool2d(1)self.fc = nn.Linear(512, 1000)def forward(self, x):x = self.conv1(x)x = self.bn1(x)x = self.relu(x)x = self.pool1(x)x = self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)x = self.adv_pool(x)x = self.flatten(x)x = self.fc(x)return xmyres = MyResNet18()