個人原文:www.hijerry.cn/p/26959.htm…html
這是一次使用python進行機器學習的實驗。python
一是總結本身學過的各類python包,二是瞭解一下使用python進行機器學習大概是什麼樣的,不過此次使用主要的目的仍是熟悉Tensorflow的使用。git
本次實驗使用到的python包及其版本:程序員
機器環境是:macOS 10.13github
這裏只介紹本次實驗所用到的Tensorflow的關鍵知識、概念,想了解詳情能夠參考官方文檔:https://tensorflow.google.cn/programmers_guide/low_level_intro 。web
TensorFlow 中的核心數據單位是張量。一個張量由一組造成陣列(任意維數)的原始值組成。張量的階是它的維數,而它的形狀是一個整數元組,指定了陣列每一個維度的長度。如下是張量值的一些示例:shell
3. # 0階張量;也叫標量;形狀是[]
[1., 2., 3.] # 1階張量;也叫向量;形狀是[3]
[[1., 2., 3.], [4., 5., 6.]] # 2階張量;也叫矩陣;形狀是[2, 3]
[[[1., 2., 3.]], [[7., 8., 9.]]] # 3階張量;形狀是[2, 1, 3]
複製代碼
TensorFlow 使用 numpy 陣列來表示張量值。編程
您能夠將 TensorFlow Core 程序看做由兩個互相獨立的部分組成:api
tf.Graph
)。tf.Session
)。計算圖是排列成一個圖的一系列 TensorFlow 指令。圖由兩種類型的對象組成。數組
重要提示:tf.Tensors
不具備值,它們只是計算圖中元素的手柄。
咱們來構建一個簡單的計算圖。最基本的指令是一個常量。構建指令的 Python 函數將一個張量值做爲輸入值。生成的指令不須要輸入值。它在運行時輸出的是被傳遞給構造函數的值。咱們能夠建立以下所示的兩個浮點數常量 a
和 b
:
a = tf.constant(3.0, dtype=tf.float32)
b = tf.constant(4.0) # 默認dtype=tf.float32
total = a + b
print(a)
print(b)
print(total)
複製代碼
打印語句會生成:
Tensor("Const:0", shape=(), dtype=float32)
Tensor("Const_1:0", shape=(), dtype=float32)
Tensor("add:0", shape=(), dtype=float32)
複製代碼
請注意,打印張量並不會如您可能預期的那樣輸出值 3.0
、4.0
和 7.0
。上述語句只會構建計算圖。這些 tf.Tensor
對象僅表明將要運行的指令的結果。
圖中的每一個指令都擁有惟一的名稱。這個名稱不一樣於使用 Python 分配給相應對象的名稱。張量是根據生成它們的指令命名的,後面跟着輸出索引,如上文的 "add:0"
所示。
要評估張量,您須要實例化一個 tf.Session
對象(一般被稱爲會話)。會話會封裝 TensorFlow 運行時的狀態,並運行 TensorFlow 指令。若是說 tf.Graph
像一個 .py
文件,那麼 tf.Session
就像一個可執行的 python
。
下面的代碼會建立一個 tf.Session
對象,而後調用其 run
方法來評估咱們在上文中建立的 total
張量:
sess = tf.Session()
print(sess.run(total))
複製代碼
當您使用 Session.run
請求輸出節點時,TensorFlow 會回溯整個圖,並流經提供了所請求的輸出節點對應的輸入值的全部節點。所以此指令會打印預期的值 7.0:
7.0
複製代碼
您能夠將多個張量傳遞給 tf.Session.run
。run
方法以透明方式處理元組或字典的任何組合,以下例所示:
print(sess.run({'ab':(a, b), 'total':total}))
複製代碼
它返回的結果擁有相同的佈局結構:
{'total': 7.0, 'ab': (3.0, 4.0)}
複製代碼
在調用 tf.Session.run
期間,任何 tf.Tensor
都只有單個值。例如,如下代碼調用 tf.random_uniform
來生成一個 tf.Tensor
,後者會生成隨機的三元素矢量(值位於 [0,1)
):
vec = tf.random_uniform(shape=(3,))
out1 = vec + 1
out2 = vec + 2
print(sess.run(vec))
print(sess.run(vec))
print(sess.run((out1, out2)))
複製代碼
每次調用 run
時,結果都會顯示不一樣的隨機值,但在單個 run
期間(out1
和 out2
接收到相同的隨機輸入值),結果顯示的值是一致的:
[ 0.52917576 0.64076328 0.68353939]
[ 0.66192627 0.89126778 0.06254101]
(
array([ 1.88408756, 1.87149239, 1.84057522], dtype=float32),
array([ 2.88408756, 2.87149239, 2.84057522], dtype=float32)
)
複製代碼
部分 TensorFlow 函數會返回 tf.Operations
,而不是 tf.Tensors
。對指令調用 run
的結果是 None
。您運行指令是爲了產生反作用,而不是爲了檢索一個值。這方面的例子包括稍後將演示的[初始化](https://tensorflow.google.cn/programmers_guide/low_level_intro#Initializing Layers)和訓練指令。
目前來說,這個圖不是特別有趣,由於它老是生成一個常量結果。圖能夠參數化以便接受外部輸入,也稱爲佔位符。佔位符表示承諾在稍後提供值,它就像函數參數。
x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)
z = x + y
複製代碼
前面三行有點像函數。咱們定義了這個函數的兩個輸入參數(x
和 y
),而後對它們運行指令。咱們可使用 run 方法的 feed_dict
參數來爲佔位符提供真正的值,從而經過多個輸入值來評估這個圖:
print(sess.run(z, feed_dict={x: 3, y: 4.5}))
print(sess.run(z, feed_dict={x: [1, 3], y: [2, 4]}))
複製代碼
上述操做的結果是輸出如下內容:
7.5
[ 3. 7.]
複製代碼
另請注意,feed_dict
參數可用於覆蓋圖中的任何張量。佔位符和其餘 tf.Tensors
的惟一不一樣之處在於若是沒有提供值給它們,那麼佔位符會顯示錯誤。
它是一種可極大地簡化機器學習編程的高階 TensorFlow API。Estimator 會封裝下列操做:
爲了更好的理解它是個啥,請看後面的 Premade Estimator
和 Custom Estimator
章節。
直接看線性模型的目標是什麼吧,如下圖爲例:
模型的目標是找到一條直線(圖紅色直線),讓每個藍色點到與直線的y距離最小。
下面來更數學化一點的介紹:
給定一個大小爲n的點集 ,
線性模型的目標就是尋找一組 和 構成的直線 ,
使得全部點的損失值 越小越好。
由於若是咱們找到了這麼一組 和 ,咱們就能夠預測某一個 的 值。
這裏我想多說幾句,線性模型在實際應用中不必定能很好的預測 的值,這是由於實際的數據分佈也許不是線性的,多是二次、三次、圓形甚至無規則,因此判斷何時能用線性模型很重要。一個比較好的實踐方法是,先用matplotlib畫出數據分佈,觀察一下看看,就比如上面的藍色點,一看就知道是線性分佈,因此能夠用線性模型來作。可是這種方法在大部分狀況下也不能用,由於數據不少狀況下有多個特徵(多元),一元、二元都還好,能看出來,到了三元、四元數據,可能連圖都畫不出來。。這時候又怎麼辦呢?也很簡單,用線性模型和其餘模型一塊兒套一下,評估、對比看看結果如何。
那麼如今問題是,怎麼讓 loss
最小呢?請接着往下看。
廢話很少說,直接上寫好的代碼:
def fit_linear_model(data, num_steps, alpha):
""" train with the machine learning :param data: training data :param num_steps: training steps :param alpha: learning rate :return: W and b of trained linear model """
# variables
W = tf.Variable(1, dtype=tf.float64)
b = tf.Variable(1, dtype=tf.float64)
x = tf.placeholder(tf.float64)
y = tf.placeholder(tf.float64)
# predict
pred = W * x + b
# loss
loss = tf.reduce_sum(tf.square(pred - y))
# optimizer
optimizer = tf.train.GradientDescentOptimizer(alpha)
# train
train = optimizer.minimize(loss)
train_set, test_set = split_test_set(data, frac=0.3, random=True)
sess = tf.Session()
sess.run(tf.global_variables_initializer())
for i in range(num_steps):
sess.run(train, {x: train_set['x'], y: train_set['y']})
final_W, final_b = sess.run([W, b], {x: train_set['x'], y: train_set['y']})
# evaluate
final_loss, evaluate_loss = evaluate(train_set, test_set, final_W, final_b)
print('W: {}, b: {}, final loss: {}, evaluate loss: {}'.format(final_W, final_b, final_loss, evaluate_loss))
return final_W, final_b
複製代碼
下面一步一步講解代碼:
1
; 是佔位符用於接收數據。loss
GradientDescentOptimizer
來優化模型,減少loss
,這個類的原理是梯度降低,能夠看到咱們傳遞了學習速率 alpha
即 α
。能夠好奇的是,咱們沒有計算梯度,而是調用了minimize
方法,這個方法分兩步進行,第一步是使用 compute_gradients
計算梯度,第二步是使用 apply_gradients
更新參數值,憑藉經驗能夠知道,第一步其實就是在計算偏導數,那麼tensorflow是怎麼作到,能夠計算任意元線性模型的偏導數的呢,我大概掃描了一下源碼,猜想應該是用了計算圖
。split_test_set
將數據集劃分爲 訓練集:測試集= 7:3,即有30%的數據用做測試集。global_variables_initializer
是個神魔戀,幾乎tensorflow應用都要執行這個東西,看看文檔就知道它會建立初始化程序時圖中就存在的變量好比代碼中的 和 ,其實就是 variables_initializer(global_variables())
的縮寫。run
方法獲取到。講道理應該能夠經過相似 get_variable
的方法拿到值,看了下貌似沒有這個方法。evaluate
評估模型,計算模型在訓練集、測試集上的損失值。這是個封裝好的函數,只要傳入數據集、訓練步數、學習率就能夠獲得訓練好的模型了(即 和 )。
須要提醒一下的是,第3三、35行的 {x: train_set['x'], y: train_set['y']}
不能把 key
寫成單引號的 {'x': train_set['x'], 'y': train_set['y']}
。
那麼下面是須要拿到數據,這裏我用的是隨機生成的數據:
def linear_data(data_size, devi_degree):
""" Make random linear data :param data_size: data size :param devi_degree: degree of deviation :return: linear data with x and y """
# standard linear function
x = np.array(range(data_size), dtype=np.float64)
y = 3 * x + 0.6
# make deviation
y += np.random.randn(data_size) * devi_degree
data = pd.DataFrame({'x': x, 'y': y})
return data
複製代碼
作法是:
data_size
的標準一元一次函數點集devi_degree
再來劃分訓練集和測試集:
def split_test_set(df, frac=0.3, random=True):
""" Split DataFrame to train set and test set :param df: :param frac: :param random: :return: """
test_size = int(len(df) * min(frac, 1))
if random:
df = df.sample(frac=1).reset_index(drop=True)
return df[test_size:].reset_index(drop=True), df[:test_size].reset_index(drop=True)
複製代碼
def evaluate(train_set, test_set, W, b):
""" Evaluate the model's loss :param train_set: :param test_set: :param W: :param b: :return: train_loss, evaluate_loss """
x = tf.placeholder(tf.float64)
y = tf.placeholder(tf.float64)
# predict
pred = W * x + b
# loss
loss = tf.reduce_sum(tf.square(pred - y))
sess = tf.Session()
sess.run(tf.global_variables_initializer())
train_loss = sess.run(loss, {x: train_set['x'], y: train_set['y']})
evaluate_loss = sess.run(loss, {x: test_set['x'], y: test_set['y']})
sess.close()
return train_loss, evaluate_loss
複製代碼
這裏的loss評估是使用預測值-實際值平方再求和,與 理論基礎
章節描述的 loss
同樣。
這部分就簡單了,把點畫一下,把直線畫一下就能夠了,注意直線最好和點不同的顏色,因此標紅。
def print_linear_model(data, W, b):
""" print the data and the predictions of linear model :param data: :param W: W of linear model :param b: b of linear model """
x = np.array(data['x'])
y = np.array(data['y'])
pred = np.array(W * x + b)
plt.scatter(x, y, linewidths=1)
plt.plot(x, pred, color='red')
plt.show()
複製代碼
每一部分的函數寫完啦,如今要作的是將這幾個函數組合起來用,上代碼。
import tensorflow as tf
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--data_size', default=50, type=int, help='data size')
parser.add_argument('--num_steps', default=1000, type=int, help='number of trannig steps')
parser.add_argument('--devi_degree', default=10, type=int, help='Degree of deviation in stand linear data')
parser.add_argument('--alpha', default=0.00001, type=float, help='learning rate of gradient decent')
def main(argv):
args = parser.parse_args(argv[1:])
data = linear_data(args.data_size, args.devi_degree)
W, b = fit_linear_model(data, args.num_steps, args.alpha)
print_linear_model(data, W, b)
if __name__ == '__main__':
tf.logging.set_verbosity(tf.logging.INFO)
tf.app.run(main)
複製代碼
這裏用到了 argparse
包來處理傳入的參數
把我上面給的全部代碼,都複製到tf-linear.py
文件裏,接着運行:
python tf-linear.py --data_size=100 --alpha=0.000001 --devi_degree=30 --num_steps=5000
複製代碼
便可看到以前那個圖啦。
這裏須要注意一下,alpha
參數不要設太大了。。否則會梯度爆炸。。程序員運行不出結果( 和 都會變成 nan
)
若是你看懂了基礎版本的代碼。。。那這部分就簡單的很了,仍是同樣,直接上代碼:
def fit_estimator(data, num_steps):
""" train with estimator :param data: :param num_steps: :return: """
feature_columns = [
tf.feature_column.numeric_column('x')
]
estimator = tf.estimator.LinearRegressor(feature_columns=feature_columns)
train_set, test_set = split_test_set(data, frac=0.3, random=True)
input_fn = tf.estimator.inputs.numpy_input_fn(
{'x': train_set['x']}, train_set['y'], batch_size=4, num_epochs=None, shuffle=True
)
estimator.train(input_fn=input_fn, steps=num_steps)
W = estimator.get_variable_value('linear/linear_model/x/weights')
b = estimator.get_variable_value('linear/linear_model/bias_weights')
final_W, final_b = float(W), float(b)
final_loss, evaluate_loss = evaluate(train_set, test_set, final_W, final_b)
print('W: {}, b: {}, final loss: {}, evaluate loss: {}'.format(final_W, final_b, final_loss, evaluate_loss))
return final_W, final_b
複製代碼
仍是按步驟來解釋吧:
線性迴歸器
,也就是 estimator
split_test_set
將數據集劃分爲 訓練集:測試集= 7:3,即有30%的數據用做測試集。線性迴歸器
dir
命令才知道,能夠用這種方法獲取到參數,若是有其餘優雅的方法能夠告訴我哦。evaluate
評估模型,計算模型在訓練集、測試集上的損失值。能夠看到,比我們本身手擼模型要簡單多了。首先,預測函數不用本身寫了,優化器也不用本身建立了,甚至連for-loop也不用了,直接調用 train
方法就萬事大吉了。
這裏須要注意一下,LinearRegressor
有個 weight_column
參數,若是不給的話,初始值是1
,形狀是 (1,)
,能夠理解爲是 np.array([1.])
,因此才能把他轉成 float
,若是大於1階,估計會轉換失敗。
數據、繪圖都不變,只須要修改 main
方法爲以下便可:
def main(argv):
args = parser.parse_args(argv[1:])
data = linear_data(args.data_size, args.devi_degree)
# W, b = fit_linear_model(data, args.num_steps, args.alpha)
W, b = fit_estimator(data, args.num_steps)
print_linear_model(data, W, b)
複製代碼
接着仍是運行:
python tf-linear.py --data_size=100 --alpha=0.000001 --devi_degree=30 --num_steps=5000
複製代碼
而後能夠看到和以前那個圖差很少的樣子啦。
依然先上代碼:
def fit_custom_estimator(data, num_steps, alpha):
""" train with custom estimator :param data: :param num_steps: :param alpha: :return: """
def model_fn(features, labels, mode):
W = tf.get_variable('W', 1., dtype=tf.float64)
b = tf.get_variable('b', 1., dtype=tf.float64)
# predict
pred = W * tf.cast(features['x'], dtype=tf.float64) + b
# loss
loss = tf.reduce_sum(tf.square(pred - labels))
# optimizer
optimizer = tf.train.GradientDescentOptimizer(alpha)
# global step
global_step = tf.train.get_global_step()
# train
train = tf.group(
optimizer.minimize(loss),
tf.assign_add(global_step, 1)
)
return tf.estimator.EstimatorSpec(
mode=mode,
predictions=pred,
loss=loss,
train_op=train
)
feature_columns = [
tf.feature_column.numeric_column('x')
]
estimator = tf.estimator.Estimator(
model_fn=model_fn
)
train_set, test_set = split_test_set(data, frac=0.3, random=True)
input_fn = tf.estimator.inputs.numpy_input_fn(
{'x': train_set['x']}, train_set['y'], batch_size=4, num_epochs=None, shuffle=True
)
estimator.train(input_fn=input_fn, steps=num_steps)
W = estimator.get_variable_value('W')
b = estimator.get_variable_value('b')
final_W, final_b = float(W), float(b)
final_loss, evaluate_loss = evaluate(train_set, test_set, final_W, final_b)
print('W: {}, b: {}, final loss: {}, evaluate loss: {}'.format(final_W, final_b, final_loss, evaluate_loss))
return final_W, final_b
複製代碼
能夠分爲兩部分,第一部分是編寫 model_fn
,第二部分是調用自定義的estimator。
先看第一部分吧,也就是11行~38行。
1
loss
。GradientDescentOptimizer
類,用於優化模型。global_step
即當前步數(這個步數是全局的)。GradientDescentOptimizer
優化模型,而且把 global_step
加一。group
方法表示把多個操做放在一個節點裏,這個方法沒有返回值。Estimator
類,並把一些必要參數傳給它。第二部分調用自定義的estimator大部分代碼和調用預約義的estimator同樣,就是在第44~46行,咱們把本身寫的 model_fn
做爲參數傳入到實例化的 Estimator
裏。
在咱們調用estimator的 train
方法時,tensorflow就會在內部調用咱們寫的 model_fn
方法來計算loss、prediction以及訓練模型。
model_fn
的 mode
參數是啥?它其實有三個值分別是:ModeKeys.TRAIN、ModeKeys.EVAL、ModeKeys.PREDICT,用來指示本次調用的目的是訓練、評估仍是預測。不過在本次實驗中,咱們寫了本身的evaluate
方法,因此就不區分這三種狀況了。
def main(argv):
args = parser.parse_args(argv[1:])
data = linear_data(args.data_size, args.devi_degree)
# W, b = fit_linear_model(data, args.num_steps, args.alpha)
# W, b = fit_estimator(data, args.num_steps)
W, b = fit_custom_estimator(data, args.num_steps, args.alpha)
print_linear_model(data, W, b)
複製代碼
在使用 Estimator
訓練模型的時候,tensorflow會自動記錄訓練過程當中某些數據的變化,好比 loss
值,拿premade estimator來講,在訓練完成後,控制會打印出如圖所示的一些信息:
能夠看到有一些數據被存到了:
/var/folders/4c/14xc6rkj1ndgw2x5kw5sw8hr0000gn/T/tmpo5gg328l/model.ckpt.
複製代碼
查看這個目錄發現裏面還有不少其餘文件
下面運行Tensorboard,來查看此次訓練中的數據變化:
tensorboard --logdir /var/folders/4c/14xc6rkj1ndgw2x5kw5sw8hr0000gn/T/tmpo5gg328l
複製代碼
這會啓動一個web服務,點擊終端提示的網址便可打開,個人是在 6006
端口。
本次實驗的開源github地址:https://github.com/JerryCheese/tensorflow-study
參考的文檔:
[1] TensorFlow 完整的TensorFlow入門教程, https://blog.csdn.net/lengguoxing/article/details/78456279
[2] TensorFlow 使用入門, https://tensorflow.google.cn/get_started/premade_estimators