近来看网上一些tf入门教程,都是先讲下道理,然后搭建图,然后训练,然后结束。
然后读者一般是看懂了的就看懂了,没看懂的就像看了一篇软文 训练模型的教程,训练好的模型就是要用来用的,跟着训练了一遍模型,而读者却没有得到模型
有些教程体贴一点,教你训练好自己的模型之后如何保存,然后就没有然后了,因为没有教你如何恢复
tips: 该系列教程tensorflow版本皆为1.x
当然,最先讲的还是会话(Sesssion)
Python是一门效率比较低的语言,而TensorFlow底层是使用C++实现,以保证计算效率,上层的Python、Java、甚至JavaScript等代码用来设计、定义模型和构建的Graph,而编程语言之间的通信是比较耗时的,特别是在分布式并行计算中,所以Tensorflow使用Session来管理,只有当Session.run()的时候才会传到底层进行计算
定义一个Session
sess = tf.Session()
各种导包操作我就不一一阐述了,常用包的命名缩写都是大家约定俗成的,该导哪些包大家都懂
定义图结构
为了代码的工整,也为了我前面提到的模型的恢复,我们将整个神经网络专门定义到一个Network类中
考虑到这篇文章只是个Tensorflow入门而已,就讲一个用传统BP神经网络来拟合一个一元多次函数吧先定义一个类
不专门定义到一个类中后续恢复时又要重新定义结构。
劳神费力,虽千万人,吾肯定不往矣
class Network():
...
...
...
既然时一元函数,那么输入和输出层都只有一个神经元,放置一个隐藏层,隐藏层那就随便来10神经元吧
所以整个网络的结构就是:1个神经元的输入层+10个神经元的隐藏层+1个神经元的输出层
懒得画图了,大家脑补下,还望见谅
一个很简单的神经网络,简单到直接写出表达式:
就是用这个去拟合一个一元函数(是不是感觉有点大材小用了)
本小节所有代码皆在此类中,注意缩进
先定义训练数据输入的占位符
x = tf.placeholder(shape=[1,None],dtype=tf.float32)
y = tf.placeholder(shape=[1,None],dtype=tf.float32)
x和y就是训练数据的占位符(如其函数名),而训练数据就是 y = f(x) 的数据
定义输入层权重和偏置
Tensorflow张量的定义一般有这几种
- tf.Variable 定义变量
- tf.constant 定义常量
- 还有上面的tf.placeholder 输入占位符
而权重和偏置自然是变量啦,反向传播梯度下降时它们都是要不断变化的
inputW = tf.Variable(tf.random_normal([10,1]))
inputB = tf.Variable(tf.random_normal([10,1]))
大家可能会问,为什么权重是10x1的矩阵而不是1x10,这个和个人习惯有关,我习惯用权重乘以输入,输入为列向量,也可以定义为1x10,用输入乘以权重,输入行向量。反正这个就是要把x原来的1维变成10维。如果大家对神经网络不特别熟悉的话还是不要改啦,按我的来嘛。
同理定义隐藏层权重和偏置
hideW = tf.Variable(tf.random_normal([1,10]))
hideB = tf.Variable(tf.random_normal([1,1]))
构建网络
h1 = tf.nn.sigmoid(tf.add(tf.matmul(inputW,x),inputB))
output = tf.add(tf.matmul(hideW,h1),hideB)
采用sigmoid激励函数
tf.matmul(a,b)就是将张量a乘以张量b,tf.add(a,b)就是将张量a加上张量b
得到最终的output
输出层按理应当也采用激励函数,但我这为了省略数据归一化和还原,不对其采用激励函数,数据不归一化会导致梯度更新慢,网络收敛很慢,至于为什么,大家就要参考吴叔叔的课程和Rosenbrock函数了
定义loss和train_step
loss= tf.reduce_mean(tf.reduce_sum(tf.square(y-output)))
opt = tf.train.AdamOptimizer(1.0)
train_step = opt.minimize(loss)
loss函数的定义相信大家有了对tf.matmul和tf.add的了解,就能一眼看出其被定义为预测值和实际值的最小二乘
优化器opt我选择了一个强大的Adam优化器,要用就要用牛逼的,优化速率定位1.0
让优化器最小化loss就是我们训练模型需要做的事情了
到此,类Network编写完毕
主函数
先得到我们用来训练的所谓的大量数据其实是自己编的,没错就是自己编的
x_data = np.linspace(-1,1,100).reshape(1,100)
noise = np.random.normal(0,0.05,x_data.shape)
y_data = x_data**3+1+noise
现在就知道我们要拟合的函数为 y = x^3+1 了noise为白噪声,来点噪声的目的主要是为了告诉大家数据不是我编的(啪啪!)
定义Session
net = Network()
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
先实例化我们刚刚辛辛苦苦写的Network对象,然后定义一个sess,光定义一个sess还不够,要进行初始化Tensorflow才会承认我们刚刚辛辛苦苦写的一堆变量(Variable)
然后就是训练网络了
非常简单,一个循环搞定!
简单是简单,但就是运行时看不到训练过程,所以我用matplotlib库加了动态更新的图在上面,具体什么样我就不附上了,源码在本文最末尾,大家自个去运行看看呗
train_step = 200
for step in range(train_step):
print('第',step+1,'次训练')
sess.run(net.train_step,feed_dict={net.x:x_data,net.y:y_data})
pre = sess.run(net.output,feed_dict={net.x:x_data})
训练次数为200次
pre即我们模型此次训练后预测到的结果
重点要讲的是,当某个变量的计算要设计输入数据时,要用feeddict将数据传进去,如上面代 码所示
最终拟合效果如下:
拟合效果还行,还看得过去
保存模型
接上开头的话,大家学习要的不就是个成就感么,很多教程就是到上一步,然后让大家看看训练的精确度有多高,然后表示对CPU或GPU的工作给予高度的肯定,让其能够再接再厉发光发热,然后就没了,然后就真的没了,训练半天没得到能用模型,就是仅仅让自己的CPU或GPU发了下光和热,
saver = tf.train.Saver()
saver.save(sess, "./tmp/model.ckpt")
Tensorflow有专门保存模型的tf.train.Saver()
将模型保存到当前目录下的tmp文件夹下
保存后有4个文件,然而没有一个是.ckpt后缀的,至于为什么加个ckpt,官方是如是说的
即使模型的预测值和实际值大相径庭,但比较时自个亲自训练的模型,自己养大的孩子,也比那些预测得很好但预测完之后什么都没了的好
Note: There is not a physical file called /tmp/model.ckpt. It is the prefix of filenames created for the checkpoint. Users only interact with the prefix instead of physical checkpoint files.
模型的恢复放到下一篇文讲:
Tensorflow入门教程及模型保存和恢复(二)
最后附上全部代码:
import numpy as np
import tensorflow as tf
import pylab as pl
class Network():
x = tf.placeholder(shape=[1,None],dtype=tf.float32)
y = tf.placeholder(shape=[1,None],dtype=tf.float32)
inputW = tf.Variable(tf.random_normal([10,1]))
inputB = tf.Variable(tf.random_normal([10,1]))
hideW = tf.Variable(tf.random_normal([1,10]))
hideB = tf.Variable(tf.random_normal([1,1]))
h1 = tf.nn.sigmoid(tf.add(tf.matmul(inputW,x),inputB))
output = tf.add(tf.matmul(hideW,h1),hideB)
loss= tf.reduce_mean(tf.reduce_sum(tf.square(y-output)))
opt = tf.train.AdamOptimizer(1)
train_step = opt.minimize(loss)
if __name__ == '__main__':
x_data = np.linspace(-1,1,100).reshape(1,100)
noise = np.random.normal(0,0.05,x_data.shape)
y_data = x_data**3+1+noise
net = Network()
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
saver = tf.train.Saver()
fig = pl.figure(1)
pl.ion()
train_step = 200
for step in range(train_step):
print('第',step+1,'次训练')
sess.run(net.train_step,feed_dict={net.x:x_data,net.y:y_data})
pre = sess.run(net.output,feed_dict={net.x:x_data})
pl.clf()
pl.scatter(x_data,y_data)
pl.plot(x_data.T,pre.T,'r')
pl.pause(0.01)
saver.save(sess, "./tmp/model.ckpt")