前言
本文接着上一篇繼續來聊Tensorflow
的接口,上一篇中用較低層的接口實現了線性模型,本篇中將用更高級的API——tf.estimator
來改寫線性模型。
還記得之前的文章《機器學習筆記2 - sklearn之iris數據集》嗎?本文也將使用tf.estimator
改造該示例。
本文代碼都是基於API版本r1.4
。本文中本地開發環境為Pycharm
,在文中不再贅述。
tf.estimator
內置模型
比起用底層API“較硬”的編碼方式,tf.estimator
的在使用時更像是對模型描述(或定義)的過程。Tensorflow
把訓練
、評估
、數據處理
等這些過程全部封裝起來,讓開發人員更專注於解決實際問題的建模過程,而不是糾結於代碼實現過程。如果用tf.estimator
改造上一篇中的線性模型的話,完整代碼如下:
本例中使用的庫numpy
是一個開源工具,是一個功能非常強大且執行效率很高的庫,主要用作數值處理及矩陣操作等。
import numpy as np
import tensorflow as tf
# 定義特性列,線性模型中特性是列是x,shape=[1],因此定義如下:
feature_columns = [tf.feature_column.numeric_column("x", shape=[1])]
# 使用tf.estimator內置的LinearRegressor來完成線性回歸算法
# tf.estimator提供了很多常規的算法模型以便用戶調用,不需要用戶自己重復造輪子
# 到底為止,短短兩行代碼我們的建模工作就已經完成了
estimator = tf.estimator.LinearRegressor(feature_columns=feature_columns)
# 有了模型之后,我們要使用模型完成訓練->評估->預測這幾個步驟
# 訓練數據依舊是(1.,0.),(2.,-1.),(3.,-2.),(4.,-3.)這幾個點,拆成x和y兩個維度的數組
x_train = np.array([1., 2., 3., 4.])
y_train = np.array([0., -1., -2., -3.])
# 評估數據為(2.,-1.01),(5.,-4.1),(8.,-7.),(1.,0.)這四個點,同樣拆分成x和y兩個維度的數組
x_eval = np.array([2., 5., 8., 1.])
y_eval = np.array([-1.01, -4.1, -7., 0.])
# 用tf.estimator.numpy_input_fn方法生成隨機打亂的數據組,每組包含4個數據
input_fn = tf.estimator.inputs.numpy_input_fn({"x": x_train}, y_train, batch_size=4, num_epochs=None, shuffle=True)
# 循環1000次訓練模型
estimator.train(input_fn=input_fn, steps=1000)
# 生成訓練數據,分成1000組,每組4個數據
train_input_fn = tf.estimator.inputs.numpy_input_fn({"x": x_train}, y_train, batch_size=4, num_epochs=1000, shuffle=False)
# 生成評估數據,分成1000組,每組4個數據
eval_input_fn = tf.estimator.inputs.numpy_input_fn({"x": x_eval}, y_eval, batch_size=4, num_epochs=1000, shuffle=False)
# 訓練數據在模型上的預測准確率
train_metrics = estimator.evaluate(input_fn=train_input_fn)
# 評估數據在模型上的預測准確率
eval_metrics = estimator.evaluate(input_fn=eval_input_fn)
print("train metrics: %r"% train_metrics)
print("eval metrics: %r"% eval_metrics)
輸出結果如下:
train metrics: {'average_loss': 4.4709815e-08, 'loss': 1.7883926e-07, 'global_step': 1000}
eval metrics: {'average_loss': 0.0025470245, 'loss': 0.010188098, 'global_step': 1000}
自定義模型
雖然tf.estimator
內置了大量的常用模型,但也並不代表我們必須使用內置模型。如有需要,我們可以用底層API實現自定義模型,同時,繼續使用tf.estimator
提供的高級特性。如上例中,我們需要定義自己的線性回歸模型,僅需要按如下步驟操作:
替換estimator
# 下面這行替換了原先的estimator = tf.estimator.LinearRegressor(feature_columns=feature_columns)
estimator = tf.estimator.Estimator(model_fn=model_fn)
定義並實現model_fn方法
...
def model_fn(features, labels, mode):
# 用底層API構建線性模型
W = tf.get_variable("W", [1], dtype=tf.float64)
b = tf.get_variable("b", [1], dtype=tf.float64)
y = W * features['x'] + b
loss = tf.reduce_sum(tf.square(y - labels))
# 獲取訓練全局參數step
global_step = tf.train.get_global_step()
# 梯度下降算法,學習率是0.01
optimizer = tf.train.GradientDescentOptimizer(0.01)
# 將優化器和全局step的累加方法打包成一個方法組,相當於把若干個方法打包成事務執行的模式
train = tf.group(optimizer.minimize(loss), tf.assign_add(global_step, 1))
# 將所有內容封裝成符合tf.estimator.Estimator規范的對象
return tf.estimator.EstimatorSpec(
mode=mode,
predictions=y,
loss=loss,
train_op=train)
...
生成並啟動TensorBoard
在tf.estimator
中,生成TensorBoard
的方法也被集成在了底層,我們要做的,僅僅是傳入參數model_dir
而已:
在LinearRegressor例中代碼如下:
...
estimator = tf.estimator.LinearRegressor(feature_columns=feature_columns, model_dir='d1')
...
...
estimator = tf.estimator.Estimator(model_fn=model_fn, model_dir='d2')
...
TensorBoard
的啟動和上一篇文章中一樣,在Pycharm
的控制台中執行:
# 以LinearRegressor的代碼為例
tensorboard --logdir=d1
啟動TensorBoard
大致效果如下:
IRIS數據集
現在我們來改造之前用sklearn
實現的IRIS
數據集。之前用了決策樹
和鄰近算法
兩種算法來實現,這次用的是Tensorflow
提供的深度學習模型DNNClassifier
,完整代碼如下(代碼是官網提供的Demo代碼,僅僅將DNNClassifier
中參數model_dir
改為了當前目錄下iris_model
目錄):
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
from six.moves.urllib.request import urlopen
import numpy as np
import tensorflow as tf
# 數據集
IRIS_TRAINING = "iris_training.csv"
IRIS_TRAINING_URL = "http://download.tensorflow.org/data/iris_training.csv"
IRIS_TEST = "iris_test.csv"
IRIS_TEST_URL = "http://download.tensorflow.org/data/iris_test.csv"
def main():
# 先將數據集保存到本地
if not os.path.exists(IRIS_TRAINING):
raw = urlopen(IRIS_TRAINING_URL).read()
with open(IRIS_TRAINING, "wb") as f:
f.write(raw)
if not os.path.exists(IRIS_TEST):
raw = urlopen(IRIS_TEST_URL).read()
with open(IRIS_TEST, "wb") as f:
f.write(raw)
# 讀取數據集
training_set = tf.contrib.learn.datasets.base.load_csv_with_header(
filename=IRIS_TRAINING,
target_dtype=np.int,
features_dtype=np.float32)
test_set = tf.contrib.learn.datasets.base.load_csv_with_header(
filename=IRIS_TEST,
target_dtype=np.int,
features_dtype=np.float32)
feature_columns = [tf.feature_column.numeric_column("x", shape=[4])]
# 創建一個三層的DNN深度學習分類器,三層分別有10、20、10個神經元
classifier = tf.estimator.DNNClassifier(feature_columns=feature_columns,
hidden_units=[10, 20, 10],
n_classes=3,
model_dir="iris_model")
# 定義訓練用的數據集輸入
train_input_fn = tf.estimator.inputs.numpy_input_fn(
x={"x": np.array(training_set.data)},
y=np.array(training_set.target),
num_epochs=None,
shuffle=True)
# 訓練模型
classifier.train(input_fn=train_input_fn, steps=2000)
# 定義測試用的數據集輸入
test_input_fn = tf.estimator.inputs.numpy_input_fn(
x={"x": np.array(test_set.data)},
y=np.array(test_set.target),
num_epochs=1,
shuffle=False)
# 評估准確率
accuracy_score = classifier.evaluate(input_fn=test_input_fn)["accuracy"]
print("\nTest Accuracy: {0:f}\n".format(accuracy_score))
# 預測兩個新樣本
new_samples = np.array(
[[6.4, 3.2, 4.5, 1.5],
[5.8, 3.1, 5.0, 1.7]], dtype=np.float32)
predict_input_fn = tf.estimator.inputs.numpy_input_fn(
x={"x": new_samples},
num_epochs=1,
shuffle=False)
predictions = list(classifier.predict(input_fn=predict_input_fn))
predicted_classes = [p["classes"] for p in predictions]
print(
"New Samples, Class Predictions: {}\n"
.format(predicted_classes))
if __name__ == "__main__":
main()
運行結果:
Test Accuracy: 0.966667
New Samples, Class Predictions: [array([b'1'], dtype=object), array([b'2'], dtype=object)]
Process finished with exit code 0
可以看到,用tf.estimator
提供的DNNClassifier
,僅需要如下代碼即可實現一個三層的DNN,並將模型保存在本地的iris_model
文件夾下:
classifier = tf.estimator.DNNClassifier(feature_columns=feature_columns,
hidden_units=[10, 20, 10],
n_classes=3,
model_dir="iris_model")
啟動TensorBoard
,看到的效果如下:
Run on ML Engine of Google Cloud Platform
前面幾篇文章中,我在本地運行代碼的之后,同時在阿里雲PAI
上執行了一次代碼。本來我也是想在PAI
上再進行本文中的示例代碼的,不過我花了一天多的時間,最后還是失敗了,主要原因如下:
-
PAI
目前只支持到Tensorflow 1.2
,而官方目前已經出到Tensorflow 1.4
(馬上要出1.5了),而Tensorflow 1.2
是不支持tf.estimator.DNNClassifier
的(代碼中需要用到) -
PAI
雖然是可視化拖拽,但是代碼還是需要按照PAI
的要求進行少量改造,不便於本地代碼直接放到雲端執行 -
PAI
的相關文檔太少,遇到問題很難解決,就算提交工單技術支持也比較敷衍,這讓我這樣的初學者感到非常大的挫折感
說來也可笑,我的代碼無論如何調整在PAI
中運行都會報錯,在PAI
官方的技術QQ群里尋求幫助,半天沒人搭理,然后有一個群友說PAI
確實不好用,建議我用Google Cloud Platform
。備受挫折的我就注冊了一個Google Cloud Platform
,果然,即便是全英文的文檔,也讓我在不到2小時
的時間里,從注冊賬號到執行代碼成功。這真不是我崇洋媚外或者故意黑阿里,我僅僅敘述了我自己的親身經歷而已。相比PAI
,Google Cloud Platform
的ML Engine
就是一個虛擬雲主機(Linux),可以直接用Google的Web版遠程控制台進行操作,就跟操作一台真實的Linux一樣的體驗。因此本地代碼也可以直接拷貝過去就能執行,不需要任何修改。運行速度上,我覺得比PAI
快很多(沒有數據,只是感覺)。
使用Google Cloud Platform
的ML Engine
,需要一些前提條件:
-
收費,需要綁定信用卡(VISA或MASTER),不過注冊賬號是送300美金體驗1年(也就是一年內不超過300美金的消費是免費的),官方承諾,免費體驗額度用完,如果要產生后續扣信用卡費用的行為,需要用戶確認之后才會繼續扣款
-
較好的英文閱讀能力(能基本看懂英文技術、幫助文檔)
-
翻牆(你懂的)
-
會操作Linux系統
以下就是我在Google Cloud Platform
的ML Engine
的Web控制台中操作的動圖(注冊過程略):
注意:
-
我事先做過了實驗,所以代碼已經放在文件
~/cloudml-samples-master/mymltest/tensorflowdemo3/code.py
中,動圖中僅僅是復制了一份代碼到新的執行目錄下。 -
Google Cloud Platform
的幫助文檔我放在了最后的參考文檔
中。
參考文檔
官方文檔:
https://www.tensorflow.org/get_started/get_started
https://www.tensorflow.org/get_started/estimator
ML Engine幫助文檔:
https://cloud.google.com/ml-engine/docs/getting-started-training-prediction