本文介绍了我遇到的一些TensorFlow的函数和类,记录了相关用法和自己的理解,一方面为了整理自己的思路,加深印象,另一方面也方便以后查阅。

构建卷积层

tf.nn.conv2d

tf.nn.conv2d(
input,
filter,
strides,
padding,
use_cudnn_on_gpu=True,
data_format=’NHWC’,
dilations=[1, 1, 1, 1],
name=None
)

  • 最常见的卷积层构造方法。
  • 参数解释:
    • input:为输入卷积层的数据,用4维的tensor来表示,shape为[batch, in_height, in_width, in_channels],具体含义是[训练时一个batch的图片数量, 图片高度, 图片宽度, 图像通道数],注意这是一个4维的Tensor,要求类型为float32和float64其中之一;
    • filter:表示卷积核,同样是4维的tensor,shape为[filter_height, filter_width, in_channels, out_channels],具体含义是[卷积核的高度,卷积核的宽度,图像通道数,卷积核个数],ilter_height, filter_width也可以说是卷积核在input的对应维度上的window size;第三维in_channels,就是参数input的第四维;out_channels有两个含义,一是有多少个卷积核,二是最终输出图像的通道数(一个卷积核的计算结果对应一个输出通道的值);
    • strides:表示卷积核在各个维度上的跨度,同样是4维tensor(各个维度的意义和input参数相同),[1, 1, 1, 1]表示卷积核一个一个像素移动着计算,[1, 2, 2, 1]表示在in_height和in_width维度上每隔一个像素计算一次。
    • padding:表示如何计算图像边缘的像素。只有两个可选值:VALIDSAMEVALID表示图像边缘一圈的像素不计算,SAME表示将图像边缘进行扩充以计算所有图像内像素(通常用0填充)。详细内容可参见TensorFlow官方Neural Network教程
  • return:返回值为经过卷积计算的结果(也被成为feature map),shape为[batch, out_height, out_width, out_channels],其中batch和input的batch相同,后三个参数则由卷积核来决定。
  • 如何计算:由卷积核在input图像上不断滑动,做卷积预算,最终得出输出的feature map,feature map的长宽要试输入图像、卷积核和padding方式共同决定。

tf.nn.max_pool

tf.nn.max_pool(
value,
ksize,
strides,
padding,
data_format=’NHWC’,
name=None
)

  • 函数作用:构建池化层的操作,实现下采样。
  • 参数解释:
    • value:需要池化的输入,一般池化层接在卷积层后面,所以输入通常是feature map,所以依然是[batch, height, width, channels]这样的shape;
    • ksize:池化窗口在各维度上的大小,一般是[1, height, width, 1],即保持batch和channel维度不变,只在height和width维度上做池话操作;
    • strides:和卷积类似,窗口在每一个维度上滑动的步长,一般是[1, stride, stride, 1](保持batch和channel不变);
    • padding:和卷积类似,可取’VALID’ 或者’SAME’;
  • return:返回经过maxpool计算后的tensor,shape为[batch, height, width, channels]。
  • 这个函数实现的是max_pool(最大池化操作),即在一个范围内的值中选最大值,作为卷积后二次提取的特征。

tf.train.Optimizer类

  • 这个类是tensorflow中各种优化器的父类,作用:给定一个目标函数/损失函数,根据一定的算法(优化器算法),来不断计算、更新函数中的变量,以使得最终函数取得最小值,即达到优化目标。
  • 下面介绍一些常用的函数API,虽然在各个优化器子类中会有一定变化,但大体还是一致的。

常用的优化类

  • GradientDescentOptimizer:梯度下降优化器;
  • AdamOptimizer:Adam优化器,一般直接用这个就行;

init

通常会在初始化时定义优化器的学习率/更新率,egoptimizer = tf.train.AdamOptimizer(1e-3)

compute_gradients

compute_gradients(
loss,
var_list=None,
gate_gradients=GATE_OP,
aggregation_method=None,
colocate_gradients_with_ops=False,
grad_loss=None
)

  • 函数作用:计算loss函数中各变量的梯度。
  • 参数解释:
    • loss:损失函数,在tf中就是一个变量;
    • var_list:需要计算梯度的变量列表,只有在这个列表中的变量才会计算梯度,默认是TRAINABLE_VARIABLES collection中的所有变量。那有人可能会问,TRAINABLE_VARIABLES collection里那么多变量,岂不是会有很多多余的?是这样的,但因为有那些多余的变量都不再loss函数中,因此计算出来的梯度也是0,所以也不会有影响。
    • gate_gradients:指定了计算操作可以并行化的程度,通常不管。
  • return:A list of (gradient, variable) pairs,即返回一个梯度-变量对的列表,反应了每个变量所计算出的梯度。

apply_gradients

apply_gradients(
grads_and_vars,
global_step=None,
name=None
)

  • 函数作用:用计算出来的梯度更新变量。
  • 参数解释:
    • grads_and_vars:List of (gradient, variable) pairs,即为compute_gradients函数的返回值。
    • global_step:标记变量,记录已进行了多少次变量更新操作,每更新一次global_step会自动加1。
    • 注:在一次apply_gradients中会进行多次变量更新操作(会一直优化变量直到达到了指定误差),因此需要一个变量来记录进行了多少次更新。
  • return:返回op,执行该操作即进行优化更新操作。

minimize

minimize(
loss,
global_step=None,
var_list=None,
gate_gradients=GATE_OP,
aggregation_method=None,
colocate_gradients_with_ops=False,
name=None,
grad_loss=None
)

  • 函数作用:该函数相当于compute_gradients和apply_gradients的封装(先进行compute_gradients再进行apply_gradients),直接就返回一个最小化的Operation操作,没有中间先得到梯度、再应用梯度这些步骤了,更为简便。
  • return:返回op,执行该操作即进行优化更新操作。

如何使用Optimizer

  1. 直接调用minimize函数

    1
    2
    3
    4
    5
    opt = GradientDescentOptimizer(learning_rate=0.1)
    opt_op = opt.minimize(loss)

    # 执行优化操作
    opt_op.run()
  2. 如果想对梯度进行一些修改,或者想观察梯度的变化情况,则分开执行compute_gradientsapply_gradients函数,中间可随意操作梯度,只要保证梯度以(gradient, variable) list的形式传入即可。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    opt = GradientDescentOptimizer(learning_rate=0.1)
    # 计算梯度
    grads_and_vars = opt.compute_gradients(loss)

    # 对梯度进行操作
    capped_grads_and_vars = [(MyCapper(gv[0]), gv[1]) for gv in grads_and_vars]

    # 更新变量
    opt_op = opt.apply_gradients(capped_grads_and_vars)

    # 执行优化操作
    opt_op.run()

注1:执行一次minimzie()/apply_gradients()返回的op操作,就相当于更新一次神经网络各边的权值(即函数中指定的var_list),明显只更新一次肯定不能使网络达到最优,因此就要多次调用op操作,而具体调用多少次呢?大多情况下没有明确的答案,要看何时loss函数收敛到一定范围就可以了。
注2:一般是一个mini-batch调用一次优化op,然后设置一个epoch次数(如300、500),使神经网络在全量数据集上优化更新一个较大的次数,就认为达到最优了。
注3:最终loss函数画出来应该是一个先下降再趋于平稳的曲线,说明模型确实学到东西了,且loss函数最终收敛了(至少达到了局部极小值)。如果画出来的曲线还在下降,说明更新次数不够,还应该继续训练。

模型、数据可视化

Protocal Buffer

  • Protocol buffers 是谷歌推出一种数据序列化格式,通常以string字符串的形式实现,数据转化为protocal buffer后就可以存储到磁盘上并在需要时重新加载。
  • protocol buffer是一种跨平台、跨语言的格式,可类比XML,但更为轻量化。
  • tensoeflow中就使用protocal buffer格式来存储模型和数据。

TensorBoard

  • TensorBoard主要作用是可视化,可以读取存储到磁盘上的Protocol Buffer格式的文件,并进行可视化展示,计算图、计算过程的变量变化都可进行可视化显示。
  • TensorBoard官方文档

tf.summary函数

在tensorflow程序中主要使用tf.summary的各类方法将数据转化为protocol buffer格式并存储到磁盘上,具体介绍如下。
除了保存基本的变量外,还可保存文本(tf.summary.text)、图像(tf.summary.image)、音频(tf.summary.audio)格式的文件,且都可以用tensorboard进行展示。

tf.summary.scalar

tf.summary.scalar(
name,
tensor,
collections=None,
family=None
)

  • 函数作用:将标量转化为probuf格式,一般在画loss、accuary时会用到这个函数。
  • 参数解释:
    • name:将标量以什么名字进行保存,在tensorboard中就会使用这个名字来显示变量。type:string。
    • tensor:待存储的tensor,注意tensor的shape必须为(),即tensor必须是标量(即就一个数)。
    • 其他参数一般用不到。
  • return:会返回一个string格式的tensor,存储probuf化的标量信息。

tf.summary.histogram

tf.summary.histogram(
name,
values,
collections=None,
family=None
)

  • 函数作用:用于存储tensor,不同于scalar只能存储一个数,distribution任何shape都可以存,其用来记录tensor中各个元素的分布情况。
  • 参数解释:
    • name:tensorboard中显示的名字;
    • values:待存储的的tensor值;
  • renturn:会返回一个string格式的tensor,存储probuf化的tensor信息。
  • 在tensorboard中有两种查看histogram信息的方法:HISTOGRAMS和DISTRIBUTIONS,前者以直方形式显示统计结果, 后者提供更为抽象的统计信息。
  • HISTOGRAMS可理解为频数分布直方图的堆叠,有两种显示模式:OVERLAY和OFFSET,OVERLAY意为覆盖,即不同的线代表不同的时间/step。如果较晚的线与较早的线重合,就以覆盖方式画线,横轴:值,纵轴:数量;OFFSET则将先按时间/step的前后分开画,但横纵轴的含义不变,横轴:值,纵轴:数量。
  • DISTRIBUTIONS可理解为多分位数折线图的堆叠,
  • TensorBoard Histogram Dashboard文档

tf.summary.merge

tf.summary.merge(
inputs,
collections=None,
name=None
)

  • 函数作用:用于管理多个summary,将多个protocol buffer的数据整合到一个protocol buffer中,所以每次就可以只计算总的probuf了。
  • 参数解释:
    • inputs:待合并的多个tensor,每个tensor都是包含protocol buffer的string类型;type:包含多个tensor的list;
    • 其他参数不常用。
  • return:返回一个string类型的tensor,存储整合后的protocol buffer(包含了inputs中的各个probuf)。

tf.summary.merge_all

tf.summary.merge_all(
key=tf.GraphKeys.SUMMARIES,
scope=None,
name=None
)

  • 函数作用:用于管理所有的summary,将所有的protocol buffer整合到一个里,一次性计算所有的summary。
  • 参数解释:
    • key:指定了整合哪个范围内的summary,默认为tf.GraphKeys.SUMMARIES(就是所有的summary,summary默认都添加到这里)。
    • scope:过滤掉不想整合的summary。
    • 三个参数通常情况下都不指定。
  • return:返回一个string类型的tensor,存储整合后的protocol buffer。

tf.summary.FileWriter类

  • 将变量转化为protocol buffer形式后,就需要将其写到文件中了,提供的将probuf写到文件中的类为:FileWriter
  • tensorflow称存储probuf的文件为event file,就是存储计算过程中各种事件的文件的意思。
  • event file采用的是异步更新机制(系统会在空闲的时候才更新文件),保证了对文件的操作不会拖慢模型训练速度。

构造函数

init(
logdir,
graph=None,
max_queue=10,
flush_secs=120,
graph_def=None,
filename_suffix=None,
session=None
)

  • logdir参数:要将event file保存在哪个目录下;
  • 其他参数不用管。

tf.FileWriter.add_graph

add_graph(
graph,
global_step=None,
graph_def=None
)

  • 函数作用:将graph存储到FileWriter对应的event file中。之后tensorboard读取event file就可以对计算图进行可视化展示。
  • 参数解释:
    • graph:待存储的计算图;
    • global_step:计数变量,每次调用函数就+1;
  • return:无返回值。与其他返回op的函数不同,执行这个函数后数据就直接被写到文件了。
  • 通常不使用这个函数,而是直接将sess.graph传入FileWriter的构造函数中。

tf.FileWriter.add_summary

add_summary(
summary,
global_step=None
)

  • 函数作用:将summary函数产生的protocol buffer数据存储到FileWriter对应的event file中。
  • 参数解释:
    • summary:待存储的summary protocol buffer;注意必须先run()/eval()才能生成probuf,之后才能传入函数,不然就只传了一个空tensor。
    • global_step:计数变量,每次调用函数就+1;
  • return:无返回值,函数直接执行写入操作(实际是异步的)。

Tensor shape information:将tensor的shape反映到图中(通过边的粗细程度)

tf.FileWriter.flush()

  • 将所有pending的数据立即写入文件。
  • 因为add函数是异步执行的,所以调用函数后系统不会立即将数据写入文件。

使用tensorboard

1
2
3
4
5
6
# 初始化一个存储文件
writer = tf.summary.FileWriter('.')
# 将计算图存到文件中
writer.add_graph(tf.get_default_graph())
# 文件写到磁盘上
writer.flush()
  • 如何调用TensorBoard:

    1. tensorboard --logdir=PATH;省略形式:tensorboard --logdir .,直接在当前目录下运行。
      注:annaconda下需要先进入相应python环境,才可以使用tensorboard命令。
    2. 之后在localhost: 6006上即可访问。
  • tensorboard分析:
    在tensorboard中会按照name scope将相关节点都聚集到一个父节点中,因此良好的name scope命名规则会很有利于模型可视化。

    • 关于name_scopevariable_scope的区别:
      name_scope:为了更好地管理变量的命名空间而提出的。比如在 tensorboard 中,因为引入了 name_scope, 我们的 Graph 看起来才井然有序。
      variable_scope:大部分情况下是跟 tf.get_variable() 配合使用,来实现变量共享的功能。但tensorboard也会对同一variable_scope的变量进行聚集和整理。
  • tensorboard中的标签页:
    GRAPHS:可视化图结构;

SCALARS:存储标量,横坐标是STEP时就表示标量随训练步骤的变化情况;
DISTRIBUTIONS:可理解为多分位数折线图 的堆叠。横轴表示训练步数,纵轴表示权重值。而从上到下的折现分别表示权重分布的不同分位数:[maximum, 93%, 84%, 69%, 50%, 31%, 16%, 7%, minimum]
HISTOGRAMS:是DISTRIBUTIONS的另一种形式,一般选OFFSET形式展示。其中,横轴表示值,纵轴表示值对应元素的数量,每个切片显示一个直方图,切片按步数排列;旧的切片较深,新的切片颜色较浅.如图,可以看到在第393步时,以4.91为中心的bin中有161个元素.

  • 可以在distribution和historgram中打印变量的梯度,看梯度最终是否趋于0(即在0附近集中),如果趋于0的话变量就收敛了。
  • 注:标签中横轴的含义:STEP: 迭代步长;RELATIVE: 相对时间(小时,相对于起始点);WALL:也训练时间。

tf.nn.embedding_lookup

tf.nn.embedding_lookup(
params,
ids,
partition_strategy=’mod’,
name=None,
validate_indices=True,
max_norm=None
)

  • 函数作用:根据序号查找tensor中的相应项。当params为一个tensor时,根据ids返回查到的行;当params是多个tensor所构成的list时,则根据分隔策略返回查到的各个tensor的行。
  • 构成params的tensor通常为2维的,此时查到的是行;如果tensor是一维的,则是查找相应的项。
  • 参数解释:
    • params:待查找项的集合;type:单个tensor或多个tensor的list。
    • ids:用来查找项的索引;type:整型list或tensor,1-D/mul-D都可。
    • partition_strategy:分隔策略,指定了怎样由索引去查找对应项。type:”mod” 或 “div”。
      只有在len(params) > 1时才有用,即params是由多个tensor构成时才有用。
  • return:返回一个tensor,表示查到的params的行。shape为ids的shape再加上所查到的行,即rank = ids_rank + 1。
  • partition_strategy进一步解释:
    • 默认为mod:按余数来分隔各个tensor,0~n-1分别表示第一个tensor到最后一个tensor的第一项,n~2n-1分别表示第一个tensor到最后一个tensor的第二项,以此类推;
    • div:按除数来分隔tensor,从前到后依次查找每个tensor中的元素。
      example code:
      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
      # params为单个tensor的情况
      params = tf.constant([[10, 20, 30],[40, 50, 60]])
      ids = tf.constant([0, 1, 1])
      print(tf.nn.embedding_lookup(params,ids).eval())
      # 输出
      # [[10 20 30]
      # [40 50 60]
      # [40 50 60]]


      # params为多个tensor的情况,需要指定分隔策略
      params1 = tf.constant([[10, 20, 30],[40, 50, 60]])
      params2 = tf.constant([[70, 80, 90],[100, 110, 120]])
      ids = tf.constant([0, 1, 2, 3])

      print(tf.nn.embedding_lookup([params1, params2],ids).eval())
      # 输出
      # [[ 10 20 30]
      # [ 70 80 90]
      # [ 40 50 60]
      # [100 110 120]]

      print(tf.nn.embedding_lookup([params1, params2],ids, partition_strategy='div').eval())
      # div分隔策略下,输出为
      # [[ 10 20 30]
      # [ 40 50 60]
      # [ 70 80 90]
      # [100 110 120]]

      # 若ids是多维的,查到的内容不变,则仅仅是将查找结果整理成相应的多维数组的形式而已。
      params1 = tf.constant([[10, 20, 30],[40, 50, 60]])
      params2 = tf.constant([[70, 80, 90],[100, 110, 120]])

      ids = tf.constant([[0, 1], [2, 3]])

      print(tf.nn.embedding_lookup([params1, params2],ids, partition_strategy='div').eval())
      # 输出
      # [[[ 10 20 30]
      # [ 40 50 60]]

      # [[ 70 80 90]
      # [100 110 120]]]

如何构建词汇表

词汇表本质是一个word: id字典,用于将token词和对应的整数映射起来,即把每个词变成一个独立的整数,这样我们就可以通过相应的index来存储、操作token,节省存储空间、加快速度,也便于后期做词嵌入。
tensorflow有VocabularyProcessortf.keras.preprocessing.text.Tokenizer两种实现方法,下面分别介绍。

VocabularyProcessor类

  • 这个类tensoflow官方已经不推荐使用了(替代手段为tf.keras.preprocessing.text.Tokenizer的相关API),但因为有时要阅读别人代码,所以还是简单总结下这个类的使用方法。
  • 构造方法:tf.contrib.learn.preprocessing.VocabularyProcessor (max_document_length, min_frequency=0, vocabulary=None, tokenizer_fn=None)
    max_document_length:文档的最大长度。如果文本的长度大于最大长度,那么它会被剪切,反之则用0填充;
    min_frequency:词频的最小值,出现次数小于最小词频的词不会被收录到词汇表中;
    vocabulary:CategoricalVocabulary对象,直接none就行;
    tokenizer_fn:分词函数,直接none就行。
  • example code

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    from tensorflow.contrib import learn
    import numpy as np
    max_document_length = 4
    x_text =[
    'i love you',
    'me too'
    ]
    vocab_processor = learn.preprocessing.VocabularyProcessor(max_document_length)
    vocab_processor.fit(x_text) # 训练一个词汇表
    print(next(vocab_processor.transform(['i me too'])).tolist()) # 输出index形式的文档
    x = np.array(list(vocab_processor.transform(x_text)))
    print(x)

    # 输出
    [1, 4, 5, 0]
    [[1 2 3 0]
    [4 5 0 0]]
  • 一些常用函数:
    VocabularyProcessor.fit():训练词汇表,词汇表第0位默认为<UNK>(”: 0”),用于标示不在词汇表中的词;
    VocabularyProcessor.transform():根据词汇表将句子转化成相应的数字序列,并返回;
    VocabularyProcessor.fit_transform():先训练一个词汇表,再返回训练集的数字序列;
    VocabularyProcessor.vocabulary_.mapping[key]:根据key(即token词)取对应id;
    VocabularyProcessor.vocabulary
    .mapping.get(key, default=None):根据key(即token词)取对应id,没取到返回的default参数值;
    VocabularyProcessor.vocabulary
    ._mapping.keys():返回所有keys;

tf.keras.preprocessing.text.Tokenizer类

1
2
3
4
5
# Tokenizer
tokenizer = tf.keras.preprocessing.text.Tokenizer(num_words=20000, oov_token='<UNK>')
tokenizer.fit_on_texts(train_text)
train_idxs = tokenizer.text_to_sequences(train_text)
test_idxs = tokenizer.text_to_sequences(test_text)

更多见:文本预处理方法小记-知乎

关于collection

tf.add_to_collection

add_to_collection(
name,
value
)

  • 函数作用:将某个变量添加到默认graph的某个collection中(是tf.tf.get_default_graph().add_to_collection的简写形式)。
  • 参数解释:
    • name:collection的名字,若collection不存在则自动创建一个并添加;
    • value:要被添加的变量;
  • return:无返回值。

其他函数

关于axis参数和dimension的理解

  • 在Tensorflow和numpy的函数中,经常会有axis这个参数,这是一个和维度dimension息息相关的概念,下面来进行介绍。
  • axis:中文意思为“轴”,本质是一个方向!表示在当前维度上数量增加的方向,与笛卡尔坐标系中的x轴和y轴表达是一个意思。
  • axis是从0开始计数的,在一个矩阵/2D tensor中,axis0表示竖直方向,axis1表示水平方向。在更高维的张量中,axis0、axis1也是表示相同的方向。
  • 但注意,在一维tensor(即数组/向量)中axis的表示有些不同,一维tensor只有一个axis,为axis0,且axis0的方向是水平的。原因在于:一维tensor本质应该是列向量,这样axis0方向就是竖直的了,与高维tensor统一;但因为人们的表示习惯,把一维tensor表示为行向量了,因此才会有axis0的差异。
  • dimension:维度,本质和axis是一样的,也是指方向。但有两种上下文的理解:1、这个东西是几维几维的,是指这个东西有多少个“方向”来描述;2、这个东西的1维2维3维,是指第几个方向,即1维对应axis 0,2维对应axis 1,这样。
  • 详细解释参照:NUMPY AXES EXPLAINED

tf.split

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

  • 函数作用:将大tensor分割成几个小tensor
  • 参数解释:
    • value:输入数据,为tensor类型;
    • num_or_size_splits:指定了两种分割策略。若输入的是一个整数n,则会在axis维度上平均分成n个小tensor;若输入的是一个tensor/list,则会按照指定的大小来分;
    • axis:指定了要沿哪个维度来分,注意维度的计数从0开始。
  • return:包含被分割的小tensor的list
  • example code:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    value = tf.get_variable(name="value", shape=[5, 30])

    # 平均分 & 沿维度1分
    split0, split1, split2 = tf.split(value, num_or_size_splits=3, axis=1)
    print(split0.shape) # [5, 10]

    # 按指定的大小分 & 沿维度1分
    split0, split1, split2 = tf.split(value, [4, 15, 11], 1)
    print(split0.shape) # [5, 4]
    print(split1.shape) # [5, 15]
    print(split2.shape) # [5, 11]

tf.concat

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

  • 函数作用:将多个小tensor拼接为一个大tensor(沿着axis指定的维度)
  • 参数解释:
    • values:将要拼接的tensor集合;type:list of tensor。
    • axis:在哪个维度上进行拼接;type:int;
  • 返回值:一个拼接后的新tensor。
  • 注:待拼接的各个tensor,除了axis维度,其他维度的长度必须相等。

tf.reshape

tf.reshape(
tensor,
shape,
name=None
)

  • 函数作用:改变一个tensor的shape。
  • 参数解释:
    • tensor:原tensor。
    • shape:要被改变成的shape;type:list;
  • 返回值:reshaped tensor。
  • 注1:shape中的某个维度可以指定为-1,表示该维度的长度由系统推断,最多只能指定一个维度为-1。如果shape就直接是[-1],则会将原tensor平铺为1-Dtensor。
  • 注2:要把元素都填充到新tensor的各个维度中,肯定要以一定的顺序读取原tensor中的元素,那按什么样的顺序进行读取呢?可以理解为深度优先,即先把一个元素所有的低维度子元素读完,再读下一个元素。填充时也是这个顺序。

tf.transpose

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

  • 函数作用:对矩阵进行转置。更本质来说是对tensor的维度顺序进行调整,如将第3维调整到第2维,第2维调到第1维等;
  • 参数解释:
    • a:待转置的tensor;
    • perm:在新tensor中原先各维度的排列顺序,即指定如何转置tensor;type:list;默认为[n-1, …, 0],表示从后到前依次转置,即为2-D矩阵的转置方式。
    • conjugate:用处不大,只在元素为复数时有用。
  • return:转置后的新tensor。
  • example code
    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
    x = tf.constant([[1, 2, 3], [4, 5, 6]])
    tf.transpose(x)
    # [[1, 4]
    # [2, 5]
    # [3, 6]]

    # Equivalently
    tf.transpose(x, perm=[1, 0])
    # [[1, 4]
    # [2, 5]
    # [3, 6]]

    # 'perm' is more useful for n-dimensional tensors, for n > 2
    x = tf.constant([[[ 1, 2, 3],
    [ 4, 5, 6]],
    [[ 7, 8, 9],
    [10, 11, 12]]])

    # (2, 2, 3) shape转置为 (2, 3, 2) shape
    tf.transpose(x, perm=[0, 2, 1])
    # [[[1, 4],
    # [2, 5],
    # [3, 6]],
    # [[7, 10],
    # [8, 11],
    # [9, 12]]]

tf.squeeze

tf.squeeze(
input,
axis=None,
name=None
)

  • 函数作用:去除tensor中大小为1的维度(因为这些维度本身意义不大)。对应了函数名squeeze“挤压”。
  • 参数解释:
    • input:输入tensor
    • axis:可以自己指定要去除具体哪一个大小为1的维度,list类型
  • return:返回一个处理后的tensor
  • example code
    1
    2
    3
    4
    5
    6
    # 't' is a tensor of shape [1, 2, 1, 3, 1, 1]
    tf.shape(tf.squeeze(t)) # [2, 3]

    # remove specific size 1 dimensions
    # 't' is a tensor of shape [1, 2, 1, 3, 1, 1]
    tf.shape(tf.squeeze(t, [2, 4])) # [1, 2, 3, 1]

tf.expand_dims

tf.expand_dims(
input,
axis=None,
name=None,
dim=None(deprecated)
)

  • 函数作用:对tensor添加维度,添加的维度长度为1。常用来对tensor增加batch维。
  • 参数解释:
    • input:待操作的tensor;
    • axis:指定在哪个位置添加维度,位置从0开始;
    • dim:这是deprecated的参数,不推荐使用;
  • return:改变后的tensor。
  • example code
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 't' is a tensor of shape [2]
    tf.shape(tf.expand_dims(t, 0)) # [1, 2]
    tf.shape(tf.expand_dims(t, 1)) # [2, 1]
    tf.shape(tf.expand_dims(t, -1)) # [2, 1]

    # 't2' is a tensor of shape [2, 3, 5]
    tf.shape(tf.expand_dims(t2, 0)) # [1, 2, 3, 5]
    tf.shape(tf.expand_dims(t2, 2)) # [2, 3, 1, 5]
    tf.shape(tf.expand_dims(t2, 3)) # [2, 3, 5, 1]

tf.tile

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

  • 函数作用:通过多次复制一个tensor的方式构造新tensor。可以理解为用tf.concat拼接相同的tensor。
  • 参数解释:
    • input:将要被复制的tensor;
    • multiple:指定input中各个维度将要被复制的次数;type:list,且长度必须和input的维度数相等。
  • return:所构造的新tensor。
  • example code
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    with tf.Session(): 
    x = tf.constant([[1, 2, 3], [4, 5, 6]])
    y1 = tf.tile(x, [2, 1])
    y2 = tf.tile(x, [1, 2])
    print(y1.eval())
    print(y2.eval())

    # 输出:
    # [[1 2 3]
    # [4 5 6]
    # [1 2 3]
    # [4 5 6]]
    # [[1 2 3 1 2 3]
    # [4 5 6 4 5 6]]

tf.stack

tf.stack(
values,
axis=0,
name=’stack’
)

  • 函数作用:将一个list内的多个tensor叠成一个(rank+1)的tensor,多的那一维就是tensor的数量。
  • 与tf.concat的区别,stack可以用来叠加scalar,concat则多用来叠加tensor。
  • 参数解释:
    • values:list of tensor;
    • axis:要叠到新tensor的哪个维度上,默认为0,即各个tensor以行的形式进行排列。
  • return:返回新的tensor
  • example code
    1
    2
    3
    4
    5
    x = tf.constant([1, 4])
    y = tf.constant([2, 5])
    z = tf.constant([3, 6])
    tf.stack([x, y, z]) # [[1, 4], [2, 5], [3, 6]] (Pack along first dim.)
    tf.stack([x, y, z], axis=1) # [[1, 2, 3], [4, 5, 6]]

tf.math.zero_fraction

tf.math.zero_fraction(
value,
name=None
)

  • Aliases:tf.nn.zero_fraction
  • 函数作用:返回value中值为0的元素所占的比例。因为relu激活函数有时会大面积的将输入参数设为0,所以此函数可以有效衡量relu激活函数的有效性。

tf.gather()

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

  • “切片函数”:默认从axis=0维切片(即获取axis=1~:的部分),然后将切的slice填到indices对应的元素中。
  • 参数解释:
    • params:待切片的tensor;
    • indices:指定了第一维中要切哪些的下标
  • example
    1
    2
    3
    params = [[0, 1], [2, 3]]
    indices = [[0, 0], [1, 1]]
    output = [[[0, 1], [0, 1]], [[2, 3], [2, 3]]]

tf.gather_nd()

tf.gather_nd(
params,
indices,
name=None,
batch_dims=0
)

  • 也是“切片函数”,和gather()的区别就是:并非从axis=0切片,而是根据indices[-1]维来切片,即按照indices中最后一维元素值来访问params的下标,取params中对应下标元素的slice,然后将切后的slice作为indices中的最后一维元素来填充。
  • 参数解释:
    • params:待切片的tensor;
    • indices:最后一维指定的元素指定了如何访问下标(要切哪些下标)。
      1
      2
      3
      params = [[0, 1], [2, 3]]
      indices = [[0, 0], [1, 1]]
      output = [0, 3]

tf.reduce_sum(input_tensor, axis)

  • 将tensor中axis维的元素相加,相加后axis维就没了,tenosr的总rank数即减少1,因此叫reduce.
  • 如果不指定axis,则tensor中所有元素相加,最后返回一个scalar。

tf.reduce_mean(input_tensor, axis)

  • reduce_sum类似,唯一不同是sum是做加法,mean是做平均。

如何设置GPU的使用

  • 通过CUDA_VISIBLE_DEVICES环境变量可以设置程序可以看到的GPU,因此也就只能使用相应的GPU。注:这种方法不仅会对tensorflow程序生效,对所有的程序都会生效。
  • 两种设置方式:
    • 在python程序中设置(推荐),os.environ["CUDA_VISIBLE_DEVICES"] = "2, 3",后面的数是GPU的序号,可通过nvidia-smi命令来查看。这种设置方法只对此python程序有效,推荐。
    • 在命令行中设置,export CUDA_VISIBLE_DEVICES = "2, 3"

杂七杂八

  • tensorflow中 *实现为element-wise乘法,即tensor的对应项乘,要求左右两边tensor shape一致,因此乘出来的tensor shape也会一致。

Post Date: 2019-03-06

版权声明: 本文为原创文章,转载请注明出处