不知不觉写到了第七篇,理一下思路:html
DNN/CNN都是目前机器学习领域最经常使用的算法,学习了这些例子,算是前进了一大步。
可是从产品的观念看,咱们还有不少事情要作,好比如何从算法成为一个程序?程序如何调试?技术如何产品化?
下面咱们就说一说这方面的问题。前端
TensorFlow是一个快速迭代中的产品,欣欣向荣的同时,做为尝鲜者,你须要忍受API不断的变动。
有些朋友的作法是,下载一套就一直使用,轻易不升级。并且你看python的包管理工具pip也是推荐这种模式的,pip不多像brew/apt这些包管理工具同样每次都提醒你升级新的软件包。
两种状况下,我也会推荐这种方式:python
若是是以学习为目的,我以为按期关注官方网站、官方的文档,根据本身的进度适时更新仍是很重要的。
从个人体验上,TensorFlow对于版本的更新对API的影响控制的仍是很是好的。其实前面所讲解的那些例子,不少来自于0.7.x版本的TensorFlow,基本上不加修改或者不多修改就顺利运行在当前1.4.x的TensorFlow中。
这一点可比革命完全的swift强多了,网上有一个很著名的梗:曾经我觉得看一本《Swift 从入门到精通》就能够了,谁知道我还得看《Swift 2.0 从入门到精通》、《Swift 3.0 从入门到精通》,《Swift 4.0 从入门到精通》。
从这一篇开始说这个问题主要缘由也是,其实TensorFlow在主要的算法部分在各版本保持了很好的一致性。而在周边的功能部分变化仍是比较大的,好比说XLA、对GPU的支持、整合Keras等特征。因此我建议开发使用的版本至少是选用TensorFlow 1.x以后的版原本入手。git
在第四篇中咱们介绍了一个最简单的机器学习算法,主体是线性回归方程接softmax分类。
源码来自于老版本的TensorFlow,在最新版本中这个源码作了修改。增长了程序结构方面的考虑。
没有在一开始就讲解新的版本,你看了下面的源码就知道了。跟算法无关的部分太多了,实质上提升了入门的门槛。而如今咱们掌握了基本的算法,再回头来看外围的结构部分,理解起来就很快了。算法
#!/usr/bin/env python # -*- coding=UTF-8 -*- # 注意以上部分官方本来是没有的,其中直接执行是为了使用更方便, # 否则须要用这种方式执行: python mnist_softmax.py # 字体编码是由于须要添加中文注释,不然执行时会报错 # ============================================================================== # Copyright 2015 The TensorFlow Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== """A very simple MNIST classifier. See extensive documentation at https://www.tensorflow.org/get_started/mnist/beginners """ #下面三行是为了利用python3中的一些先进特征而添加, #实际在本代码中并无使用这些特征,所以技术上说是能够屏蔽的, #但无论是否使用,每次引入避免由于习惯引发的低级错误也是推荐的, #由于python大多当作脚运行,极可能执行到的时候才会报错。 from __future__ import absolute_import from __future__ import division from __future__ import print_function #引入命令行参数解析库 import argparse import sys #全部的示例,若是使用官方推荐的方式安装tensorflow包, #都应当已经已经安装在本地,好比mac上路径是: #/usr/local/lib/python2.7/site-packages/tensorflow/examples/tutorials/mnist #因此下面这个是直接从系统包中引用数据准备脚本 from tensorflow.examples.tutorials.mnist import input_data import tensorflow as tf #这个变量将用来保存命令行参数,首先清空 #在这里声明是为了成为全局变量,能够直接在函数中调用 FLAGS = None #主程序,其实python做为重要的脚本语言, #并非必须定义主函数,但显然这种方式让程序更规范 def main(_): # Import data #下面的算法部分基本不须要重复解释,能够看之前版本的注释 #FLAGS.data_dir是从命令行传递来的参数, #表明数据目录 mnist = input_data.read_data_sets(FLAGS.data_dir) # Create the model x = tf.placeholder(tf.float32, [None, 784]) W = tf.Variable(tf.zeros([784, 10])) b = tf.Variable(tf.zeros([10])) y = tf.matmul(x, W) + b # Define loss and optimizer y_ = tf.placeholder(tf.int64, [None]) # The raw formulation of cross-entropy, # # tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(tf.nn.softmax(y)), # reduction_indices=[1])) # # can be numerically unstable. # # So here we use tf.losses.sparse_softmax_cross_entropy on the raw # outputs of 'y', and then average across the batch. #交叉熵的计算,系统已经有内置的函数, #不须要本身计算了,对比原来的源码能够看一下, #上面英文也写出了本身计算的缺陷 #注意参数:labels表示认为标注的正确值,logits是模型计算出的值 cross_entropy = tf.losses.sparse_softmax_cross_entropy(labels=y_, logits=y) train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy) sess = tf.InteractiveSession() tf.global_variables_initializer().run() # Train for _ in range(1000): batch_xs, batch_ys = mnist.train.next_batch(100) sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys}) # Test trained model #注意这里的比较,y_没有跟之前版本同样作argmax, #由于y_的值mnist.test.labes已是数字自己, #而不是原来表明10个分类的一维数组, #这个是由上面read_data_sets函数中one_hot=True参数决定的, #没有这个参数表明直接读数值数据,实际上在下载的数据包中就是数值 correct_prediction = tf.equal(tf.argmax(y, 1), y_) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})) if __name__ == '__main__': #使用引入的argparse库解析命令行参数 parser = argparse.ArgumentParser() #本脚本只支持一个参数,用于指定测试数据集文件夹 #默认的文件夹路径作了修改,指向本地已有的数据集, #省得每次启动时用参数从新指定 parser.add_argument('--data_dir', type=str, default='MNIST_data', help='Directory for storing input data') #解析的参数存入FLAGS,注意这是全局变量 FLAGS, unparsed = parser.parse_known_args() #tf的架构定义, #包装了main主函数,熟悉c语言之类的用户看起来确定更亲切 tf.app.run(main=main, argv=[sys.argv[0]] + unparsed)
粗看起来源码比咱们最先看到的复杂了不少是吧?其实有效的代码并无多少,这里作几个解释:express
前面的例子咱们体会很深了,随着算法的复杂度提升,训练所耗费的时间愈来愈长。这还只是10M左右的数据集和一个小的练习。在大型的系统中,可能须要一个集群的工做环境作几周的运算。
在真正投产的时候,实际就只是用训练的数据配合预测部分的代码完成工做便可。这就须要把训练数据保存下来。
TensorFlow的保存、恢复很是容易,首先要生成一个保存器:apache
filesaver = tf.train.Saver()
随后在要保存数据的位置,把数据保存到文件中:编程
datafile='data/softmax_data.tfdata' filesaver.save(sess,datafile)
在须要用到数据的位置,再把数据还原回来,一般产品化的时候,都是利用这个还原来替代大量长时间的运算,而且也保护了真正重要的数据:小程序
datafile='data/softmax_data.tfdata' filesaver.restore(sess,datafile)
应用到程序中,程序的主要变更通常是结构上的。或者训练、预测分红两个程序。或者用开关判断分红两个部分。下面就用本篇上面的代码做为例子添加上数据保存、恢复的功能:swift
#!/usr/bin/env python # -*- coding=UTF-8 -*- # ============================================================================== # Copyright 2015 The TensorFlow Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== """A very simple MNIST classifier. See extensive documentation at https://www.tensorflow.org/get_started/mnist/beginners """ from __future__ import absolute_import from __future__ import division from __future__ import print_function import argparse import sys,os from tensorflow.examples.tutorials.mnist import input_data import tensorflow as tf FLAGS = None #数据文件保存的位置 datafile='data/softmax_data.tfdata' #增长一个函数用于判断数据文件是否已经存在 def datafile_exist(): #tf写出的数据文件实际是分了几部分, #咱们判断其中的索引文件是否存在 #假设索引文件存在,就有完整的数据文件 return os.path.exists(datafile+".index") def main(_): # Import data mnist = input_data.read_data_sets(FLAGS.data_dir) # Create the model x = tf.placeholder(tf.float32, [None, 784]) W = tf.Variable(tf.zeros([784, 10])) b = tf.Variable(tf.zeros([10])) y = tf.matmul(x, W) + b # Define loss and optimizer y_ = tf.placeholder(tf.int64, [None]) cross_entropy = tf.losses.sparse_softmax_cross_entropy(labels=y_, logits=y) train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy) filesaver = tf.train.Saver() sess = tf.InteractiveSession() tf.global_variables_initializer().run() if FLAGS.train or (not datafile_exist()): #若是命令行指定训练模式,或者数据文件根本不存在则执行训练流程 # Train for _ in range(1000): batch_xs, batch_ys = mnist.train.next_batch(100) sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys}) print("Training finished, data write to file.") #训练结束,写出数据 filesaver.save(sess,datafile) # Test trained model correct_prediction = tf.equal(tf.argmax(y, 1), y_) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})) if (not FLAGS.train) and datafile_exist(): #若是已经存在数据文件,而且没有要求强行从新训练,则恢复文件 print("Restore data...") #恢复数据 filesaver.restore(sess,datafile) # Test trained model correct_prediction = tf.equal(tf.argmax(y, 1), y_) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--data_dir', type=str, default='MNIST_data', help='Directory for storing input data') #添加了一个参数,用于强制程序运行在训练数据状态 #使用时,若是添加-t或者--train参数执行,则运行在训练模式 #若是没有参数,则判断数据文件是否存在,已经存在直接进入测试模式 #没有存在,则先训练,写出数据集,再恢复数据集作一次测试 parser.add_argument('-t','--train', action='store_true',default=False, help='Force do train') FLAGS, unparsed = parser.parse_known_args() tf.app.run(main=main, argv=[sys.argv[0]] + unparsed)
在机器学习领域,学习算法作练习问题不大,实际应用,不管性能多强的电脑你都老是感受慢。
JIT XLA是TensorFlow推出的一项新特征,就是运行时编译器。主要功能是把数学模型部分进行运行时编译,融合可组合的op以提升性能,内存需求也会下降不少。
XLA全称:Accelerated Linear Algebra,也就是加速线性代数。
使用XLA很是容易,只要在Session初始化的时候添加以下代码:
config = tf.ConfigProto() jit_level = tf.OptimizerOptions.ON_1 config.graph_options.optimizer_options.global_jit_level = jit_level with tf.Session(config=config) as sess: ...
Session的运行就会在XLA方式之下。实际上XLA的运行须要CPU或者GPU的支持。在个人老电脑上,虽然硬件不支持XLA的加速,但内存的需求也下降了大概一倍左右,可说效果明显。
不一样于传统程序的DEBUG,机器学习类的程序,在程序逻辑上大多不会犯什么大错误,毕竟整个流程很简单。
大多的过程其实是在“调优”而不是“调试”。在调优中须要关注到的,主要是数据方面的问题。而数据无论原来是什么格式,进入机器学习系统后,每每都已经变成了抽象的矩阵。因此整个调优过程每每充满着痛苦和无力感。
TensorBoard是一个官方出品的优秀工具,能够经过读取事件文件的形式把数据图形化的显示出来,从而大幅下降调优难度。
使用TensorBoard自己很简单,是一个B/S结构的程序,在浏览器中就能够操做全部功能。比较麻烦的是生成事件文件,须要在程序代码中嵌入不少用于监控的语句,用于输出数据留待tensorboard分析。
官方有一个写的很好的样例:mnist_with_summaries.py
,可是这个源码看上去偏于复杂。咱们仍是以上面的softmax分类识别的源码为例,对此作一个介绍。有了这个基础,官方的样例留给读者在读完这里的内容以后,本身尝试去分析。
生成事件数据文件只须要两行语句:
#在Session创建后执行: train_writer = tf.summary.FileWriter('logs/train', sess.graph) #在Session关闭前执行: train_writer.close()
程序执行完成后,将在./logs/train/
目录下生成事件数据文件。随后能够用tensorboard来分析和显示数据文件:
> tensorboard --logdir=logs/ TensorBoard 0.4.0rc3 at http://127.0.0.1:6006 (Press CTRL+C to quit)
命令中指定数据文件路径只须要指定到上一级便可,由于tensorbaord支持多组数据文件对比显示,这个咱们后面再介绍。
查看图形化的分析结果可使用浏览器访问:http://127.0.0.1:6006。
可以显示出来完整的数学模型,只是图的可读性感受仍是比较差,大多节点要思考一下才能明白表明的是什么。并且除了图基本也没有提供其它参数,难以作更深刻的分析。
咱们还能够继续定义一些输出来改善数学模型的显示:
咱们能够监控常量的状态:
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) #第一个参数是指定显示时图表的名字,第二个参数是监控的值 tf.summary.scalar('accuracy', accuracy)
能够监控变量的状态:
cross_entropy = tf.losses.sparse_softmax_cross_entropy(labels=y_, logits=y) #第一个参数是指定显示图表的名字,第二个参数是监控的值 tf.summary.histogram('cost', cross_entropy)
能够对节点命名以便更直观的看到图形化后模型的结构:
with tf.name_scope("pic_samples"): x = tf.placeholder(tf.float32, [None, 784]) with tf.name_scope("weight"): W = tf.Variable(tf.zeros([784, 10]))
注意:with tf.name_scope("pic_samples"):
这种形式,其实也是TensorFlow中的变量做用域的定义。
由于机器学习中的变量通常都占用了比较大的空间,咱们确定但愿尽量重复使用变量,因此若是在大系统中,会存在不少变量。这时候就须要有做用域在对变量作出管理。这方面的功能,初学确定用到不到,用到的时候看看手册就够了,这里很少说。
咱们既然监控了变量、常量,必然须要tensorflow的运算才能获得这些值,虽然这些值只是输出到事件文件中的。因此记住一点,只要使用了任何的summary操做,咱们就须要在FileWriter定义的同时,定义一个运算操做,并在以后在Session.run中运算这个操做,随后把返回的结果添加到事件文件中去,这样才能真正把监控的值输出到事件文件中去:
#注意这一行应当在全部须要监控的变量、常量、图片等都设置好后,最后运行 #此语句以后定义的观察器将都不会被输出到事件文件。也就没法被查看了 merged = tf.summary.merge_all() train_writer = tf.summary.FileWriter('logs/train', sess.graph) ... summary,_ = sess.run([merged,train_step], feed_dict={x: batch_xs, y_: batch_ys}) train_writer.add_summary(summary, i)
由于定义了placeholder,因此每次sess.run()都是须要喂数据的。即使咱们定义的merged这个操做并不须要数据。因此若是单独运行这个操做,附带喂入数据确定是很不经济。所以一般的方法,都是跟主要的操做一块儿运行。同时运行多个操做,而且同时获得多个返回值的语法既是python的语言特点,也是TensorFlow支持的功能。
如今重复运行程序,获得新的事件文件,再次启动tensorboard而后用浏览器查看,咱们能够看到更多的内容了:
请注意看两个命名的节点,都已经有更友好的节点名了。
咱们监控的变量,能清晰的看到代价函数的值逐渐变小,表示逐渐趋于收敛。(请忽略这个粗糙示例中的抖动,这里仅是为了示例可视化的效果。)
其它监控的各类值基本相似,这里就不一一贴出图片了,建议你把源码执行一下而后看看效果。
TensorBoard功能很是强大,不少功能超乎咱们的想象。前面咱们介绍过一个小程序,本身把输入的数据图形化,而后写出到一个图片文件。
这样的功能,若是使用TensorBoard将更加容易,好比下面代码监控输入矩阵及计算的权重值,并以图片的形式显示出来:
x = tf.placeholder(tf.float32, [None, 784]) image_shaped_input = tf.reshape(x, [-1, 28, 28, 1]) tf.summary.image('input_images', image_shaped_input, 10) ... W = tf.Variable(tf.zeros([784, 10])) Wt=tf.transpose(W) #由于权重值跟图片数据定义不一样,要先转置一下,再转成图片 image_shaped_weight = tf.reshape(Wt, [-1, 28, 28, 1]) tf.summary.image('weight_images', image_shaped_weight, 10)
最终生成图片的样子咱们前面的内容中见过,这里也不贴图了。
其中权重部分,由于不是[None,784]这样的形式,而是[784,10],因此要先使用tf.transpose转换成[10,784]的形式,再reshape成28x28的图片,最后才能以图片的方式显示出来。
这一节的最后部分,把上面示例的完整代码列出来,以供你参考实验,由于上面讲解都很详细了,例子中就没有加过多的注释。请注意这个例子由于经历了屡次的补丁和无逻辑意义的做用域定义,程序结构上比较混乱,你们在正式的项目中可千万不要模仿。
#!/usr/bin/env python # -*- coding=UTF-8 -*- # ============================================================================== # Copyright 2015 The TensorFlow Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== """A very simple MNIST classifier. See extensive documentation at https://www.tensorflow.org/get_started/mnist/beginners """ from __future__ import absolute_import from __future__ import division from __future__ import print_function import argparse import sys,os from tensorflow.examples.tutorials.mnist import input_data import tensorflow as tf FLAGS = None datafile='data/softmax_data.tfdata' def datafile_exist(): return os.path.exists(datafile+".index") def main(_): # Import data mnist = input_data.read_data_sets(FLAGS.data_dir) # Create the model with tf.name_scope("pic_samples"): x = tf.placeholder(tf.float32, [None, 784]) image_shaped_input = tf.reshape(x, [-1, 28, 28, 1]) tf.summary.image('input_images', image_shaped_input, 10) with tf.name_scope("weight"): W = tf.Variable(tf.zeros([784, 10])) Wt=tf.transpose(W) image_shaped_weight = tf.reshape(Wt, [-1, 28, 28, 1]) tf.summary.image('weight_images', image_shaped_weight, 10) b = tf.Variable(tf.zeros([10])) tf.summary.histogram('bias', b) y = tf.matmul(x, W) + b # Define loss and optimizer y_ = tf.placeholder(tf.int64, [None]) cross_entropy = tf.losses.sparse_softmax_cross_entropy(labels=y_, logits=y) tf.summary.histogram('cost', cross_entropy) train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy) filesaver = tf.train.Saver() sess = tf.InteractiveSession() tf.global_variables_initializer().run() correct_prediction = tf.equal(tf.argmax(y, 1), y_) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) tf.summary.scalar('accuracy', accuracy) merged = tf.summary.merge_all() train_writer = tf.summary.FileWriter('logs/train', sess.graph) if FLAGS.train or (not datafile_exist()): # Train for i in range(1000): batch_xs, batch_ys = mnist.train.next_batch(100) sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys}) summary,_ = sess.run([merged,train_step], feed_dict={x: batch_xs, y_: batch_ys}) train_writer.add_summary(summary, i) # Test trained model if (i % 100 == 0): summary1,ac = sess.run([merged,accuracy], feed_dict={x: mnist.test.images, y_: mnist.test.labels}) train_writer.add_summary(summary1,i) print("Training finished, data write to file.") filesaver.save(sess,datafile) print(ac) train_writer.close() if (not FLAGS.train) and datafile_exist(): print("Restore data...") filesaver.restore(sess,datafile) # Test trained model correct_prediction = tf.equal(tf.argmax(y, 1), y_) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--data_dir', type=str, default='MNIST_data', help='Directory for storing input data') parser.add_argument('-t','--train', action='store_true',default=False, help='Force do train') FLAGS, unparsed = parser.parse_known_args() tf.app.run(main=main, argv=[sys.argv[0]] + unparsed)
注意程序运行的时候要使用-t参数,由于图示部分的代码主要加在了训练环节。
读完了这个程序,再去读官方的示例应当容易不少,很是建议你以官方程序为范例仔细阅读。
做为实践环节的最后一部分,介绍一下一般机器学习项目的通常工做流程和团队。
稍微成熟的公司,通常都已经有本身规范的研发流程和管理方式,此处列出的流程仅供参考。其实主要是强调算法专家的角色和数据收集的工做。这两组人员在通常的项目中是没有或者位置并非很重要的。可是在机器学习项目中,每每是核心部分。在图像识别等监督学习领域,数据收集、标注成本每每占了最主要的预算。
(待续...)
TensorBoard:可视化学习
tensorflow里面name_scope, variable_scope等如何理解?
python argparse用法总结
谷歌官博详解XLA:可在保留TensorFlow灵活性的同时提高效率
完整机器学习项目的工做流程
mnist_softmax_xla.py mnist_deep.py mnist_with_summaries.py