5、Tensorflow基礎(三)神經元函數及優化方法


1、激活函數

  激活函數(activation function)運行時激活神經網絡中某一部分神經元,將激活信息向后傳入下一層的神經網絡。神經網絡之所以能解決非線性問題(如語音、圖像識別),本質上就是激活函數加入了非線性因素,彌補了線性模型的表達力,把“激活的神經元的特征”通過函數保留並映射到下一層。

  因為神經網絡的數學基礎是處處可微的,所以選取的激活函數要能保證數據輸入與輸出也
是可微的。那么激活函數在 TensorFlow 中是如何表達的呢?
  激活函數不會更改輸入數據的維度,也就是輸入和輸出的維度是相同的。TensorFlow 中有
如下激活函數,它們定義在 tensorflow-1.1.0/tensorflow/python/ops/nn.py 文件中,這里包括平滑
非線性的激活函數,如 sigmoid、tanh、elu、softplus 和 softsign,也包括連續但不是處處可微的
函數 relu、relu6、crelu 和 relu_x,以及隨機正則化函數 dropout:

 1 tf.nn.relu()
 2 tf.nn.sigmoid()
 3 tf.nn.tanh()
 4 tf.nn.elu()
 5 tf.nn.bias_add()
 6 tf.nn.crelu()
 7 tf.nn.relu6()
 8 tf.nn.softplus()
 9 tf.nn.softsign()
10 tf.nn.dropout()#防止過擬合,用來舍棄某些神經元

上述激活函數的輸入均為要計算的 x(一個張量),輸出均為與 x 數據類型相同的張量。

1.1、Sigmoid函數:

                             

1.2、tanh函數:

            

1.3、relu函數:and softplus函數:

                                

2、卷積函數:

  卷積函數定義在 tensorflow-1.1.0/tensorflow/python/ops 下的 nn_impl.py 和

nn_ops.py 文件中。

1 tf.nn.convolution(input, filter, padding, strides=None,dilation_rate=None, name=None, data_format=None)
2 tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None,data_format= None, name=None)
3 tf.nn.depthwise_conv2d (input, filter, strides, padding, rate=None, name=None,data_format=None)
4 tf.nn.separable_conv2d (input, depthwise_filter, pointwise_filter, strides, padding,rate=None, name=None, data_format=None)
5 tf.nn.atrous_conv2d(value, filters, rate, padding, name=None)
6 tf.nn.conv2d_transpose(value, filter, output_shape, strides, padding='SAME',data_format='NHWC', name=None)
7 tf.nn.conv1d(value, filters, stride, padding, use_cudnn_on_gpu=None,data_format= None, name=None)
8 tf.nn.conv3d(input, filter, strides, padding, name=None)
9 tf.nn.conv3d_transpose(value, filter, output_shape, strides, padding='SAME',name=None)

(1)tf.nn.convolution(input, filter, padding, strides=None, dilation_rate=None, name=None,
data_format =None)這個函數計算 N 維卷積的和。

(2)tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, data_format=None,
name=None)這個函數的作用是對一個四維的輸入數據 input 和四維的卷積核 filter 進行操作,然
后對輸入數據進行一個二維的卷積操作,最后得到卷積之后的結果。

def conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None,data_format=None, name=None)
# 輸入:
# input:一個 Tensor。數據類型必須是 float32 或者 float64
# filter:一個 Tensor。數據類型必須是 input 相同
# strides:一個長度是 4 的一維整數類型數組,每一維度對應的是 input 中每一維的對應移動步數,
# 比如,strides[1]對應 input[1]的移動步數
# padding:一個字符串,取值為 SAME 或者 VALID
# padding='SAME':僅適用於全尺寸操作,即輸入數據維度和輸出數據維度相同
# padding='VALID:適用於部分窗口,即輸入數據維度和輸出數據維度不同
# use_cudnn_on_gpu:一個可選布爾值,默認情況下是 True
# name:(可選)為這個操作取一個名字
# 輸出:一個 Tensor,數據類型是 input 相同

事例;

input_data = tf.Variable( np.random.rand(10,9,9,3), dtype = np.float32 )
filter_data = tf.Variable( np.random.rand(2, 2, 3, 2), dtype = np.float32)
y = tf.nn.conv2d(input_data, filter_data, strides = [1, 1, 1, 1], padding = 'SAME')

打印出 tf.shape(y)的結果是[10 9 9 2]。

(3)tf.nn.depthwise_conv2d (input, filter, strides, padding, rate=None, name=None,data_format=
None) 這個函數輸入張量的數據維度是[batch, in_height, in_width, in_channels],卷積核的維度是
[filter_height, filter_width, in_channels, channel_multiplier],在通道 in_channels 上面的卷積深度是
1,depthwise_conv2d 函數將不同的卷積核獨立地應用在 in_channels 的每個通道上(從通道 1
到通道 channel_multiplier),然后把所以的結果進行匯總。最后輸出通道的總數是 in_channels *
channel_multiplier。

input_data = tf.Variable( np.random.rand(10, 9, 9, 3), dtype = np.float32 )
filter_data = tf.Variable( np.random.rand(2, 2, 3, 5), dtype = np.float32)
y = tf.nn.depthwise_conv2d(input_data, filter_data, strides = [1, 1, 1, 1], padding = 'SAME')

這里打印出 tf.shape(y)的結果是[10 9 9 15]。

(4)tf.nn.separable_conv2d (input, depthwise_filter, pointwise_filter, strides, padding, rate=None,
name=None, data_format=None)是利用幾個分離的卷積核去做卷積。在這個 API 中,將應用一個
二維的卷積核,在每個通道上,以深度 channel_multiplier 進行卷積。

 1 def separable_conv2d (input, depthwise_filter, pointwise_filter, strides, padding,
 2 rate=None, name=None, data_format=None)
 3 # 特殊參數:
 4 # depthwise_filter:一個張量。數據維度是四維[filter_height, filter_width, in_channels,
 5 # channel_multiplier]。其中,in_channels 的卷積深度是 1
 6 # pointwise_filter:一個張量。數據維度是四維[1, 1, channel_multiplier * in_channels,
 7 # out_channels]。其中,pointwise_filter 是在 depthwise_filter 卷積之后的混合卷積
 8 使用示例如下:
 9 input_data = tf.Variable( np.random.rand(10, 9, 9, 3), dtype = np.float32 )
10 depthwise_filter = tf.Variable( np.random.rand(2, 2, 3, 5), dtype = np.float32)
11 pointwise_filter = tf.Variable( np.random.rand(1, 1, 15, 20), dtype = np.float32)
12 # out_channels >= channel_multiplier * in_channels
13 y = tf.nn.separable_conv2d(input_data, depthwise_filter, pointwise_filter,strides = [1, 1, 1, 1], padding = 'SAME')
14 
15 這里打印出 tf.shape(y)的結果是[10 9 9 20]。

(5)tf.nn.atrous_conv2d(value, filters, rate, padding, name=None)計算 Atrous 卷積,又稱孔卷
積或者擴張卷積。
使用示例如下:

1 input_data = tf.Variable( np.random.rand(1,5,5,1), dtype = np.float32 )
2 filters = tf.Variable( np.random.rand(3,3,1,1), dtype = np.float32)
3 y = tf.nn.atrous_conv2d(input_data, filters, 2, padding='SAME')
4 
5 這里打印出 tf.shape(y)的結果是[1 5 5 1]。

(6)tf.nn.conv2d_transpose(value, filter, output_shape, strides, padding='SAME', data_format='NHWC',
name=None)
① 在解卷積網絡(deconvolutional network)中有時稱為“反卷積”,但實際上是 conv2d
的轉置,而不是實際的反卷積。

 1 def conv2d_transpose(value, filter, output_shape, strides, padding='SAME',
 2 data_format='NHWC', name=None)
 3 # 特殊參數:
 4 # output_shape:一維的張量,表示反卷積運算后輸出的形狀
 5 # 輸出:和 value 一樣維度的 Tensor
 6 使用示例如下:
 7 x = tf.random_normal(shape=[1,3,3,1])
 8 kernel = tf.random_normal(shape=[2,2,3,1])
 9 y = tf.nn.conv2d_transpose(x,kernel,output_shape=[1,5,5,3],strides=[1,2,2,1],padding="SAME")
10 
11 這里打印出 tf.shape(y)的結果是[1 5 5 3]。

(7)tf.nn.conv1d(value, filters, stride, padding, use_cudnn_on_gpu=None, data_format=None,
name=None)和二維卷積類似。這個函數是用來計算給定三維的輸入和過濾器的情況下的一維卷
積。不同的是,它的輸入是三維,如[batch, in_width, in_channels]。卷積核的維度也是三維,少
了一維 filter_height,如 [filter_width, in_channels, out_channels]。stride 是一個正整數,代表卷積
核向右移動每一步的長度。

(8)tf.nn.conv3d(input, filter, strides, padding, name=None)和二維卷積類似。這個函數用來計
算給定五維的輸入和過濾器的情況下的三維卷積。和二維卷積相對比:

    ● input 的 shape 中多了一維 in_depth,形狀為 Shape[batch, in_depth, in_height,in_width,in_channels];

    ● filter 的 shape 中多了一維 filter_depth,由 filter_depth, filter_height, filter_width 構成了卷
積核的大小;
    ● strides 中多了一維,變為[strides_batch, strides_depth, strides_height, strides_width,
strides_channel],必須保證 strides[0] = strides[4] = 1。

(9)tf.nn.conv3d_transpose(value, filter, output_shape, strides, padding='SAME', name=None)
和二維反卷積類似,不再贅述。

 3、池化函數

  在神經網絡中,池化函數一般跟在卷積函數的下一層,它們也被定義在 tensorflow-1.1.0/

tensorflow/python/ops 下的 nn.py 和 gen_nn_ops.py 文件中

 1 tf.nn.avg_pool(value, ksize, strides, padding, data_format='NHWC', name=None)
 2 tf.nn.max_pool(value, ksize, strides, padding, data_format='NHWC', name=None)
 3 tf.nn.max_pool_with_argmax(input, ksize, strides, padding, Targmax=None, name=None)
 4 tf.nn.avg_pool3d(input, ksize, strides, padding, name=None)
 5 tf.nn.max_pool3d(input, ksize, strides, padding, name=None)
 6 tf.nn.fractional_avg_pool(value, pooling_ratio, pseudo_random=None, overlapping=None,
 7 deterministic=None, seed=None, seed2=None, name=None)
 8 tf.nn.fractional_max_pool(value, pooling_ratio, pseudo_random=None, overlapping=None,
 9 deterministic=None, seed=None, seed2=None, name=None)
10 tf.nn.pool(input, window_shape, pooling_type, padding, dilation_rate=None, strides=None,
11 name=None, data_format=None)

  池化操作是利用一個矩陣窗口在張量上進行掃描,將每個矩陣窗口中的值通過取最大值或
平均值來減少元素個數。每個池化操作的矩陣窗口大小是由 ksize 指定的,並且根據步長 strides
決定移動步長。下面就分別來說明

(1)tf.nn.avg_pool(value, ksize, strides, padding, data_format='NHWC', name=None)。這個函
數計算池化區域中元素的平均值。

 1 def avg_pool(value, ksize, strides, padding, data_format='NHWC', name=None)
 2 # 輸入:
 3 # value:一個四維的張量。數據維度是[batch, height, width, channels]
 4 # ksize:一個長度不小於 4 的整型數組。每一位上的值對應於輸入數據張量中每一維的窗口對應值
 5 # strides:一個長度不小於 4 的整型數組。該參數指定滑動窗口在輸入數據張量每一維上的步長
 6 # padding:一個字符串,取值為 SAME 或者 VALID
 7 # data_format: 'NHWC'代表輸入張量維度的順序,N 為個數,H 為高度,W 為寬度,C 為通道數(RGB 三
 8 # 通道或者灰度單通道)
 9 # name(可選):為這個操作取一個名字
10 # 輸出:一個張量,數據類型和 value 相同
11 
12 
13 使用示例如下:
14 
15 input_data = tf.Variable( np.random.rand(10,6,6,3), dtype = np.float32 )
16 filter_data = tf.Variable( np.random.rand(2, 2, 3, 10), dtype = np.float32)
17 y = tf.nn.conv2d(input_data, filter_data, strides = [1, 1, 1, 1], padding = 'SAME')
18 output = tf.nn.avg_pool(value = y, ksize = [1, 2, 2, 1], strides = [1, 1, 1, 1],
19 padding ='SAME')
20 上述代碼打印出 tf.shape(output)的結果是[10 6 6 10]。計算輸出維度的方法是:shape(output) =
21 (shape(value) - ksize + 1) / strides。

(2)tf.nn.max_pool(value, ksize, strides, padding, data_format='NHWC', name=None)。這個函
數是計算池化區域中元素的最大值。

1 input_data = tf.Variable( np.random.rand(10,6,6,3), dtype = np.float32 )
2 filter_data = tf.Variable( np.random.rand(2, 2, 3, 10), dtype = np.float32)
3 y = tf.nn.conv2d(input_data, filter_data, strides = [1, 1, 1, 1], padding = 'SAME')
4 output = tf.nn.max_pool(value = y, ksize = [1, 2, 2, 1], strides = [1, 1, 1, 1],
5 padding ='SAME')
6 
7 上述代碼打印出 tf.shape(output)的結果是[10 6 6 10]。

(3)tf.nn.max_pool_with_argmax(input, ksize, strides, padding, Targmax = None, name=None)。
這個函數的作用是計算池化區域中元素的最大值和該最大值所在的位置。
在計算位置 argmax 的時候,我們將 input 鋪平了進行計算,所以,如果 input = [b, y, x, c],
那么索引位置是(( b * height + y) * width + x) * channels + c。
使用示例如下,該函數只能在 GPU 下運行,在 CPU 下沒有對應的函數實現

1 input_data = tf.Variable( np.random.rand(10,6,6,3), dtype = tf.float32 )
2 filter_data = tf.Variable( np.random.rand(2, 2, 3, 10), dtype = np.float32)
3 y = tf.nn.conv2d(input_data, filter_data, strides = [1, 1, 1, 1], padding = 'SAME')
4 output, argmax = tf.nn.max_pool_with_argmax(input = y, ksize = [1, 2, 2, 1],
5 strides = [1, 1, 1, 1], padding = 'SAME')

返回結果是一個張量組成的元組(output, argmax),output 表示池化區域的最大值;argmax
的數據類型是 Targmax,維度是四維。

(4)tf.nn.avg_pool3d()和 tf.nn.max_pool3d()分別是在三維下的平均池化和最大池化。
(5)tf.nn.fractional_avg_pool()和tf.nn.fractional_max_pool()分別是在三維下的平均池化和最大池化。
(6)tf.nn.pool(input, window_shape, pooling_type, padding, dilation_rate=None, strides=None,
name=None, data_format=None)。這個函數執行一個 N 維的池化操作。

 4、分類函數:

  TensorFlow 中常見的分類函數主要有sigmoid_cross_entropy_with_logits、softmax、log_softmax、softmax_cross_entropy_with_logits 等,它們也主要定義在 tensorflow-1.1.0/tensorflow/python/ops 的nn.py 和 nn_ops.py 文件中。

1 tf.nn.sigmoid_cross_entropy_with_logits(logits, targets, name=None)
2 tf.nn.softmax(logits, dim=-1, name=None)
3 tf.nn.log_softmax(logits, dim=-1, name=None)
4 tf.nn.softmax_cross_entropy_with_logits(logits, labels, dim=-1, name=None)
5 tf.nn.sparse_softmax_cross_entropy_with_logits(logits, labels, name=None)

(1)tf.nn.sigmoid_cross_entropy_with_logits(logits, targets, name=None):

1 def sigmoid_cross_entropy_with_logits(logits, targets, name=None):
2 # 輸入:logits:[batch_size, num_classes],targets:[batch_size, size].logits 用最后一
3 # 層的輸入即可
4 # 最后一層不需要進行 sigmoid 運算,此函數內部進行了 sigmoid 操作
5 # 輸出:loss [batch_size, num_classes]

這個函數的輸入要格外注意,如果采用此函數作為損失函數,在神經網絡的最后一層不需
要進行 sigmoid 運算。
(2)tf.nn.softmax(logits, dim=-1, name=None)計算Softmax 激活,也就是 softmax = exp(logits) /
reduce_sum(exp(logits), dim)。
(3)tf.nn.log_softmax(logits, dim=-1, name=None)計算 log softmax 激活,也就是 logsoftmax =
logits - log(reduce_sum(exp(logits), dim))。
(4)tf.nn.softmax_cross_entropy_with_logits(_sentinel=None, labels=None, logits=None, dim=-1,
name =None):

1 def softmax_cross_entropy_with_logits(logits, targets, dim=-1, name=None):
2 # 輸入:logits and labels 均為[batch_size, num_classes]
3 # 輸出: loss:[batch_size], 里面保存的是 batch 中每個樣本的交叉熵

(5)tf.nn.sparse_softmax_cross_entropy_with_logits(logits, labels, name=None) :

1 def sparse_softmax_cross_entropy_with_logits(logits, labels, name=None):
2 # logits 是神經網絡最后一層的結果
3 # 輸入:logits: [batch_size, num_classes] labels: [batch_size],必須在[0, num_classes]
4 # 輸出:loss [batch_size],里面保存是 batch 中每個樣本的交叉熵

5、優化方法:

  TensorFlow 提供了很多優化器(optimizer),我們重點介紹下面這 8 個:

1 class tf.train.GradientDescentOptimizer
2 class tf.train.AdadeltaOptimizer
3 class tf.train.AdagradOptimizer
4 class tf.train.AdagradDAOptimizer
5 class tf.train.MomentumOptimizer
6 class tf.train.AdamOptimizer
7 class tf.train.FtrlOptimizer
8 class tf.train.RMSPropOptimizer

這 8 個優化器對應 8 種優化方法,分別是梯度下降法(BGD 和 SGD)、Adadelta 法、Adagrad
法(Adagrad 和 AdagradDAO)、Momentum 法(Momentum 和 Nesterov Momentum)、Adam、
Ftrl 法和 RMSProp 法,其中 BGD、SGD、Momentum 和 Nesterov Momentum 是手動指定學習
率的,其余算法能夠自動調節學習率。

(1)BGD 法

  BGD 的全稱是 batch gradient descent,即批梯度下降。這種方法是利用現有參數對訓練集

中的每一個輸入生成一個估計輸出 y i ,然后跟實際輸出 y i 比較,統計所有誤差,求平均以后得
到平均誤差,以此作為更新參數的依據。它的迭代過程為:
  (1)提取訓練集中的所有內容{x 1 , …, x n },以及相關的輸出 y i ;
  (2)計算梯度和誤差並更新參數。
  這種方法的優點是,使用所有訓練數據計算,能夠保證收斂,並且不需要逐漸減少學習率;
缺點是,每一步都需要使用所有的訓練數據,隨着訓練的進行,速度會越來越慢。
  那么,如果將訓練數據拆分成一個個批次(batch),每次抽取一批數據來更新參數,是不
是會加速訓練呢?這就是最常用的 SGD。

   

2.SGD 法
  SGD 的全稱是 stochastic gradient descent,即隨機梯度下降。因為這種方法的主要思想是將
數據集拆分成一個個批次(batch),隨機抽取一個批次來計算並更新參數,所以也稱為 MBGD
(minibatch gradient descent)。
SGD 在每一次迭代計算 mini-batch 的梯度,然后對參數進行更新。與 BGD 相比,SGD 在
訓練數據集很大時,仍能以較很的速度收斂。但是,它仍然會有下面兩個缺點。
(1)由於抽取不可避免地梯度會有誤差,需要手動調整學習率(learning rate),但是選擇
合適的學習率又比較困難。尤其在訓練時,我們常常想對常出現的特征更新速度很一些,而對
不常出現的特征更新速度慢一些,而 SGD 在更新參數時對所有參數采用一樣的學習率,因此無法滿足要求。

(2)SGD 容易收斂到局部最優,並且在某些情況下可能被困在鞍點。
為了解決學習率固定的問題,又引入了 Momentum 法。

3.Momentum 法
Momentum 是模擬物理學中動量的概念,更新時在一定程度上保留之前的更新方向,利用
當前的批次再微調本次的更新參數,因此引入了一個新的變量 v(速度),作為前幾次梯度的累
加。因此,Momentum 能夠更新學習率,在下降初期,前后梯度方向一致時,能夠加速學習;
在下降的中后期,在局部最小值的附近來回震盪時,能夠抑制震盪,加很收斂。

4.Nesterov Momentum 法
Nesterov Momentum 法由Ilya Sutskever 在Nesterov 工作的啟發下提出的,是對傳統Momentum
法的一項改進,其基本思路如圖

       

標准 Momentum 法首先計算一個梯度(短的 1 號線),然后在加速更新梯度的方向進行一
個大的跳躍(長的 1 號線);Nesterov 項首先在原來加速的梯度方向進行一個大的跳躍(2 號線),
然后在該位置計算梯度值(3 號線),然后用這個梯度值修正最終的更新方向(4 號線)。
上面介紹的優化方法都需要我們自己設定學習率,接下來介紹幾種自適應學習率的優化
方法。

5.Adagrad 法
Adagrad 法能夠自適應地為各個參數分配不同的學習率,能夠控制每個維度的梯度方向。
這種方法的優點是能夠實現學習率的自動更改:如果本次更新時梯度大,學習率就衰減得很一
些;如果這次更新時梯度小,學習率衰減得就慢一些。

6.Adadelta 法
Adagrad 法仍然存在一些問題:其學習率單調遞減,在訓練的后期學習率非常小,並且需

要手動設置一個全局的初始學習率。Adadelta 法用一階的方法,近似模擬二階牛頓法,解決了
這些問題。

7.RMSprop 法
RMSProp 法與 Momentum 法類似,通過引入一個衰減系數,使每一回合都衰減一定比例。
在實踐中,對循環神經網絡(RNN)效果很好。
8.Adam 法
Adam 的名稱來源於自適應矩估計
① (adaptive moment estimation)。Adam 法根據損失函數
針對每個參數的梯度的一階矩估計和二階矩估計動態調整每個參數的學習率。
9.各個方法的比較
Karpathy 在 MNIST 數據集上用上述幾個優化器做了一些性能比較,發現如下規律
② :在不
怎么調整參數的情況下,Adagrad 法比 SGD 法和 Momentum 法更穩定,性能更優;精調參數的
情況下,精調的 SGD 法和 Momentum 法在收斂速度和准確性上要優於 Adagrad 法。

各個優化器的損失值比較結果如圖

各個優化器的測試准確率比較如圖

各個優化器的訓練准確率比較如圖


免責聲明!

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



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