對于卷積公式

可能有的人知道,可能有的人不知道,或者也僅僅只是知道而不理解,但是不管你知不知道這個公式的意義,都不影響你自己去實作一個卷積,他具體的數學意義,我先不講,因為有很多人講的都比我清楚透徹,而我要告訴你的,則是再卷積神經網路里面的卷積操作是如何實作的
提到卷積神經網路,聽到的最多的應該就是卷積,激活,池化這三個操作,就拿VGG16這個經典網路模型來說,其實就是通過卷積+激活+池化這三種操作堆疊而成的,
那么他們具體是什么東西,又是如何實作的,這次就來用numpy手擼一下他們的具體實作原理,
卷積

在神經網路中的卷積,就是利用一個卷積核在你的影像矩陣上滑動,上圖中,假設灰色矩陣就是我們輸入的影像矩陣,里面的數字就代表著影像中的像素值,綠色矩陣就是卷積核,灰色矩陣上面的綠色區域就是當前卷積核所覆寫的區域每次卷積核滑動的距離叫做步長,在上圖中步長就是1,具體操作就是,將卷積核與在影像矩陣中所覆寫的區域對應位置相乘再相加,最后的藍色矩陣就是這次卷積的結果,
Padding
在上面的卷積操作中,原矩陣的大小是4x4,但是卷積完了之后就變成2x2的矩陣了,這是因為邊緣上的像素永遠不會位于卷積核中心,而卷積核也沒法擴展到邊緣區域以外,
這個結果我們是不能接受的,有時我們還希望輸入和輸出的大小應該保持一致,為解決這個問題,可以在進行卷積操作前,對原矩陣進行邊界填充(Padding),也就是在矩陣的邊界上填充一些值,以增加矩陣的大小,通常都用“0”來進行填充的,

對于卷積核大小為3的卷積,通常只需要在外圍填充一圈0就足夠了,對于不同大小的卷積核,為了使輸出影像的大小一致,在影像外圍填充多少圈0是不一樣的,填充公式如下:

P代表因該填充幾圈,K代表卷積核的邊長,卷積核通常都是奇數
多通道卷積
我們都知道,一張彩色圖片是RGB三通道,也就是它的channel有3個,那么在這種多通道的情況下應該如何實作卷積操作呢,很簡單,輸入的影像有多少個通道,那么我們的卷積核也有多少個通道就可以了

跟單通道的卷積操作一樣,把卷積核按照對應通道放在圖片上滑動,對應位置相乘再相加,最后把三個通道得到的卷積結果加起來就行了
代碼實作
#input:輸入的資料,input_channel:輸入資料的通道數,out_channel:輸出的特征圖的通道數,kernel_size:卷積核的大小,stride:步長
def convolution(input,input_channel,out_channel,kernel_size,stride):
kernel = np.random.randn(out_channel,input_channel,kernel_size,kernel_size) #創建卷積核
padding = int((kernel_size - 1) / 2) #計算填充的大小
padding_input = []
# 進行對輸入矩陣的填充
for i in range(input_channel):
padding_input.append(np.pad(input[i], ((padding, padding), (padding, padding)), 'constant', constant_values=(0, 0)))
padding_input = np.array(padding_input)
#根據填充后的輸入尺寸,卷積核大小,步長,計算輸出矩陣的大小
out_size = int((len(input[0])+2*padding-kernel_size)/stride+1)
# 創建一個0填充的輸出矩陣
out = np.zeros((out_channel,out_size,out_size))
for i in range(out_channel):
out_x = 0
out_y = 0
x_end = padding_input.shape[1] - padding - 1 # 卷積邊界
x = padding
y = padding
while x<=x_end:
if y>padding_input.shape[1]-padding-1: #卷積核超出右側邊界時,向下移動一個步長
y = padding
x = x+stride
out_y = 0
out_x = out_x + 1
if x>x_end:
break
#卷積操作
out[i][out_x][out_y] = np.sum(padding_input[:,x-padding:x+padding+1,y-padding:y+padding+1]*kernel[i])
y = y+stride
out_y += 1
return out
激活
就是對矩陣中的每個值都進行激活函式的運算,拿Relu舉例,Relu就是大于0的不動,小于0的讓它變為0:

代碼實作
def ReLu(input):
out = np.maximum(0,input)
return out
池化
池化操作跟卷積操作其實很類似,都需要一個核在圖片上進行滑動,對核覆寫的區域進行一些操作,只不過區別就在于,卷積的核里面有數字,卷積操作就是覆寫區域跟核做運算,而池化的核是空的,最常見的池化操作有最大池化,平均池化等,
最大池化
就是選出被核覆寫的區域中的最大值

平均池化
就是選出被核覆寫區域中的平均值

代碼實作
以最大池化為例
#input:輸入的資料,pooling_size:卷積核大小,stride:步長
def pooling(input,pooling_size,stride):
out_size = int((len(input[0])-pooling_size)/stride+1) #計算池化后的輸出矩陣的大小
out = np.zeros((len(input[0]),out_size,out_size)) #初始化輸出矩陣
# 對每個通道開始池化
for i in range(input.shape[0]):
out_x = 0
out_y = 0
in_x = 0
in_y = 0
#開始滑動
while True:
if out_y>=out_size:
in_y = 0
in_x+=pooling_size
out_x+=1
out_y = 0
if out_x==out_size:
break
#池化操作
out[i][out_x][out_y] = np.max(input[i,in_x:in_x+pooling_size,in_y:in_y+pooling_size])
in_y+=pooling_size
out_y+=1
return out
可視化
下面這是一次卷積操作輸出的三個通道

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/423560.html
標籤:AI
