如果需要訓練的數據大小不大,例如不到1G,那么可以直接全部讀入內存中進行訓練,這樣一般效率最高。
但如果需要訓練的數據很大,例如超過10G,無法一次載入內存,那么通常需要在訓練的過程中分批逐漸讀入。
使用 tf.data API 可以構建數據輸入管道,輕松處理大量的數據,不同的數據格式,以及不同的數據轉換。
一,構建數據管道
可以從 Numpy array, Pandas DataFrame, Python generator, csv文件, 文本文件, 文件路徑, tfrecords文件等方式構建數據管道。
其中通過Numpy array, Pandas DataFrame, 文件路徑構建數據管道是最常用的方法。
通過tfrecords文件方式構建數據管道較為復雜,需要對樣本構建tf.Example后壓縮成字符串寫到tfrecoreds文件,讀取后再解析成tf.Example。
但tfrecoreds文件的優點是壓縮后文件較小,便於網絡傳播,加載速度較快。
首先看下iris部分數據:
'feature_names': ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)'], 'filename': '/usr/local/lib/python3.6/dist-packages/sklearn/datasets/data/iris.csv', 'target': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]), 'target_names': array(['setosa', 'versicolor', 'virginica'], dtype='<U10')
1,從Numpy array構建數據管道
# 從Numpy array構建數據管道 import tensorflow as tf import numpy as np from sklearn import datasets iris = datasets.load_iris() ds1 = tf.data.Dataset.from_tensor_slices((iris["data"],iris["target"])) for features,label in ds1.take(5): print(features,label)
tf.Tensor([5.1 3.5 1.4 0.2], shape=(4,), dtype=float64) tf.Tensor(0, shape=(), dtype=int64)
tf.Tensor([4.9 3. 1.4 0.2], shape=(4,), dtype=float64) tf.Tensor(0, shape=(), dtype=int64)
tf.Tensor([4.7 3.2 1.3 0.2], shape=(4,), dtype=float64) tf.Tensor(0, shape=(), dtype=int64)
tf.Tensor([4.6 3.1 1.5 0.2], shape=(4,), dtype=float64) tf.Tensor(0, shape=(), dtype=int64)
tf.Tensor([5. 3.6 1.4 0.2], shape=(4,), dtype=float64) tf.Tensor(0, shape=(), dtype=int64
2,從 Pandas DataFrame構建數據管道
# 從 Pandas DataFrame構建數據管道 import tensorflow as tf from sklearn import datasets import pandas as pd iris = datasets.load_iris() dfiris = pd.DataFrame(iris["data"],columns = iris.feature_names) ds2 = tf.data.Dataset.from_tensor_slices((dfiris.to_dict("list"),iris["target"])) for features,label in ds2.take(3): print(features,label)
{'sepal length (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=5.1>, 'sepal width (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=3.5>, 'petal length (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=1.4>, 'petal width (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=0.2>} tf.Tensor(0, shape=(), dtype=int64)
{'sepal length (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=4.9>, 'sepal width (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=3.0>, 'petal length (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=1.4>, 'petal width (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=0.2>} tf.Tensor(0, shape=(), dtype=int64)
{'sepal length (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=4.7>, 'sepal width (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=3.2>, 'petal length (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=1.3>, 'petal width (cm)': <tf.Tensor: shape=(), dtype=float32, numpy=0.2>} tf.Tensor(0, shape=(), dtype=int64)
3,從Python generator構建數據管道
# 從Python generator構建數據管道 import tensorflow as tf from matplotlib import pyplot as plt from tensorflow.keras.preprocessing.image import ImageDataGenerator # 定義一個從文件中讀取圖片的generator image_generator = ImageDataGenerator(rescale=1.0/255).flow_from_directory( "./data/cifar2/test/", target_size=(32, 32), batch_size=20, class_mode='binary') classdict = image_generator.class_indices print(classdict)
Found 2000 images
belonging to 2 classes. {'airplane': 0, 'automobile': 1}
def generator(): for features,label in image_generator: yield (features,label) ds3 = tf.data.Dataset.from_generator(generator,output_types=(tf.float32,tf.int32)) %matplotlib inline %config InlineBackend.figure_format = 'svg' plt.figure(figsize=(6,6)) for i,(img,label) in enumerate(ds3.unbatch().take(9)): ax=plt.subplot(3,3,i+1) ax.imshow(img.numpy()) ax.set_title("label = %d"%label) ax.set_xticks([]) ax.set_yticks([]) plt.show()
4,從csv文件構建數據管道
# 從csv文件構建數據管道 ds4 = tf.data.experimental.make_csv_dataset( file_pattern = ["./data/titanic/train.csv","./data/titanic/test.csv"], batch_size=3, label_name="Survived", na_value="", num_epochs=1, ignore_errors=True) for data,label in ds4.take(2): print(data,label)
OrderedDict([('PassengerId', <tf.Tensor: shape=(3,), dtype=int32, numpy=array([540, 58, 764], dtype=int32)>), ('Pclass', <tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 3, 1], dtype=int32)>), ('Name', <tf.Tensor: shape=(3,), dtype=string, numpy=
array([b'Frolicher, Miss. Hedwig Margaritha', b'Novel, Mr. Mansouer',
b'Carter, Mrs. William Ernest (Lucile Polk)'], dtype=object)>), ('Sex', <tf.Tensor: shape=(3,), dtype=string, numpy=array([b'female', b'male', b'female'], dtype=object)>), ('Age', <tf.Tensor: shape=(3,), dtype=float32, numpy=array([22. , 28.5, 36. ], dtype=float32)>), ('SibSp', <tf.Tensor: shape=(3,), dtype=int32, numpy=array([0, 0, 1], dtype=int32)>), ('Parch', <tf.Tensor: shape=(3,), dtype=int32, numpy=array([2, 0, 2], dtype=int32)>), ('Ticket', <tf.Tensor: shape=(3,), dtype=string, numpy=array([b'13568', b'2697', b'113760'], dtype=object)>), ('Fare', <tf.Tensor: shape=(3,), dtype=float32, numpy=array([ 49.5 , 7.2292, 120. ], dtype=float32)>), ('Cabin', <tf.Tensor: shape=(3,), dtype=string, numpy=array([b'B39', b'', b'B96 B98'], dtype=object)>), ('Embarked', <tf.Tensor: shape=(3,), dtype=string, numpy=array([b'C', b'C', b'S'], dtype=object)>)]) tf.Tensor([1 0 1], shape=(3,), dtype=int32)
OrderedDict([('PassengerId', <tf.Tensor: shape=(3,), dtype=int32, numpy=array([845, 66, 390], dtype=int32)>), ('Pclass', <tf.Tensor: shape=(3,), dtype=int32, numpy=array([3, 3, 2], dtype=int32)>), ('Name', <tf.Tensor: shape=(3,), dtype=string, numpy=
array([b'Culumovic, Mr. Jeso', b'Moubarek, Master. Gerios',
b'Lehmann, Miss. Bertha'], dtype=object)>), ('Sex', <tf.Tensor: shape=(3,), dtype=string, numpy=array([b'male', b'male', b'female'], dtype=object)>), ('Age', <tf.Tensor: shape=(3,), dtype=float32, numpy=array([17., 0., 17.], dtype=float32)>), ('SibSp', <tf.Tensor: shape=(3,), dtype=int32, numpy=array([0, 1, 0], dtype=int32)>), ('Parch', <tf.Tensor: shape=(3,), dtype=int32, numpy=array([0, 1, 0], dtype=int32)>), ('Ticket', <tf.Tensor: shape=(3,), dtype=string, numpy=array([b'315090', b'2661', b'SC 1748'], dtype=object)>), ('Fare', <tf.Tensor: shape=(3,), dtype=float32, numpy=array([ 8.6625, 15.2458, 12. ], dtype=float32)>), ('Cabin', <tf.Tensor: shape=(3,), dtype=string, numpy=array([b'', b'', b''], dtype=object)>), ('Embarked', <tf.Tensor: shape=(3,), dtype=string, numpy=array([b'S', b'C', b'C'], dtype=object)>)]) tf.Tensor([0 1 1], shape=(3,), dtype=int32)
5,從文本文件構建數據管道
# 從文本文件構建數據管道 ds5 = tf.data.TextLineDataset( filenames = ["./data/titanic/train.csv","./data/titanic/test.csv"] ).skip(1) #略去第一行header for line in ds5.take(5): print(line)
tf.Tensor(b'493,0,1,"Molson, Mr. Harry Markland",male,55.0,0,0,113787,30.5,C30,S', shape=(), dtype=string)
tf.Tensor(b'53,1,1,"Harper, Mrs. Henry Sleeper (Myna Haxtun)",female,49.0,1,0,PC 17572,76.7292,D33,C', shape=(), dtype=string)
tf.Tensor(b'388,1,2,"Buss, Miss. Kate",female,36.0,0,0,27849,13.0,,S', shape=(), dtype=string)
tf.Tensor(b'192,0,2,"Carbines, Mr. William",male,19.0,0,0,28424,13.0,,S', shape=(), dtype=string)
tf.Tensor(b'687,0,3,"Panula, Mr. Jaako Arnold",male,14.0,4,1,3101295,39.6875,,S', shape=(), dtype=string)
6,從文件路徑構建數據管道
s6 = tf.data.Dataset.list_files("./data/cifar2/train/*/*.jpg") for file in ds6.take(5): print(file)
tf.Tensor(b'./data/cifar2/train/automobile/1263.jpg', shape=(), dtype=string)
tf.Tensor(b'./data/cifar2/train/airplane/2837.jpg', shape=(), dtype=string)
tf.Tensor(b'./data/cifar2/train/airplane/4264.jpg', shape=(), dtype=string)
tf.Tensor(b'./data/cifar2/train/automobile/4241.jpg', shape=(), dtype=string)
tf.Tensor(b'./data/cifar2/train/automobile/192.jpg', shape=(), dtype=string)
from matplotlib import pyplot as plt def load_image(img_path,size = (32,32)): label = 1 if tf.strings.regex_full_match(img_path,".*/automobile/.*") else 0 img = tf.io.read_file(img_path) img = tf.image.decode_jpeg(img) #注意此處為jpeg格式 img = tf.image.resize(img,size) return(img,label) %matplotlib inline %config InlineBackend.figure_format = 'svg' for i,(img,label) in enumerate(ds6.map(load_image).take(2)): plt.figure(i) plt.imshow((img/255.0).numpy()) plt.title("label = %d"%label) plt.xticks([]) plt.yticks([])
7,從tfrecords文件構建數據管道
import os import numpy as np # inpath:原始數據路徑 outpath:TFRecord文件輸出路徑 def create_tfrecords(inpath,outpath): writer = tf.io.TFRecordWriter(outpath) dirs = os.listdir(inpath) for index, name in enumerate(dirs): class_path = inpath +"/"+ name+"/" for img_name in os.listdir(class_path): img_path = class_path + img_name img = tf.io.read_file(img_path) #img = tf.image.decode_image(img) #img = tf.image.encode_jpeg(img) #統一成jpeg格式壓縮 example = tf.train.Example( features=tf.train.Features(feature={ 'label': tf.train.Feature(int64_list=tf.train.Int64List(value=[index])), 'img_raw': tf.train.Feature(bytes_list=tf.train.BytesList(value=[img.numpy()])) })) writer.write(example.SerializeToString()) writer.close() create_tfrecords("./data/cifar2/test/","./data/cifar2_test.tfrecords/") from matplotlib import pyplot as plt def parse_example(proto): description ={ 'img_raw' : tf.io.FixedLenFeature([], tf.string), 'label': tf.io.FixedLenFeature([], tf.int64)} example = tf.io.parse_single_example(proto, description) img = tf.image.decode_jpeg(example["img_raw"]) #注意此處為jpeg格式 img = tf.image.resize(img, (32,32)) label = example["label"] return(img,label) ds7 = tf.data.TFRecordDataset("./data/cifar2_test.tfrecords").map(parse_example).shuffle(3000) %matplotlib inline %config InlineBackend.figure_format = 'svg' plt.figure(figsize=(6,6)) for i,(img,label) in enumerate(ds7.take(9)): ax=plt.subplot(3,3,i+1) ax.imshow((img/255.0).numpy()) ax.set_title("label = %d"%label) ax.set_xticks([]) ax.set_yticks([]) plt.show()
二,應用數據轉換
Dataset數據結構應用非常靈活,因為它本質上是一個Sequece序列,其每個元素可以是各種類型,例如可以是張量,列表,字典,也可以是Dataset。
Dataset包含了非常豐富的數據轉換功能。
-
map: 將轉換函數映射到數據集每一個元素。
-
flat_map: 將轉換函數映射到數據集的每一個元素,並將嵌套的Dataset壓平。
-
interleave: 效果類似flat_map,但可以將不同來源的數據夾在一起。
-
filter: 過濾掉某些元素。
-
zip: 將兩個長度相同的Dataset橫向鉸合。
-
concatenate: 將兩個Dataset縱向連接。
-
reduce: 執行歸並操作。
-
batch : 構建批次,每次放一個批次。比原始數據增加一個維度。 其逆操作為unbatch。
-
padded_batch: 構建批次,類似batch, 但可以填充到相同的形狀。
-
window :構建滑動窗口,返回Dataset of Dataset.
-
shuffle: 數據順序洗牌。
-
repeat: 重復數據若干次,不帶參數時,重復無數次。
-
shard: 采樣,從某個位置開始隔固定距離采樣一個元素。
- take: 采樣,從開始位置取前幾個元素。
# map:將轉換函數映射到數據集每一個元素 ds = tf.data.Dataset.from_tensor_slices(["hello world","hello China","hello Beijing"]) ds_map = ds.map(lambda x:tf.strings.split(x," ")) for x in ds_map: print(x)
tf.Tensor([b'hello' b'world'], shape=(2,), dtype=string)
tf.Tensor([b'hello' b'China'], shape=(2,), dtype=string)
tf.Tensor([b'hello' b'Beijing'], shape=(2,), dtype=string)
# flat_map:將轉換函數映射到數據集的每一個元素,並將嵌套的Dataset壓平。 ds = tf.data.Dataset.from_tensor_slices(["hello world","hello China","hello Beijing"]) ds_flatmap = ds.flat_map(lambda x:tf.data.Dataset.from_tensor_slices(tf.strings.split(x," "))) for x in ds_flatmap: print(x)
tf.Tensor(b'hello', shape=(), dtype=string)
tf.Tensor(b'world', shape=(), dtype=string)
tf.Tensor(b'hello', shape=(), dtype=string)
tf.Tensor(b'China', shape=(), dtype=string)
tf.Tensor(b'hello', shape=(), dtype=string)
tf.Tensor(b'Beijing', shape=(), dtype=string)
# interleave: 效果類似flat_map,但可以將不同來源的數據夾在一起。 ds = tf.data.Dataset.from_tensor_slices(["hello world","hello China","hello Beijing"]) ds_interleave = ds.interleave(lambda x:tf.data.Dataset.from_tensor_slices(tf.strings.split(x," "))) for x in ds_interleave: print(x)
tf.Tensor(b'hello', shape=(), dtype=string)
tf.Tensor(b'hello', shape=(), dtype=string)
tf.Tensor(b'hello', shape=(), dtype=string)
tf.Tensor(b'world', shape=(), dtype=string)
tf.Tensor(b'China', shape=(), dtype=string)
tf.Tensor(b'Beijing', shape=(), dtype=string)
# filter:過濾掉某些元素。 ds = tf.data.Dataset.from_tensor_slices(["hello world","hello China","hello Beijing"]) # 找出含有字母a或B的元素 ds_filter = ds.filter(lambda x: tf.strings.regex_full_match(x, ".*[a|B].*")) for x in ds_filter: print(x)
tf.Tensor(b'hello China', shape=(), dtype=string)
tf.Tensor(b'hello Beijing', shape=(), dtype=string)
# zip:將兩個長度相同的Dataset橫向鉸合。 ds1 = tf.data.Dataset.range(0,3) ds2 = tf.data.Dataset.range(3,6) ds3 = tf.data.Dataset.range(6,9) ds_zip = tf.data.Dataset.zip((ds1,ds2,ds3)) for x,y,z in ds_zip: print(x.numpy(),y.numpy(),z.numpy())
0 3 6
1 4 7
2 5 8
# condatenate:將兩個Dataset縱向連接。 ds1 = tf.data.Dataset.range(0,3) ds2 = tf.data.Dataset.range(3,6) ds_concat = tf.data.Dataset.concatenate(ds1,ds2) for x in ds_concat: print(x)
tf.Tensor(0, shape=(), dtype=int64)
tf.Tensor(1, shape=(), dtype=int64)
tf.Tensor(2, shape=(), dtype=int64)
tf.Tensor(3, shape=(), dtype=int64)
tf.Tensor(4, shape=(), dtype=int64)
tf.Tensor(5, shape=(), dtype=int64)
# reduce:執行歸並操作。 ds = tf.data.Dataset.from_tensor_slices([1,2,3,4,5.0]) result = ds.reduce(0.0,lambda x,y:tf.add(x,y)) result
<tf.Tensor: shape=(), dtype=float32, numpy=15.0>
# batch:構建批次,每次放一個批次。比原始數據增加一個維度。 其逆操作為unbatch。 ds = tf.data.Dataset.range(12) ds_batch = ds.batch(4) for x in ds_batch: print(x)
tf.Tensor([0 1 2 3], shape=(4,), dtype=int64)
tf.Tensor([4 5 6 7], shape=(4,), dtype=int64)
tf.Tensor([ 8 9 10 11], shape=(4,), dtype=int64)
# padded_batch:構建批次,類似batch, 但可以填充到相同的形狀。 elements = [[1, 2],[3, 4, 5],[6, 7],[8]] ds = tf.data.Dataset.from_generator(lambda: iter(elements), tf.int32) ds_padded_batch = ds.padded_batch(2,padded_shapes = [4,]) for x in ds_padded_batch: print(x)
tf.Tensor(
[[1 2 0 0]
[3 4 5 0]], shape=(2, 4), dtype=int32)
tf.Tensor(
[[6 7 0 0]
[8 0 0 0]], shape=(2, 4), dtype=int32)
# window:構建滑動窗口,返回Dataset of Dataset. ds = tf.data.Dataset.range(12) # window返回的是Dataset of Dataset,可以用flat_map壓平 ds_window = ds.window(3, shift=1).flat_map(lambda x: x.batch(3,drop_remainder=True)) for x in ds_window: print(x)
tf.Tensor([0 1 2], shape=(3,), dtype=int64)
tf.Tensor([1 2 3], shape=(3,), dtype=int64)
tf.Tensor([2 3 4], shape=(3,), dtype=int64)
tf.Tensor([3 4 5], shape=(3,), dtype=int64)
tf.Tensor([4 5 6], shape=(3,), dtype=int64)
tf.Tensor([5 6 7], shape=(3,), dtype=int64)
tf.Tensor([6 7 8], shape=(3,), dtype=int64)
tf.Tensor([7 8 9], shape=(3,), dtype=int64)
tf.Tensor([ 8 9 10], shape=(3,), dtype=int64)
tf.Tensor([ 9 10 11], shape=(3,), dtype=int64)
# shuffle:數據順序洗牌。 ds = tf.data.Dataset.range(12) ds_shuffle = ds.shuffle(buffer_size = 5) for x in ds_shuffle: print(x)
tf.Tensor(1, shape=(), dtype=int64)
tf.Tensor(4, shape=(), dtype=int64)
tf.Tensor(0, shape=(), dtype=int64)
tf.Tensor(6, shape=(), dtype=int64)
tf.Tensor(5, shape=(), dtype=int64)
tf.Tensor(2, shape=(), dtype=int64)
tf.Tensor(7, shape=(), dtype=int64)
tf.Tensor(11, shape=(), dtype=int64)
tf.Tensor(3, shape=(), dtype=int64)
tf.Tensor(9, shape=(), dtype=int64)
tf.Tensor(10, shape=(), dtype=int64)
tf.Tensor(8, shape=(), dtype=int64)
# repeat:重復數據若干次,不帶參數時,重復無數次。 ds = tf.data.Dataset.range(3) ds_repeat = ds.repeat(3) for x in ds_repeat: print(x)
tf.Tensor(0, shape=(), dtype=int64)
tf.Tensor(1, shape=(), dtype=int64)
tf.Tensor(2, shape=(), dtype=int64)
tf.Tensor(0, shape=(), dtype=int64)
tf.Tensor(1, shape=(), dtype=int64)
tf.Tensor(2, shape=(), dtype=int64)
tf.Tensor(0, shape=(), dtype=int64)
tf.Tensor(1, shape=(), dtype=int64)
tf.Tensor(2, shape=(), dtype=int64)
# shard:采樣,從某個位置開始隔固定距離采樣一個元素。 ds = tf.data.Dataset.range(12) ds_shard = ds.shard(3,index = 1) for x in ds_shard: print(x)
tf.Tensor(1, shape=(), dtype=int64)
tf.Tensor(4, shape=(), dtype=int64)
tf.Tensor(7, shape=(), dtype=int64)
tf.Tensor(10, shape=(), dtype=int64)
# take:采樣,從開始位置取前幾個元素。 ds = tf.data.Dataset.range(12) ds_take = ds.take(3) list(ds_take.as_numpy_iterator())
[0, 1, 2]
三,提升管道性能
訓練深度學習模型常常會非常耗時。
模型訓練的耗時主要來自於兩個部分,一部分來自數據准備,另一部分來自參數迭代。
參數迭代過程的耗時通常依賴於GPU來提升。
而數據准備過程的耗時則可以通過構建高效的數據管道進行提升。
以下是一些構建高效數據管道的建議。
-
1,使用 prefetch 方法讓數據准備和參數迭代兩個過程相互並行。
-
2,使用 interleave 方法可以讓數據讀取過程多進程執行,並將不同來源數據夾在一起。
-
3,使用 map 時設置num_parallel_calls 讓數據轉換過程多進行執行。
-
4,使用 cache 方法讓數據在第一個epoch后緩存到內存中,僅限於數據集不大情形。
-
5,使用 map轉換時,先batch, 然后采用向量化的轉換方法對每個batch進行轉換。
1,使用 prefetch 方法讓數據准備和參數迭代兩個過程相互並行。
import tensorflow as tf # 打印時間分割線 @tf.function def printbar(): ts = tf.timestamp() today_ts = ts%(24*60*60) hour = tf.cast(today_ts//3600+8,tf.int32)%tf.constant(24) minite = tf.cast((today_ts%3600)//60,tf.int32) second = tf.cast(tf.floor(today_ts%60),tf.int32) def timeformat(m): if tf.strings.length(tf.strings.format("{}",m))==1: return(tf.strings.format("0{}",m)) else: return(tf.strings.format("{}",m)) timestring = tf.strings.join([timeformat(hour),timeformat(minite), timeformat(second)],separator = ":") tf.print("=========="*8,end = "") tf.print(timestring) import time # 數據准備和參數迭代兩個過程默認情況下是串行的。 # 模擬數據准備 def generator(): for i in range(10): #假設每次准備數據需要2s time.sleep(2) yield i ds = tf.data.Dataset.from_generator(generator,output_types = (tf.int32)) # 模擬參數迭代 def train_step(): #假設每一步訓練需要1s time.sleep(1) # 訓練過程預計耗時 10*2+10*1+ = 30s printbar() tf.print(tf.constant("start training...")) for x in ds: train_step() printbar() tf.print(tf.constant("end training...")) # 使用 prefetch 方法讓數據准備和參數迭代兩個過程相互並行。 # 訓練過程預計耗時 max(10*2,10*1) = 20s printbar() tf.print(tf.constant("start training with prefetch...")) # tf.data.experimental.AUTOTUNE 可以讓程序自動選擇合適的參數 for x in ds.prefetch(buffer_size = tf.data.experimental.AUTOTUNE): train_step() printbar() tf.print(tf.constant("end training..."))
================================================================================16:18:31 start training... ================================================================================16:19:01 end training... ================================================================================16:19:01 start training with prefetch... ================================================================================16:19:23 end training...
2,使用 interleave 方法可以讓數據讀取過程多進程執行,並將不同來源數據夾在一起。
ds_files = tf.data.Dataset.list_files("./data/titanic/*.csv") ds = ds_files.flat_map(lambda x:tf.data.TextLineDataset(x).skip(1)) for line in ds.take(4): print(line)
tf.Tensor(b'493,0,1,"Molson, Mr. Harry Markland",male,55.0,0,0,113787,30.5,C30,S', shape=(), dtype=string)
tf.Tensor(b'53,1,1,"Harper, Mrs. Henry Sleeper (Myna Haxtun)",female,49.0,1,0,PC 17572,76.7292,D33,C', shape=(), dtype=string)
tf.Tensor(b'388,1,2,"Buss, Miss. Kate",female,36.0,0,0,27849,13.0,,S', shape=(), dtype=string)
tf.Tensor(b'192,0,2,"Carbines, Mr. William",male,19.0,0,0,28424,13.0,,S', shape=(), dtype=string)
ds_files = tf.data.Dataset.list_files("./data/titanic/*.csv") ds = ds_files.interleave(lambda x:tf.data.TextLineDataset(x).skip(1)) for line in ds.take(8): print(line)
tf.Tensor(b'181,0,3,"Sage, Miss. Constance Gladys",female,,8,2,CA. 2343,69.55,,S', shape=(), dtype=string)
tf.Tensor(b'493,0,1,"Molson, Mr. Harry Markland",male,55.0,0,0,113787,30.5,C30,S', shape=(), dtype=string)
tf.Tensor(b'405,0,3,"Oreskovic, Miss. Marija",female,20.0,0,0,315096,8.6625,,S', shape=(), dtype=string)
tf.Tensor(b'53,1,1,"Harper, Mrs. Henry Sleeper (Myna Haxtun)",female,49.0,1,0,PC 17572,76.7292,D33,C', shape=(), dtype=string)
tf.Tensor(b'635,0,3,"Skoog, Miss. Mabel",female,9.0,3,2,347088,27.9,,S', shape=(), dtype=string)
tf.Tensor(b'388,1,2,"Buss, Miss. Kate",female,36.0,0,0,27849,13.0,,S', shape=(), dtype=string)
tf.Tensor(b'701,1,1,"Astor, Mrs. John Jacob (Madeleine Talmadge Force)",female,18.0,1,0,PC 17757,227.525,C62 C64,C', shape=(), dtype=string)
tf.Tensor(b'192,0,2,"Carbines, Mr. William",male,19.0,0,0,28424,13.0,,S', shape=(), dtype=string)
3,使用 map 時設置num_parallel_calls 讓數據轉換過程多進行執行
ds = tf.data.Dataset.list_files("./data/cifar2/train/*/*.jpg") def load_image(img_path,size = (32,32)): label = 1 if tf.strings.regex_full_match(img_path,".*/automobile/.*") else 0 img = tf.io.read_file(img_path) img = tf.image.decode_jpeg(img) #注意此處為jpeg格式 img = tf.image.resize(img,size) return(img,label) # 單進程轉換 printbar() tf.print(tf.constant("start transformation...")) ds_map = ds.map(load_image) for _ in ds_map: pass printbar() tf.print(tf.constant("end transformation...")) # 多進程轉換 printbar() tf.print(tf.constant("start parallel transformation...")) ds_map_parallel = ds.map(load_image,num_parallel_calls = tf.data.experimental.AUTOTUNE) for _ in ds_map_parallel: pass printbar() tf.print(tf.constant("end parallel transformation..."))
4,使用 cache 方法讓數據在第一個epoch后緩存到內存中,僅限於數據集不大情形。
import time # 模擬數據准備 def generator(): for i in range(5): #假設每次准備數據需要2s time.sleep(2) yield i ds = tf.data.Dataset.from_generator(generator,output_types = (tf.int32)) # 模擬參數迭代 def train_step(): #假設每一步訓練需要0s pass # 訓練過程預計耗時 (5*2+5*0)*3 = 30s printbar() tf.print(tf.constant("start training...")) for epoch in tf.range(3): for x in ds: train_step() printbar() tf.print("epoch =",epoch," ended") printbar() tf.print(tf.constant("end training...")) import time # 模擬數據准備 def generator(): for i in range(5): #假設每次准備數據需要2s time.sleep(2) yield i # 使用 cache 方法讓數據在第一個epoch后緩存到內存中,僅限於數據集不大情形。 ds = tf.data.Dataset.from_generator(generator,output_types = (tf.int32)).cache() # 模擬參數迭代 def train_step(): #假設每一步訓練需要0s time.sleep(0) # 訓練過程預計耗時 (5*2+5*0)+(5*0+5*0)*2 = 10s printbar() tf.print(tf.constant("start training...")) for epoch in tf.range(3): for x in ds: train_step() printbar() tf.print("epoch =",epoch," ended") printbar() tf.print(tf.constant("end training..."))
================================================================================17:07:57 start training... ================================================================================17:08:07 epoch = 0 ended ================================================================================17:08:17 epoch = 1 ended ================================================================================17:08:27 epoch = 2 ended ================================================================================17:08:27 end training... ================================================================================17:08:27 start training... ================================================================================17:08:37 epoch = 0 ended ================================================================================17:08:37 epoch = 1 ended ================================================================================17:08:37 epoch = 2 ended ================================================================================17:08:37 end training...
5,使用 map轉換時,先batch, 然后采用向量化的轉換方法對每個batch進行轉換。
# 先map后batch ds = tf.data.Dataset.range(100000) ds_map_batch = ds.map(lambda x:x**2).batch(20) printbar() tf.print(tf.constant("start scalar transformation...")) for x in ds_map_batch: pass printbar() tf.print(tf.constant("end scalar transformation...")) # 先batch后map ds = tf.data.Dataset.range(100000) ds_batch_map = ds.batch(20).map(lambda x:x**2) printbar() tf.print(tf.constant("start vector transformation...")) for x in ds_batch_map: pass printbar() tf.print(tf.constant("end vector transformation..."))
================================================================================17:09:10 start scalar transformation... ================================================================================17:09:13 end scalar transformation... ================================================================================17:09:13 start vector transformation... ================================================================================17:09:14 end vector transformation...
參考:
開源電子書地址:https://lyhue1991.github.io/eat_tensorflow2_in_30_days/
GitHub 項目地址:https://github.com/lyhue1991/eat_tensorflow2_in_30_days