tensorflow常用函数以及技巧

我常用的深度学习框架有两种 pytorch 和 tensorflow ,在学校的时候习惯用 pytorch ,实习工作时主要用 tensorflow. 本文主要记录我常用的 tensorflow 函数以及一些使用实例

常用函数

Casting

主要用于类型的转换,常用的函数有:

  • tf.cast(x, dtype, name=None)

Reduce

  • tf.reduce_sum(input_tensor, reduction_indices=None, keep_dims=False, name=None) 在指定维度上求和
  • tf.reduce_max(input_tensor, reduction_indices=None, keep_dims=False, name=None) 在指定维度上求最大值
  • tf.reduce_min(input_tensor, reduction_indices=None, keep_dims=False, name=None) 在指定维度上求最小值
  • tf.reduce_mean(input_tensor, reduction_indices=None, keep_dims=False, name=None) 在指定维度上求均值

实例一:求张量的 mask 和长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import tensorflow as tf

sess = tf.InteractiveSession()

# placeholder
inputs = tf.placeholder(tf.int32,
shape=[None, None],
name='inputs')

# 获取inputs的mask,
inputs_mask = tf.cast(inputs, tf.bool)

# 获取 inputs 在0轴的长度
inputs_len = tf.reduce_sum(tf.cast(inputs_mask, tf.int32), axis=1)

x = [[1, 2, 3],
[4, 5, 0],
[6, 0, 0]]

mask, lens = sess.run([inputs_mask, inputs_len],
feed_dict={inputs: x})

print("Mask:\n", mask)
print("Length:\n", lens)

输出结果

1
2
3
4
5
6
Mask:
[[ True True True]
[ True True False]
[ True False False]]
Length:
[3 2 1]

shape 与 shape 变换

  • tf.shape(input, name=None) 获取张量的形状
  • tf.reshape(tensor, shape, name=None) 变换张量形状
  • tf.squeeze(input, squeeze_dims=None, name=None) 压缩(去除)张量维度(压缩形状大小为1的维度)
  • tf.expand_dims(input, dim, name=None) 扩充张量维度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import tensorflow as tf

sess = tf.InteractiveSession()

inputs = tf.placeholder(tf.int32, [1, 2, 3], name="inputs")
print(sess.run(tf.shape(inputs))) # (1, 2, 3)

# 挤压维度,因为第0维的大小为1故可以去除
inputs1 = tf.squeeze(inputs, 0)
print(sess.run(tf.shape(inputs1))) # (2, 3)

# 扩充维度
inputs2 = tf.expand_dims(inputs1, 0)
print(sess.run(tf.shape(inputs2))) # (1, 2, 3)

Slicing and Joining 切片和连接

tf.slice(input_, begin, size, name=None)

tf的切片略难理解,先理解函数参数的意义

  • input_:张量
  • begin: n维列表,begin[i] 表示从inputs中第i轴抽取数据时,从第i轴的begin[i]个元素开始抽取数据
  • size: n维列表,size[i]表示要抽取的第i轴元素的数目

举个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import tensorflow as tf
sess = tf.InteractiveSession()
inputs = tf.constant([[[1, 1, 1], [2, 2, 2]],
[[3, 3, 3], [4, 4, 4]],
[[5, 5, 5], [6, 6, 6]]])

"""
slice_1: [[[3 3 3]]]

第0轴: begin[0] = 1 代表从第1个元素开始取,size[0] = 1 代表取一个元素,
所以第0轴的结果是 [[[3, 3, 3], [4, 4, 4]]]
第1轴: begin[1] = 0 代表从第0个元素开始取,size[1] = 1 代表取一个元素,
故得到 [[[3, 3, 3]]]
第2轴: begin[2] = 0 代表从第0个元素开始取,size[2] = 3 代表取两个元素
故得到 [[[3, 3]]]

所以最后结果为[[[3, 3]]]
"""
print("slice_1")
print(sess.run(tf.slice(inputs, [1, 0, 0], [1, 1, 2], name="slice_1")))

"""
slice_2: [[[3 3 3]
[4 4 4]]
[[5 5 5]
[6 6 6]]]

-1 代表取完该维度的所有元素
"""
print("slice_2")
print(sess.run(tf.slice(inputs, [1, 0, 0], [2, -1, -1])))

tf.split(value, num_or_size_splits, axis=0, num=None, name=‘split’)

  • value: 张量
  • num_or_size_splits:int / list / tensor 指定切割大小
  • axis:指定切割维度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import tensorflow as tf
sess = tf.InteractiveSession()
inputs = tf.constant([[1, 2, 3],
[4, 5, 6]])
"""
在第0轴上切割
[[1 2 3]]

[[4 5 6]]
"""
outputs = tf.split(inputs, 2, axis=0)

"""
在第1轴上切割
[[1]
[4]]

[[2]
[5]]

[[3]
[6]]
"""
outputs1 = tf.split(inputs, 3, axis=1)

"""
指定每个切割区间大小(各区间大小之和应该等于该维度大小)
[[1]
[4]]

[[2 3]
[5 6]]
"""
outputs2 = tf.split(inputs, [1, 2], axis=1)

tf.tile(input, multiples, name=None)

  • input:输入张量
  • multiples:list 指定每个维度重复多少次

第i维的输出为 input.dims(i) * multiples[i], tf.tile 常用于 trilinear 等操作上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import tensorflow as tf
sess = tf.InteractiveSession()
inputs = tf.constant([[1, 2, 3],
[4, 5, 6]])

"""
在最后扩充一维,并重复三次

[[[1 1 1]
[2 2 2]
[3 3 3]]

[[4 4 4]
[5 5 5]
[6 6 6]]]
"""
expands = tf.tile(tf.expand_dims(inputs, 2), [1, 1, 3])

tf.pad(tensor, paddings, mode=‘CONSTANT’, name=None, constant_values=0)

  • paddings: 是一个张量,[N, 2]形式,N代表张量的阶,2代表必须是2列
  • mode:可以取三个值
    • CONSTANT:常量填充,值可由 constant_value 设定
    • REFLECT: 映射填充,上下(axis=0)填充顺序和paddings是相反的,左右(axis=1)顺序补齐
    • SYMMETRIC:对称填充,上下(axis=0)填充顺序是和paddings相同的,左右(axis=1)对称补齐

填充的方向一般是有向前和向后填充 , pad函数也是类似的,paddings 必须是两列用于指定维度的前后填充大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import tensorflow as tf
sess = tf.InteractiveSession()
inputs = tf.constant([[[1, 1]],
[[2, 2]]])

"""
第0轴:[1, 1] 指定向前和向后填充1个单位
第1轴:[1, 1] 指定向前和向后填充1个单位
第2轴:[1, 2] 指定向前填充1个单位向后填充2个单位

[[[0 0 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]]

[[0 0 0 0 0]
[0 1 1 0 0]
[0 0 0 0 0]]

[[0 0 0 0 0]
[0 2 2 0 0]
[0 0 0 0 0]]

[[0 0 0 0 0]
[0 0 0 0 0]
[0 0 0 0 0]]]
"""
outputs = tf.pad(inputs, [[1, 1], [1, 1], [1, 2]])

tf.concat(values, axis, name=‘concat’)

在指定维度拼接.

tf.stack(values, axis=0, name=”pack”)

将一个R维张量列表沿着axis轴组合成一个R+1维的张量。作用有点像 python 的 zip 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import tensorflow as tf
sess = tf.InteractiveSession()

x = [1, 2]
y = [3, 4]

"""
[[1 3]
[2 4]]
"""
outputs = tf.stack([x, y], axis=1)

"""
[[1 2]
[3 4]]
"""
outputs = tf.stack([x, y], axis=0)

tf.unstack(value, num=None, axis=0, name=‘unstack’)

tf.stack 的反过程

1
2
3
4
5
6
7
8
9
10
11
12
import tensorflow as tf

sess = tf.InteractiveSession()

x = [[1, 2],
[3, 4]]

"""
[1 3]
[2 4]
"""
a, b = tf.unstack(x, axis=1)

tf.reverse_sequence(input, seq_lengths, seq_axis=None, batch_axis=None, name=None, seq_dim=None, batch_dim=None)

常用在双向的网络上,如双向的RNN

tf.transpose(a, perm=None, name=‘transpose’, conjugate=False)

维度位置交换函数,此函数用的比较频繁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import tensorflow as tf

sess = tf.InteractiveSession()

x = tf.constant([[[1, 2],
[3, 4]]])

y = tf.transpose(x, [1, 0, 2])

"""
x shape: [1 2 2]
x: [[[1 2]
[3 4]]]
"""
print("x shape:", sess.run(tf.shape(x)))
print("x:", sess.run(x))

"""
y shape: [2 1 2]
y: [[[1 2]]

[[3 4]]]
"""
print("y shape:", sess.run(tf.shape(y)))
print("y:", sess.run(y))

tf.gather(params, indices, validate_indices=None, name=None, axis=0)

取得 axis 轴指定下标 indices 的值

1
2
3
4
5
6
7
8
9
10
11
12
import tensorflow as tf

sess = tf.InteractiveSession()

x = tf.constant([[1, 2, 3, 4],
[5, 6, 7, 8]])

"""
[[2 4]
[6 8]]
"""
y = tf.gather(x, [1, 3], axis=1)

Sequence

tf.sequence_mask(lengths, maxlen=None, dtype=tf.bool, name=None)

构建序列长度的mask标志,是以长度来构建的

1
2
3
4
5
6
7
8
9
10
11
12
13
import tensorflow as tf

sess = tf.InteractiveSession()

lens = tf.constant([1, 2, 3])
max_len = tf.reduce_max(lens)

"""
[[ True False False]
[ True True False]
[ True True True]]
"""
mask = tf.sequence_mask(lens, max_len)

常用技巧

Attention的计算

attention其实并不是什么高大上的东西,从数学角度上看其实原理很简单,如果对Attention机制能有较深的理解且能实现,那么对于大部分的 paper 都可以实现了。

Attention 机制的简单理解

假设有三个值 \(Q\)\(K\)\(V\) (\(Q\)代表 \(Query\), \(K\)代表\(Key\)\(V\)代表\(Value\)),其中 \(K\)\(V\) 是匹配的,那么Attention可以认为是通过 \(Query\) 去注意 \(Value\) 中重要的信息

\[\begin{align*} & sim = Similarity(Q, K) \\ & \alpha = softmax(sim) \\ & V_{attn} = \alpha \cdot V \end{align*}\]

其中 Simlarity 是计算相似度的函数,最简单的可通过MLP来计算,在NLP中一般有 \(K = V\)

一个简单的例子

实现一个简单的 self atttion,self attention中 \(Q = K = V\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import tensorflow as tf
sess = tf.InteractiveSession()

def attention(value):
Q = value
K = value
V = value

shape = V.get_shape().as_list()

W = tf.get_variable('attn_W',
shape=[shape[-1], shape[-1]],
initializer=tf.truncated_normal_initializer(stddev=0.1))

U = tf.get_variable('attn_U',
shape=[shape[-1], shape[-1]],
initializer=tf.truncated_normal_initializer(stddev=0.1))

P = tf.get_variable('attn_P',
shape=[shape[-1], 1],
initializer=tf.truncated_normal_initializer(stddev=0.1))

Q = tf.reshape(Q, [-1, shape[-1]])
K = tf.reshape(K, [-1, shape[-1]])

sim = tf.tanh(tf.add(
tf.matmul(Q, W),
tf.matmul(K, U)))
alpha = tf.nn.softmax(
tf.reshape(tf.matmul(sim, P), [-1, shape[1], 1]), axis=1)
V_attn = tf.multiply(alpha, V)
return V_attn, alpha

inputs = tf.random_normal([2, 3, 5])
attn, alpha = attention(inputs)

init = tf.global_variables_initializer()
sess.run(init)

print("inputs:\n", sess.run(inputs))
print("alpha:\n", sess.run(alpha))
print("attn applied:\n", sess.run(attn))

sean lee wechat
欢迎关注我的公众号!
感谢支持!