1 maxmium()与minmium()

 

maximum()用于限制最小值,也即是说,将一个tensor中小于指定值的元素替换为指定值:

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

minium()方法与maximum()方法想法,用于限制一个tensor的最大值,即将tensor中大于指定值的元素替换为指定值:

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

如果要同时限制一个tensor的最大值和最小值,可以这么做:

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

这种同时调用minmium()和maxmium()的方法不够便捷,所以TensorFlow中提供了clip_by_value()方法来实现这一功能。

 

2 clip_by_value()

 

clip_by_value()底层也是通过调用minmium()和maxmium()方法来实现同时限制最大值、最小值功能,我们现在来感受一下:

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

3 relu()

 

relu()方法将tensor最小值限制为0,相当于tf.maxmium(a,0),注意,relu()方法在tf.nn模块中:

In [19]:
a = tf.range(-5,5,1)
a
Out[19]:
<tf.Tensor: id=61, shape=(10,), dtype=int32, numpy=array([-5, -4, -3, -2, -1,  0,  1,  2,  3,  4])>
In [20]:
tf.nn.relu(a)
Out[20]:
<tf.Tensor: id=63, shape=(10,), dtype=int32, numpy=array([0, 0, 0, 0, 0, 0, 1, 2, 3, 4])>
In [21]:
b = tf.random.uniform([3,4],minval=-10, maxval=10, dtype=tf.int32)
b
Out[21]:
<tf.Tensor: id=68, shape=(3, 4), dtype=int32, numpy=
array([[ 2,  0,  6,  7],
       [ 6,  2, -1, -8],
       [-7, -6, -1, -6]])>
In [22]:
tf.nn.relu(b)
Out[22]:
<tf.Tensor: id=70, shape=(3, 4), dtype=int32, numpy=
array([[2, 0, 6, 7],
       [6, 2, 0, 0],
       [0, 0, 0, 0]])>
 

4 cli_by_norm()

 

cli_by_norm()方法是根据tensor的L2范数(模)和给定裁切值按比例对tensor进行限幅。这种方法可以在不改变方向的前提下,按比例对向量进行限幅。 我们先手动实现这一过程,先定义一个向量:

In [23]:
a = tf.random.normal([2,3],mean=10)
a
Out[23]:
<tf.Tensor: id=77, shape=(2, 3), dtype=float32, numpy=
array([[10.035151, 11.085695, 11.230698],
       [11.222443, 10.120184,  8.86371 ]], dtype=float32)>
 

然后求这个向量的L2范数,也就是向量的模:

In [24]:
n = tf.norm(a)
n
Out[24]:
<tf.Tensor: id=83, shape=(), dtype=float32, numpy=25.625225>
 

向量处理模,就可以将向量缩放到0到1范围:

In [25]:
a1 = a / n
a1
Out[25]:
<tf.Tensor: id=85, shape=(2, 3), dtype=float32, numpy=
array([[0.3916122 , 0.4326087 , 0.4382673 ],
       [0.43794513, 0.39493054, 0.34589785]], dtype=float32)>
 

对向量限幅时,例如限制在10范围内:

In [29]:
a2 = a1 * 10
a2
Out[29]:
<tf.Tensor: id=94, shape=(2, 3), dtype=float32, numpy=
array([[3.916122 , 4.326087 , 4.382673 ],
       [4.3794513, 3.9493055, 3.4589787]], dtype=float32)>
 

clip_by_norm()方法实现的就是上述步骤:

In [30]:
tf.clip_by_norm(a,10)
Out[30]:
<tf.Tensor: id=111, shape=(2, 3), dtype=float32, numpy=
array([[3.9161217, 4.326087 , 4.382673 ],
       [4.3794513, 3.9493055, 3.4589784]], dtype=float32)>
 

当然,cli_by_norm()方法内部还做了一个判断:如果给定的裁切值大于tensor的模,那就不会去对tensor进行修改,依旧返回tensor本身。继续上面例子,a的模为25.625225,如果给定的裁切值大于这个值,就不会对a进行限幅:

In [31]:
tf.clip_by_norm(a,26)
Out[31]:
<tf.Tensor: id=128, shape=(2, 3), dtype=float32, numpy=
array([[10.035151, 11.085695, 11.230698],
       [11.222443, 10.120184,  8.86371 ]], 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 [52]:
t1 = tf.random.normal([3],mean=10)
t1
Out[52]:
<tf.Tensor: id=298, shape=(3,), dtype=float32, numpy=array([9.564309, 9.443071, 8.37221 ], dtype=float32)>
In [53]:
t2 = tf.random.normal([3],mean=10)
t2
Out[53]:
<tf.Tensor: id=305, shape=(3,), dtype=float32, numpy=array([10.853721,  9.294285,  8.552048], dtype=float32)>
In [54]:
t3 = tf.random.normal([3],mean=10)
t3
Out[54]:
<tf.Tensor: id=312, shape=(3,), dtype=float32, numpy=array([10.658405,  9.979499,  8.440408], dtype=float32)>
In [55]:
t_list = [t1,t2,t3]
 

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

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

假设给定裁切值为25:

In [57]:
[t*25/global_norm for t in t_list]
Out[57]:
[<tf.Tensor: id=337, shape=(3,), dtype=float32, numpy=array([8.388461, 8.282128, 7.34292 ], dtype=float32)>,
 <tf.Tensor: id=340, shape=(3,), dtype=float32, numpy=array([9.519351 , 8.151634 , 7.5006485], dtype=float32)>,
 <tf.Tensor: id=343, shape=(3,), dtype=float32, numpy=array([9.348048, 8.752607, 7.402734], dtype=float32)>]
In [58]:
tf.clip_by_global_norm(t_list,25)
Out[58]:
([<tf.Tensor: id=365, shape=(3,), dtype=float32, numpy=array([8.388461 , 8.282129 , 7.3429203], dtype=float32)>,
  <tf.Tensor: id=366, shape=(3,), dtype=float32, numpy=array([9.519351, 8.151634, 7.500649], dtype=float32)>,
  <tf.Tensor: id=367, shape=(3,), dtype=float32, numpy=array([9.348048, 8.752607, 7.402734], dtype=float32)>],
 <tf.Tensor: id=355, shape=(), dtype=float32, numpy=28.50436>)
 

计算结果是一样的,不过clip_by_global_norm()返回两个值,分别是各向量限幅后的返回值列表、全局范数。

10-09 08:39