从锅炉工到AI专家(3)

剖析第一个例子

学习《机器学习》,不少IT高手是直接去翻看TensorFlow文档,但碰壁的不少。究其缘由,TensorFlow的文档跨度太大了,它首先假设你已经对“机器学习”和人工智能很是熟悉,全部的文档和样例,都是用于帮助你从之前的计算平台迁移至TensorFlow,而并非一份入门教程。
因此本文尽力保持一个比较缓慢的节奏和阶梯,但愿弥合这种距离。本文定位并不是取代TensorFlow文档,而是但愿经过对照本文和TensorFlow文档,帮助你更顺利的进入Google的机器学习世界。
基于这个思路,这一节开始对上一节的例子作一个更详细的讲解。html

import tensorflow as tf
import numpy as np

代码一开始,引入了两个python扩展库,第一个是咱们的主角tensorflow,第二个是一个数学计算库,numpy。数学计算一般有有两个方向,一个是符号计算,或者叫化简公式;咱们这里用到的是另一个方向,就是数值计算,也就是无论公式多么复杂,最后的结果是否是无限不循环的小数,最终都计算出来具体的数值结果。因此习惯上也称numpy库叫作数值计算库。
有心人可能想到了,这个库跟前面提到的大计算器“Octave”功能是对应的。这里能够额外举一个使用python配合numpy解前面五元一次方程的例子:python

#!/usr/bin/env python 
# -*- coding=UTF-8 -*-

import numpy as np

#五元一次方程的左边部分的系数,定义为矩阵A
A=np.mat("2,1,1,1,1;1,2,1,1,1;1,1,2,1,1;1,1,1,2,1;1,1,1,1,2")
#方程组右侧的常数,定义为矩阵(向量)B
B=np.mat("6;12;24;48;96")
#使用numpy内置的解方程函数求解
x=np.linalg.solve(A,B)

#打印出来两个矩阵和结果
print "A=\n",A,"\nB=\n",B,"\nsolve=\n",x,"\n"

执行后运算结果是这样的:linux

> ./solveEqu.py 
A=
[[2 1 1 1 1]
 [1 2 1 1 1]
 [1 1 2 1 1]
 [1 1 1 2 1]
 [1 1 1 1 2]] 
B=
[[ 6]
 [12]
 [24]
 [48]
 [96]] 
solve=
[[-25.]
 [-19.]
 [ -7.]
 [ 17.]
 [ 65.]]

计算结果同Octave是彻底相同的。
其实在IT行业原本解决一个问题就会有不少方法,只看用户喜欢哪一种方法和习惯哪一种方法。就目前的用户群看,Octave / Matlab是在学术界普及型的工具。而python则仍是IT行业流行度比较高。“机器学习”恰好是一个跨学科的领域。因此,Python + numpy跟Octave / Matlab的选择,仍是交给你们的喜爱吧。
这里的重点是,numpy跟tensorflow如何取舍和配合。
从根本上说,numpy和tensorflow最终都是完成矩阵的数值计算,numpy倾向于打造一个python数值计算器,包络数学计算的各个方面。tensorflow主要是进行机器学习相关的算法实现,好比梯度降低算法就是tensorflow的一部分而numpy则没有。
在尚未tensorflow的的年代,使用python进行机器学习算法研究的人中,不少是利用numpy这样的工具支持,自行实现各类机器学习算法的。本文省去了“梯度降低法”的具体公式,可是网上有不少详细的资料,评估一下复杂度,利用numpy实现起来,估计也就是半天的工做量。
从这个角度看起来,numpy和tensorflow就是单纯的互补关系吗?不彻底是,主要缘由是tensorflow的框架特征。
tensorflow设计了一种很独特的框架,从本例中应当能看出来,tensorflow的强项在于构建数学模型,复杂的数学模型可能要不少行代码,才能一点点拼接成,这个数学模型在tensorflow中称为图(Graph)。在这个模型构建的过程当中,实际上tensorflow并不进行模型的任何计算。一直到最后整个模型构建彻底完成,才在session.run()的时候真正的将这张图或者说这个数学模型运行起来。
这种设计,看上去就好像是在python中增长了一个黑盒,盒子上各类仪表逐一设定,最后打开开关开始运行。这种设计既有利于把研发人员注意力集中到数学模型的设计上,也有利于后端使用c/c++等语言实现高效率的运算,甚至也为使用GPU和可能的集群计算打下了基础。
把这个模式搞清楚,tensorflow和numpy的分工也就清楚了。全部须要当即计算直接出结果的任务,而且计算量不大、属于数据准备过程当中或者交叉验算过程当中的任务,能够交给numpy。须要构建机器学习数学模型的任务,都必须使用tensorflow进行。此外因为tensorflow会进行不少遍的循环,因此若是其中能够抽象出来,在外部使用numpy完成少许计算,而后以常量的形式构建在tensorflow中的运算,能够考虑提早使用numpy完成,这样能够提升总体数学模型的效率。c++

x = np.float32(np.random.rand(100,1))
y = np.dot(x,0.5) + 0.7

这两句就是利用numpy的计算功能“模拟”准备一组房屋面积和价格的数据。在正式的机器学习系统中,这样的数据集必定是提早准备好的,python的功能就是把这些数据集“喂”到tensorflow中去。而在这里由于咱们是一个演示性的实验,因此用随机数的方式制备数据,这样的准备工做,就必须利用tensorflow外围的工具包实现。总体看起来,在这个例子中,颇有numpy出题、tensorflow解答的意味。程序员

b = tf.Variable(np.float32(0.3))
a = tf.Variable(np.float32(0.3))

在tensorflow中定义两个变量,变量的初始值设置为0.3,这里设置这个值没有特别的意思,至关于随机数。在实际的梯度降低法中,通常都采用函数生成随机数进行初始化或者提早对模型进行数学分析,设置初始值的时候人为避开可能的“局部最优解”,从而获得最优的“机器学习”效果。tensorflow中变量的做用跟全部语言中变量的功能是相同的,用于参与计算和保存计算结果。算法

y_value = tf.multiply(x,a) + b

用tensorflow构建数学模型的主公式,y_value就至关于前面伪代码描述算法时候的y'。我多嘴一句,这里由于是模型的一部分,必须用tensorflow的内置函数来实现,不能够用numpy的函数。
此外,numpy和tensorflow属于不一样组织出品的两个不一样的产品,所以尽管都是用python语言,也有不少重合、一样的功能,可是所采用的保留字和格式并不必定同样。甚至一样的保留字所表明的函数,用法也彻底不一样,切莫混淆。好比这里tf.multiply函数,跟上面np.dot函数,从程序功能和数学意义上,是彻底相同的两个函数,分属tensorflow和numpy,可是你看保留字的差异很是巨大。编程

loss = tf.reduce_mean(tf.square(y_value - y))
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)

这三句仍然是tensorflow构建数学模型,但主要属于解方程的部分。构建了代价函数,并使用代价函数和给定的降低梯度值来解方程。做为主要工做模式为黑盒和可拼接的图,tensorflow的语句大多能够串起来写到一条语句里面----尽管这样会下降可读性,但未来读别人的程序,你仍然会见到不少这样的写法。好比上面三条语句跟下面一条语句是等效的:后端

train=tf.train.GradientDescentOptimizer(0.5).minimize(tf.reduce_mean(tf.square(y_value - y)))

接下来是tensorflow开始运行的部分:数组

init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)

在tensorflow内部任何一个开始运行的运算必然要分配一系列的资源,这些资源就从属于一个session。一个模型开始运行前,全部模型中定义的变量都要先初始化。初始化变量自己也是一个任务,须要在全部其它任务开始前首先sess.run()。这就是上面三句代码的含义。bash

for step in xrange(0, 200):
    sess.run(train) 
    if step % 5 == 0:
        print step, sess.run(loss),sess.run(a), sess.run(b)

进入到了梯度降低求解的执行部分,sess.run(train)一条语句其实完成了全部的主要工做,tensorflow黑盒的本质能够看得很清楚了,无论背后有多少复杂的运算,都隐藏在里一个任务执行中。两点须要解释:

  • 在这个例子中,数据集有限,而且算法上不须要不断添加数据集,所以这里没有给tensorflow“喂”数据的过程。这个之后会看到,大多数机器学习的代码,给tensorflow构建的图提供批次的数据是循环中主要的工做。
  • 每次sess.run()都会返回tensorflow数学模型中的某个值,多是函数的返回值,也多是变量的值,总之都是使用sess.run()反馈回来的。忽略掉返回值就好像是一个调用,但实际都是一回事。

这个例子能够说是机器学习中,最简单的一个实验。可是麻雀虽小,五脏俱全,但愿你大体弄清楚了tensorflow的工做模式。
至此第一个例子源代码部分咱们算完整的讲解了一遍,若是感受已经明白了,建议你跳过下一节直接看第二个例子,不然,下面准备了一些真正基础的内容,相信能够帮你解惑。

TensorFlow基础入门

习惯上学习一种新技术或者新语言,都是从Hello World入门,若是不是“机器学习”的不少概念须要更多篇幅和影响本文的结构的话,咱们也是应当从这里开始的,不过我想从这里补上也不晚,毕竟机器学习自己可能更重要。
好的代码会说话,咱们直接列代码在这里。为了节省篇幅,4个相关的基础示例咱们放到同一个源码中展现,并加上详细的注释来帮助你理解:

#!/usr/bin/env python
# -*- coding=UTF-8 -*-

import tensorflow as tf

#----------------------------------------------------
#Hello World 示例
#在tf中定义一个常量
#前一个例子中咱们使用了变量
#这里的常量跟一般编程中的常量含义是相同的,也就是其中的值不可再改变
hello = tf.constant('Hello World from TensorFlow!')
#启动一个任务
sess = tf.Session()
#运行任务并返回hello常量的值
print sess.run(hello)
#在屏幕输出:Hello World from TensorFlow!

#----------------------------------------------------
#一个简单的整数常量计算示例
a = tf.constant(4)
b = tf.constant(7)
with tf.Session() as sess:
    print "a=4 / b=7"
    print "a + b = %i" % sess.run(a+b)
    print "a * b = %i" % sess.run(a*b)
#屏幕输出:
#  a=4 / b=7
#  a + b = 11
#  a * b = 28
#----------------------------------------------------
# 矩阵常量运算的例子
matrix1 = tf.constant([[3., 3.]])
matrix2 = tf.constant([[2.],[2.]])
product = tf.matmul(matrix1, matrix2)
with tf.Session() as sess:
    result = sess.run(product)
    print result
#屏幕输出:
# [[12.]]
#----------------------------------------------------
#占位符placeholder示例
#占位符是tf中的从python向
#tensorflow传输数据的主要手段
#与此对应,咱们上一节例子中使用的变量,
#用于在tensorflow中参与运算和返回结果
x = tf.placeholder(tf.int16)
y = tf.placeholder(tf.int16)
add = tf.add(x, y)
mul = tf.multiply(x, y)

with tf.Session() as sess:
    print "12 + 36 = %i" % sess.run(add, feed_dict={x:12,y:36})
    print "12 * 36 = %i" % sess.run(mul, feed_dict={x:12,y:36})
#屏幕输出:
# 12 + 36 = 48
# 12 * 36 = 432

相信你看起来应当不困难,看起来这几个小例子简单,可是排除了“机器学习”算法方面的复杂性,tensorflow的主要特色也就是这些。
因此入门的难度,主要仍是集中在“机器学习”自己上。
关于上面4个例子,惟一我认为须要解释的就是,由于咱们是把4段独立的代码集成过来,因此tf.Session咱们其实是初始化了4次。
也就是4个例子,都在各自的Session中运行的。在这个例子中,看不出来任何问题和反作用,可是若是在大的项目中你应当理解,这几个Session,互相是不一样的任务,其中定义的任务,一样也是互相是不干扰的,这个特征跟在tensorflow中定义几个不一样的图是一样的意思。图的例子咱们后面会涉及到。
关于TensorFlow的安装,在官方的文档中有很是详细、分操做系统的讲解,咱们只是官方文档的补充,并非打算替代官方文档,因此请移步至官方文档参考。阅读英文文档有困难的,请使用最下面的参考连接,有中文社区的连接。

第二个例子

做为加深印象,咱们把tensorflow官方文档中最简单的一个例子列在下面。一样是梯度降低法求解,略微增长了数据的维度,配以逐句的注释,让你再熟悉一遍。

#!/usr/bin/env python 
# -*- coding=UTF-8 -*-

import tensorflow as tf
import numpy as np

# 使用 NumPy 生成假数据集x_data
# 数据集是一个2维数组,每维100个随机数
x_data = np.float32(np.random.rand(2, 100)) 
#运算获得数据结果集y_data
#下面tensorflow的梯度降低目标就是求这里给定的向量常数[0.100,0.200]及偏移量常数0.300
y_data = np.dot([0.100, 0.200], x_data) + 0.300
    
# 使用tensorflow构造线性模型
# 模型的数学公式是:y=W*x+b

# 首先定义了两个tf变量:b和W 
b = tf.Variable(tf.zeros([1]))      #b初始化为0
W = tf.Variable(tf.random_uniform([1, 2], -1.0, 1.0))       #W是1*2维的矩阵(向量),使用随机数初始化

#定义公式模型
#tf.matmul是矩阵乘法,两个参数必须都是矩阵
#先前房价例子中的tf.multiply是两个天然数相乘或者经过放射达成一个矩阵对一个天然数相乘
#另外注意在numpy中,这两种乘法使用一样的函数np.dot
y = tf.matmul(W, x_data) + b 

# 定义代价函数
loss = tf.reduce_mean(tf.square(y - y_data))
#梯度降低法求解
optimizer = tf.train.GradientDescentOptimizer(0.5)
#求解目标为最小化代价函数的值
train = optimizer.minimize(loss)

# 初始化变量
init = tf.global_variables_initializer()

# 启动图(启动模型)
sess = tf.Session()
sess.run(init)

# 求解(原文称“拟合”,也很贴切)
for step in xrange(0, 401):
    sess.run(train) #没有定义占位符,因此不用喂值
    if step % 20 == 0:
        print step, sess.run(W), sess.run(b)

代码看上去跟前面的例子看上去差异很小是吧?若是不是提早知道,可能你都会看错。
最后的运算结果会相似这样:

0 [[ 0.7986201 -0.3793277]] [0.41285288]
20 [[0.18275234 0.09449076]] [0.30691865]
40 [[0.1086577  0.18028098]] [0.30492488]
60 [[0.0999373 0.1954711]] [0.30222526]
80 [[0.09947924 0.19870569]] [0.3009042]
100 [[0.09972703 0.19956781]] [0.30035478]
120 [[0.09988438 0.1998434 ]] [0.30013746]
140 [[0.09995399 0.19994119]] [0.300053]
160 [[0.09998206 0.1999776 ]] [0.3000204]
180 [[0.09999307 0.19999143]] [0.30000782]
200 [[0.09999734 0.19999675]] [0.300003]
220 [[0.09999898 0.19999875]] [0.30000114]
240 [[0.09999961 0.19999954]] [0.30000043]
260 [[0.09999985 0.19999982]] [0.30000016]
280 [[0.09999986 0.19999984]] [0.30000016]
300 [[0.09999986 0.19999984]] [0.30000016]
320 [[0.09999986 0.19999984]] [0.30000016]
340 [[0.09999986 0.19999984]] [0.30000016]
360 [[0.09999986 0.19999984]] [0.30000016]
380 [[0.09999986 0.19999984]] [0.30000016]
400 [[0.09999986 0.19999984]] [0.30000016]

能够看到,增长了一维数据,一样的算法也能够获得不错的结果。
本节的最后再说一下python2和python3,tensorflow对两个版本都能很好支持,python还能够支持c/c++/go等多种高级语言,但由于外围工具的缘由,目前仍然是对python的支持最好。
对python版本的偏心纯属我的偏好,有的人喜欢python2,有的人则是python3的拥趸。其实对于一个成熟的程序员来说,真的学会了python,随便换用哪一个版本都不是大问题,那些语法的差距没有你想象的那么大。
对于应用范围来说,主要写通用性系统脚本的,首先用python2,由于几乎全部linux/mac电脑内置都已经有了python2。主要写独立性应用系统的,可使用python3,其中一些特征不少人认为有利于企业型的应用系统编写,而且反正部署也是独立运行的,不用考虑兼容性。

(待续...)

引文及参考

大数据与多维度
多元线性回归模型公式
梯度降低法
numpy官网
TensorFlow中文社区

相关文章
相关标签/搜索