TensorFlow中的兩種conv2d方法和kernel_initializer


tf.nn.conv2d

在使用TF搭建CNN的過程中,卷積的操作如下

convolution = tf.nn.conv2d(X, filters, strides=[1,2,2,1], padding="SAME") 

這個函數中各個參數的含義是什么呢?

  • X:輸入數據的mini-batch,為一個4D tensor;分別表示的含義為[n_batch,height,width,channel]
  • filters:為卷積核,為一個4D tensor,分別表示的含義為 [filter_height, filter_width, in_channels, out_channels]
  • stride:為步長,使用方法為[1,stride,stride,1]
    該方法先將filter展開為一個2D的矩陣,形狀為[filter_heightfilter_width in_channels, out_channels],再在圖片上面選擇一塊大小進行卷積計算的到一個大小為[batch, out_height, out_width, filter_height * filter_width * in_channels]的虛擬張量。
    再將上面兩部相乘(右乘filter矩陣)
  • padding:string類型的量,只能是"SAME","VALID"其中之一,這個值決定了不同的卷積方式。下面使用圖表示兩種的計算形式

當使用VALID的時候,如果卷積計算過程中,剩下的不夠一步,則剩下的像素會被拋棄,SAME則會補0.

filter_primes = np.array([2., 3., 5., 7., 11., 13.], dtype=np.float32)
x = tf.constant(np.arange(1, 13+1, dtype=np.float32).reshape([1, 1, 13, 1]))
filters = tf.constant(filter_primes.reshape(1, 6, 1, 1))

valid_conv = tf.nn.conv2d(x, filters, strides=[1, 1, 5, 1], padding='VALID')
same_conv = tf.nn.conv2d(x, filters, strides=[1, 1, 5, 1], padding='SAME')

with tf.Session() as sess:
    print("VALID:\n", valid_conv.eval())
    print("SAME:\n", same_conv.eval())

輸出內容為

VALID:
 [[[[ 184.]
   [ 389.]]]]
SAME:
 [[[[ 143.]
   [ 348.]
   [ 204.]]]]

實際計算向量如下所示:

print("VALID:")
print(np.array([1,2,3,4,5,6]).T.dot(filter_primes))
print(np.array([6,7,8,9,10,11]).T.dot(filter_primes))
print("SAME:")
print(np.array([0,1,2,3,4,5]).T.dot(filter_primes))
print(np.array([5,6,7,8,9,10]).T.dot(filter_primes))
print(np.array([10,11,12,13,0,0]).T.dot(filter_primes))
>>

VALID:
184.0
389.0
SAME:
143.0
348.0
204.0

再來做一個小實驗,使用VALID的時候:

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))
op = tf.nn.conv2d(input, filter, strides=[1, 2, 2, 1], padding='VALID')
init = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init)
    print(op)
# print(sess.run(op))  
>>Tensor("Conv2D:0", shape=(1, 2, 2, 1), dtype=float32)

使用SAME的時候

input = tf.Variable(tf.random_normal([1,5,5,5]))
filter = tf.Variable(tf.random_normal([3,3,5,1]))
op = tf.nn.conv2d(input, filter, strides=[1, 2, 2, 1], padding='SAME')
init = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init)
    print(op)
# print(sess.run(op))  
>>Tensor("Conv2D:0", shape=(1, 3, 3, 1), dtype=float32)

note:在做卷積的過程中filter的shape為[hight,width,channel],也就是說如果為如果輸入只有一個channel的時候,filter為一個矩陣,如果channel為3的時候,這個時候的filter就有了厚度為3。

tf.layer.conv2d

同時TF也提供了tf.layer.conv2d的方法

def conv2d(inputs,
           filters,
           kernel_size,
           strides=(1, 1),
           padding='valid',
           data_format='channels_last',
           dilation_rate=(1, 1),
           activation=None,
           use_bias=True,
           kernel_initializer=None,
           bias_initializer=init_ops.zeros_initializer(),
           kernel_regularizer=None,
           bias_regularizer=None,
           activity_regularizer=None,
           trainable=True,
           name=None,
           reuse=None):

這個方法和tf.nn.conv2d有着相同的作用,相當於對其的更高層的api。兩個方法的調用過程如下:

tf.layers.conv2d-> tf.nn.convolution . 
tf.layers.conv2d->Conv2D->Conv2D.apply()->_Conv->_Conv.apply()->_Layer.apply()->_Layer.\__call__()->_Conv.call()->nn.convolution()...

我用這兩個方法搭建了相同的神經網絡,可是得到的准確率相差很大,其他部分代碼一張樣。代碼和准確率如下。為何差別這么的大?

    def conv2d(self,input,ksize,stride,name):
        with tf.name_scope(name):
            with tf.variable_scope(name):
                w = tf.get_variable("%s-w" %name,shape= ksize,initializer=tf.truncated_normal_initializer())
                b = tf.get_variable("%s-b" %name,shape = [ksize[-1]],initializer = tf.constant_initializer())
                out = tf.nn.conv2d(input,w,strides=[1,stride,stride,1],padding="SAME",name="%s-conv"%name)
                out = tf.nn.bias_add(out,b,name='%s-bias_add' %name)
                out = tf.nn.relu(out,name="%s-relu"%name)
        return out

conv1 = tf.layers.conv2d(X,filters=conv1_fmaps, \
                         kernel_size = conv1_ksize,strides=conv1_stride,\
                         padding=conv1_pad,activation=tf.nn.relu,name='conv1')

為何差異這么大呢?我現在還沒弄查出結果,如果知道答案請指出,先謝過。

tf.layers.conv2d中默認的kernel_initializer

tf.layer.conv2d這里面默認的kernel_initializer為None,經查閱源碼

    self.kernel = vs.get_variable('kernel',
                                  shape=kernel_shape,
                                  initializer=self.kernel_initializer,
                                  regularizer=self.kernel_regularizer,
                                  trainable=True,
                                  dtype=self.dtype)

這里面有一段說明

   If initializer is `None` (the default), the default initializer passed in
    the constructor is used. If that one is `None` too, we use a new
    `glorot_uniform_initializer`. If initializer is a Tensor, we use
    it as a value and derive the shape from the initializer.

也就是說使用的是
glorot_uniform_initializer來進行初始化的。這種方法又被稱為Xavier uniform initializer,相關的文獻在這里 。另外TF中tf.layers.dense也是使用的這個初始化方法。我把初始化方法都改成了使用tf.truncated_normal_initializer,上面模型的結果沒有什么改善。看來初始化方法不是主要原因。求解。


免責聲明!

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



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