文章目錄
- 一、LeNet-5網絡結構簡介
- 二、LeNet-5每一層的實現原理
- 2.1. 第一層 (C1) :卷積層(Convolution Layer)
- 2.2. 第二層 (S2) :池化層(Pooling Layer)
- 2.3. 第三層(C3):卷積層(Convolution Layer)
- 2.4. 第四層(S4):池化層(Pooling Layer)
- 2.5. 第五層(C5):卷積層(全連接卷積層)
- 2.6. 第六層(F6):全連接層(Fully Connected Layer)
- 2.7. 第七層(Output):全連接輸出層(Softmax分類層)
- 三、matlab實現LeNet-5
- 四、訓練測試網絡
LeNet-5是一種經典的卷積神經網絡(CNN),由Yann LeCun等人提出,最初用于手寫數字識別任務。它的結構簡單清晰,為CNN的發展奠定了基礎。
一、LeNet-5網絡結構簡介
LeNet-5的網絡結構如下圖所示
詳細參數:
層名 | 類型 | 輸入尺寸 | 尺寸 | 輸出尺寸 | 激活函數 |
---|---|---|---|---|---|
輸入 | 圖片 | 28×28×1 | - | 32×32×1 | |
C1 | 卷積 | 28×28×1,padding為2 | 5×5, 6個卷積核,步長為1 | 28×28×6 | relu |
S2 | 池化 | 28×28×6 | 2×2 池化,步長為2 | 14×14×6 | |
C3 | 卷積 | 14×14×6,padding為0 | 5×5, 16個卷積核 | 10×10×16 | relu |
S4 | 池化 | 10×10×16 | 2×2 池化,步長為2 | 5×5×16 | |
C5 | 卷積(全連接卷積) | 5×5×16 | 5×5, 120個卷積核 | 120×1 | relu |
F6 | 全連接層 | 120 | 全連接 | 84×1 | relu |
輸出 | 全連接層 | 84 | 全連接 | 10 (數字類別) | softmax |
二、LeNet-5每一層的實現原理
2.1. 第一層 (C1) :卷積層(Convolution Layer)
輸入: 一張灰度圖像,尺寸為 28 × 28 × 1 28\times28\times1 28×28×1。
參數:
- 卷積核尺寸為 5 × 5 5\times5 5×5,共 6個卷積核。
- 偏置項 (bias):每個卷積核對應一個獨立的偏置值,因此共有6個偏置值。
輸出: 經過卷積后的特征圖,共6個,尺寸為 28 × 28 × 6 28\times28\times6 28×28×6。
(1)卷積計算原理:
卷積層本質上是將卷積核(濾波器)與輸入圖像進行卷積操作:
Output ( x , y ) = f ( ∑ i = 1 5 ∑ j = 1 5 Kernel ( i , j ) × Input ( x + i , y + j ) + b ) \text{Output}(x,y) = f\left(\sum_{i=1}^{5}\sum_{j=1}^{5}\text{Kernel}(i,j)\times\text{Input}(x+i,y+j)+b\right) Output(x,y)=f(i=1∑5?j=1∑5?Kernel(i,j)×Input(x+i,y+j)+b)
- 這里:
- Kernel ( i , j ) \text{Kernel}(i,j) Kernel(i,j):表示卷積核的第 i i i行、第 j j j列元素的數值。
- Input ( x + i , y + j ) \text{Input}(x+i,y+j) Input(x+i,y+j):表示輸入圖像中對應位置上的數值。
- b b b:表示該卷積核的偏置項(bias)。
- f ( ? ) f(\cdot) f(?):激活函數(例如sigmoid或tanh函數或relu函數),推薦使用relu函數。
上面公式中求和部分的計算原理可以通過下面簡單的例子理解
(2)輸出特征圖尺寸的計算:
卷積后輸出尺寸計算公式為:
Output?Size = ( Input?Size ? Kernel?Size ) + 2 × Padding?Size Stride + 1 \text{Output Size} = \frac{(\text{Input Size}-\text{Kernel Size})+2\times\text{Padding Size}}{\text{Stride}} + 1 Output?Size=Stride(Input?Size?Kernel?Size)+2×Padding?Size?+1
- 輸入:28×28,卷積核:5×5,Stride(步長)=1,padding=2時,計算結果為:
( 28 ? 5 ) + 2 × 2 1 + 1 = 28 \frac{(28 - 5)+2\times2}{1} + 1 = 28 1(28?5)+2×2?+1=28
- 因此,輸出特征圖為28×28,6個卷積核則輸出為28×28×6。
對于padding的理解,可以通過下面簡單的例子理解
當padding為0時
當padding為2時
2.2. 第二層 (S2) :池化層(Pooling Layer)
輸入: C1層輸出特征圖,共6個,尺寸為 28 × 28 × 6 28\times28\times6 28×28×6。
參數:
- 池化(Pooling)窗口大小: 2 × 2 2\times2 2×2,步長(Stride)= 2。
- 無需額外參數,無權值,通常采用平均池化或最大池化,推薦使用最大池化。
輸出: 池化后的特征圖,共6個,尺寸為 14 × 14 × 6 14\times14\times6 14×14×6。
(1)池化計算原理:
池化層的作用是降低特征圖的尺寸,同時提取特征的重要信息,增強網絡的泛化能力。
常見池化方法:
-
平均池化(Average Pooling): 取池化窗口區域內所有像素值的平均值作為輸出。
例如窗口區域:
[ 2 4 6 8 ] \begin{bmatrix} 2 & 4\\ 6 & 8 \end{bmatrix} [26?48?]
平均池化的結果為:
2 + 4 + 6 + 8 4 = 5 \frac{2+4+6+8}{4}=5 42+4+6+8?=5 -
最大池化(Max Pooling): 取池化窗口區域內的最大像素值作為輸出。
以上面的區域為例:
max ? { 2 , 4 , 6 , 8 } = 8 \max\{2,4,6,8\}=8 max{2,4,6,8}=8
LeNet-5原論文中使用的是平均池化。
一個圖形化的例子如下所示
(2)輸出特征圖尺寸的計算:
池化后輸出尺寸計算公式為:
Output?Size = Input?Size ? Pooling?Size Stride + 1 \text{Output Size} = \frac{\text{Input Size}-\text{Pooling Size}}{\text{Stride}} + 1 Output?Size=StrideInput?Size?Pooling?Size?+1
- 輸入:28×28,池化窗口:2×2,Stride=2時,計算結果為:
28 ? 2 2 + 1 = 14 \frac{28-2}{2}+1=14 228?2?+1=14
- 所以S2層輸出特征圖為 14 × 14 × 6 14\times14\times6 14×14×6。
2.3. 第三層(C3):卷積層(Convolution Layer)
輸入: 第二層(S2)輸出,尺寸為 14 × 14 × 6 14 \times 14 \times 6 14×14×6。
參數:
- 卷積核大小為 5 × 5 5\times 5 5×5,共 16個卷積核。
- 偏置(bias):每個卷積核一個偏置,共16個偏置。
這里卷積的計算需要注意的是,輸入是多個通道了,這種情況下的計算可以通過下面的圖示理解
一個簡單的計算示例如下,這里輸入是 4 × 4 × 3 4\times 4\times3 4×4×3,卷積核大小為 3 × 3 3\times3 3×3,但是也要是三通道
2.4. 第四層(S4):池化層(Pooling Layer)
輸入: 第三層(C3)輸出,尺寸為 10 × 10 × 16 10\times10\times16 10×10×16。
參數:
- 池化窗口大小: 2 × 2 2\times2 2×2,步長: 2 2 2。
- LeNet-5使用平均池化(Average Pooling),輸出特征圖尺寸為: 5 × 5 × 16 5\times5\times16 5×5×16。
2.5. 第五層(C5):卷積層(全連接卷積層)
輸入:第四層(S4)輸出特征圖,尺寸為 5 × 5 × 16 5\times5\times16 5×5×16。
參數:
- 卷積核尺寸: 5 × 5 5\times5 5×5
- 卷積核數量:120個
- 偏置數量:120個(每個卷積核對應1個偏置)
由于輸入特征圖大小為 5 × 5 × 16 5\times5\times 16 5×5×16,而卷積核也為 5 × 5 5\times5 5×5,因此每個卷積核與輸入特征圖卷積后會變成 1 × 1 1\times1 1×1 的輸出,這相當于對整個特征圖做了全連接卷積(即卷積核與整個輸入特征圖區域進行完全連接)。因此,這一層又稱為 全連接卷積層。
2.6. 第六層(F6):全連接層(Fully Connected Layer)
輸入:第五層(C5)輸出,共120個神經元(1維向量)。
參數:
- 全連接層的神經元數量:84個
- 權重數量: 84 × 120 84\times120 84×120(120個輸入與84個神經元全連接)
- 偏置數量:84個(每個神經元一個偏置)
全連接層的計算本質為矩陣乘法+偏置+激活函數:
- 計算公式:
Output = f ( W × Input ( 120 × 1 ) + b ) \text{Output} = f\left(W \times \text{Input}_{(120\times1)}+b\right) Output=f(W×Input(120×1)?+b)
其中:
- W W W:是一個大小為 84 × 120 84\times120 84×120 的權重矩陣
- b b b:大小為 84 × 1 84\times1 84×1 偏置向量
- f f f:激活函數(如tanh或sigmoid)
- 輸出為84維的向量,即大小為: 84 × 1 84\times1 84×1。
2.7. 第七層(Output):全連接輸出層(Softmax分類層)
輸入:第六層(F6)輸出的84個神經元(向量)。
參數:
- 輸出類別:10個類別(0-9手寫數字)
- 權重數量: 10 × 84 10\times84 10×84(84個輸入與10個輸出神經元全連接)
- 偏置數量:10個(每個輸出神經元對應1個偏置)
最后一層通常是全連接層,并使用softmax函數作為分類器:
- 計算公式:
Z j = ∑ i = 1 84 w j , i x i + b j , j = 1 , 2 , . . . , 10 Z_j = \sum_{i=1}^{84}w_{j,i}x_i + b_j,\quad j=1,2,...,10 Zj?=i=1∑84?wj,i?xi?+bj?,j=1,2,...,10
其中:
- Z j Z_j Zj?:第 j j j個類別的線性輸出值
- w j , i w_{j,i} wj,i?:第 j j j個輸出神經元與第 i i i個輸入神經元的權重
- b j b_j bj?:第 j j j個輸出神經元的偏置
計算Softmax概率:
y j = e Z j ∑ k = 1 10 e Z k y_j = \frac{e^{Z_j}}{\sum_{k=1}^{10}e^{Z_k}} yj?=∑k=110?eZk?eZj??
- y j y_j yj?:為第 j j j 類別的概率值,所有 y j y_j yj? 和為1。
- 最終預測類別即為概率最大的類別。
- 輸出尺寸為: 10 × 1 10\times1 10×1,表示10個類別的概率分布。
分析完每一層的原理后,我們就可以計算一下這個網絡的可學習參量了
第一層:卷積核尺寸為 5 × 5 5\times5 5×5,共 6個卷積核,考慮到一個卷積核對應一個偏置量,所以
5 × 5 × 6 + 6 = 156 5\times5\times6+6=156 5×5×6+6=156
第二層:池化層無需要學習參量
第三層:卷積核大小為 5 × 5 5\times 5 5×5,共 16個卷積核,因為這一層的輸入是6個通道,并考慮到偏置量,所以
5 × 5 × 6 × 16 + 16 = 2416 5\times5\times6\times16+16=2416 5×5×6×16+16=2416
第四層:池化層無需要學習參量
第五層:卷積核大小為 5 × 5 5\times 5 5×5,共 120個卷積核,因為這一層的輸入是16個通道,并考慮到偏置量,所以
5 × 5 × 16 × 120 + 120 = 48120 5\times5\times16\times120+120=48120 5×5×16×120+120=48120
第六層:學習參數為權重數量加偏置,所以
84 × 120 + 84 = 10164 84\times120+84=10164 84×120+84=10164
第七層:學習參數為權重數量加偏置,所以
10 × 84 + 10 = 850 10\times84+10=850 10×84+10=850
所以,這個網絡的總學習參數為
156 + 2416 + 48120 + 10164 + 850 = 61706 156+2416+48120+10164+850=61706 156+2416+48120+10164+850=61706
三、matlab實現LeNet-5
這里,我們需要借用到matlab工具欄里APPS里的Deep Network Designer,如下圖所示
在Deep Network Designer, 我們創建一個空白Designer畫布
然后我們可以拖動相應的層到Designer里,并連接各個層,如下圖所示
然后,我們需要設置各個層的參量,對于輸入圖像來說
第一層:
第二層:注意到第一層和二層之間有一個Batchnormalization和激活函數,Batchnormalization一般是為了讓訓練效果更好,所有的卷積層和全連接層后一般都跟激活函數
第三層:
第四層:
第五層:
第六層:
第七層:

設置完成后,我們就可以用Analyze這個按鈕分析這個網絡了,分析內容很詳細,可以看到這個網絡的可學習參量為61.7k,跟我們上面計算的結果相同。至此,我們就完成了LeNet-5的搭建。
然后用Export按鈕輸出到工作區,這個網絡的名稱會自動命名為net_1。
四、訓練測試網絡
加載圖像數據集
dataFolder = "DigitsData"; % 指定圖像數據所在的文件夾路徑
imds = imageDatastore(dataFolder, ...IncludeSubfolders=true, ... % 包括子文件夾中的圖像LabelSource="foldernames"); % 使用文件夾名稱作為圖像的標簽
imageDatastore
用于批量加載圖像,并自動將圖像按文件夾名稱分類(用于監督學習)。
可視化部分圖像
figure
tiledlayout("flow"); % 設置自適應布局以顯示多個圖像
perm = randperm(10000, 20); % 從 10000 張圖像中隨機選取 20 張
for i = 1:20nexttile % 在下一圖塊中顯示圖像imshow(imds.Files{perm(i)}); % 顯示圖像文件路徑對應的圖像
end
隨機選出 20 張圖像用于展示,驗證數據是否正確讀取和標注。
查看類別和樣本數量
classNames = categories(imds.Labels); % 提取所有類別名稱
labelCount = countEachLabel(imds); % 統計每個類別的圖像數量
有助于檢查數據是否均衡,每類是否數量相近。
查看圖像尺寸
matlab復制編輯img = readimage(imds,1); % 讀取第 1 張圖像
size(img) % 查看圖像尺寸(應該為 28×28×1)
確保圖像大小和通道數(灰度或彩色)與網絡輸入一致。
劃分訓練集、驗證集、測試集
[imdsTrain, imdsValidation, imdsTest] = splitEachLabel(imds, 0.7, 0.15, 0.15, "randomized");
從每個類別中隨機劃分出 70% 用于訓練,15% 用于驗證,15% 用于測試。
設置訓練選項
options = trainingOptions("sgdm", ...InitialLearnRate=0.01, ... % 初始學習率MaxEpochs=4, ... % 最大訓練輪數Shuffle="every-epoch", ... % 每個 epoch 打亂訓練數據ValidationData=imdsValidation, ... % 指定驗證數據集ValidationFrequency=30, ... % 每訓練 30 個 mini-batch 進行一次驗證Plots="training-progress", ... % 實時顯示訓練過程圖表Metrics="accuracy", ... % 關注準確率指標Verbose=false); % 不在命令行輸出詳細訓練信息
控制訓練過程的關鍵參數,適用于小型數據集的快速實驗。
訓練神經網絡
net = trainnet(imdsTrain, net_1, "crossentropy", options);
使用自定義網絡
net_1
和交叉熵損失函數對訓練集進行訓練,返回訓練好的網絡net
。
在測試集上評估準確率
accuracy = testnet(net, imdsTest, "accuracy");
在未見過的測試集上評估模型性能,輸出預測準確率(值范圍 0~1)。
進行預測并生成標簽
scores = minibatchpredict(net, imdsTest); % 對測試集進行批量預測,輸出每類得分
YTest = scores2label(scores, classNames); % 將得分轉換為預測的標簽
scores
是每張圖像對每個類別的得分;scores2label
會選擇得分最高的類別作為預測結果。
可視化預測結果
numTestObservations = numel(imdsTest.Files); % 測試集中圖像的總數
idx = randi(numTestObservations, 9, 1); % 隨機選取 9 個圖像索引用于展示figure
tiledlayout("flow") % 設置圖像自動排列布局
for i = 1:9nexttileimg = readimage(imdsTest, idx(i)); % 讀取圖像imshow(img) % 顯示圖像title("Predicted Class: " + string(YTest(idx(i)))) % 顯示預測類別
end
隨機從測試集中挑選 9 張圖像,并在圖像上標注預測的類別結果,可用于人工評估模型表現。
完整代碼如下
%%
dataFolder = "DigitsData"; % 指定圖像數據所在的文件夾路徑
imds = imageDatastore(dataFolder, ...IncludeSubfolders=true, ... % 包括子文件夾中的圖像LabelSource="foldernames"); % 使用文件夾名稱作為圖像的標簽figure
tiledlayout("flow"); % 設置自適應布局以顯示多個圖像
perm = randperm(10000, 20); % 從 10000 張圖像中隨機選取 20 張
for i = 1:20nexttile % 在下一圖塊中顯示圖像imshow(imds.Files{perm(i)}); % 顯示圖像文件路徑對應的圖像
end
%%
classNames = categories(imds.Labels); % 提取所有類別名稱
labelCount = countEachLabel(imds); % 統計每個類別的圖像數量%%
img = readimage(imds,1); % 讀取第 1 張圖像
size(img); % 查看圖像尺寸(應該為 28×28×1)%%
% 從每個類別中隨機劃分出 70% 用于訓練,15% 用于驗證,15% 用于測試。
[imdsTrain, imdsValidation, imdsTest] = splitEachLabel(imds, 0.7, 0.15, 0.15, "randomized");%%
% options = trainingOptions("sgdm", ...
% InitialLearnRate=0.01, ...
% MaxEpochs=4, ...
% Shuffle="every-epoch", ...
% ValidationData=imdsValidation, ...
% ValidationFrequency=30, ...
% Plots="training-progress", ...
% Metrics="accuracy", ...
% Verbose=false);
options = trainingOptions("sgdm", ...InitialLearnRate=0.01, ... % 初始學習率MaxEpochs=4, ... % 最大訓練輪數Shuffle="every-epoch", ... % 每個 epoch 打亂訓練數據ValidationData=imdsValidation, ... % 指定驗證數據集ValidationFrequency=30, ... % 每訓練 30 個 mini-batch 進行一次驗證Plots="training-progress", ... % 實時顯示訓練過程圖表Metrics="accuracy", ... % 關注準確率指標Verbose=false); % 不在命令行輸出詳細訓練信息%%
% 使用自定義網絡 net_1 和交叉熵損失函數對訓練集進行訓練,返回訓練好的網絡 net。
net = trainnet(imdsTrain,net_1,"crossentropy",options);% 在未見過的測試集上評估模型性能,輸出預測準確率(值范圍 0~1)。
accuracy = testnet(net, imdsTest, "accuracy");scores = minibatchpredict(net, imdsTest); % 對測試集進行批量預測,輸出每類得分
YTest = scores2label(scores, classNames); % 將得分轉換為預測的標簽numTestObservations = numel(imdsTest.Files); % 測試集中圖像的總數
idx = randi(numTestObservations, 9, 1); % 隨機選取 9 個圖像索引用于展示figure
tiledlayout("flow") % 設置圖像自動排列布局
for i = 1:9nexttileimg = readimage(imdsTest, idx(i)); % 讀取圖像imshow(img) % 顯示圖像title("Predicted Class: " + string(YTest(idx(i)))) % 顯示預測類別
end