【深度學習】多通道圖像卷積過程及計算方式

之前,有寫了一篇博文,【深度學習入門】——親手實現圖像卷積操作介紹卷積的相應知識,但那篇文章更多的是以濾波器的角度去講解卷積。但實際上是神經網絡中該博文內容並不適應。

之前的文章爲了便於演示,針對的是二維卷積,比如一張圖片有 RGB 三個顏色通道,我的方式是每個通道單獨卷積,然後將各個通道合成一張圖片,再可視化出來。但真實工程不會是這樣的,很多東西需要進一步說明白。

熟悉 TensorFlow 的同學大概對這個函數比較熟悉。

tf.nn.conv2d(
    input,
    filter,
    strides,
    padding,
    use_cudnn_on_gpu=True,
    data_format='NHWC',
    dilations=[1, 1, 1, 1],
    name=None
)

其中 input 自然是卷積的輸入,而 filter 自然就是濾波器。
它們的格式說明如下:

input:
[batch, in_height, in_width, in_channels]

filter
[filter_height, filter_width, in_channels, out_channels]

input 的 4 個參數很好理解,分別是批數量、高、寬、通道數。

但是,我當時在學習時有一個疑惑不能理解,那就是爲什麼 filter 有 2 個通道相關的參數呢?

按照網絡上的建議,我大概知道 input 的 in_channels 和 filter 的 in_channels 要對應起來,而 out_channels 是卷積後生成的 featuremap 的通道數量,但是其中的計算細節,我並不知道。

爲什麼顏色通道爲 3 的圖像,經過卷積後,它的通道數量可以變成 128 或者其它呢?這是我的疑問。

後來,我發現自己有這個疑問是因爲對卷積的概念理解不清楚。

我誤以爲,卷積過程中濾波器是 2 維的,只有寬高,通道數爲 1.

這裏寫圖片描述

實際上,真實的情況是,卷積過程中,輸入層有多少個通道,濾波器就要有多少個通道,但是濾波器的數量是任意的,濾波器的數量決定了卷積後 featuremap 的通道數。

在這裏插入圖片描述

如果把輸入當做一個立方體的話,那麼 filter 也是一個立方體,它們卷積的結果也是一個立方體,並且上面中 input、filter、Result 的通道都是一致的。

但卷積過程的最後一步要包括生成 feature,很簡單,將 Result 各個通道對應座標的值相加就生成了 feature,相當於將多維的 Result 壓縮成了 2 維的 feature。

可能有同學會問,爲什麼需要壓縮 Result 到 2 維呢?

我們回顧,卷積的公式。

y(n)=i=x(i)h(ni) y(n) = \sum_{i=-\infty}^{\infty} x(i)*h(n-i)

卷積無非就是一個累乘然後累加的過程,所以從數學上來看,這並不違背規則,實際上真實的情況是爲了卷積過程的通道對應,原因下面分析。

之前我們會困擾是因爲所有的文獻都以 3x3 或者 5x5 的形式指代濾波器,讓我們誤以爲濾波器只能是 2 維的。

也有細心的同學會問,卷積過程,怎麼改變輸入層的通道數?

比如,輸入層是一張彩色圖片,它有 RGB 3 個通道,但經過卷積後的 featuremap 卻有 128 個通道,那它是怎麼實現的呢?

奧祕在於濾波器的數量
在這裏插入圖片描述

大家注意上圖,我們假定用 N 表示濾波器的數量,那麼每一個濾波器會生成一個 2 維的 feature,N 個濾波器就生成 N 個 feature,N 個 feature 組成了卷積後的 featuremap,而 N 就是 featuremap 的通道數。

input:
[batch, in_height, in_width, in_channels]

filter
[filter_height, filter_width, in_channels, out_channels]

我們再看 Tensorflow 中 filter 的參數說明,是不是就一目瞭然了呢?

我們也可以再仔細體會,單個濾波器卷積結果要壓縮成 2 維的妙處,這樣保證了卷積後的輸出通道和卷積濾波器的數量對應上了。

代碼實現

之前的文章,我實現卷積的過程只考慮到了 2 維,並且實現手法比較傳統。

def _con_each(src_block,kernel):
    pixel_count = kernel.size;
    pixel_sum = 0;
    _src = src_block.flatten();
    _kernel = kernel.flatten();
    
    
    for i in range(pixel_count):
        pixel_sum += _src[i]*_kernel[i];
        
    return pixel_sum 

現在,可以進行改進。

前面說過,卷積公式本質就是一個累乘然後累加的過程,它的結果是一個數值。而線性代數中兩個向量的內積恰恰可以這樣表示,所以完全可以改寫。

import numpy as np

def _conv_epoch(src_block,filter):
    input = src_block.flatten()
    filter = filter.flatten().T

    return np.dot(input,filter)

當然,完整的圖像卷積需要掃描式地重複許多次。

"""
input_size:(h,w,c)
filter_size:(h,w,ic,oc)
"""
def conv(img,input_size,filter_size,stride=1):
    ih = input_size[0]
    iw = input_size[1]
    ic = input_size[2]

    filter_oc = filter_size[3]
    filter_h = filter_size[0]
    filter_w = filter_size[1]
    filter_ic = filter_size[2]

    l = int((ih - filter_h) / stride + 1)
    m = int((iw - filter_w) / stride + 1)

    result = np.zeros(shape=(l,m,filter_oc),dtype=np.uint8)

    for i in range(l):
        for j in range(m):
            for k in range(filter_oc):
                f = np.random.uniform(0,1,filter_w*filter_h*filter_ic).T
                input = img[i:i+filter_h,j:j+filter_w,:]

                result[i,j,k] = _conv_epoch(input,f)

    return result

現在,我們可以測試一下我們的代碼效果。

def test():
    img = plt.imread("../datas/cat.jpg")

    print("img shape ",img.shape)

    result = conv(img,img.shape,(3,3,img.shape[2],3))

    plt.figure()
    plt.subplot(121)
    plt.imshow(img)
    plt.subplot(122)
    plt.imshow(result)
    plt.show()

test()

讀入一張貓的照片,然後對照它的卷積效果,需要注意的是我設置的濾波器的數量爲 3 ,這是爲了便於演示。

最終效果如下:
在這裏插入圖片描述

需要注意的是,濾波器的數值我完全是隨機選擇,但從效果上來看,它們還是抽取了一些輪廓細節。可見卷積操作的威力之大。在深度學習中,一個神經網絡通常有成百上千個 filter,它們通過一反覆學習,最終形成了可靠的特徵表達能力。

最後,我要說明的是,卷積過程很慢,特變是又 python 實現,雖然我已經在前一篇文章的基礎上更改了卷積代碼,讓 for 循環改成了向量點積的方式,但整個圖像的卷積過程,還可以改善,這涉及到一個叫做 im2col 的技術,它大致的原理是讓卷積過程中,矩陣的乘法參與的更徹底,最後整個卷積過程用一個矩陣乘法表示,因爲篇幅有限,有興趣的同學可以自行搜索對應的文獻。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章