【深度學習 走進tensorflow2.0】TensorFlow 2.0 常用模塊tf.data

無意中發現了一個巨牛的人工智能教程,忍不住分享一下給大家。教程不僅是零基礎,通俗易懂,而且非常風趣幽默,像看小說一樣!覺得太牛了,所以分享給大家。點這裏可以跳轉到教程。人工智能教程

背景:
很多時候,我們希望使用自己的數據集來訓練模型。然而,面對一堆格式不一的原始數據文件,將其預處理並讀入程序的過程往往十分繁瑣,甚至比模型的設計還要耗費精力。爲此,TensorFlow 提供了 tf.data 這一模塊,包括了一套靈活的數據集構建 API,能夠幫助我們快速、高效地構建數據輸入的流水線,尤其適用於數據量巨大的場景。

1、小數據集對象的建立:

tf.data 的核心是 tf.data.Dataset 類,提供了對數據集的高層封裝。tf.data.Dataset 由一系列的可迭代訪問的元素(element)組成,每個元素包含一個或多個張量。比如說,對於一個由圖像組成的數據集,每個元素可以是一個形狀爲 長×寬×通道數 的圖片張量,也可以是由圖片張量和圖片標籤張量組成的元組(Tuple)。
最基礎的建立 tf.data.Dataset 的方法是使用 tf.data.Dataset.from_tensor_slices() ,適用於數據量較小(能夠整個裝進內存)的情況。具體而言,如果我們的數據集中的所有元素通過張量的第 0 維,拼接成一個大的張量(例如,前節的 MNIST 數據集的訓練集即爲一個 [60000, 28, 28, 1] 的張量,表示了 60000 張 28*28 的單通道灰度圖像),那麼我們提供一個這樣的張量或者第 0 維大小相同的多個張量作爲輸入,即可按張量的第 0 維展開來構建數據集,數據集的元素數量爲張量第 0 位的大小。具體示例如下:

import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

import tensorflow as tf
import numpy as np

# X = tf.constant([2013, 2014, 2015, 2016, 2017])
# Y = tf.constant([12000, 14000, 15000, 16500, 17500])


# 也可以使用NumPy數組,效果相同
X = np.array([2013, 2014, 2015, 2016, 2017])
Y = np.array([12000, 14000, 15000, 16500, 17500])


dataset = tf.data.Dataset.from_tensor_slices((X, Y))
for x, y in dataset:
   print(x.numpy(), y.numpy())

輸出:

2013 12000
2014 14000
2015 15000
2016 16500
2017 17500

用tf.data.Dataset.from_tensor_slices((train_data, train_label)) 導入數據集:


(train_data, train_label), (test_data, test_lable) = tf.keras.datasets.mnist.load_data()
train_data = np.expand_dims(train_data.astype(np.float32) / 255.0, axis=-1)      # [60000, 28, 28, 1]
mnist_dataset = tf.data.Dataset.from_tensor_slices((train_data, train_label))

print(mnist_dataset)

2、大數據集的建立:
對於特別巨大而無法完整載入內存的數據集,我們可以先將數據集處理爲 TFRecord 格式,然後使用 tf.data.TFRocrdDataset() 進行載入。我們會在後面的連載文章中介紹 TFRecord 格式的詳細使用方式,或者你也可以參考 下文 以瞭解詳情:
https://tensorflow.google.cn/tutorials/load_data/tfrecord

3、數據集對象的預處理

tf.data.Dataset 類爲我們提供了多種數據集預處理方法。最常用的如:
Dataset.map(f) :對數據集中的每個元素應用函數 f ,得到一個新的數據集(這部分往往結合 tf.io 進行讀寫和解碼文件, tf.image 進行圖像處理);
Dataset.shuffle(buffer_size) :將數據集打亂(設定一個固定大小的緩衝區(Buffer),取出前 buffer_size 個元素放入,並從緩衝區中隨機採樣,採樣後的數據用後續數據替換);
Dataset.batch(batch_size) :將數據集分成批次,即對每 batch_size 個元素,使用 tf.stack() 在第 0 維合併,成爲一個元素。
Dataset.prefetch() :預取出數據集中的若干個元素(可提升訓練流程並行效率)。
除此以外,還有 Dataset.repeat() (重複數據集的元素)、 Dataset.reduce() (與 Map 相對的聚合操作)、 Dataset.take ()(截取數據集中的前若干個元素)等,可參考 API 文檔 進一步瞭解。
https://tensorflow.google.cn/versions/r2.0/api_docs/python/tf/data/Dataset

一個例子:
使用 Dataset.map() 將所有圖片旋轉 90 度:

import matplotlib.pyplot as plt 
(train_data, train_label), (test_data, test_lable) = tf.keras.datasets.mnist.load_data()
train_data = np.expand_dims(train_data.astype(np.float32) / 255.0, axis=-1)      # [60000, 28, 28, 1]
mnist_dataset = tf.data.Dataset.from_tensor_slices((train_data, train_label))

def rot90(image, label):
   image = tf.image.rot90(image)
   return image, label

mnist_dataset = mnist_dataset.map(rot90)

for image, label in mnist_dataset:
   plt.title(label.numpy())
   plt.imshow(image.numpy()[:, :, 0])
   plt.show()

使用 Dataset.batch() 將數據集劃分批次,每個批次的大小爲 4:

mnist_dataset = mnist_dataset.batch(4)

使用 Dataset.shuffle() 將數據打散後再設置批次,緩存大小設置爲 10000:

mnist_dataset = mnist_dataset.shuffle(buffer_size=10000).batch(4)

注意:
Dataset.shuffle() 時緩衝區大小 buffer_size 的設置
tf.data.Dataset 作爲一個針對大規模數據設計的迭代器,本身無法方便地獲得自身元素的數量或隨機訪問元素。因此,爲了高效且較爲充分地打散數據集,需要一些特定的方法。Dataset.shuffle() 採取了以下方法:
設定一個固定大小爲 buffer_size 的緩衝區(Buffer);
初始化時,取出數據集中的前 buffer_size 個元素放入緩衝區;
每次需要從數據集中取元素時,即從緩衝區中隨機採樣一個元素並取出,然後從後續的元素中取出一個放回到之前被取出的位置,以維持緩衝區的大小。

因此,緩衝區的大小需要根據數據集的特性和數據排列順序特點來進行合理的設置。比如:
當 buffer_size 設置爲 1 時,其實等價於沒有進行任何打散;
當數據集的標籤順序分佈極爲不均勻(例如二元分類時數據集前 N 個的標籤爲 0,後 N 個的標籤爲 1)時,較小的緩衝區大小會使得訓練時取出的 Batch 數據很可能全爲同一標籤,從而影響訓練效果。一般而言,數據集的順序分佈若較爲隨機,則緩衝區的大小可較小,否則則需要設置較大的緩衝區。

4、數據集元素的獲取與使用

構建好數據並預處理後,我們需要從其中迭代獲取數據以用於訓練。tf.data.Dataset 是一個 Python 的可迭代對象,因此可以使用 For 循環迭代獲取數據。

Keras 支持使用 tf.data.Dataset 直接作爲輸入。當調用 tf.keras.Model 的 fit() 和 evaluate() 方法時,可以將參數中的輸入數據 x 指定爲一個元素格式爲 (輸入數據, 標籤數據) 的 Dataset ,並忽略掉參數中的標籤數據 y 。例如,對於上述的 MNIST 數據集,常規的 Keras 訓練方式是:

model.fit(x=train_data, y=train_label, epochs=num_epochs, batch_size=batch_size)

使用 tf.data.Dataset 後,我們可以直接傳入 Dataset :

model.fit(mnist_dataset, epochs=num_epochs)

由於已經通過 Dataset.batch() 方法劃分了數據集的批次,所以這裏也無需提供批次的大小。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章