TensorFlow2.0(5):張量限幅


 

 

 

注:本系列所有博客將持續更新並發布在github上,您可以通過github下載本系列所有文章筆記文件。

 

1 maxmium()與minmium()

 

maximum()用於限制最小值,也即是說,將一個tensor中小於指定值的元素替換為指定值:

In [1]:
import tensorflow as tf
In [2]:
a = tf.range(10)
a
Out[2]:
<tf.Tensor: id=3, shape=(10,), dtype=int32, numpy=array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=int32)>
In [3]:
tf.maximum(a, 4)
Out[3]:
<tf.Tensor: id=5, shape=(10,), dtype=int32, numpy=array([4, 4, 4, 4, 4, 5, 6, 7, 8, 9], dtype=int32)>
In [4]:
b = tf.random.uniform([3,4], minval=1, maxval=10, dtype=tf.int32)
b
Out[4]:
<tf.Tensor: id=9, shape=(3, 4), dtype=int32, numpy=
array([[8, 2, 4, 1],
       [9, 5, 4, 7],
       [6, 5, 8, 6]], dtype=int32)>
In [5]:
tf.maximum(b, 4)
Out[5]:
<tf.Tensor: id=11, shape=(3, 4), dtype=int32, numpy=
array([[8, 4, 4, 4],
       [9, 5, 4, 7],
       [6, 5, 8, 6]], dtype=int32)>
 

minium()方法與maximum()方法想法,用於限制一個tensor的最大值,即將tensor中大於指定值的元素替換為指定值:

In [6]:
tf.minimum(a, 6)
Out[6]:
<tf.Tensor: id=13, shape=(10,), dtype=int32, numpy=array([0, 1, 2, 3, 4, 5, 6, 6, 6, 6], dtype=int32)>
In [7]:
tf.minimum(b, 6)
Out[7]:
<tf.Tensor: id=15, shape=(3, 4), dtype=int32, numpy=
array([[6, 2, 4, 1],
       [6, 5, 4, 6],
       [6, 5, 6, 6]], dtype=int32)>
 

如果要同時限制一個tensor的最大值和最小值,可以這么做:

In [8]:
tf.minimum(tf.maximum(b,4),6)
Out[8]:
<tf.Tensor: id=19, shape=(3, 4), dtype=int32, numpy=
array([[6, 4, 4, 4],
       [6, 5, 4, 6],
       [6, 5, 6, 6]], dtype=int32)>
 

這種同時調用minmium()和maxmium()的方法不夠便捷,所以TensorFlow中提供了clip_by_value()方法來實現這一功能。

 

2 clip_by_value()

 

clip_by_value()底層也是通過調用minmium()和maxmium()方法來實現同時限制最大值、最小值功能,我們現在來感受一下:

In [9]:
b
Out[9]:
<tf.Tensor: id=9, shape=(3, 4), dtype=int32, numpy=
array([[8, 2, 4, 1],
       [9, 5, 4, 7],
       [6, 5, 8, 6]], dtype=int32)>
In [10]:
tf.clip_by_value(b,4,6)
Out[10]:
<tf.Tensor: id=23, shape=(3, 4), dtype=int32, numpy=
array([[6, 4, 4, 4],
       [6, 5, 4, 6],
       [6, 5, 6, 6]], dtype=int32)>
 

3 relu()

 

relu()方法將tensor最小值限制為0,相當於tf.maxmium(a,0),注意,relu()方法在tf.nn模塊中:

In [11]:
a = tf.range(-5,5,1)
a
Out[11]:
<tf.Tensor: id=27, shape=(10,), dtype=int32, numpy=array([-5, -4, -3, -2, -1,  0,  1,  2,  3,  4], dtype=int32)>
In [12]:
tf.nn.relu(a)
Out[12]:
<tf.Tensor: id=28, shape=(10,), dtype=int32, numpy=array([0, 0, 0, 0, 0, 0, 1, 2, 3, 4], dtype=int32)>
In [13]:
b = tf.random.uniform([3,4],minval=-10, maxval=10, dtype=tf.int32)
b
Out[13]:
<tf.Tensor: id=32, shape=(3, 4), dtype=int32, numpy=
array([[-8, -1, -4,  7],
       [-6, -3,  2, -8],
       [ 5,  6,  2,  5]], dtype=int32)>
In [14]:
tf.nn.relu(b)
Out[14]:
<tf.Tensor: id=33, shape=(3, 4), dtype=int32, numpy=
array([[0, 0, 0, 7],
       [0, 0, 2, 0],
       [5, 6, 2, 5]], dtype=int32)>
 

4 cli_by_norm()

 

cli_by_norm()方法是根據tensor的L2范數(模)和給定裁切值按比例對tensor進行限幅。這種方法可以在不改變方向的前提下,按比例對向量進行限幅。 我們先手動實現這一過程,先定義一個向量:

In [15]:
a = tf.random.normal([2,3],mean=10)
a
Out[15]:
<tf.Tensor: id=39, shape=(2, 3), dtype=float32, numpy=
array([[ 9.93618 , 10.367402,  9.617832],
       [ 8.890949,  9.650288,  9.430309]], dtype=float32)>
 

然后求這個向量的L2范數,也就是向量的模:

In [16]:
n = tf.norm(a) 
n
Out[16]:
<tf.Tensor: id=44, shape=(), dtype=float32, numpy=23.66054>
 

向量處理模,就可以將向量縮放到0到1范圍:

In [17]:
a1 = a / n
a1
Out[17]:
<tf.Tensor: id=45, shape=(2, 3), dtype=float32, numpy=
array([[0.41994733, 0.43817267, 0.4064925 ],
       [0.3757712 , 0.4078642 , 0.39856696]], dtype=float32)>
 

對向量限幅時,例如限制在10范圍內:

In [18]:
a2 = a1 * 10
a2
Out[18]:
<tf.Tensor: id=47, shape=(2, 3), dtype=float32, numpy=
array([[4.1994734, 4.3817267, 4.064925 ],
       [3.757712 , 4.078642 , 3.9856696]], dtype=float32)>
 

clip_by_norm()方法實現的就是上述步驟:

In [19]:
tf.clip_by_norm(a,10)
Out[19]:
<tf.Tensor: id=63, shape=(2, 3), dtype=float32, numpy=
array([[4.1994734, 4.3817267, 4.064925 ],
       [3.757712 , 4.0786424, 3.9856696]], dtype=float32)>
 

當然,cli_by_norm()方法內部還做了一個判斷:如果給定的裁切值大於tensor的模,那就不會去對tensor進行修改,依舊返回tensor本身。繼續上面例子,a的模為25.625225,如果給定的裁切值大於這個值,就不會對a進行限幅:

In [20]:
tf.clip_by_norm(a,26)
Out[20]:
<tf.Tensor: id=79, shape=(2, 3), dtype=float32, numpy=
array([[ 9.936181, 10.367402,  9.617832],
       [ 8.890949,  9.650288,  9.430309]], dtype=float32)>
 

5 clip_by_global_norm()

 

在梯度更新等諸多場景中,需要同時綜合多個參數(tensor)進行梯度更新,這時候,clip_by_norm()就滿足不了需求了,所以就有了cip_by_global_norm()方法。cip_by_global_norm()方法限幅原理與clip_by_norm()是一樣的,都是綜合范數和給定的裁切值進行限幅,不同的是,cip_by_global_norm()方法方法計算范數時是綜合給定的多個tensor進行計算。

 

注:clip_by_global_norm()方法用於修正梯度值,控制梯度爆炸的問題。梯度爆炸和梯度彌散的原因一樣,都是因為鏈式法則求導的關系,導致梯度的指數級衰減。為了避免梯度爆炸,需要對梯度進行修剪。

 

以下面三個向量為例,同時進行限幅:

In [21]:
t1 = tf.random.normal([3],mean=10)
t1
Out[21]:
<tf.Tensor: id=85, shape=(3,), dtype=float32, numpy=array([8.257121, 7.466969, 8.756236], dtype=float32)>
In [22]:
t2 = tf.random.normal([3],mean=10)
t2
Out[22]:
<tf.Tensor: id=91, shape=(3,), dtype=float32, numpy=array([10.112761, 10.555879,  9.646121], dtype=float32)>
In [23]:
t3 = tf.random.normal([3],mean=10)
t3
Out[23]:
<tf.Tensor: id=97, shape=(3,), dtype=float32, numpy=array([9.884818, 8.648524, 9.125227], dtype=float32)>
In [24]:
t_list = [t1,t2,t3]
 

首先計算全局L2范數,計算公式為: global_norm = sqrt(sum([L2norm(t)**2 for t in t_list]))

In [25]:
global_norm = tf.norm([tf.norm(t) for t in t_list])
 

假設給定裁切值為25:

In [26]:
[t*25/global_norm for t in t_list]
Out[26]:
[<tf.Tensor: id=121, shape=(3,), dtype=float32, numpy=array([7.4725804, 6.757504 , 7.9242725], dtype=float32)>,
 <tf.Tensor: id=124, shape=(3,), dtype=float32, numpy=array([9.151909, 9.552924, 8.729607], dtype=float32)>,
 <tf.Tensor: id=127, shape=(3,), dtype=float32, numpy=array([8.945623, 7.826795, 8.258204], dtype=float32)>]
In [27]:
tf.clip_by_global_norm(t_list,25)
Out[27]:
([<tf.Tensor: id=148, shape=(3,), dtype=float32, numpy=array([7.47258  , 6.7575035, 7.9242725], dtype=float32)>,
  <tf.Tensor: id=149, shape=(3,), dtype=float32, numpy=array([9.151908, 9.552924, 8.729606], dtype=float32)>,
  <tf.Tensor: id=150, shape=(3,), dtype=float32, numpy=array([8.945623 , 7.8267946, 8.2582035], dtype=float32)>],
 <tf.Tensor: id=136, shape=(), dtype=float32, numpy=27.624733>)
 

計算結果是一樣的,不過clip_by_global_norm()返回兩個值,分別是各向量限幅后的返回值列表、全局范數。


免責聲明!

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



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