tf一些函数
对于 tf.get_variable() vs tf.Variable(),tf.name_scope() vs tf.variable_scope()
最近刚接手tf,有些函数,理解不清楚,做一下笔记。
对于函数,知道平时的用法就行了,不用纠结里面的每一个参数的意思,基本用不到,还浪费时间
一、name-scope 和 variable-scope
name-scope 和 variable-scope 都是命名空间,意思就是,定义一个空间,在这个空间里面的,定义的变量,基本都会以这个命名空间作为其变量名的前缀。(但是也有例外)
比如下图中,尽管是两段代码,但都是在variable-scope定义的名称为“V1”的空间中。而reuse命令,允许共享当前scope(同一个命名空间)下的所有变量,所以a1和a3的变量名都是 V1/a1:0
import tensorflow as tf
import numpy as np
with tf.variable_scope('V1'):
a1 = tf.get_variable(name='a1', shape=[1], initializer=tf.constant_initializer(1))
with tf.variable_scope('V1', reuse=True):
a3 = tf.get_variable('a1')
- name_scope对 get_variable()创建的变量 的名字不会有任何影响,而创建的op会被加上前缀.
- tf.get_variable_scope() 返回的只是 variable_scope,不管 name_scope.所以以后我们在使用tf.get_variable_scope().reuse_variables() 时可以无视name_scope
- name_scope 是给op_name加前缀, variable_scope是给get_variable()创建的变量的名字加前缀。
二 、tf 的 reduce(降维)操作
1. 基础理解
1.1 向量的“维”
这里加入基础理解的意思,是为了防止概念不清晰。
比如若说X是一个n维向量,表示X中,含有n个元素,可以在n维坐标系中表示出来(原点到X坐标的向量)。另外,在机器学习的降维中,是把X从n维降到k维。 ---但这不是tf中的‘‘降维”
n维向量!= n维数组!!!
1.2 tf中的“维”
在tf中,用constant定义的tensor
- 0维数组,表示一个数,如:6。(可以理解为“点”)
- 1维数组,表示一个a = [1,2,3]。(可以理解为“线”,这也和上面的向量联系起来了。一方面,这是一个三维向量,另一方面,在n维坐标系中,向量是一条直线,是“线”)
- 2维数组,表示一个
b = [[1,2,3],
[4,5,6]]。(可以理解为“面”,或者矩阵) - 3维数组,表示一个
c = [[[1,2,3],[4,5,6]],
[[7,8,9],[1,1,1]],
[[2,2,2],[3,3,3]]]
可以理解为“体”。
1.3 对数组进行索引
索引,类似于Python中的列表索引,0表示第一个元素,但又不一样。
比如,对上面的2维数组b进行索引 ,得到
b[0] = [1,2,3],
c[0] = [[1.2.3].[4.5.6]] (都是数组里的第一个元素)
1.4 tf.reduce_xxx操作中的axis
因为版本的不同,有时参数axis(轴)等同于参数reduction_indices(减去索引)
这里所说的,应该是侧重于axis轴的意思
这里作者就按照坐标轴来理解。比如,上面的三维数组c,我把它理解为由x,y,z轴组成的坐标系中的三个自变量组成的“体”,c的0维就相当于x变量,剩下的由y和z两个组成的几个平面,也就是剩下的3个二维数组。这里的“c的0维”不等于“c[0]”。
那么。去掉c的0维,就相当于只剩下y,z两个变量组成的三个平面。
reduc_sum(c,0)那就理解为,先把三个平面相加,再去掉x自变量。
所以,就相当于,先把每个第0维里面的元素(即三个二维数组)按照规则相加,再去掉最外面的“0维”。于是,最后剩一个二维数组。
最外面的中括号代表了第0维,再往里代表了第1维(比如[[1,2,3],[4,5,6]],第一维里面的元素相加,就是[1,2,3]+[4,5,6]),再往里代表了第2维(比如[1,2,3].)
1.5 代码
具体别的情况,就看下面代码。其中,keep_dims表示的意思就是,照常计算,但不消除该维。
import tensorflow as tf
import numpy as np
x = [[[1,2,3],[4,5,6]],
[[7,8,9],[1,1,1]],
[[2,2,2],[3,3,3]]]
x = tf.constant(x)
# x 后面没有参数,就默认为把所有的元素相加
a = tf.reduce_sum(x)
#把所有0维里的元素都按照规则相加,再消去0维度(即消去代表0维度的中括号)
b = tf.reduce_sum(x,0)
#以下没有消去的维度都是保留的
#只举一个例子,把目前所有1维里的元素都相加如 ==>得到相加的结果 ==>再消去1维度
# [1,2,3]+[4,5,6] ==> [5,7,9] ==>5,7,9
c = tf.reduce_sum(x,1)
#只举一个例子, 把所有第2维的元素都相加 ==> 得到结果 ==> 消去2维度
# [1+2+3],[4+5+6] ==> [6],[15] ==> 6,15
d = tf.reduce_sum(x,2)
# 把第1维所有元素相加,得到结果 ==> 再把第2维所有元素相加,得到结果 ==>消去1,2维度
# [[[5,7,9]],[[8,9,10]],[[5,5,5]]]==>[[[21]],[[27]],[[15]]]==>[21,27,15]
f = tf.reduce_sum(x,[1,2])
#把0维所有元素相加以后,1维所有元素相加以后,2维所有元素相加以后消去012维
g = tf.reduce_sum(x,[0,1,2])
#把目前所有1维里的元素都相加如 ==>得到相加的结果 ==>但不消去1维度(即括号不去掉)== 保留该维度
# [1,2,3]+[4,5,6] ==> [5,7,9] ==>[5,7,9]
h = tf.reduce_sum(x,1,keep_dims=True)
#只举一个例子, 把所有第2维的元素都相加 ==> 得到结果 ==> 但不消去2维度 == 保留该维度
# [1+2+3],[4+5+6] ==> [6],[15] ==> [6],[15]
i = tf.reduce_sum(x,2,keep_dims=True)
with tf.Session() as sess:
print('a',sess.run(a))
print('b',sess.run(b))
print('c',sess.run(c))
print('d',sess.run(d))
print('f',sess.run(f))
print('g',sess.run(g))
print('h',sess.run(h))
print('i',sess.run(i))
三、tf.nn.embedding_lookup()函数
基础知识
shape,列表和数组,
-
1 shape方法
shape方法得到的结果的是一个tuple,从第一个元素到最后一个元素,依次表示的是从数组第0维里含有元素的个数,第1维含有元素的个数,第2维。。。如上面的x数组,x.shape ==>(3,2,3);再如a=[1,2,3]是一个列表,那么a.shape ==>(3,)一定有一个逗号,表示这是一个元组的数据结构 -
2 列表和数组
在运用一个函数或者方法之前,一定要弄明白,需要传入的参数是什么数据结构的!!!就比如这个方法,作者一开始定义好x之后,没有用np.array设置成数组,或者用constant设置成一个整体的tensor,结果x一直是列表形式,最终出现毛病。
def embedding_lookup(params, ids, partition_strategy="mod"),主要讲这三个参数。
params可以是一个tensor,也可以是多个tensor,不过输入多个tensor的时候,需要用作为params=[a,b,c]的形式进行输入。
当系统认为,params的长度等于1时候,就和平时的索引一样,按照ids中的id索引就行。
但是当系统认为params的长度大于1的时候,就会用第三个参数(默认是“mod”)的模式,将params去掉里面每个tensor的中括号以后所有的元素个数,按照求余数相同的方式分成len(params)个切片,每个切片里的第i个id对应该切片里的第i个元素。
举个栗子:
a = [[1,2,3],[4,5,6]]
b = [[7,8,9],[1,1,1]]
c = [[2,2,2],[3,3,3]]
a = tf.constant(a)
b = tf.constant(b)
c = tf.constant(c)
那么传入params=[a,b,c],此时,系统认为params里是多个量了(3个tensor),那么去掉每个tensor的最外面中括号共有3*2=6个元素,所以一共可以有6个id,分别是012345,有3个tensor,就有3个切片。按照余数相同分组,不均匀就前面的分的多。分为[0,3],[1,4],[2,5],对应的分别是 a,b,c。索引id时,id等于3,就相当于索引的是a中第二个元素,所以索引的是[4,5,6].
另外,返回的tensor的shape应该是shape(ids) + shape.params[1:]
这里的params指的是被索引的那个param
比如ids = [3],那么返回的结果的shape应该是shape.[3] + shape.a[1:]=(1,)+(2,3)[1:] = (1,)+(3,) = (1,3),所以结果应该是[[4,5,6]],二维数组
但这里,这个函数是把a作为一个params了,作为一个tensor了。
另外需要注意的是,tf中传入多个参数,就是用中括号把参数作为一个整体,以列表的方式传给函数。所以,有的函数一旦遇到[a,b,c]这种形式的,就会认为a,b,c是参数。
所以,若是x一开始定义为列表格式的话,那么这个函数就会把这个‘表示为列表的中括号’认为是类似于上面的[a,b,c]这个传参的中括号,从而把x认为是由三个tensor组成的。然后就会启动上面所说的切片模式。比如用上面说的x,那么就会认为x是三个[2,3]的数组。
但是如果用np.array(x)或者用x=tf.constant(x)以后,x就被认为是一个整个的tensor了,那么这时候就认为x是1个,就不会启动上面的切片模式,就是正常的索引现象。
以后在遇到的时候,需要先明确x是列表,还是由constant定义好了的一个tensor(或者nparray定义的数组)。自己用的时候,最好用后者,把x当成是一个整体,若是需要输入多个,那么就用[a,b,c]的方式输入。
四、 tf.gather(params,indices,..)
合并 - 索引indices所指示的params中的切片
比如a = tf.gather(x,[0,2]),就是索引x这个tensor的0维下的元素的位置索引为0,2的两个元素 并合并起来,即结果再放进一个列表中, 即使只有一个元素也要放进列表中
import tensorflow as tf
x = [[[1,2,3],[4,5,6]],
[[7,8,9],[1,1,1]],
[[2,2,2],[3,3,3]]]
a = tf.gather(x,[0,2])
with tf.Session() as sess:
print('a',sess.run(a))#输出123,456和222,333
五、tf 的onehot函数
参考[tf.one_hot()函数简介](https://www.jianshu.com/writer#/notebooks/17771847/notes/18446692/preview
比如,输入indices是一维列表,[1,3,5,7,9],depth等于10
那么就会输出一个shape为[5,10]的二维数组,其中第一行中的索引为1的位置为1,其余为0.第二行中索引为3的位置为1,其余值为0.
如果输入的indices是一个shape是[1,5]的二维数组,[[1,3,5,7,9]],那么输出就会使一个[1,5,10]的三维数组。其中的onehot形式是一样的。