TensorFlow基本概念与操作

TensorFlow 基本概念与操作

  1. 计算图

    1. 除了默认图之外,TF支持通过tf.Graph来生成新的。

    2. 可以用来隔离张量,运算等

    3. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      # Get default graph
      tf.get_default_graph()
      g1 = tf.Graph()
      with g1.as_default():
      # Pass
      with tf.Session(graph=g1) as sess:
      tf.global_variables_initializer().run()
      with tf.variable_scope("name", resue=True):
      pass
      with g1.device('/gpu:0'):
      pass
1
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
1. 在一个计算图中,可以通过集合来管理不同的资源, 通过tf.add_to_collection将自愿加入一个或者多个集合中, 然后通过tf.get_collection来获取一个集合里面的所有资源。
2. 常用的几个自动维护的集合
| 集合名称 | 集合内容 | 使用场景 |
| ------------------------------------- | --------------------------------- | ------------------- |
| tf.GraphKeys.VARIABLES | 所有变量(可以通过tf.all_variables来获取所有变量) | 持久化TensorFlow模型(存储) |
| tf.GraphKeys.TRAINABLE_VARIABLES | 可学习的变量(参数)tf.trainable_variables | 模型训练,生成模型可视化内容 |
| tf.GraphKeys.SUMMARIES | 日志生成相关的张量 | TF计算可视化 |
| tf.GraphKeys.QUEUE_RUNNERS | 处理输入的QueueRunner | 输入处理 |
| tf.GraphKeys.MOVING_AVERAGE_VARIABLES | 所有计算了滑动平均值的变量 | 计算变量的滑动平均值 |
1. 张量
1. 从功能的角度看,张量可以被简单理解为多维数组
2. 零阶张量-scalar, 一阶 vector, n 阶 n-matrix
3. tensorflow中的张量保存的是计算过程而不是结果, `Tensor("add:0", shape=(2,), dtype=float32)`, 保存了三个属性,名字,维度,类型。名字不仅是id,也是操作
4. 可以通过tensor.get_shape来获取convolution之后的维度,而不用自己去计算了。(engineer trick)
1. 会话
1. 建议使用with context,不用担心异常之后leak或者忘记写close的尴尬
2. ```python
with tf.Session().as_default():
  1. 1
    2
    sess.run(tensor)
    tensor.eval(session=sess)功能一样
  2. 交互式会话,适合jupyter调试 tf.InteractiveSession() 该方法默认将生成的会话注册为默认会话

  3. 无论哪种方法都可以通过ConfigProto Protocol Buffer来配置需要生成会话。 可以配置并行的线程数,GPU分配策略,运算超时时间。allow_soft_placement,用来配置GPU,true允许将GPU运算放在CPU算,log_device会记录每个节点在哪个设备上,来方便调试,生产环境可以设置为false

    1
    2
    3
    4
    config = tf.ConfigProto(allow_soft_placement=True,
    log_device_placement=True)
    sess1 = tf.InteractiveSession(config=config)
    sess2 = tf.Session(config=config)
  1. 变量初始化

    1. | Function | 随机分布 | 参数 |
      | ——————- | ——————————- | ——————- |
      | tf.random_normal | 正态分布 | mean, stddev, dtype |
      | tf.truncated_normal | 正态分布,如果随机出来的值偏离平均值超过2个标准差,则重新随机 | |
      | tf.random_uniform | 平均分布 | |
      | tf.random_gamma | Gamma分布 | alpha, beta |

    2. | Function | 功能 |
      | ———– | ———- |
      | tf.zeros | |
      | tf.ones | |
      | tf.fill | 全部为给定数值的数组 |
      | tf.constant | 给定值常量 |

    3. 通过其他变量来初始化

      1. W2 = tf.Variable(weights.initialized_value())
    4. 维度在运行中是可能改变的,但是需要设置参数validate_shape=False

      W1 = 2, 3 w2 = 2, 2 tf.assign(w1, w2, validate_shape=False)

    5. 之所以使用placeholder而不用tf.constant,是因为constant每次需要生成一个节点插入到计算图中,而placeholder则只是一个节点,每次更新内容而已

    6. 如果选取batch_size的数据

      1
      2
      3
      4
      start = (i*batch_size) % dataset_size
      end = min(start + batch_size, dataset_size)
      feed_dict={x:X[start:end], y_: Y[start:end]}
  2. 深层NN

    1. 单层的Perceptron不具有异或的学习能力(Perceptrons: An introduction to computational geometry),但是加了隐藏层就有了该能力。DNN具有组合特征提取的功能,这个特性对于不易提取特征向量的问题,比如图片识别,语音识别有很大的帮助。

    2. 经典损失函数

      1. 多分类问题:

        1. 判断一个输出向量和期望的向量有多接近呢?cross entropy刻画了俩个概率分布之间的距离。是分类问题中使用比较广的一种损失函数。

        2. 为什么多分类一般需要softmax? 因为cross entropy刻画的是概率分布之间的距离,而神经网络的输出却不一定是一个概率分布。softmax就是将输出转化为概率分布的函数。原始的神经网络的输出被用作置信度来生成新的输出。从交叉熵的公式可以看到其是不对称的,刻画的是通过概率分布q来表达p的困难程度。所以当交叉熵作为神经网络的损失函数时,P代表的是正确答案,q代表的是预测值。H(p, q)= -plog(q)

          1
          2
          H((1,0,0), (0.5,0.4,0.1)) = -(1*log0.5 + 0 + 0) = 0.3
          H((1,0,0), (0.8,0.1,0.1)) = 0.1
        3. 因为交叉熵一般与softmax一起用,所以tensorflow提供了该函数封装

          1
          2
          tf.nn.softmax_cross_entropy_with_logits(y, y_) # y predict, y_ ground truth
          tf.nn.sparse_softmax_cross_entropy_with_logits 适用于只有一个正确答案
      2. 回归问题

        1. 回归问题一般只有一个输出节点,常用的Loss为均方误差。 MSE

          1
          mse = tf.reduce_mean(tf.square(y_ - y))
      3. 自定义损失函数

        1. 有些时候需要根据问题自己去定义更加符合实际的损失函数,例如有些时候预测多了,比预测少了损失要小一些,这时用MSE就无法体现出多少的差别。

        2. 可以使用的函数有

          1
          tf.reduce_sum(tf.select(tf.greater(v1, v2), func1, func2))

          tf.select和tf.greater都是element-wise operation

      4. 对于相同的神经网络不同的损失函数可以对训练得到的模型产生重要影响

    3. 优化算法

      1. GD vs SGD:
        1. GD 在全部数据集上更新, SGD只在一条数据上更新,一般使用折中, batch GD。

    1. 学习率

      1. tf提供的学习率的设置方法: 指数衰减法。 tf.train.exponential_decay

        1
        2
        3
        4
        5
        6
        tf.train.exponential_decay 实现了
        decayed_lr = lr * decay_rate^(global_step/ decay_step)
        decay_rate 衰减率 decay_step衰减速度,
        一般exponential_decay中如果staircase=False,衰减是连续的
        如果是True,则会对global/decay取整,导致整个学习率是阶梯状的。如果将decay_step设置为all/batch_size,则表示每过一轮全部数据,学习率衰减一次,那么表示每轮中的所有数据的贡献率是一致的(因为lr一致)
      2. 1
        2
        3
        4
        5
        global_step = tf.Variable(0)
        lr = tf.train.exponential_decay(0.1, global_step, 100, 0.96, staircase=True)
        # 使用指数衰减学习率,在mini传入global_step将自动更新global 参数, 从而使得学习率也得到相应的更新
        train_step = tf.train.GradientDescentOptimizer(lr).minimize(loss, global_step=global_step)
    2. 正则化

      1. L1, L2, L2不会让参数稀疏的原因在于当参数很小的时候,比如0.001那么的它的平方基本上就可以忽略不计了。于是模型不会进一步将这个参数调整为0. 实践中也可以结合L1, L2同时使用

      2. 1
        2
        3
        4
        tf.contrib.layers.l2_regularizer(lambda, w)
        或者不使用contrib使用tf.nn.l2_loss
        regularizer = tf.nn.l2_loss(w)
        loss = tf.reduce_mean(loss + lambda * regularizer)
      3. 更加general的写法,尤其当网络定于与损失函数的定义不在同一个文件或者不在同一个函数中的时候,此时采用collection更加方便

        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
        import tensorflow as tf
        def get_weight(shape, lambda):
        var = tf.Variable(tf.random_normal(shape), dtype=tf.float32)
        #添加集合
        tf.add_to_collection('losses', tf.contrib.layers.l2_regularzier(labmda)(var))
        return var
        x = tf.placeholder(tf.float32, shape=(None, 2))
        y_ = tf.placeholder(tf.float32, shape=(None,1))
        batch_size = 8
        # 每层的节点数目
        layer_dimension = [2, 10, 10, 10, 1]
        n_layers = len(layer_dimension)
        cur_layer = x
        in_dimension = layer_dimension[0]
        #循环构建网络
        for i in range(1, n_layers):
        out_dimension = layer_dimension[i]
        weight = get_weight([in_dimension, out_dimension], 0.001)
        bias = tf.Variable(tf.constant(0.1, shape=[out_dimension]))
        cur_layer = tf.nn.relu(tf.matmul(cur_layer, weight) + bias)
        in_dimension = layer_dimension[i]
        mse_loss = tf.reduce_mean(tf.square(y_ - cur_layer))
        #添加集合
        tf.add_to_collection('losses', mse_loss)
        #将所有损失相加
        loss = tf.add_n(tf.get_collection('losses'))
    3. 滑动平均模型

      1. 1
        2
        3
        tf.train.ExponentialMovingAverage
        # 提供decay, step, 维护一个shadown varialbe
        shadow_variable = decay * shadow_variable + (1-decay) * variable
      2. 从公式可以看到,decay越大模型越稳定。实际中,常取值0.999 或者 0.9999, 为了使模型在训练前期可以更新得更快, ExponentialMovingAverage还提供了num_updates参数来动态设置decay的大小, $min{decay, \cfrac{1+num_updates}{10+num_updates}}$

      3. 直接使用代码

        1
        # todo