致讀者:目前你所看到的是鐵人賽搶先版。 陸續還有更新。
tf.estimator
tf.estimator
也是一個 High-level API,作用與 tf.keras
相同,都是為了提供一個簡單的深度學習建模介面。該介面提供了一些已經包裝好程式碼的 tf.estimator.Estimator
類別,但也容許使用者提供客製化的模型建立函式作為 model_fn 的引數,建立相等的 tf.estimator.Estimator
。
有些人可能想要問,既然和 tf.keras
差不多,那麼 tf.estimator.Estimator
有什麼特別呢?tf.estimator.Estimator
具有以下特性是 tf.keras
缺乏或還在開發中的特徵:
關於如何建制一個 tf.Estimator
,我們就來介紹例子:
tf.Estimator
預先封裝好的(pre-made)tf.Estimator
都把模型的邏輯撰寫完畢,使用者只要提供資料餵送的設計即可。而在tf.Estimator
官方文件中,則提供了程式撰寫的流程:
tf.data.Dataset
(見下文)去包覆未處理過的資料,將之依照需要處理,最後以批次的方式來完成 mini-batch 的訓練。tf.feature_column
來定義已封裝好的 tf.Estimator
使用的特徵,是什麼樣的資料型態,若是非數值特徵又是如何編碼。tf.Estimator
類別:欲實例化 tf.Estimator
類別產生物件,通常需要提供 Step 2 所建立的 tf.feature_column
物件們。目前在 tf.estimator
下的預先封裝好的 tf.Estimator
有:
使用預先封裝好的 tf.Estimator
有兩個優點:那就是如何配置運算元的程式邏輯都已封裝在類別裡,使用者無須擔心。除此之外,呼叫 tf.summary
等所撰寫的訓練記錄檔的程式邏輯也已封裝在類別裡,使用者也無撰寫任何有可能傷害模型效率的原始碼。
tf.Estimator
如果要客製化 tf.Estimator
,方法也很簡單,只要提供 model_fn
即可。這個model_fn
是一個函式主要提供模型建構的邏輯。
若你是用 tf.keras
來建模,卻也希望能轉換成 tf.Estimator
,已獲得tf.Estimator
的好處,那麼你可以這麼做:呼叫 tf.keras.estimator.model_to_estimator
函式。下面的程式碼,先用 tf.keras
的 Sequential API 建立了一個很簡單的模型,最後用上述的轉換函式來轉成 tf.Estimator
estimator_model = tf.keras.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(1, activation='softmax')
])
# Compile 模型,這一定要在呼叫 model_to_estimator 前執行
estimator_model.compile(
optimizer='adam',
loss='binary_crossentropy',
metric='accuracy')
est_mobilenet_v2 = tf.keras.estimator.model_to_estimator(keras_model=estimator_model)
tf.data
我們在已封裝的 tf.Estimator
中,提到準備一到多個 tf.data.Dataset
輸入函式來做訓練輸入,現在就來看看該怎麼利用 tf.data.Dataset
來準備我們的輸入資料供訓練,驗證和預測用。首先,我們要來看看創建資料集的方法:
tf.data.Dataset
進行轉換:這裏的轉換可以看作是 data pipeline 的其中一個步驟,該步驟執行後會保證輸出為 tf.data.Dataset
tf.data.Dataset
可以使用於 tf.Estimator
和 tf.keras
的模型建制 API,在進入 tf.data
的詳盡內容前,先來看看如何利用 tf.data.Dataset
來餵送資料給 tf.keras.Model
和 tf.Estimator
。
tf.keras.Model
tf.keras.Model
能夠使用的資料型態為在記憶體中的 numpy.ndarray
,所以我們可以用 tf.data.Dataset.from_tensor_slices
來建立 tf.data.Dataset
,tf.data.Dataset.from_tensor_slices
的使用也很簡單,必須鎚入一個兩個元素的 tuple 物件,第一個元素為特徵,第二個則是標注。兩個元素都是 numpy.ndarray
物件。由此法建立的 tf.data.Dataset
還可以藉著應用上 tf.data
模組下的資料轉換類別,建立資料修改的 pipeline。常見的轉換包括了 shuffle
和 batch
。另外,有鑒於訓練時需要多次餵送所有的訓練資料(被稱為一個 epoch),所以使用 repeat
方法保證餵送所有的 tf.data.Dataset
長度是無限大或無限次從頭餵送。
接下來,我們就來看一個程式範例,在這個範例中我們會先用 tf.keras
裡的 dataset
模組下載fashion_mnist
這組資料並載入在記憶體中。這組資料及在記憶體中會是以 numpy.ndarray
的方式儲存。然後,將做完正規化的影像和標注傳入tf.data.Dataset.from_tensor_slices
方法慘生 tf.data.Dataset
物件,最後用此物件來做訓練,驗證和預測。
train, test = tf.keras.datasets.fashion_mnist.load_data()
images, labels = train
images = images/255.0 #做正規劃,使影像值在 0 - 1 之殲
labels = labels.astype(np.int32) #本來為 uint8 轉為 int32
fmnist_train_ds = tf.data.Dataset.from_tensor_slices((images, labels))
fmnist_train_ds = fmnist_train_ds.shuffle(5000).batch(32) # data pipeline
#...建立 tf.keras.Model 模型使用 Sequential API,然後 compile(省略)
model.fit(fmnist_train_ds, epochs=2) # 沒有使用 repeat() 時,會跑 epochs*len(images)/batch_size 而 batch_size = 32
model.fit(fmnist_train_ds.repeat(), epochs=2, steps_per_epoch=20) # 使用 repeat() 時,會跑 epochs*steps_per_epoch
loss, accuracy = model.evaluate(fmnist_train_ds) # 不使用 repeat()
loss, accuracy = model.evaluate(fmnist_train_ds.repeat(), steps=10) #使用 repeat(),用 step 參數控制要多少的資料被評估
# 建立測試資料集,其中標注不可得
predict_ds = tf.data.Dataset.from_tensor_slices(images).batch(32)
result = model.predict(predict_ds , steps = 10)
在上面的程式範例中,我們可以看到 repeat()
的行為可以透過參數,如 fit
方法的 steps_per_epoch
和 evaluate
方法中的 step
來控制。筆者可以想到使用 repeat
的場景便是和 shuffle
結合,讓 shuffle
產生無限多個訓練資料序列(因為訓練例子通常很多,所以可以假設 shuffle
的空間是指數成長的序列個數)。
至於 shuffle
所需要使用者提供的引數為 buffer_size
,shuffle
演算法的實踐邏輯則是從資料原本的排列從頭取出 buffer_size
的元素來做任意排列。這個引數主要是防止巨量的訓練資料,而造成記憶體不足的現象,如果是小量的訓練資料,則可以將buffer_size
設為訓練資料的長度。另外,shuffle
還有一個參數為 reshuffle_each_iteration
,這個參數只接受布林值,若設為 True
(預設),則在每次疊代就做一次任意排列的動作,若為 False
,則不會。
tf.Estimator
tf.estimator.Estimator
使用 tf.data.Dataset
的方法與 tf.keras.Model
很相像,都是在呼叫tf.estimator.Estimator
的 training
, evaluate
和 inference
以 input_fn
方式傳入。不一樣的是 tf.estimator.Estimator
不可以直接以 tf.data.Dataset
的方式傳入,而是必須以 python function 來傳入。
除此之外,tf.estimator.Estimator
能夠接受較廣的資料集,而非只有從記憶體載入。下面的程式碼則是建立一個資料集,該資料集會從硬碟做 csv 檔案解析,最後載入資料到記憶體中。
import tensorflow_datasets as tfds
def train_input_fn():
# 需要將資料集用函式包裝,方可傳入`tf.estimator.Estimator`的方法
titanic = tf.data.experimental.make_csv_dataset(
titanic_file, batch_size=32, # titanic_file,檔案名稱
label_name="survived")
titanic_batches = (
titanic.cache().repeat().shuffle(500)
.prefetch(tf.data.experimental.AUTOTUNE))
return titanic_batches
# ...建立 feature_column 後傳入 tf.estimator.Estimator 建構子,最後生成物件(省略)
model = model.train(input_fn=train_input_fn, steps=100)
上面的程式碼可以看到先由 tf.data.experimental.make_csv_dataset
來解析 titanic_file 所指導的csv 檔案名稱。titanic_file 在多檔案的情況下,可以是檔案名稱的共同樣式,最後可以呼叫 glob
將依據樣式找到的檔案讀入,也可以是一個 python list of strings,在 python list 的每一個字串,都是一個儲存訓練資料的檔案名稱。
程式碼中的資料管線則是和 tf.keras.Model
的程式碼相似,但是先呼叫 cache
方法,提取在 tf.data.Dataset
cache 在記憶體的訓練資料,若 cache 的 filename 引數有指定,則會 cache 到所指定的檔案。將訓練資料先 cache
起來,在處理檔案的資料集上特別有用。另外一個則是 prefetch
則是預先將訓練例子載入到記憶體中。prefetch
需要一個必要的參數為 buffer_size,這裏的 buffer_size 指的是必須先行載入到記憶體的元素個數。這理解的是元素個數,是為了和訓練個數作區別。這裏的元素個數會因為 tf.data.Dataset
目前提取資料的方式而改變,若 tf.data.Dataset
最後做了 batch 轉換,那麼 prefetch
先行提取的元素個數則為 batch_size * 元素個數。若沒有做 batch 轉換,則prefetch
先行提取的元素個數 則為訓練個數。