Padding的值

Padding是指在向量的每一維前後填充一定大小的邊界,其常用於在卷積

和池化這種具有「濾波」窗口的運算中,以調整輸出大小、防止個別數據的丟棄

需要注意的是,Padding並不是真的在向量的前後填充了值,而只是標記了padding的位置

卷積和池化在計算時,會先確定濾波窗口的位置,窗口中如果包含了padding,則padding部分並不參與計算

舉一個例子來說明這樣處理的合理性

假設在做最大池化,向量的形狀如上圖,末尾有一padding

如果padding有具體的值,比如說0,則這個窗口的輸出就為0,padding的值影響了輸出的結果,而捨棄了源數據,這與paddding運算的目的相悖

如果padding的值是一個非常小的值,假設是負無窮,在上面的情形下看似不影響結果,但是無法適用於更多的情況,比如卷積,比如平均池化。所以padding不參與計算也有利於實現上的解耦,顯然也更節省內存

Padding模式

Padding運算作用於輸入向量的每一維,每一維的操作都是一致的,所以理解Padding的操作,只需要理解一維向量的padding過程

假設一個一維向量,輸入形狀為input_size,經過濾波操作後的輸出形狀為output_size,濾波窗口為filter_size,需要padding的個數為padding_needed,濾波窗口滑動步長為stride,則之間滿足關係:

output\_size = (input\_size + padding\_needed -filter\_size)/ stride + 1

由公式可知,指定padding_needed可以確定output_size的值,反過來,如果已知輸出的形狀,則進而可以確定padding的數量。

這是兩種處理padding的方案,pytorch採用的是第一種,即在卷積或池化時先確定padding數量,自動推導輸出形狀;tensorflow和caffe採用的是更為人熟知的第二種,即先根據Valid還是Same確定輸出大小,再自動確定padding的數量

Valid和Same是預設的兩種padding模式,Valid指不padding,same指輸出大小儘可能和輸入大小成比例

下面是tensorflow計算padding的代碼:

void GetWindowedOutputSize(int64_t input_size, int32_t filter_size, int32_t dilation_rate,
int32_t stride, const std::string& padding_type,
int64_t* output_size,int32_t* padding_before,
int32_t* padding_after) {
CHECK_GT(stride, 0);
CHECK_GE(dilation_rate, 1);

int32_t effective_filter_size = (filter_size - 1) * dilation_rate + 1;
if (padding_type == "valid") {
if (output_size) { *output_size = (input_size - effective_filter_size + stride) / stride; }
if (padding_before) { *padding_before = 0; }
if (padding_after) { *padding_after = 0; }
} else if (padding_type == "same") {
int64_t tmp_output_size = (input_size + stride - 1) / stride;
if (output_size) { *output_size = tmp_output_size; }
const int32_t padding_needed = std::max(
0,
static_cast<int32_t>((tmp_output_size - 1) * stride + effective_filter_size - input_size));
// For odd values of total padding, add more padding at the right
// side of the given dimension.
if (padding_before) { *padding_before = padding_needed / 2; }
if (padding_after) { *padding_after = padding_needed - padding_needed / 2; }
} else {
UNIMPLEMENTED();
}
if (output_size) { CHECK_GE((*output_size), 0); }
}

其中考慮了空洞卷積的情況(dilation),所以稍微複雜一些

另外需要格外注意的,代碼中計算了padding_after和padding_before兩個值,是指要將padding_needed平均分配到向量的兩側。這就引出一個問題,當padding_needed是奇數時,是在前pad多一些,還是在後面pad多一些?

Padding前後

tensorflow特別指出,當padding個數為奇數時,需要在後面多padding一些

caffe則跟tensorflow相反,caffe採用對稱padding,相當於會將多餘的padding分配給前面

為了兼容兩種框架,需要根據配置對padding的策略進行調整

下面解釋CUDNN如何調整padding分配的策略

以池化舉例,CUDNN池化kernel的參數有:

handle
Input. Handle to a previously created cuDNN context.
poolingDesc
Input. Handle to a previously initialized pooling descriptor.
alpha, beta
Input. Pointers to scaling factors (in host memory) used to blend the computation result with prior value in the output layer as follows: dstValue = alpha[0]*result + beta[0]*priorDstValue. Please refer to this section for additional details.
xDesc
Input. Handle to the previously initialized input tensor descriptor.
x
Input. Data pointer to GPU memory associated with the tensor descriptor xDesc.
yDesc
Input. Handle to the previously initialized output tensor descriptor.
y
Output. Data pointer to GPU memory associated with the output tensor descriptor yDesc.

CUDNN首先需要先制定輸入形狀和輸出形狀,xDesc指定了輸入向量的形狀,ydesc指定了輸出向量的形狀

然後CUDNN又通過poolingdesc指定了每一維padding_before的值

所以根據公式可以自然推導padding_after的值


推薦閱讀:
相关文章