2019/1/7

TensorFlow Dataset 操作

25 TensorFlow Dataset 操作

練習用CNN 處理MINST 手寫資料集,當要了解資料集讀進後要如何提取打包成一批一批以供供模型訓練,查閱大部份的參考文獻大部份都是用 mnist.train.next_batch()函數,再配合feed_dict 饋入模型,但用此函數執行時會產生警告表示此函數已被退化,要使用tf.data方法取代。於是就再摸索,之後知道使用feed_dict 的方法不是很有效率,當資料量大時不適合,有文獻建議使用Pipeline  (渠道)的方法饋入資料,如此可確保GPU 不用等候資料饋送進入。所以要先來了解此方法,以便於CNN的實例中使用。

►Importing data (導入資料)
在 tf 的 Dataset API 裡最重要的函數應該是 tf.data.Dataset.from_tensor_slices(),此函數是用以產生dataset 以便從中提取批量的資料,帶入函數的參數可以是np.array 的變資料,也可於用tensor 於參數中產生,如
# 由 numpy array建立
x = np.random.sample((100,2))
dataset = tf.data.Dataset.from_tensor_slices(x)
#-------------------------------
# 由 tensor 建立
dataset = tf.data.Dataset.from_tensor_slices(tf.random_uniform([100, 2]))
#-------------------------------
# 含兩個 numpy arrays
features, labels = (np.random.sample((100,2)), np.random.sample((100,1)))
dataset = tf.data.Dataset.from_tensor_slices((features,labels))
上述中帶入兩個array是很重要的方式,因在大部份料集都會分features 和 labels。另也可用持位器導入動態資料集或用generator的方式可導入不同長度的資料。我們首先討論上列較簡單的方式。

►設定 dataset
上述建立後,接下來就是要何何從其中提取出來饋入模型中,有關提取的屬設定有很多,如
  • shuffle: 打亂,為一個size,每次取進此尺度的大小,再從其中取出
  • batch: 批量,每次提取的樣本的數量
  • repeat: 整個資料集要重覆提取幾次鎮
  • map: 對映,可設一數學操作對映到所有資料集裡的樣本,如全部加 1
這些屬性加到dataset的方法一般有兩種,
# 方法1
dataset = tf.data.Dataset.from_tensor_slices(x).batch(10).repeat(1).shuffle(4)
#------方法2
dataset = tf.data.Dataset.from_tensor_slices(x)
dataset = dataset.shuffle(4)
dataset = dataset.batch(10)
dataset = dataset.repeat(1)
方法1是用一行把屬性串接起來,方法2是每次加入一個屬性,兩者功能一樣。repeat是設要重覆讀幾遍。另shuffle 若不設定是全部樣本裡隨機提,若設為1是每次一個一個循序提取,所以一般使用上也不用設定。

►建立 Iterator (迭代器)
迭代器是指要怎麼從dataset裡提取樣本出來,也包括要如何改變datase裡的資料。迭代器有下列四種:
  • One shot: 一次性,每次從dataset裡依設定方式提取batch size 的樣本,不用饋入任何數值。 
  • Initializable: 可初始化的,一般是用於動態饋入不同的資料到dataset裡,使用initializer啟始操作並用feed_dict 饋入資料。
  • Reinitializable:可重新初始化的,可以由不同的dataset初始化,對有不同資料如 訓練集和測試集需處是非常有用的 。很像使用塔式吊車選擇不同的容器。
  • Feedable: 可饋入的,可以用來選擇要使用那個I迭器。
使用的方法如
iter = dataset.make_one_shot_iterator()
el = iter.get_next()
#-----
iter = dataset.make_initializable_iterator()
el = iter.get_next()
#-----
iter = dataset.make_initializable_iterator()
features, labels = iter.get_next()
第一用法one_shot,第二用法是initializable含一array,第三是initializable含二array。

► dataset 屬性設定簡單例子
import numpy as np
import tensorflow as tf
np.random.seed(0)
x = np.random.sample((11,2))
shuffle_size = 4
batch_size = 4
repeat_size = 2
dataset = tf.data.Dataset.from_tensor_slices(x)
dataset = dataset.shuffle(shuffle_size)
dataset = dataset.batch(batch_size)
dataset = dataset.repeat(repeat_size)
iter = dataset.make_one_shot_iterator()
el = iter.get_next()
aa = int(np.ceil(x.shape[0]/batch_size)*repeat_size)
with tf.Session() as sess:
    for i in range(aa):
        print(sess.run(el))
執行後得到
[[0.4236548  0.64589411]
 [0.60276338 0.54488318]
 [0.79172504 0.52889492]
 [0.5488135  0.71518937]]
[[0.43758721 0.891773  ]
 [0.07103606 0.0871293 ]
 [0.77815675 0.87001215]
 [0.56804456 0.92559664]]
[[0.0202184  0.83261985]
 [0.96366276 0.38344152]
 [0.97861834 0.79915856]]
[[0.60276338 0.54488318]
 [0.96366276 0.38344152]
 [0.43758721 0.891773  ]
 [0.79172504 0.52889492]]
[[0.5488135  0.71518937]
 [0.0202184  0.83261985]
 [0.56804456 0.92559664]
 [0.4236548  0.64589411]]
[[0.07103606 0.0871293 ]
 [0.77815675 0.87001215]
 [0.97861834 0.79915856]]
此例饋入的資料有11個樣本,batch size 設為4,shuffle 設為4,repeat 設為2,執行後可見被重覆提取了兩遍,每一遍的最後一次因樣本不足,只取到3個樣本,之後再讀第二遍,最後一次一樣只取得3個樣本,且兩遍取出的次序並不相同。

► Initializable Iterator
先看此迭代器和 one_shot 一樣的用法
import numpy as np
import tensorflow as tf
np.random.seed(0)
x = np.random.sample((11,2))
shuffle_size = 4
batch_size = 4
repeat_size = 2
dataset = tf.data.Dataset.from_tensor_slices(x)
dataset = dataset.shuffle(shuffle_size)
dataset = dataset.batch(batch_size)
dataset = dataset.repeat(repeat_size)
iter = dataset.make_initializable_iterator()
el = iter.get_next()
aa = int(np.ceil(x.shape[0]/batch_size)*repeat_size)
with tf.Session() as sess:
    sess.run(iter.initializer)
    for i in range(aa):
        print(sess.run(el))
此例中用的是Initializable Iterator,輸出的結果格式上是和上例是一樣的,注意第12行迭代器改成Initializable Iterator,且在第16行要先迭代器初始化。

下列再來看迭代器經由placeholder導入資料集,
import numpy as np
import tensorflow as tf
np.random.seed(0)
data = np.random.sample((11,2))
x = tf.placeholder(tf.float32, shape=[None,2])
shuffle_size = 4
batch_size = 4
repeat_size = 2
dataset = tf.data.Dataset.from_tensor_slices(x)
dataset = dataset.shuffle(shuffle_size)
dataset = dataset.batch(batch_size)
dataset = dataset.repeat(repeat_size)
iter = dataset.make_initializable_iterator()
el = iter.get_next()
aa = int(np.ceil(data.shape[0]/batch_size)*repeat_size)
with tf.Session() as sess:
    sess.run(iter.initializer, feed_dict={ x: data })
    for i in range(aa):
        print(sess.run(el))
此例於第5行用placeholder 接受第17行初始化後用feed_dict導入的data。輸出的結果格式上和上兩都是例是一樣的。我們可於程式中動態改變feed_dict 導入的資料集,如下例。

接下來再看看導入兩個array 的用法,首先導入訓練集之後再提取出來,之後再導入測試集也再取出來,取出來的樣本就可供模型使用。
import numpy as np
import tensorflow as tf
np.random.seed(0)
train_data = (np.random.sample((11,2)),np.random.sample((11,1)))
test_data = (np.random.sample((11,2)),np.random.sample((11,1)))
x, y = tf.placeholder(tf.float32, shape=[None,2]), 
              tf.placeholder(tf.float32, shape=[None,1])
shuffle_size = 4
batch_size = 4
repeat_size = 2
dataset = tf.data.Dataset.from_tensor_slices((x,y))
dataset = dataset.shuffle(shuffle_size)
dataset = dataset.batch(batch_size)
dataset = dataset.repeat(repeat_size)
iter = dataset.make_initializable_iterator()
features, labels = iter.get_next()
aa = int(np.ceil(train_data[0].shape[0]/batch_size)*repeat_size)
with tf.Session() as sess:
    sess.run(iter.initializer, feed_dict={ x: train_data[0], y: train_data[1]})
    for i in range(aa):
        r1, r2 = sess.run([features, labels])
        print(r1)
        print(r2)
    sess.run(iter.initializer, feed_dict={ x: test_data[0], y: test_data[1]})
    for i in range(aa):
        r3, r4 = sess.run([features, labels])
        print(r3)
        print(r4)
[[0.4236548  0.6458941 ]
 [0.96366274 0.3834415 ]
 [0.60276335 0.5448832 ]
 [0.56804454 0.92559665]]
[[0.11827443]
 [0.14335328]
 [0.7805292 ]
 [0.5218483 ]]
...................
[[0.43860152 0.9883738 ]
 [0.36371076 0.57019675]
 [0.10204481 0.20887676]]
[[0.19658236]
 [0.13818295]
 [0.36872518]]
程式第19行和第24行分別導入訓練集和測試集,導入後再取出來,取出來的樣本就可供模型使用。輸出的上半部份是訓練集第一個批量,下半部份是測試集最後一個批量。

► Reinitializable Iterator
上述方法是導入不同的資料於同一個dataset ,但我們也可以於不同的dataset中切換,這種方法就可再初始化迭代器。
import numpy as np
import tensorflow as tf
np.random.seed(0)
EPOCHS = 10
train_data = (np.random.sample((10,2)), np.random.sample((10,1)))
test_data = (np.random.sample((10,2)), np.random.sample((10,1)))
train_dataset = tf.data.Dataset.from_tensor_slices(train_data)
test_dataset = tf.data.Dataset.from_tensor_slices(test_data)
iter = tf.data.Iterator.from_structure(train_dataset.output_types,
                                train_dataset.output_shapes)
features, labels = iter.get_next()
train_init_op = iter.make_initializer(train_dataset)
test_init_op = iter.make_initializer(test_dataset)
with tf.Session() as sess:
    sess.run(train_init_op) 
    for i in range(EPOCHS):
        r1, r2 = sess.run([features, labels])
        print(r1)
        print(r2)
    sess.run(test_init_op) 
    for i in range(EPOCHS):
        r3, r4 = sess.run([features, labels])
        print(r3)
        print(r4)
[0.5488135  0.71518937]
[0.31179588]
[0.60276338 0.54488318]
[0.69634349]
[0.4236548  0.64589411]
[0.37775184]
-----------
[0.67204781 0.24536721]
[0.69562545]
[0.42053947 0.55736879]
[0.28351885]
[0.86055117 0.72704426]
[0.37992696]
程式第7,8行建立不同的dataset,第9行建立迭代器,第12,13分別建立迭代器的初始化,之後就可叫用不同迭代器初始器偶取出樣本。

► 饋入樣本到模型實例
此例中產生一組隨機的資料集含兩個features 和一個 label,用模型是兩個全連接的neuron 和一輸出級,模型是讓預測的y 值loss 最小。
import numpy as np
import tensorflow as tf
np.random.seed(0)
EPOCHS = 100
BATCH_SIZE = 16
features, labels = (np.array([np.random.sample((100,2))]), 
                    np.array([np.random.sample((100,1))]))
dataset = tf.data.Dataset.from_tensor_slices(
                    (features,labels)).repeat().batch(BATCH_SIZE)
iter = dataset.make_one_shot_iterator()
x, y = iter.get_next()
net1 = tf.layers.dense(x, 8, activation=tf.tanh)
net2 = tf.layers.dense(net1, 8, activation=tf.tanh)
prediction = tf.layers.dense(net2, 1, activation=tf.tanh)
loss = tf.losses.mean_squared_error(prediction, y)
train_op = tf.train.AdamOptimizer().minimize(loss)
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for i in range(EPOCHS):
        _, loss_value = sess.run([train_op, loss])
        print("Iter: {}, Loss: {:.4f}".format(i, loss_value))
Iter: 0, Loss: 0.4964
Iter: 1, Loss: 0.4803
Iter: 2, Loss: 0.4644
Iter: 3, Loss: 0.4489
---------------------
Iter: 96, Loss: 0.0976
Iter: 97, Loss: 0.0974
Iter: 98, Loss: 0.0973
Iter: 99, Loss: 0.0972
程式第9行repeat()指可無限制次數 重新於資料集中提取資料,dataset取出的x於第8行中饋入第一層網路net1,而取出的y則用於第15行中計算loss,執行100次後loss降為0.0972。此方法的優點是不用feed_dict的方式饋入樣本 ,下一單元我們將用此方法於CNN 實例中,其他tf.data.Dataset 更深入的用法請參考下列參考文獻。

參考文獻
https://towardsdatascience.com/how-to-use-dataset-in-tensorflow-c758ef9e4428

沒有留言:

張貼留言