介紹
卷積神經(jīng)網(wǎng)絡(luò) (CNN) 徹底改變了計(jì)算機(jī)視覺領(lǐng)域,成為圖像和視頻分析應(yīng)用的基石。在本文中,我們將深入研究使 CNN 強(qiáng)大的關(guān)鍵組件和操作,探索卷積、最大池化、步長、填充、上采樣、下采樣等概念。此外,我們將使用 Python 和流行的深度學(xué)習(xí)框架討論數(shù)據(jù)集上的簡單 CNN 模型。
卷積神經(jīng)網(wǎng)絡(luò) (CNN) 由各種類型的層組成,這些層協(xié)同工作以從輸入數(shù)據(jù)中學(xué)習(xí)分層表示。每個層在整體架構(gòu)中都發(fā)揮著獨(dú)特的作用。讓我們探索典型 CNN 中的主要層類型:
1. 輸入層 輸入層是網(wǎng)絡(luò)的初始數(shù)據(jù)輸入點(diǎn)。在基于圖像的任務(wù)中,輸入層表示圖像的像素值。在下面的示例中,我們假設(shè)我們正在處理大小為 28x28 像素的灰度圖像。
從tensorflow.keras.layersimport Input input_layer=Input(shape=(28, 28, 1))2.卷積層 卷積層是 CNN 的核心構(gòu)建塊。這些層使用可學(xué)習(xí)的濾波器對輸入數(shù)據(jù)應(yīng)用卷積運(yùn)算。這些濾波器掃描輸入,提取邊緣、紋理和圖案等特征在卷積神經(jīng)網(wǎng)絡(luò) (CNN) 中,“核”和“濾波器”這兩個術(shù)語經(jīng)?;Q使用,指的是同一個概念。讓我們來分析一下這些術(shù)語的含義:
from tensorflow.keras.layers import Conv2D conv_layer=Conv2D(filters=32, kernel_size=(3, 3), activation='relu')(input_layer)2.1核 核是卷積運(yùn)算中使用的小矩陣。它是一組可學(xué)習(xí)的權(quán)重,應(yīng)用于輸入數(shù)據(jù)以生成輸出特征圖。核是讓 CNN 自動學(xué)習(xí)輸入數(shù)據(jù)中特征的空間層次結(jié)構(gòu)的關(guān)鍵元素。在圖像處理中,核可能是 3x3 或 5x5 這樣的小矩陣。
2.2濾波器 另一方面,濾波器是一組多個內(nèi)核。在大多數(shù)情況下,卷積層使用多個濾波器來捕獲輸入數(shù)據(jù)中的不同特征。每個濾波器與輸入進(jìn)行卷積以產(chǎn)生特征圖,并且網(wǎng)絡(luò)通過在訓(xùn)練期間調(diào)整這些濾波器的權(quán)重(參數(shù))來學(xué)習(xí)提取各種模式。
在這個例子中,我們定義了一個有 32 個濾波器的卷積層,每個濾波器的內(nèi)核大小為 3x3。在訓(xùn)練期間,神經(jīng)網(wǎng)絡(luò)會調(diào)整這 32 個濾波器的權(quán)重(參數(shù)),以從輸入數(shù)據(jù)中學(xué)習(xí)不同的特征。讓我們通過一個圖像示例來看一下:
核形狀(3X3)
總之,核是在輸入數(shù)據(jù)上滑動或卷積的小矩陣,而濾波器是一組用于從輸入中提取各種特征的核,從而允許神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)分層表示。
3.激活層 (ReLU) 在卷積操作之后,逐元素應(yīng)用激活函數(shù)(通常是整流線性單元 (ReLU)),以將非線性引入模型。ReLU 可幫助網(wǎng)絡(luò)學(xué)習(xí)復(fù)雜的關(guān)系并使模型更具表現(xiàn)力。使用哪種激活完全取決于您的用例,在大多數(shù)情況下,研究人員使用 ReLU,但也可以使用一些激活,例如:Leaky ReLU、ELU。
ReLU 激活 在 Python 中實(shí)現(xiàn)整流線性單元 (ReLU) 函數(shù)非常簡單。ReLU 是神經(jīng)網(wǎng)絡(luò)中常用的引入非線性的激活函數(shù)。這是一個簡單的 Python 實(shí)現(xiàn):
def relu(x): return max(0,x)4.池化層 池化層(例如MaxPooling或AveragePooling)可減少卷積層生成的特征圖的空間維度。例如,MaxPooling 從一組值中選擇最大值,重點(diǎn)關(guān)注最顯著的特征。
最大池化——平均池化 池化層減少了空間維度。MaxPooling 通常用于:
從tensorflow.keras.layers import MaxPooling2D pooling_layer = MaxPooling2D(pool_size=( 2 , 2 ))(conv_layer)5.全連接(密集)層 全連接層將一層中的每個神經(jīng)元連接到下一層中的每個神經(jīng)元。這些層通常位于網(wǎng)絡(luò)的末端,將學(xué)習(xí)到的特征轉(zhuǎn)換為預(yù)測或類概率。全連接層通常用于網(wǎng)絡(luò)的末端。對于分類任務(wù):
從tensorflow.keras.layers import Dense、Flatten
flatten_layer = Flatten()(pooling_layer) density_layer = Dense(units= 128 ,activation= 'relu' )(flatten_layer)6.Dropout 層 Dropout 層用于正則化以防止過擬合。在訓(xùn)練期間,隨機(jī)神經(jīng)元被“丟棄”,這意味著它們被忽略,從而迫使網(wǎng)絡(luò)學(xué)習(xí)更穩(wěn)健和更通用的特征。它通過在訓(xùn)練期間隨機(jī)忽略一小部分輸入單元來幫助防止過擬合:
Dropout 機(jī)制
從tensorflow.keras.layers import Dropout
dropout_layer = Dropout(rate= 0.5)(dense_layer)7.批量標(biāo)準(zhǔn)化層 批量標(biāo)準(zhǔn)化 (BN) 是神經(jīng)網(wǎng)絡(luò)中用于穩(wěn)定和加速訓(xùn)練過程的一種技術(shù)。它通過在訓(xùn)練期間調(diào)整和縮放層輸入來標(biāo)準(zhǔn)化層輸入。批量標(biāo)準(zhǔn)化背后的數(shù)學(xué)細(xì)節(jié)涉及標(biāo)準(zhǔn)化、縮放和移位操作。讓我們深入研究批量標(biāo)準(zhǔn)化的數(shù)學(xué)原理。假設(shè)我們有一個大小為m且包含n 個特征的小批量。批量標(biāo)準(zhǔn)化的輸入可以總結(jié)如下:
7.1均值計(jì)算 計(jì)算每個特征的小批量的均值μ :
數(shù)組 X 的平均值
這里,xi 表示小批量中第 i個特征的值。
7.2方差計(jì)算 計(jì)算每個特征的小批量方差σ2 :
方差計(jì)算
7.3標(biāo)準(zhǔn)化 通過減去平均值并除以標(biāo)準(zhǔn)差(σ)來標(biāo)準(zhǔn)化輸入:
在范圍內(nèi)標(biāo)準(zhǔn)化
這里,?是為了避免被零除而添加的一個小常數(shù)。
7.4縮放和平移 引入可學(xué)習(xí)參數(shù)(γ和β)來縮放和平移標(biāo)準(zhǔn)化值:
這里,γ是尺度參數(shù),β是平移參數(shù)。
批量標(biāo)準(zhǔn)化操作通常插入神經(jīng)網(wǎng)絡(luò)層中的激活函數(shù)之前。它已被證明具有正則化效果,可以緩解內(nèi)部協(xié)變量偏移等問題,使訓(xùn)練更穩(wěn)定、更快速。這是一個簡單的代碼,用于 CNN 或任何深度神經(jīng)網(wǎng)絡(luò)中的批量標(biāo)準(zhǔn)化。
從tensorflow.keras.layers import BatchNormalization
batch_norm_layer = BatchNormalization()(dropout_layer)總之,批量標(biāo)準(zhǔn)化對輸入進(jìn)行標(biāo)準(zhǔn)化,縮放和移動標(biāo)準(zhǔn)化值,并引入可學(xué)習(xí)的參數(shù),使網(wǎng)絡(luò)在訓(xùn)練期間能夠適應(yīng)。批量標(biāo)準(zhǔn)化的使用已成為深度學(xué)習(xí)架構(gòu)中的標(biāo)準(zhǔn)做法。 8.Flatten Layer Flatten Layer 將多維特征圖轉(zhuǎn)換為一維向量,為輸入到全連接層準(zhǔn)備數(shù)據(jù)。
flatten_layer=Flatten()(batch_norm_layer)9.上采樣層 上采樣是深度學(xué)習(xí)中用來增加特征圖空間分辨率的技術(shù)。它通常用于圖像分割和生成等任務(wù)。以下是常見上采樣方法類型的簡要說明:
9.1最近鄰 (NN) 上采樣 最近鄰 (NN) 上采樣,也稱為通過復(fù)制或復(fù)制進(jìn)行上采樣,是一種簡單而直觀的方法。在這種方法中,輸入中的每個像素都被復(fù)制或復(fù)制以生成更大的輸出。雖然簡單明了,但 NN 上采樣可能會導(dǎo)致塊狀偽影和精細(xì)細(xì)節(jié)的丟失,因?yàn)樗粫谙噜徬袼刂g進(jìn)行插值。
最近鄰上采樣
9.2轉(zhuǎn)置卷積(反卷積)上采樣 轉(zhuǎn)置卷積,通常稱為反卷積,是一種可學(xué)習(xí)的上采樣方法。它涉及使用具有可學(xué)習(xí)參數(shù)的卷積運(yùn)算來增加輸入的空間維度。轉(zhuǎn)置卷積層中的權(quán)重在優(yōu)化過程中進(jìn)行訓(xùn)練,使網(wǎng)絡(luò)能夠?qū)W習(xí)特定于任務(wù)的上采樣模式。
importtensorflowas tf from tensorflow.keras.layers import Conv2DTranspose #轉(zhuǎn)置卷積上采樣 transposed_conv_upsampling=Conv2DTranspose(filters=32, kernel_size=(3, 3), strides=(2, 2), padding='same')
每種上采樣方法都有其優(yōu)點(diǎn)和權(quán)衡,選擇取決于任務(wù)的具體要求和數(shù)據(jù)的特點(diǎn)。
填充和步幅
這些是卷積神經(jīng)網(wǎng)絡(luò) (CNN) 中的關(guān)鍵概念,它們會影響卷積運(yùn)算后輸出特征圖的大小。讓我們討論三種類型的填充,并解釋一下步幅的概念。
有效填充(無填充):在有效填充(也稱為無填充)中,在應(yīng)用卷積運(yùn)算之前不會向輸入添加任何額外填充。因此,卷積運(yùn)算僅在濾波器和輸入完全重疊的地方執(zhí)行。這通常會導(dǎo)致輸出特征圖的空間維度減少。
from tensorflow.keras.layersimportConv2D # 有效填充 valid_padding_conv=Conv2D(filters=32,kernel_size=(3,3), strides=(1,1),padding='valid')
相同填充:相同填充確保輸出特征圖具有與輸入相同的空間維度。它通過向輸入添加零填充來實(shí)現(xiàn)這一點(diǎn),這樣濾波器就可以在輸入上滑動而不會超出其邊界。填充量經(jīng)過計(jì)算以保持維度相同。
from tensorflow.keras.layers import Conv2D #Keras中的填充 same_padding_conv=Conv2D(filters=32,kernel_size=(3,3), strides=(1,1),padding='same')
步幅:步幅定義卷積過程中濾波器在輸入上移動的步長。步幅越大,輸出特征圖的空間維度就越小??梢哉{(diào)整步幅來控制網(wǎng)絡(luò)中的下采樣級別。
從tensorflow.keras.layers導(dǎo)入Conv2D
# Keras 中帶步幅的卷積示例 conv_with_stride = Conv2D(filters= 32 , kernel_size=( 3 , 3 ), strides=( 2 , 2 ), padding= 'same' )在此示例中,步幅設(shè)置為 (2, 2),表示濾波器在水平和垂直方向上每次移動兩個像素。步幅是控制特征圖的空間分辨率和影響網(wǎng)絡(luò)感受野的關(guān)鍵參數(shù)。
在本文中,我想探索如何從頭開始構(gòu)建一個簡單的卷積神經(jīng)網(wǎng)絡(luò)。讓我們做早期計(jì)算機(jī)視覺學(xué)習(xí)任務(wù)中最流行的分類任務(wù):貓與狗分類。此任務(wù)包括以下幾個步驟:
導(dǎo)入庫:
import tensorflow_datasets as tfds import tensorflow as tf from tensorflow.keras import layers import keras from keras.models import Sequential,Model from keras.layers import Dense,Conv2D,Flatten,MaxPooling2D,GlobalAveragePooling2D from keras.utils import plot_model import numpy as np import matplotlib.pyplot as plt import scipy as sp import cv2
加載數(shù)據(jù):Cats vs Dogs 數(shù)據(jù)集
!curl -O https://download.microsoft.com/download/ 3 /E/ 1 /3E1C3F21-ECDB- 4869 - 8368 -6DEBA77B919F/kagglecatsanddogs_5340.zip !unzip -qkagglecatsanddogs_5340.zip ! ls
下面的單元將對圖像進(jìn)行預(yù)處理并創(chuàng)建批次,然后將其輸入到我們的模型中。
def augment_images ( image, label ): # 轉(zhuǎn)換為浮點(diǎn)數(shù) image = tf.cast(image, tf.float32) # 標(biāo)準(zhǔn)化像素值 image = (image/ 255 ) # 調(diào)整大小為 300 x 300 image = tf.image.resize(image,( 300 , 300 )) return image, label # 使用上面的實(shí)用函數(shù)預(yù)處理圖像 augmented_training_data = train_data.map ( augment_images) # 在訓(xùn)練前打亂并創(chuàng)建批次 train_batches = augmented_training_data.shuffle( 1024 ).batch( 32 )
過濾掉損壞的圖像
在處理大量現(xiàn)實(shí)世界的圖像數(shù)據(jù)時,損壞的圖像是常有的事。讓我們過濾掉標(biāo)題中不包含字符串“JFIF”的編碼不良的圖像。
import os num_skipped = 0 for folder_name in ("Cat", "Dog"): folder_path = os.path.join("PetImages", folder_name) for fname in os.listdir(folder_path): fpath = os.path.join(folder_path, fname) try: fobj = open(fpath, "rb") is_jfif = tf.compat.as_bytes("JFIF") in fobj.peek(10) finally: fobj.close() if not is_jfif: num_skipped += 1 # Delete corrupted image os.remove(fpath) print("Deleted %d images" % num_skipped)生成Dataset
image_size = (300, 300) batch_size = 128 train_ds, val_ds = tf.keras.utils.image_dataset_from_directory( "PetImages", validation_split=0.2, subset="both", seed=1337, image_size=image_size, batch_size=batch_size, )
可視化數(shù)據(jù)
這是訓(xùn)練數(shù)據(jù)集中的前 9 張圖片。如你所見,標(biāo)簽 1 是“狗”,標(biāo)簽 0 是“貓”。
import matplotlib.pyplot as plt plt.figure(figsize=(6, 6)) for images, labels in train_ds.take(1): for i in range(9): ax = plt.subplot(3, 3, i + 1) plt.imshow(images[i].numpy().astype("uint8")) plt.title(int(labels[i])) plt.axis("off")
使用圖像數(shù)據(jù)增強(qiáng)
如果您沒有大型圖像數(shù)據(jù)集,最好通過對訓(xùn)練圖像應(yīng)用隨機(jī)但現(xiàn)實(shí)的變換(例如隨機(jī)水平翻轉(zhuǎn)或小幅隨機(jī)旋轉(zhuǎn))來人為地引入樣本多樣性。這有助于讓模型接觸訓(xùn)練數(shù)據(jù)的不同方面,同時減緩過擬合。
data_augmentation = keras.Sequential( [ layers.RandomFlip("horizontal"), layers.RandomRotation(0.1), ] )data_augmentation讓我們通過反復(fù)應(yīng)用數(shù)據(jù)集中的第一個圖像來直觀地看到增強(qiáng)樣本的樣子:
plt.figure(figsize=(6, 6)) for images, _ in train_ds.take(1): for i in range(9): augmented_images = data_augmentation(images) ax = plt.subplot(3, 3, i + 1) plt.imshow(augmented_images[0].numpy().astype("uint8")) plt.axis("off")
配置數(shù)據(jù)集以提高性能
我們將數(shù)據(jù)增強(qiáng)應(yīng)用到我們的訓(xùn)練數(shù)據(jù)集,并確保使用緩沖預(yù)取,這樣我們就可以從磁盤中獲取數(shù)據(jù)而不會導(dǎo)致 I/O 阻塞:
# 將 `data_augmentation` 應(yīng)用于訓(xùn)練圖像。 train_ds = train_ds.map( lambda img, label: (data_augmentation(img), label), num_parallel_calls=tf.data.AUTOTUNE, ) # 在 GPU 內(nèi)存中預(yù)取樣本有助于最大限度地提高 GPU 利用率。 train_ds = train_ds.prefetch(tf.data.AUTOTUNE) val_ds = val_ds.prefetch(tf.data.AUTOTUNE)構(gòu)建分類器
這看起來會很熟悉,因?yàn)樗c我們之前構(gòu)建的模型幾乎相同。關(guān)鍵區(qū)別在于輸出只是一個 S 激活單元。這是因?yàn)槲覀冎惶幚韮蓚€類。
classCustomModel(Sequential): def __init__(self): super(CustomModel, self).__init__() self.add(Conv2D(16, input_shape=(300, 300, 3), kernel_size=(3, 3), activation='relu', padding='same')) self.add(MaxPooling2D(pool_size=(2, 2))) self.add(Conv2D(32, kernel_size=(3, 3), activation='relu', padding='same')) self.add(MaxPooling2D(pool_size=(2, 2))) self.add(Conv2D(64, kernel_size=(3, 3), activation='relu', padding='same')) self.add(MaxPooling2D(pool_size=(2, 2))) self.add(Conv2D(128, kernel_size=(3, 3), activation='relu', padding='same')) self.add(GlobalAveragePooling2D()) self.add(Dense(1, activation='sigmoid')) # Instantiate the custom model model = CustomModel() # Display the model summarymodel.summary()損失可以根據(jù)上次進(jìn)行調(diào)整,以僅處理兩個類別。為此,我們選擇binary_crossentropy。
# 使用 GPU 進(jìn)行訓(xùn)練大約需要 30 分鐘。 #如果您的本地機(jī)器上沒有GPU,請隨意使用 model.compile(loss='binary_crossentropy', metrics=['accuracy'], optimizer=tf.keras.optimizers.RMSprop(lr=0.001)) model.fit(train_ds, epochs=25, validation_data=val_ds,)
測試模型
讓我們下載一些圖像并看看類別激活圖是什么樣子的。
!wget -O cat1.jpg https://storage.googleapis.com/laurencemoroney-blog.appspot.com/MLColabImages/cat1.jpg !wget -O cat2.jpg https://storage.googleapis.com/laurencemoroney-blog.appspot.com/MLColabImages/cat2.jpg !wget -O catanddog.jpg https://storage.googleapis.com/laurencemoroney-blog.appspot.com/MLColabImages/catanddog.jpg !wget -O dog1.jpg https://storage.googleapis.com/laurencemoroney-blog.appspot.com/MLColabImages/dog1.jpg !wget -O dog2.jpg https://storage.googleapis.com/laurencemoroney-blog.appspot.com/MLColabImages/dog2.jpg
# utility function to preprocess an image and show the CAM def convert_and_classify(image): # load the image img = cv2.imread(image) # preprocess the image before feeding it to the model img = cv2.resize(img, (300,300)) / 255.0 # add a batch dimension because the model expects it tensor_image = np.expand_dims(img, axis=0) # get the features and prediction features,results = cam_model.predict(tensor_image) # generate the CAM show_cam(tensor_image, features, results) convert_and_classify('cat1.jpg') convert_and_classify('cat2.jpg') convert_and_classify('catanddog.jpg') convert_and_classify('dog1.jpg') convert_and_classify('dog2.jpg')審核編輯:黃飛
-
濾波器
+關(guān)注
關(guān)注
161文章
7834瀏覽量
178257 -
計(jì)算機(jī)視覺
+關(guān)注
關(guān)注
8文章
1698瀏覽量
46017 -
python
+關(guān)注
關(guān)注
56文章
4797瀏覽量
84756 -
cnn
+關(guān)注
關(guān)注
3文章
352瀏覽量
22237 -
卷積神經(jīng)網(wǎng)絡(luò)
+關(guān)注
關(guān)注
4文章
367瀏覽量
11877
原文標(biāo)題:CNN的原理詳解及代碼實(shí)戰(zhàn)(人手都會)
文章出處:【微信號:vision263com,微信公眾號:新機(jī)器視覺】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論