在大數據的時代,資料的大小動輒是GB或是TB等級,這樣的資料不可能一次性的被儲存到記憶體之上並提供給機器學習模型進行訓練。這種時候我們需要做事情就是讓資料從硬碟上直接送到GPU本身進行訓練,這樣就能避免記憶體大小對我們的資料造成的限制。在利用TensorFlow以及PyTorch建立張量章節中,我們簡單示範了如何利用生成器(generator
)物件實作資料流。在本章節中,我們將示範
tensorflow-dataset
所提供的各API進行資料抽取、轉置以及載入(ETL)流程。tf.keras.utils.Sequence
物件的資料流。有人可能會問「都有基於生成器的作法了,為何還需要基於前述物件的實作呢?」,原因如下:
tf.keras.utils.Sequence
物件的資料流實作,將可以繼承這兩個物件本身已經預先設計好的功能(例如:on_epoch_end
等功能),使用者無須再花時間及精力來實作一些進階功能。tensorflow-dataset
的資料流實作Tensorflow-dataset是一由TensorFlow所提供的函式庫,可以整合資料的抽取(Extract)、轉置(Transform)、以及載入(Load)過程[1]為一體的函式庫。
用戶可以通過Tensorflow-dataset所提供的資料集,或是利用自己準備的資料集,通過Tensorflow-dataset提供的API將資料輕易的導入訓練流程之中。
以下將
tensorflow-dataset
簡稱為tfds
。
tfds.load
函式來進行資料的導入。使用時至少需要傳遞以下參數:name
: 資料集的名稱,同時可包含資料集版本。範例:name=mnist:3.0.0
split
: 所要讀取的部份,如果有分類,也可以直接呼叫分類,例如:train
。同時可以利用分類加上數值或是比例(i.e. train[:50000]
或train[:80%]
)來進行切片。as_supervised
:須為布林值。如果為真,則回傳監督式學習所需要的資料以及標籤;若為否,則回傳一個包含所有特徵的字典物件。如果並非使用預先提供的資料集,則需要另外宣告data_dir
來指名資料及的所在位置。另外,若同時希望載入資料相關資料,可以宣告with_info=True
來取得資訊(若有提供)。
更多資訊可以參考官方文件。
當用戶透過tfds.load()
函式將資料抽出之後,可以利用所建立的tf.data.Dataset
物件進行資料的轉置以及載入。以下是幾個基礎的用法:
dataset.shuffle(BUFFER_SIZE)
。dataset.repeat(NUM_EPOCHS)
dataset.map(lambda x: ...)
dataset.batch(BATCH_SIZE)
當用戶完成自訂的抽出以及轉置流程後,可以利用前述建立的物件進行資料載入。其方法很簡單,可以直接將物件傳遞至model.fit()
函式中進行訓練,或是利用.take()
方法查看個別資料的狀況。
以下是一些簡單的範例。
tfds.load()
函式抽出資料執行以上程式碼,將會得到以下輸出:
此時所指定的資料將會被下載,並同時建立一個tf.data.Dataset
物件。
tf.data.Datset
類別的自帶方法(method)轉置資料首先我們可以抽取訓練資料集,並同時將with_info
以及as_supervised
參數設定為True
用以取得資料集資訊以及整理好的(image, label)
串列。
我們可以透過輸出確認此資料集長度:
我們可以透過以下程式碼來確認前幾筆影像以及其標籤:
其輸出為:
接下來,我們可以開始對資料集本身進行轉置的動作。
要對資料集進行重新排序,可以利用.shuffle(BUFFER_SIZE)
方法來達成。其中的BUFFER_SIZE
為所選取的資料大小。例如一個具有一萬筆資料的資料集進行重新排序時,將會進行打散。函式將會根據所設定的BUFFER_SIZE
參數,從打散的資料集中取出前BUFFER_SIZE
個資料。當需要在每次迭代時重新打散,則可以將reshuffle_each_iteration
參數設定為True
。若希望其結果可以重現,可以將seed
參數設定至一個定值。
以下是簡單的範例:
其輸出為:
在資料的轉置過程中,我們時常會需要將資料進行正規化,或是做出一些計算等等,這時候可以透過.map()
方法來進行處理。
以下是將MNIST資料集的影像進行正規化範例:
原本在MNIST資料集中的影像,其值域為[0~255]
之間,藉由上方的程式碼,我們可以將數值正規化到[0~1]
的區間。
在訓練模型時,我們會將資料處理成批次資料送進模型訓練。tf.data.Dataset
物件也提供了相應的方法來進行批次化。只需要簡單的呼叫.batch()
方法並設定相應的參數即可。
我們可以藉由輸出發現資料已經被批次化了。
更加詳細的內容將在利用Tensorflow-Dataset進行各種文件的導入以及加速並改善Tensorflow-Dataset的效率中介紹。
tf.keras.utils.Sequence
的資料流實作在官方文件中,要求一個繼承tf.keras.utils.Sequence
的物件必須要有實作__getitem__()
以及__len__()
方法。這兩個方法實際上扮演著什麼樣的角色呢?請看下方解釋:
__getitem__()
方法:[]
搭配相對應的位置來取用資料。未實作個方法的套件將無法透過[]
進行取值。以下是一個實作了__getitem__()
方法的範例:
在上述範例中,我們建構了一個Fib()
物件,並在其中實作了__getitem__()
方法,這方法會透過gen_Fib()
函數計算在斐波那契數列中一個特定位置的值。如果我們想知道斐波那契數列前十個元素的值,可以利用上述物件來計算:
其輸出為:
透過以上範例,我們可以了解到__getitem__()
方法讓我們可以用[]
來對一個物件進行取值。__len__()
方法:len()
函數來取得一個物件的長度。一個實作了__len__()
方法的物件就能以len()
函數來取得其長度。範例如下:dataset
物件中實作__len__()
來取得資料的數量,作為資料集的長度來回傳給len()
函數。
若以len()
函數取得dataset
物件的長度,則結果為:
執行結果:
理解了__getitem__()
以及__len__()
方法的作用後,我們可以開始建構一個繼承tf.keras.utils.Sequence
物件屬性的資料流。假設我們的資料存放在data
資料夾之下,以train
, test
,以及val
三個資料夾區分訓練、測試以及驗證資料集。訓練資料為圖像資料,其檔名為xxxxx.jpg
;標籤資料為文字資料,其檔名為xxxxx.txt
,用以表達一個圖像屬於哪一個類別。這個資料集中有800類的圖片,我們建構的資料流會基於指定的索引(index)讀取對應的資料,並回傳影像以及其對應的one-hot
編碼標籤。
編按:在這裡我們並未有
batch_size
變數,本範例一次僅會回傳一組影像以及標籤。若使用者有需求,可以參考這篇文章進行實作。
Machine Learning
Notebook
技術隨筆
機器學習
Python
TensorFlow
簡稱ETL。 ↩︎