終于把卷積神經(jīng)網(wǎng)絡(luò)算法搞懂了??!
大家好,我是小寒
今天給大家介紹一個(gè)強(qiáng)大的算法模型,卷積神經(jīng)網(wǎng)絡(luò)算法
卷積神經(jīng)網(wǎng)絡(luò)( CNN)是一種用于圖像識(shí)別和處理的深度學(xué)習(xí)模型,其核心思想是通過(guò)卷積運(yùn)算自動(dòng)提取圖像中的局部特征,從而實(shí)現(xiàn)對(duì)圖像的高效處理和分類。
CNN 的設(shè)計(jì)靈感來(lái)源于生物學(xué)中的視覺皮層,能夠有效地捕捉圖像的空間和局部特征,具有較好的平移、縮放不變性。
圖片
神經(jīng)網(wǎng)絡(luò)的基本結(jié)構(gòu)
卷積神經(jīng)網(wǎng)絡(luò)由若干層組成,通常包括:
- 輸入層:負(fù)責(zé)接收原始輸入數(shù)據(jù),例如圖像。
- 卷積層:通過(guò)卷積核(過(guò)濾器)進(jìn)行局部感知,提取特征。每個(gè)卷積核掃描輸入圖像的不同區(qū)域,生成特征映射。
- 非線性激活層:通常使用 ReLU 激活函數(shù),添加非線性能力。
- 池化層:也稱為下采樣層,通常使用最大池化(Max Pooling)或平均池化(Average Pooling),用于減少特征圖的尺寸,降低計(jì)算復(fù)雜度。
- 全連接層:類似于傳統(tǒng)的神經(jīng)網(wǎng)絡(luò)層,用于將提取到的特征映射到輸出空間。
- 輸出層:根據(jù)任務(wù)類型選擇輸出函數(shù),分類任務(wù)中常用 Softmax 函數(shù)輸出類別概率。
1.輸入層
輸入層的主要功能是接收來(lái)自外部的數(shù)據(jù),通常為圖像數(shù)據(jù)。
在圖像處理中,輸入層接收的圖像通常是三維張量,包括寬度、高度和顏色通道。
例如,一個(gè) 28x28 的灰度圖像在輸入層表示為形狀為 (28, 28, 1) 的三維張量,其中 28 表示寬度和高度,1 表示通道數(shù)。對(duì)于 RGB 彩色圖像,通道數(shù)為 3,形狀為 (28, 28, 3)。
2.卷積層
卷積層是 CNN 中最重要的層,用于從輸入數(shù)據(jù)中提取特征。
卷積層的核心是卷積核(filter,也叫 kernel),它是一個(gè)小的矩陣(通常為 3x3 或 5x5),通過(guò)在輸入數(shù)據(jù)上滑動(dòng)(移動(dòng)窗口),計(jì)算局部區(qū)域的加權(quán)和,生成特征圖(feature map)。
這些卷積核在整個(gè)輸入數(shù)據(jù)上共享參數(shù),這使得卷積層能夠有效提取局部模式(如邊緣、角點(diǎn)和紋理),并且參數(shù)數(shù)量較少。
圖片
工作原理
卷積操作的工作過(guò)程如下。
- 卷積核從輸入圖像的左上角開始,對(duì)輸入圖像的局部區(qū)域進(jìn)行加權(quán)求和。
- 卷積核以步長(zhǎng)(stride)的形式滑動(dòng),通過(guò)滑動(dòng)窗口逐一計(jì)算每個(gè)局部區(qū)域的加權(quán)和,生成一個(gè)新的輸出特征圖。
- 每個(gè)卷積核負(fù)責(zé)提取不同的特征,一個(gè)卷積層通常有多個(gè)卷積核,生成多個(gè)不同的特征圖。
超參數(shù)
- 卷積核大小:卷積核的大小決定了感受野的大小,常見的尺寸為 3x3 或 5x5。
- 步長(zhǎng)(Stride):步長(zhǎng)決定了卷積核在輸入數(shù)據(jù)上移動(dòng)的步幅,步長(zhǎng)越大,輸出特征圖的尺寸越小。
- 填充(Padding):為了保持輸出特征圖的大小,通常在輸入圖像邊界處填充0。這可以控制輸出的尺寸,并避免輸入尺寸縮小過(guò)快。
如下圖所示,對(duì)于大小為 7x7x3 的輸入,應(yīng)用兩個(gè)卷積核,步長(zhǎng)(Stride)為1并且使用了填充。
圖片
3.非線性激活層
卷積層提取了局部特征,但卷積本質(zhì)上是線性運(yùn)算。為了增強(qiáng)模型的表達(dá)能力,CNN 在每個(gè)卷積層后通常會(huì)加入非線性激活函數(shù),允許模型學(xué)習(xí)和表示更復(fù)雜的特征。
常用的激活函數(shù)包括 ReLU、Sigmoid 和 Tanh,其中 ReLU 是最常見的激活函數(shù)。
ReLU 的公式為:
它的作用是將所有負(fù)值置為 0,而正值保持不變。這種簡(jiǎn)單的非線性函數(shù)在實(shí)踐中表現(xiàn)出良好的收斂性,且能夠緩解梯度消失問題。
4.池化層
池化層(也稱為下采樣層)用于對(duì)卷積層的輸出特征圖進(jìn)行降采樣,從而減少特征圖的大小,降低計(jì)算量和參數(shù)量。同時(shí)池化層保留了最重要的特征,提高模型對(duì)平移、縮放等變換的魯棒性。
常見的池化方法有最大池化和平均池化。
- 最大池化(Max Pooling)
最常用的池化方法。在局部區(qū)域內(nèi)取最大值作為該區(qū)域的輸出。
例如,對(duì)于一個(gè) 2x2 的局部區(qū)域,最大池化會(huì)輸出這四個(gè)值中的最大值。 - 平均池化(Average Pooling)
在局部區(qū)域內(nèi)取平均值作為輸出。
圖片
5.全連接層
全連接層與傳統(tǒng)的神經(jīng)網(wǎng)絡(luò)層相似,所有輸入節(jié)點(diǎn)與輸出節(jié)點(diǎn)之間是全連接的。
全連接層位于卷積層和池化層之后,通常用于將提取到的高維特征映射到分類空間。
例如,經(jīng)過(guò)多次卷積和池化操作后,特征圖被展平(Flatten)為一維向量,傳遞給全連接層進(jìn)行分類或回歸任務(wù)。
圖片
6.輸出層
輸出層是網(wǎng)絡(luò)的最后一層,根據(jù)任務(wù)的不同,輸出層的設(shè)計(jì)也會(huì)有所不同。
在分類任務(wù)中,輸出層通常使用 Softmax 激活函數(shù),它將全連接層的輸出轉(zhuǎn)換為每個(gè)類別的概率分布。
Softmax 的輸出值在 0 到 1 之間,并且所有類別的概率和為 1。
案例分享
以下是一個(gè)使用 PyTorch 實(shí)現(xiàn)的卷積神經(jīng)網(wǎng)絡(luò)進(jìn)行 Fashion-MNIST 數(shù)據(jù)集分類的示例代碼。
Fashion-MNIST 是一個(gè)圖像分類數(shù)據(jù)集,包含 10 個(gè)類別的時(shí)尚物品圖片,每張圖片是 28x28 的灰度圖像。
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torchvision import datasets, transforms
# 定義卷積神經(jīng)網(wǎng)絡(luò)模型
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
# 第一層卷積,輸入1通道(灰度圖像),輸出32通道,卷積核大小3x3
self.conv1 = nn.Conv2d(1, 32, 3, 1)
# 第二層卷積,輸入32通道,輸出64通道,卷積核大小3x3
self.conv2 = nn.Conv2d(32, 64, 3, 1)
# 池化層,窗口大小2x2
self.pool = nn.MaxPool2d(2)
# 全連接層,將池化后的特征展平后,輸入為64*12*12,輸出為128
self.fc1 = nn.Linear(64 * 12 * 12, 128)
# 全連接層,輸出10個(gè)類別
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = F.relu(self.conv1(x))
x = F.relu(self.conv2(x))
x = self.pool(x)
x = x.view(-1, 64 * 12 * 12)
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,))
])
# 加載 Fashion-MNIST 數(shù)據(jù)集
train_dataset = datasets.FashionMNIST('.', train=True, download=True, transform=transform)
test_dataset = datasets.FashionMNIST('.', train=False, download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=1000, shuffle=False)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = CNN().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
def train(model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = criterion(output, target)
loss.backward()
optimizer.step()
if batch_idx % 100 == 0:
print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)}]\tLoss: {loss.item():.6f}')
# 測(cè)試函數(shù)
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += criterion(output, target).item()
pred = output.argmax(dim=1, keepdim=True)
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
accuracy = 100. * correct / len(test_loader.dataset)
print(f'\nTest set: Average loss: {test_loss:.4f}, Accuracy: {correct}/{len(test_loader.dataset)} ({accuracy:.2f}%)\n')
# 訓(xùn)練模型
epochs = 10
for epoch in range(1, epochs + 1):
train(model, device, train_loader, optimizer, epoch)
test(model, device, test_loader)