近几年来,人工智能越来越受到社会的广泛关注,从政府部门到私营企业均如雨后春笋般建立自己的 AI 团队。而对于 AI 开发者或者想从事 AI 行业的工程师们而言,了解人工智能的发展历程,熟悉常用的项目框架,将在以后的工作中起到非常良好的帮助作用。
在接下来的 Chat 中,首先会带领大家简单了解一下人工智能的发展现状以及 Google 开发的 TensorFlow 深度学习框架。然后帮助大家掌握一种解决机器学习问题的高效方法,做到对人工智能有更深入的了解以及以项目为驱动,开展基于 TensorFlow 的 AI 项目实战开发,在代码与实践中积累知识与经验。
简单来讲,您将在本场 Chat 中学到如下内容:
- 了解人工智能的发展现状
- 理解 TensorFlow 的基本概念
- 搭建 TensorFlow 开发环境
- 熟悉 TensorFlow 的操作步骤以及代码
- 学习 TensorFlow 设计的函数以及算法
- 掌握 TensorFlow 图像识别与可视化
- 进阶 TensorFlow:应用开发与产品化
人工智能对于现如今的人们而言已经并不陌生,不管是科幻电影还是现实中的 IT 行业,多多少少的都会带有一些人工智能的影子。然而要真正的接近人工智能,了解人工智能,我们就绕不开人工智能的一个重要分支——机器学习。
机器学习是一门很年轻的学科,但并不意味着它从出生起就一直顺风顺水的发展。在几十年的时间里,机器学习经历过初生时的蓬勃期,也经过停滞不前的寒冬期,再到后来的复兴期,一直到现在的成型期与高速发展期。其中非常具有代表并且人人耳熟能详的人工智能商业应用有语音识别、图像识别、文字处理等等。
当然,人工智能能够做到的东西并不只有这些,因为按照科技的发展与人类的需求,在未来的技术革新中,只要是人类能够完成的事务,人工智能也应当能够达到相应的水准,正如从字面上理解“人工智能”一样——使机器能够胜任一些需要人类智能才能完成的复杂工作。
Tensorflow 简介TensorFlow 是谷歌基于 DistBelief 进行研发的第二代人工智能学习系统,其命名来源于本身的运行原理。Tensor(张量)意味着N维数组,Flow(流)意味着基于数据流图的计算,TensorFlow 为张量从流图的一端流动到另一端计算过程。TensorFlow 是将复杂的数据结构传输至人工智能神经网中进行分析和处理过程的系统。
TensorFlow 可被用于语音识别或图像识别等多项机器学习和深度学习领域,对 2011 年开发的深度学习基础架构 DistBelief 进行了各方面的改进,它可在小到一部智能手机、大到数千台数据中心服务器的各种设备上运行。TensorFlow 将完全开源,任何人都可以用。 ————来源于百度百科
总之,Tensorflow 十分优秀并且强大。
搭建 TensorFlow 开发环境为了使对一些对机器学习十分感兴趣的读者在读完本篇 chat 后,能够通过自己的操作实现一些简单的小案例,零距离的接触到用代码实现机器学习,本篇 chat 会一步一步地教给读者怎样搭建相应的开发环境及安装相关软件,希望读者能够跟随本篇 chat 一起动手操作,在实践中收获更多的知识。
开发环境搭建:- 虚拟机:Ubuntu16.0.4.3。
- 安装及更新python开发环境:Sudo apt-get install python-pip python-dev。
- 在python中安装tensorflow框架:Sudo pip install tensorflow。
- 安装python绘图库:Sudo apt-get install python-matplotlib。
在完成上述的步骤后,我们只需要做一些简单的准备,引入 Tensorflow 库以及声明一个计算图会话 tf.session(),然后可以正式地开始 Tensorflow 应用之旅了。
import randomimport numpy as npimport tensorflow as tfsess = tf.Session()
熟悉 TensorFlow 的操作步骤及代码实现
了解基本的输入输出
Tensorflow 的主要数据结构是张量,这个张量也可以理解为多维数组,通俗地来讲,我们接触到的每一件事物都有自己的属性,比如说一个箱子,它有长、宽、高、颜色、重量;一棵树,它有高度、树干围度、树枝分叉数、树叶颜色、树叶形状……等等。
不仅如此,一种事物区别于另外一种事物的特征有时不仅仅只有一个,要让机器获取这些特征,并且能够处理这些特征,由此看来,多维数组也就是张量可以说是一个非常好的数据结构,因为我们可以将一个事物的不同特征用不同的维度来指代。
在 Tensorflow 中,对应不同的数据类型,其生成张量的函数有很多,举例来讲:
- 用 Tensorflow 生成一个指定维度(二维)的单位张量:test_one = tf.ones([4,3])。
- 用 Tensoeflow 生成一个指定维度(二维)的正态分布的随机数张量:
test_ two = tf.random_normal([4,3],mean=0.0,stddev=1.0)。
上面仅仅是两个比较简单的例子,而在实际工程当中,张量的选取主要根据所要处理的事物特征来决定。不过在现在的起步阶段,大家可以不用太执着于这个,只需要了解张量是 Tensorflow 的基本数据结构即可,具体的应用会在下面的例子中实现,到时大家会看到张量的实际应用。
知道张量作为 Tensorflow 基本的数据结构后,接下来将介绍的是基于张量的两个重要工具:变量和占位符。
变量:可以改变的参数在接触机器学习之前,很多读者应该会了解一个叫做数学建模的学科,机器学习从某种意义上讲也是一种数学建模。这个学科就是将所需要解决的现实问题抽象成一个数学模型,然后通过不断地调整数学模型当中的参数,使该数学模型更接近所要处理的问题。同样的道理,变量在 Tensorflow 里面就是可以改变的参数,Tensorflow 通过不断地维护或者调整这些变量的值或者状态来不断地优化机器学习算法。
在上述过程中,我们简单了解了张量的相关内容,在此基础上,变量的学习也非常简单,只需要使用 tf.Variable()
函数包装一下即可,举例来说:
声明张量:
test_one = tf.ones([4,3])
。声明变量:
test_one_var = tf.Variable(tf.ones([4,3]))
。需要注意的是,在声明变量以后,需要对全局变量进行初始化:
initialize_op = tf.global_variables_initializer()
。
至此,声明后的变量就可以投入使用了。
占位符:从定义来讲:在 Tensorflow 当中表示输入输出数据的格式,可以说,任何的输入数据(变量不是输入数据,而是事先设计好的参数)想要进入到 Tensorflow 计算处理当中,都需要经过占位符,因为占位符确定了输入数据的类型,它只允许传入指定类型或是指定形状的数据,同理,它也确定了输出数据的类型,对输出数据的格式也有要求。
简单来讲,占位符的作用就是规范输入输出的数据,制定一个格式规则,传进来和传出去的数据必须经过这个格式规则才能成为有效数据。
另外,占位符另外一个很重要的作用是,在声明占位符后,任何算法的引入都可以先使用占位符作为其中的一个参数,等整个算法流程完成后,直接将输入或者输出的数据传入占位符,这些数据就会按照占位符出现的位置依次通过算法,最后得到输出。大大简化了数据处理的流程,并且使整个算法代码具有很好的可读性。举例而言:
x = tf.placeholder(tf.float32,shape=[2, 2]) #声明占位符 shape指定的是2*2的矩阵y = tf.identity(x+1) #声明算法操作x_vals = np.random.rand(2, 2) #要传入的数据sess.run(y, feed_dict={x: x_vals}) #传入数据,并得到结果
虽然比较简单,但是这些是 Tensorflow 最基本也是最重要的起步阶段,多阅读几遍的话,会发现 Tensorflow 设计的流程是非常好的,在这里可以和大家分享一下我在初学时的心得:
第一步:声明占位符,确定输入数据的类型和格式。第二步:声明算法,在上面的那个例子就是完成x+1的操作,可以看出,占位符在其中也发挥了作用。第三步:声明一个张量作为要传入的数据。第四步:运行算法,注意,在 feed_dict
函数中,完成了实际输入值传入占位符的操作。
要深入地了解 Tensorflow,可以暂时先不用了解算法内部的具体实现,但一定要知道的是其中各种算法的用法以及 Tensorflow 计算图运行的机制,了解 Tensorflow 是怎么样进行学习的,怎么样不断地调整参数,怎么样进行优化,怎么样进行监督,又怎么样规避一些算法实现上的特殊问题。
在本次的 chat 当中,不会对算法内部具体的实现做出过多介绍,因为这个讲解起来会占用大量的时间篇幅,但是,本次 chat 会尽可能的带领大家从宏观上了解 Tensorflow 运行的架构,相信大家在充分了解这个架构后,对于今后学习架构当中各个环节,了解 Tensorflow 内部算法具体实现的过程会起到十分良好的帮助作用。
本篇 chat 对 Tensorflow 的运行机制做了一个简单的抽象图示,帮助大家更快的理解 Tensorflow 是怎样建立模型,又是基于怎样的机制调整参数进而实现模型优化的。
输入输出模块在上文已经做了简单介绍,在这里需要进一步说明的是,使用 Tensorflow 训练模型时,输入的数据称为样本数据,样本数据可以理解为带有输入的需要处理的数据,同时也带有着标准的已经确定的正确输出,主要用途是帮助模型比较自己的输出和标准答案之间的差距,然后不断地修正参数,最终使得自己的输出和标准答案输出相差不多,这样看起来有点类似小学生学习一样。
同样的样本数据根据功能可以分为三个部分:
第一部分是训练样本集,顾名思义,就是用来训练模型的。第二部分是测试样本集,在模型训练完毕后,进行测试用。第三部分是验证样本集,等到模型训练并测试完毕后,取一些样本中的数据模拟成以后要处理的数据,来验证模型的优劣性,从而最终决定模型的最优参数。之后模型就可以投入使用了。
损失函数看起来很高大上,但是在 Tensorflow 里面其实很简单,刚才我们说到模型不断调整参数的方法是基于比较模型自己的输出和标准答案之间的差距,在 Tensorflow 当中,也是这样实现的,举例而言:
x_vals = tf.linspace(-1., 1., 500) #预测序列,输入值target = tf.constant(0.) #目标序列,标准答案l2_y_vals = tf.square(target - x_vals) #L2正则损失函数,就是差值的平方l2_y_out = sess.run(l2_y_vals) #运行起来,查看差距
在上面的例子当中,损失函数使用的是L2正则损失函数,其实就是欧拉函数,也就是差值的平方。
Tensorflow 当中还有很多损失函数,在刚接触时大家只需知道有哪些,怎么用,用在什么地方合适即可,在熟练掌握后,可以进一步研究其内部算法实现,从而自制一个更适合自己模型的损失函数。
反向传播当我们在上一步的损失函数当中获得输出值与标准答案之间的差值后,为了让模型能够意识到差距进而调整自己的参数,使得在下一步的处理过程中能够使模型的输出值更接近标准答案,Tensorflow 主要是通过声明一个优化函数来完成最小化损失、反向传播误差进而调整变量值这一系列操作的,它的声明也非常简单,举例而言,我们使用一个名为标准梯度下降法的优化函数,它在 Tensorflow 当中的定义是 GradientDescentOptimizer():
`train_loss = tf.train.GradientDescentOptimizer(learning_rate=0.05)
Learning_rate
就是前面提到过的学习率,这个参数是需要自己事先主动设好的。需要提醒大家的是,优化器的算法对学习率的选择是十分敏感的,不同的优化函数,不同的学习率都有各自的优缺点和使用场景:
小学习率:收敛慢,但结果精确,若算法不稳定(算法严谨性较低,可能导致不收敛的情况发生),可以选择小学习率。
大学习率:收敛快,但结果不精确,若算法收敛太慢(通常算法较为优秀,必然会收敛只是会很慢),可以选择大学习率。
当然,优化函数不仅仅只有 GradientDescentOptimizer() 这一个,在 Tensorflow 当中还提供了一些其他的优化器算法,感兴趣的读者可以参考 Tensorflow 官方文档,而且现在文档也有中文版本,仔细阅读可以获得更大的收获。
项目实战经过前面几个环节,我们已经了解了 Tensorflow 是如何从输入数据,模型建立,模型参数修正,得到正确输出等一系列运行的机制,可以说机器学习就是在与模型的参数打交道,这些参数不是靠人去计算或者指定的,而是机器通过不断地比对模型输出值和标准输出值之间的差距来将这个最佳的参数慢慢地收敛获得的。为了使大家能够将这些内容简单地融会贯通,本篇 chat 会用一个简单地案例来将上面所讲的内容串联起来,同时,也让各位读者亲身体会一下 Tensorflow 的魅力。
基于 Tensorflow 的图像识别正如开始时所说,我们刚起步的阶段不需要知道模型算法的内部实现是什么,只需要知道算法是什么,干什么用即可。所以,在这个实例当中,本次 chat 选择使用机器学习当中最简单的一种算法:最近邻域算法。
简单来讲,最近邻域算法就是一种基于实例的分类算法。
即判断一个新输入的数据是一个什么属性的值,就看与该数据特征最接近的大多数 “ 邻居数据 ” 们是什么属性的值,就好像近朱者赤近墨者黑一样,一个新数据的邻居数据“黑”属性的居多,那么我们就认定这个新数据的属性也是 “ 黑 ” 的;同理一个新数据的邻居数据 “ 红 ” 属性的居多,那么我们就认定这个新数据的属性就是 “ 红 ” 的。根据这个算法特性,我们可以实现基于 Tensorflow 对于验证码数字的识别。
可能会有读者问到,这些验证码的数据从哪里获取呢?没关系,本次 chat 会告诉大家从哪些网站可以获得样本数据集,帮助大家在以后的开发过程中,可以不被数据所困扰:http://archive.ics.uci.edu/ml/datasets.html
在这里可以获得很多有用的数据,希望各位读者点进去看看,非常有趣。
MNIST 手写体字库接下来我们要用的图像识别的字体库,感兴趣的读者可以跟随本次的 chat 将其下载下来。
好了,下面开始真正进入我们的正题。
第一步:导入 Tensorflow 编程库,另外,为了能够将结果能够输出显示出来,我们还需要引入 plt 绘图库:
import randomimport numpy as npimport tensorflow as tfimport matplotlib.pyplot as pltfrom PIL import Imagefrom tensorflow.examples.tutorials.mnist import input_data
第二步:创建一个 Tensorflow 计算会话,我们所建立的这个模型将运行在此会话当中:
sess = tf.Session()
第三步:导入我们刚刚从下载的数据集。
mnist=input_data.read_data_sets("Project/MNIST_data/", one_hot=True)
第四步:初始化占位符。
#训练用x_data_train = tf.placeholder(shape=[None, 784], dtype=tf.float32) #测试用x_data_test = tf.placeholder(shape=[None, 784], dtype=tf.float32) #标准答案,训练用y_target_train = tf.placeholder(shape=[None, 10], dtype=tf.float32) #标准答案,测试用y_target_test = tf.placeholder(shape=[None, 10], dtype=tf.float32)
第五步:声明距离函数,这里说明一下,因为我们采取的是最近邻域法,所以要有一个算法来判断哪个数据是我们输入数据的邻居,简单起见,就选择差值的绝对值作为距离,那么,该绝对值越大,说明 “ 邻居 ” 距离我们越远,影响力越小;绝对值越小,说明“邻居”距离我们越近,影响力越大。
所以在处理时,我们可以取绝对值的倒数作为加权值,这样,虽然不是非常严谨,但基本上可以描述这种邻居影响力的关系。
distance=tf.reduce_sum(tf.abs(tf.sub(x_data_train, tf.expand_dims(x_data_test,1))), reduction_indices=2)
第六步:找最近邻居。
#找到最近的邻居top_k_xvals, top_k_indices = tf.nn.top_k(tf.neg(distance), k=k) prediction_indices = tf.gather(y_target_train, top_k_indices)count_of_predictions=tf.reduce_sum(prediction_indices, reduction_indices=1)#近朱者赤近墨者黑,统计哪种“颜色”占比最高,最高的那种颜色,就是对该图像的预测值prediction = tf.argmax(count_of_predictions, dimension=1)
第七步:在测试集上运行该算法,通过上述的机制完成模型的建立。
num_loops = int(np.ceil(len(x_vals_test)/batch_size)) test_output = [] actual_vals = [] for i in range(num_loops): min_index = i*batch_size max_index = min((i+1)*batch_size,len(x_vals_train)) x_batch = x_vals_test[min_index:max_index] y_batch = y_vals_test[min_index:max_index] predictions = sess.run(prediction, feed_dict={x_data_train: x_vals_train, x_data_test: x_batch, y_target_train: y_vals_train, y_target_test: y_batch}) test_output.extend(predictions) actual_vals.extend(np.argmax(y_batch, axis=1))
第八步:输出图像。
Plt.show()
Real Value 是图片的真实值,也就是标准答案。
Pred 是经过模型后识别出来的值,就是模型输出值。
下面抽取了 0,1 还有几个不好识别的图像展示给大家,是不是此时已经隐约加深了对 Tensorflow 继续学习的兴趣了。
第九步(额外功能):如果我们想要看准确率是多少的话,可以用上文提到过的测试数据集(训练数据集也可以抽取一部分作为测试数据集,因为它也是有着正确的输入和标准的输出)与通过模型后的输出结果做比较,查看准确率是多少。
accuracy=sum([1./test_size for i in range(test_size) if test_output[i]==actual_vals[i]]) print('Correct / Total: ' + str(accuracy))
可以看出,经过训练后的模型能够达到 90 % 的正确率,那么剩下的 10 % 差距在哪里呢?就在于我们上面选择的邻居距离计算算法,由于我们选择的是最简单的差的绝对值作为衡量邻居的距离,那么只要更换这个邻居距离计算算法,使描述距离的算法更加严谨,我们就可以提升模型的准确率,那么这个任务就交给各位读者,亲自动手试一试,查阅官方 API 开发文档,实现一个更高预测率的图片识别模型。
结语至此,本篇的 chat 也要接近尾声了,可以和大家分享的是,我在最初接触 Tensorflow 机器学习框架时,也是一头雾水,感觉学习起来很吃力,但是稍微带着一些耐心去仔细阅读它的代码,发现 Tensorflow 的框架结构其实是十分的简单清晰,只是暂时还有很多的 API 和处理方式没有接触。因此,在今后的过程中,我也会继续的深入学习研究 Tensorflow 框架的使用方法。希望会有更多的心得体会能够分享给大家。
总之,本篇的 chat 只是简简单单地带领大家入一个门,细细看来知识点还是有点多的,希望对机器学习尤其是 Tensorflow 感兴趣的读者能够结合官方的开发文档自己动手实现几个小例子,这样会极大的帮助自己提高对 Tensorflow 的掌握和理解。
最后,感谢大家对我的支持,也希望在今后的机器学习之路上能够和各位读者共同进步,学有所获!
本文首发于GitChat,未经授权不得转载,转载需与GitChat联系。
阅读全文: http://gitbook.cn/gitchat/activity/5b66aca1c15d960e15d80aae
您还可以下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。