题外话
学习TensorFlow想必是每一个机器学习爱好者从业者的一门必修课。从考研结束到现在3个多月的时间我拜读周志华教授、Ian等大家的机器学习圣书后,总觉得书上得来总觉浅。加上理论懂了个大概,也能够理解到工程上的一些操作的必要性,或者说能够有查阅资料的方向了。决定开始学习TensorFlow以期望利用平台特性快捷灵活的实现任务。
TensorFlow的官网上给出了多个简单例子,但我觉得还不够的简单。所以为了查看某些特性,我会自己写一些小例子,验证猜想。TenorFlow学习加法源码
结论:
Q:Tensors和variable的区别?
A:输入数据不需要训练,使用占位符placeholder,const表示的tensor.w,b都是需要训练的参数,所以使用变量variable。Q:参数更新的流程?
使用tensor编程计算图,使用优化器optimizer最小化loss。如梯度下降优化器会计算参与的导数,乘上学习率作为参数的变化量,自动更新loss所涉及的variable型变量。Q:数据是否需要正则化?
学习过程中为了避免大数乘法、或者不同维参数数量级相差太大,我们都应该对数据进行正则化。4.Q: 学习率改怎样设置?
在参数正则化后学习率一般设置为0.1。但是我觉得学习率的设置和输入规模是有关系的,目的是让梯度的变化粒度小于期望参数多个数量级会比较好。但是小的越多,收敛速度越慢。
训练模型
为专注了解测试TensorFlow的特性,使用最简单的线性模型来习得加法。输入数据为Xi=[xi1,xi2],标签:Yi=xi1+xi2。预测结果p_y=Wx+b。使用欧式距离表示损失:loss = (Wx+b-Yi)**2。
#简单的线性函数y=wx+b
#损失函数使用简单的欧式距离
y_out = tf.matmul(trainIput,w)+b
loss = tf.reduce_sum((y_out-trainLabel)**2) #reduce_sum当数据为批量的时候才起作用,计算一个向量的元素和。
基本类型
在TensorFlow中使用Tensors来表达向量和矩阵。
A tensor is a generalization of vectors and matrices to potentially higher dimensions
Tensors有多种类型,tf.constant、tf.placeholder、tf.Variable等,我们将在以下的替代中用到。
tf.constant对应于编程语言中的常量 ,在训练当中不可以改变。与之相对应的则为tf.Variable,它的值可以多个session的中重复被使用,默认相当于全局变量,当然你可以设置起作用域。
A TensorFlow variable is the best way to represent shared, persistent state manipulated by your program. A tf.Variable represents a tensor whose value can be changed by running ops on it.
简而言之,就是需要训练的参数使用tf.Variable表示,不需要训练的输入可以使用tf.constant表示。
#输入数据不需要训练,使用占位符placeholder表示的tensor,用于接受feeddict的数据。
trainIput = tf.placeholder(tf.float32,shape=[batch_size,2])
trainLabel = tf.placeholder(tf.float32,shape=[batch_size])
#w,b都是需要训练的参数,所以使用变量variable。默认初始化,每次计算都会更新此变量
w = tf.get_variable("wights",dtype=tf.float32,shape=[2,1])
b = tf.get_variable("bias",[batch_size,1],dtype=tf.float32)
优化器
我们得到了损失函数后,我们的目标就是习得w、b以最小化loss。tf提供了梯度递减优化器GradientDescentOptimizer来最小化loss。
#使用梯度下降来最优化loss,设置参数为学习率为0.1。
with tf.name_scope("optimizer"):
optimizer = tf.train.GradientDescentOptimizer(0.1).minimize(loss)
数据模拟
设置range_random=(0,10) 随机产生模拟数据的范围,之后我们会增大范围观察拟合情况。
#生成模拟数据,x1,x2,y,其中y=x1+x2
def generaate_data(size,isNormal):
for i in range(size):
x1 = random.randint(range_random)
x2 = random.randint(range_random)
if isNormal:
x1=x1/range_random[1]
x2=x2/range_random[1]
y = x1 + x2
xlist.append([x1,x2])
ylist.append(y)
运行计算图
这里用到了sess.run()函数,两个常用参数为数组类型的fetch和feed_dict。fetch中包含我们需要计算的Variable型变量,则返回对应的结果。我们首先的要执行优化器optimizer,则已经足以完成训练任务了。传入后面的w,b,y_out则是为了观察训练数据变化。
for i in range(num_steps):
batch_inputs,batch_label = generate_batch(batch_size,i*batch_size)
feed_dict = {trainIput:batch_inputs,trainLabel:batch_label}
#我们需要观察w,b的值变化,所以讲w,b传入fetch中并返回为wr,br。
_, lossr,wr,br,y_outr= sess.run([optimizer,loss,w,b,y_out],feed_dict=feed_dict)
训练分析
config1:拟合10以内的加法,参数配置:range_random=(0,10),lr=0.1
抽取部分运行结果:
loss=1.759128
loss=89.073334
loss=178533.062500
loss=64864305501175808.000000
loss=287426062692858948260170863477138653184.000000
loss=inf
loss=nan
可以看到损失函数越来越大,为发散状态这是为什么呢?猜测为学习率过大调小lr=0.01
config2:拟合10以内的加法,参数配置:range_random=(0,10),lr=0.01
抽取部分运行结果:
loss=6.463652
loss=0.570989
loss=0.325448
loss=0.000063
loss=0.000000
end at 1460
可以看到在1460次训练后,损失函数收敛到了最小值。这说明学习率要足够的。
config3:拟合100以内的加法,参数配置:range_random=(0,100),lr=0.01或者 lr=0.001
抽取部分运行结果:
loss=4042.668945
loss=inf
无法拟合!
config4:拟合100以内的加法,参数配置:range_random=(0,100),lr=0.0001
loss=8552.422852
loss=0.009713
loss=0.022158
loss=0.001590
end at 3817
从congfig1、2、3、4、可以看出学习率和输入的规模是有关系的,输入规模越大则学习率应该越小。
config5:拟合100以内的加法,参数配置:range_random=(0,100),lr=0.0001,去掉线性模型中的偏差
#简单的线性函数y=wx
y_out = tf.matmul(trainIput,w)
运行效果:
loss=4301.073730
end at 94
只运行了94次,这说明4中训练缓慢是由b造成的。分析一下w、b的梯度下降过程:
若对w求导则,w’= 2(wx+b-y)*x 则更新后的update_w=lr*w’。对b求导:b=2(wx+b-y) update_b=lr*b’。可以看出如果x越大,则update_b比update_w小的更多。当update_w更新粒度合适时,update_b会小很多。所以我们应当减少w和b的差距,可以设置不同的学习率。问题来啦,在TensorFlow中怎样为不同的参数设置学习率?