Trong bài tutorial trước (Xây dựng mô hình Deep learning với Mid-level APIs), chúng ta đã làm quen với việc sử dụng feed_dict nhằm truy xuất dữ liệu để huấn luyện mô hình. Tuy nhiên, nhược điểm lớn nhất của phương pháp này là có tốc độ xử lý chậm do chúng ta phải đợi dữ liệu của batch tiếp theo [1].
Để khắc phục nhược điểm trên, TensorFlow đã giới thiệu tf.data API nhằm tăng hiệu suất và tiết kiệm thời gian trong quá trình huấn luyện bằng cách cho phép truy xuất và xử lý dữ liệu một cách song song. Theo kết quả mô phỏng của Wallarm Research thì việc sử dụng tf.data API có thể giúp chúng ta tiết kiệm được 20-30% thời gian huấn luyện so với khi sử dụng feed_dict [2]:
tf.data API bao gồm hai thành phần chính [3]:
– tf.data.Datasets: dùng đễ lưu trữ dữ liệu. Ở đây, chúng ta có thể hiểu dữ liệu là một chuỗi các phần tử mà mỗi phần tử gồm 1 hoặc nhiều Tensor objects
– tf.data.Iterator: dùng để truy xuất dữ liệu trong datasets (truy xuất từng phần tử một)
1) Datasets
Datasets có thể được tạo ra từ các nguồn khác nhau như từ numpy, từ các tensors, placeholder, các TFRecord format hoặc trực tiếp từ các files. Đoạn code sau là một ví dụ mô tả việc tạo Datasets từ numpy và tensors sử dụng hàm from_tensor_slices():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import numpy as np import tensorflow as tf # Create dataset from numpy np_data=np.random.sample((100,2)) numpy_dataset=tf.data.Dataset.from_tensor_slices(np_data) # Create dataset from tensor tensor_data=tf.random_uniform([100,2]) tensor_dataset=tf.data.Dataset.from_tensor_slices(tensor_data) # Print the first element of the dataset using Iterator def printDataset(dataset): iterator=dataset.make_initializable_iterator() element=iterator.get_next() with tf.Session() as sess: sess.run(iterator.initializer) print(sess.run(element)) print("\nThe first element of the numpy_dataset:") printDataset(numpy_dataset) print("\nThe first element of the tensor_dataset:") printDataset(tensor_dataset) |
Sau khi load dữ liệu vào Dataset, chúng ta có thể sử dụng một số hàm của tf.data API để thực hiện các bước xử lý dữ liệu cần thiết (xem thêm tại đây):
– batch(batch_size,..): được dùng để chia dữ liệu thành các mini_batch
– filter(predicate): lọc dữ liệu theo điều kiện quy định bởi predicate
– map(map_funcs,..): thực thi hàm map_funcs với từng phần tử của dataset
– repeat(count): lặp lại Dataset với số lần được quy định bởi count
– shuffle(buffer_size,…): dùng để shuffle các phần tử của dataset một cách ngẫu nhiên. Trong đó, buffer_size quy định kích thước của sampling buffer ( buffer_size nên lớn hơn hoặc bằng với kích thước của dataset)
– zip(datasets): kết hợp các datasets để tạo thành một Dataset mới. Hàm này thường được sử dụng để tạo dataset có dạng (data, label)
Đoạn code sau là một ví dụ về cách sử dụng một số hàm của Dataset:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
x_data=np.arange(0,10) y_data=np.arange(50,60) # Create dataset from numpy x_dataset=tf.data.Dataset.from_tensor_slices(x_data) y_dataset=tf.data.Dataset.from_tensor_slices(y_data) # Zip two dataset train_dataset=tf.data.Dataset.zip((x_dataset,y_dataset)) # Create batches with size=3 train_dataset=train_dataset.batch(batch_size=3) print("\nThe first run:") printDataset(train_dataset) print("\nThe second run:") printDataset(train_dataset) # Shuffle the dataset train_dataset=train_dataset.shuffle(buffer_size=100) print("\nAfter shuffling data:") print("The first run:") printDataset(train_dataset) print("\nThe second run:") printDataset(train_dataset) |
Như vậy, khi không sử dụng hàm shuffle() thì cả hai lần chạy đều cho kết quả giống nhau còn khi sử dụng shuffle() thì các lần chạy sẽ trả về kết quả khác nhau. Lưu ý: ví dụ trên được dùng để minh họa sự khác nhau khi không sử dụng và sử dụng shuffle(). Trong thực tế chúng ta thường Shuffle data trước khi sử dụng hàm batch()
2) Iterator
Iterator được sử dụng để truy xuất dữ liệu của Datasets. Hiện tại Tensorflow hỗ trợ 4 cấp độ của Iterator theo thứ tự tăng dần như sau:
– One-shot Iterator: là iterator đơn giản nhất, hỗ trợ việc truy xuất dữ liệu của một Dataset với một nguồn dữ liệu duy nhất.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# Example of using One-shot Iterator one_shot_dataset=tf.data.Dataset.range(10) # Create an one_shot iterator iterator=one_shot_dataset.make_one_shot_iterator() next_element=iterator.get_next() sess=tf.Session() for i in range(10): print(sess.run(next_element),end=' ') # OutOfRangeError occurs as Iterator already reaches the end of the dataset print(sess.run(next_element)) |
Lưu ý: lỗi OutOfRangeError xuất hiện khi chúng ta truy xuất dữ liệu vượt quá kích thước của Dataset. Thông thường, với mỗi epoch trong quá trình huấn luyện Model, chúng ta sử dụng hàm repeat() để truy xuất lại Dataset từ dữ liệu đầu.
– Initializable Iterator: Khác với One-shot Iterator, Initializable Iterator cho phép chúng ta tạo một dynamic dataset mà data source có thể thay đổi tại runtime. Tuy nhiên, trước khi sử dụng Iterator này, chúng ta phải sử dụng lệnh iterator.initializer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# Example of using Initializable Iterator max_value=tf.placeholder(tf.int64) init_dataset=tf.data.Dataset.range(max_value) # Create an initializable iterator iterator=init_dataset.make_initializable_iterator() next_element=iterator.get_next() # Create a tensorflow session sess=tf.Session() # Create a dataset with 5 data sess.run(iterator.initializer, feed_dict={max_value:5}) print('Dataset with 5 data: ') for i in range(5): print(sess.run(next_element),end=' ') # Create a dataset with 8 data sess.run(iterator.initializer, feed_dict={max_value:8}) print('\n\nDataset with 8 data: ') for i in range(8): print(sess.run(next_element),end=' ') |
– Reinitializable Iterator: thay vì chỉ sử dụng 1 dataset như Initializable Iterator, Reinitializable Iterator cho phép chúng ta thay đổi giữa các datasets. Iterator này thường được sử dụng với các Datasets objects có cùng một cấu trúc (ví dụ như train_dataset, valid_dataset và test_dataset)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# Example of using Reinitializable Iterator # Create two datasets with the same structure train_dataset=tf.data.Dataset.range(10) test_dataset=tf.data.Dataset.range(5) # Create a reinitializable iterator defined by its structure iterator=tf.data.Iterator.from_structure(train_dataset.output_types,train_dataset.output_shapes) next_element=iterator.get_next() # Create initialization operation for each dataset train_init_op=iterator.make_initializer(train_dataset) test_init_op=iterator.make_initializer(test_dataset) # Create a tensorflow session sess=tf.Session() # Get the train data sess.run(train_init_op) print('Train dataset: ') for i in range(10): print(sess.run(next_element),end=' ') # Switch from train_dataset to test_dataset sess.run(test_init_op) print('\n\nTest dataset: ') for i in range(5): print(sess.run(next_element),end=' ') |
– Feedable Iterator: Feedable Iterator cho phép chúng ta thay đổi giữa các interators thay vì giữa các datasets như trong Reinitializable Iterator.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# Example of using Feedable Iterator # Create two datasets with the same structure train_dataset=tf.data.Dataset.range(10) test_dataset=tf.data.Dataset.range(5) # Create a feedable iterator defined by a placeholder and its structure handle=tf.placeholder(tf.string) iterator=tf.data.Iterator.from_string_handle(handle,train_dataset.output_types,train_dataset.output_shapes) next_element=iterator.get_next() # Create one-shot iterator for train_dataset train_iterator=train_dataset.make_one_shot_iterator() # Create initializable iterator for test_dataset test_iterator=test_dataset.make_initializable_iterator() # Create a tensorflow session sess=tf.Session() # Create handle for train_iterator and test_iterator train_handle=sess.run(train_iterator.string_handle()) test_handle=sess.run(test_iterator.string_handle()) # Get the train data using train_iterator print('Train dataset(using one-shot iterator): ') for i in range(10): print(sess.run(next_element, feed_dict={handle:train_handle}),end=' ') # Switch from one-shot iterator to initializable iterator sess.run(test_iterator.initializer) print('\n\nTest dataset(using initializable iterator): ') for i in range(5): print(sess.run(next_element, feed_dict={handle:test_handle}),end=' ') |
3) Thay thế feed_dict bằng tf.data API
Khi sử dụng tf.data API, chúng ta thay thế các placeholder của input và output data trực tiếp bằng dữ liệu từ iterator.get_next(). Sau đó, loại bỏ feed_dict khi huấn luyện mô hình. Quá trình này được mô tả trong đoạn code dưới đây, với việc sử dụng sẵn chương trình Phân loại Wine dataset đã trình bày trong bài tutorial Xây dựng mô hình Deep learning với Mid-level APIs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
from sklearn import datasets from sklearn import preprocessing from sklearn.model_selection import train_test_split import tensorflow as tf # Load and processing the data wine_data = datasets.load_wine() data=wine_data['data'] min_max_scaler = preprocessing.MinMaxScaler() data_scaled = min_max_scaler.fit_transform(data) #Split into train and test set train_data, test_data, train_label, test_label = train_test_split(data_scaled,wine_data['target'],test_size=0.2) # Create a dataset from train_data,train_label tf.reset_default_graph() epoches=50 train_dataset=tf.data.Dataset.from_tensor_slices((train_data,train_label)).repeat(epoches).batch(len(train_data)) # Create an one-shot iterator iterator = train_dataset.make_one_shot_iterator() inputData, actual_labels = iterator.get_next() # Create a neural network hidden1=tf.layers.dense(inputData,128,activation=tf.nn.relu,name="layer1") hidden2=tf.layers.dense(hidden1,64,activation=tf.nn.relu,name="layer2") logit=tf.layers.dense(hidden2,3,name="logit") # Define the loss function and Optimization algorithm cross_entropy=tf.nn.sparse_softmax_cross_entropy_with_logits(labels=actual_labels,logits=logit) loss=tf.reduce_mean(cross_entropy,name="loss") training_op=tf.train.AdamOptimizer(0.01).minimize(loss) # Calculate the accuracy of the model accuracy=tf.reduce_mean(tf.cast(tf.equal(tf.argmax(logit,1),actual_labels),tf.float64)) # Create a session and initialse all variables sess=tf.Session() sess.run(tf.global_variables_initializer()) for i in range(epoches): # Train the model and calculate the accuracy of the current model _,train_accuracy=sess.run([training_op,accuracy]) # Display the accuracy result print(i+1,'. Train accuracy: ',train_accuracy) |
Như vậy, trong bài tutorial này chúng ta đã cùng tìm hiểu về cách tạo Datasets, tìm hiểu sự khác nhau giữa các Iterators và cách sử dụng tf.data API để thay thế cho feed_dict nhằm tăng hiệu quả và tiết kiệm thời gian trong quá trình huấn luyện. Các bạn có thể tham khảo Colab notebook của tutorial này trên trang Github của ItechSeeker tại đây. Ngoài ra, các bạn có thể tham khảo một ví dụ phức tạp hơn về cách sử dụng Data input pipeline thay thế cho feed_dict tại bài tutorial Huấn luyện mô hình Seq2seq sử dụng Data Pipeline.
Tài liệu tham khảo:
[1] http://www.programmersought.com/article/1339534134/
[3] https://www.tensorflow.org/guide/datasets