從鍋爐工到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中文社區

相關文章
相關標籤/搜索