继续挖坑,因为Batch normalization的inference还没有解决,不知道怎么在tf.Tensor中更新数值,参考tf.layer.BatchNormalization的思路,需要利用is_training来选择feed给BN的meanvariance。因此,需要理解TensorFlow的graph概念。除此之外,TF提供了TensorBoard作为计算图可视化的工具,也是值得借鉴的。

计算图—Graph

TensorFlow是一个通过计算图的形式来表达张量之间通过计算相互转化的过程[1]。作为TensoFlow的基本概念,TF中的所有计算都会转化为计算图上的节点,节点之间的边用于描述计算之间的依赖关系。TF中不同的计算图之间维护的节点和边是独立的。通过tf.GraphKeys可以对图进行维护,稍后会介绍。

利用tf.Graph类实例化计算图,有如下的样例,

1
2
3
4
5
import tensorflow as tf
g1 = tf.Graph()
with g1.as_default():
v = tf.get_variable{"v", initializer=tf.zeros_initializer(shape=[1])}

TensorBoard 可视化

TensorBoard可以有效地展示TF在运行过程中的计算图、各种指标随时间的变化趋势以及训练中使用的图像等信息,其利用TF运行过程中输出的日志文件可视化程序的运行状态。TensorBoard和TensorFlow运行在不同的进程中,TB会自动读取TF的日志文件,并呈现当前的运行状态。根据TensorBoard的官方文档,其实现过程有如下步骤,

1. 搭建网络

First, create the TensorFlow graph that you’d like to collect summary data from, and decide which nodes you would like to annotate with summary operations.

利用TensorFlow搭建网络,例如基于MLP的手写体识别网络,提供一个样例,可以参考。

2. Select variables to be summarized

通常我们关注训练过程中待学习的参数 (如weights, biases等) 的变化和收敛情况,通过tf.summary.scalartf.summary.histogram可以很方便地收集这些数据。下面给出一个样例,参考了[3],

1
2
3
4
5
6
7
8
9
10
11
def variable_summaries(var):
"""Attach a lot of summaries to a Tensor (for TensorBoard visualization)."""
with tf.name_scope('summaries'):
mean = tf.reduce_mean(var)
tf.summary.scalar('mean', mean)
with tf.name_scope('stddev'):
stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean)))
tf.summary.scalar('stddev', stddev)
tf.summary.scalar('max', tf.reduce_max(var))
tf.summary.scalar('min', tf.reduce_min(var))
tf.summary.histogram('histogram', var)

而对于单个Variable,可以用tf.summary.scalar(name,variable)的形式进行收集。

3. Merge summary data

定义好要收集的信息,需要对他们进行汇总,此时用tf.summary.merge_all进行汇总,例如

1
merged = tf.summary.merge_all()

4. FileWriter for saving the log

为了将训练中收集的数据保存到日志中,可以用tf.summary.FileWriter来实现,此时需要提供存放日志的文件夹位置,即logdir

1
2
3
4
5
6
logdir = './log'
if not os.path.exists(logdir):
os.mkdir(logdir)
train_writer = tf.summary.FileWriter(logdir + '/train',
sess.graph)
test_writer = tf.summary.FileWriter(logdir + '/test')

此时,利用sess.run(tf.global_variables_initializer())初始化所有Tensor和Variable,便可以开始训练网络。

5. Running

merged的类型为tensorflow.python.framework.ops.Tensor,属于tf.Graph的一部分。因此也需要在session中运行,并且要提供feed_dict。相应的python实现如下

1
2
3
4
summary, _ = sess.run(
[merged, train_step],
feed_dict=feed_dict)
train_writer.add_summary(summary)

6. 利用TensorBoard可视化

TensorFlow提供了tensorboard.py脚本用于可视化。利用如下指令启动并运行TensorBoard, 其默认端口为6006,这里利用--port显式表达

1
$ tensorboard --logdir='./log --port=6006'

注:在执行此条指令的时候,可能会报错command not found: tensorboard,解决方法参考本文

以下给出运行的样例,我写了一个notebook,欢迎来PR :)

  • Graph及FC1和train节点
  • Scalars and histograms

References

[1] TensorFlow实战Google深度学习框架
[2] tf.summary.FileWriter
[3] TensorBoard: Visualizing Learning
[3] tensorboard在linux下的启动问题

Comment and share

ResNet (Residual Neural Network)由何凯明等在15年提出,这里做个笔记,参考了TensorFlow 实战的6.4节。ResNet的结构能加速超深神经网络的训练,能有效避免过拟合,而且推广性非常好。

深度神经网络存在Degradation的问题,即随着网络层数的增加,其准确率会从逐渐提升到饱和,进而下降。这一问题在我们近期的工作也出现了,我们在增加了一层全连接后,网络的准确率反而下降了。为了解决这一问题,ResNet提出了一种新的网络思路,即允许原始输入信息直接传输到后面的层中。假定某段网络的输入是,期望输出是,如果直接将传到输出作为初始结果,那么目标函数变为

如下图所示为一个ResNet的残差学习单元,即将学习目标从改为期望与输入的残差。这种结构也被称为shortcutskip connections.

考虑到神经网络相当于对原始数据进行了压缩,存在信息损失的问题,而ResNet结构将部分输入信息直接传递到输出端,可以保留部分的信息。并且ResNet的训练误差会随着层数增加而逐渐减少,并且在测试集上也有很好的表现。

引自TensorFlow实战,在ResNet的第二篇论文Identity mapping in deep residual networks中,提出了ResNet V2。想对于ResNet V1, 将激活函数ReLU改为了Identity mapping,即y=x。同时,ResNet V2在每一层都使用了Batch Normalization,提升网络的范化能力。

ResNet的block的理解

在ResNet的实现中,包含多个block,每个block又由多个bottleneck组成,称为残差学习单元,ResNet的残差运算即在每个bottleneck内实现。因为要获取输入与block输出的残差,每个bottleneck虽然做了多次卷积和激活运算,但其输出的shape应该保持不变,因此内部的卷积运算的stridepadding都有特殊的设置 (数学推导可以参考此文)。以ResNet-50的第二个block为例,假设该block内包含两个bottleneck单元,则有,

Bottleneck One
Layer Input Kernel Stride Padding Output
Conv1,1 56x56x64 1x1x64 1 SAME 56x56x64
Conv1,2 56x56x64 3x3x64 1 SAME 56x56x64
Conv1,3 56x56x64 1x1x256 1 SAME 56x56x256
Bottleneck Two
Layer Input Kernel Stride Padding Output
Conv2,1 56x56x256 1x1x64 1 SAME 56x56x64
Conv2,2 56x56x64 3x3x64 2 SAME 28x28x64
Conv2,3 28x28x64 1x1x256 1 SAME 28x28x256

注意Bottleneck TwoConv2,3层的stride为2,因此输出的尺度大小相当于做了factor为2的降采样,因此该bottleneck不做残差的运算,其输出直接作为下一个block的输入。

ResNet-V2相对于ResNet-V1增加了Batch NormalizationL2正则,如何在block中体现? 且听下回分解。

Comment and share

趁着最近空闲一点,继续纠结TensorFlow学习率调整的问题。学习率对网路训练的影响还是挺大的,初始化时可以采用大的学习率,帮助网络快速收敛。但网络的梯度是非线性的,随着迭代次数的增加,梯度的导数趋于变小,如果继续保持大学习率,会出现优化目标在最优解附近波动的情况,如下图所示。此时,为了接近收敛点,需要调整学习率。

参考TensorFlow: 实战Google深度学习框架这本书,利用指数衰减的方法设置梯度下降算法的学习率,TensofFLow中集成了这一算法,即tf.train.exponential_decay。其实现了

其中$L_R$表示学习率,$R_d$表示衰减率,$S_g$和$S_d$分别表示总Epochs和每个Epochs中的Batches。从而没迭代一轮,学习率便更新一次。

在TensorFlow中可以用如下代码实现指数衰减的学习率更新。

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
import numpy as np
import tensorflow as tf
init_lr = tf.Variable(0., name="LR")
global_step = tf.Variable(0., name="global_step")
decay_step = tf.Variable(0., name="decay_step")
decay_rate = tf.Variable(0., name="decay_rate")
learning_rate = tf.train.exponential_decay(
learning_rate = init_lr ,
global_step = global_step,
decay_steps = decay_step,
decay_rate = decay_rate,
staircase=False,
name=None
)
# Test
lr_init = 0.1
epochs = 200
batches = 100.
d_rate = 0.9
epoch = np.arange(0,epochs,1)
lr = np.zeros(epoch.shape)
# Init a session
init_op = tf.global_variables_initializer()
sess = tf.InteractiveSession()
sess.run(init_op)
for i in epoch.astype(int):
lr[i] = sess.run(learning_rate,
feed_dict={init_lr: lr_init,
global_step: i,
decay_step: batches,
decay_rate: d_rate
})

这里提供了一个样例,其输出结果如下图,通过设置tf.exponential_decay的参数staircase可以控制learning rate是否为阶梯型或者平滑的。

而在训练网络,优化目标函数的过程中,可以参考官方手册,用如下语句进行梯度更新。

1
2
3
4
5
6
7
8
9
10
...
global_step = tf.Variable(0, trainable=False)
starter_learning_rate = 0.1
learning_rate = tf.train.exponential_decay(starter_learning_rate, global_step,
100000, 0.96, staircase=True)
# Passing global_step to minimize() will increment it at each step.
learning_step = (
tf.train.GradientDescentOptimizer(learning_rate)
.minimize(...my loss..., global_step=global_step)
)

Reference

[1] exponential decay

Comment and share

To be continue…

蒙特卡罗方法帮助产生服从样本分布的新样本,并且实现参数的估计,是一种基于贝叶斯统计的算法。机器学习中的许多重要工具都基于从某种分布中采样,以及用这些样本对目标量做一个蒙特卡罗估计。

Why sampling and Monte Carlo?

当无法精确计算和或积分时,通常可以使用蒙特卡罗采样来近似。这种想法把和或积分视作某分布下的期望,然后通过估计对应的平均值来近似这个期望。而这里的估计用到了中心极限定理,即$\hat{s}_n$收敛到以s为均值,以$Var[f(x)]/n$为方差的正态分布。而马尔可夫链蒙特卡罗方法(MCMC),就是用一种方式构建一个收敛到目标分布的估计序列。

Comment and share

Today, let's talk about transfer learning based on TensorFlow. Firstly, what is transfer learning? It is a strategy of building your own deeplearning based project with existing well-trained networks, so as to avoid some risks like overfitting, time-consuming and etc.
Continue reading

今天我们讨论数据可视化(data visualization)算法t-SNE (t-distributed Stochastic Neighbor Embedding),该方法的目的是映射高维数据向量到低维,并保留向量的相似性或者距离。通常我们描述距离会使用类似欧式距离或黎曼距离等概念,映射的方法也多为线性,如PCA。而t-SNE不同,它是用联合概率来描述样本点的相似程度,是非线性的。
Continue reading

几天没写东西了,想写自己的故事,又太矫情。今天的故事不长,主要是程序中碰到的两个Bug,记录一下解决方法。这两天在做CAE的pretrain和fine-tuning,白天给老板解释了一遍,感觉他听懂了。。。
Continue reading

写之前吐个槽,因为昨晚(2017-08-29)开着阳台门睡觉,今天妥妥感冒了。也因为好久没生病,估计要触底反弹了(手动捂脸)。言归正传,最近在用AutoEncoder (AE) 做样本的预训练和特征表示学习,有一些体会, 今天想聊聊反向卷积神经网络。先挖个坑,等明天把程序写出来可能会写得更有调理一些。[Update: 烧了两天,回来填坑。。。]
Continue reading

Author's picture

Jason Ma

We are in the same story.


Astronomer? Software engineer


Shanghai