練習用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
# 方法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
沒有留言:
張貼留言