圖像處理之卷積模式及C++實現


1. 卷積的三種模式

深度學習框架中通常會實現三種不同的卷積模式,分別是 SAME、VALID、FULL。這三種模式的核心區別在於卷積核進行卷積操作的移動區域不同,進而導致輸出的尺寸不同。我們以一個例子來看這三種模式的區別,輸入圖片的尺寸是5x5 ,卷積核尺寸是 3x3 ,stride 取 1。

1.1 FULL 模式

FULL 模式下卷積核從與輸入有一個點的相交的地方就開始卷積。如下圖所示,藍框的位置就是卷積核第一個卷積的地方,灰色部分是為了卷積能夠正常進行的 padding(一般填 0)。因此 FULL 模式下卷積核移動區域最大,卷積后輸出的尺寸也最大。
在這里插入圖片描述

1.2 VALID 模式

VALID 模式與 FULL 模式相反,在整個卷積核與輸入重疊的地方才開始卷積操作,因此不需要 padding,輸出的尺寸也最小
在這里插入圖片描述

1.3 SAME 模式

SAME 模式是最常用的一種模式,SAME 的意思是卷積后輸出的尺寸與輸入尺寸保持一致(假定 stride 為 1)。通過將卷積核的中心與輸入的第一個點進行對齊確定卷積核起始位置,然后補齊對應 padding 即可。如下圖所示,可以看到卷積輸出的尺寸與出入保持一致。
在這里插入圖片描述
SAME 模式下當卷積核邊長為偶數時,可以通過在其中一邊增加多一行(列)padding,即不對稱的 padding 實現輸出尺寸與輸入尺寸保持一致,如下圖所示(卷積核尺寸為 [公式] )
在這里插入圖片描述
以上三種模式區別在於卷積核進行卷積操作的移動區域不同,其實是確定了所需的 padding。各種模式 padding 計算如下

def get_padding(inputs, ks, mode="SAME"):
    """
    Return padding list in different modes.
    params: inputs (input array)
    params: ks (kernel size) [p, q]
    return: padding list [n,m,j,k]
    """
    pad = None
    if mode == "FULL":
        pad = [ks[0] - 1, ks[1] - 1, ks[0] - 1, ks[1] - 1]
    elif mode == "VALID":
        pad = [0, 0, 0, 0]
    elif mode == "SAME":
        pad = [(ks[0] - 1) // 2, (ks[1] - 1) // 2,
               (ks[0] - 1) // 2, (ks[1] - 1) // 2]
        if ks[0] % 2 == 0:
            pad[2] += 1
        if ks[1] % 2 == 0:
            pad[3] += 1
    else:
        print("Invalid mode")
    return pad

確定了輸入尺寸、卷積核尺寸、padding 以及 stride,輸出的尺寸就被確定下來,可以由以下公式計算。其中 $S'$, $S$ 分別是輸出、輸入尺寸, $K$ 是卷積核尺寸, $P_1$, $P_2$ 分別是兩側的 padding。
在這里插入圖片描述

2. C++代碼實現

在這里插入圖片描述
卷積運算本質上就是在濾波器和輸入數據的局部區域間做點積,最直觀明了的方法就是用滑窗的方式,c++簡單實現如下:

輸入:imput[IC][IH][IW]
IC = input.channels
IH = input.height
IW = input.width

卷積核: kernel[KC1][KC2][KH][KW]
KC1 = OC
KC2 = IC
KH = kernel.height
KW = kernel.width

輸出:output[OC][OH][OW]
OC = output.channels
OH = output.height
OW = output.width

其中,padding = VALID,stride=1,
OH = IH - KH + 1
OW = IW - KW + 1


for(int ch=0;ch<output.channels;ch++)
{
    for(int oh=0;oh<output.height;oh++)
    {
        for(int ow=0;ow<output.width;ow++)
        {
            float sum=0;
            for(int kc=0;kc<kernel.channels;kc++)
            {
                for(int kh=0;kh<kernel.height;kh++)
                {
                    for(int kw=0;kw<kernel.width;kw++)
                    {
                        sum += input[kc][oh+kh][ow+kw]*kernel[ch][kc][kh][kw];
                    }
                }
            }
            //if(bias) sum +=bias[]
            output[ch][oh][ow]=sum;
        }
    }
}

直接用滑窗的方法計算卷積,效率比較低,因此一般把卷積操作轉換為矩陣乘法。這樣可以高效的利用優化之后的矩陣乘法,具體可以參考Caffe中的im2col的實現

3. 最后


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM