在整個機器學習過程當中,除了訓練模型外,應該就屬數據預處理過程消耗的精力最多,數據預處理過程須要完成的任務包括數據讀取、過濾、轉換等等。爲了將用戶從繁雜的預處理操做中解放處理,更多地將精力放在算法建模上,TensorFlow中提供了data模塊,這一模塊以多種方式提供了數據讀取、數據處理、數據保存等功能。本文重點是data模塊中的Dataset對象。css
對於建立Dataset對象,官方文檔中總結爲兩種方式,我將這兩種方式細化後總結爲4中方式:html
(1)經過Dataset中的range()方法建立包含必定序列的Dataset對象。html5
range()方法是Dataset內部定義的一個的靜態方法,能夠直接經過類名調用。另外,Dataset中的range()方法與Python自己內置的range()方法接受參數形式是一致的,能夠接受range(begin)、range(begin, end)、range(begin, end, step)等多種方式傳參。java
import tensorflow as tf
import numpy as np
dataset1 = tf.data.Dataset.range(5)
type(dataset1)
tensorflow.python.data.ops.dataset_ops.RangeDataset
注:RangeDataset是Dataset的一個子類。 Dataset對象屬於可迭代對象, 可經過循環進行遍歷:node
for i in dataset1:
print(i)
print(i.numpy())
tf.Tensor(0, shape=(), dtype=int64) 0 tf.Tensor(1, shape=(), dtype=int64) 1 tf.Tensor(2, shape=(), dtype=int64) 2 tf.Tensor(3, shape=(), dtype=int64) 3 tf.Tensor(4, shape=(), dtype=int64) 4
能夠看到,range()方法建立的Dataset對象內部每個元素都以Tensor對象的形式存在,能夠經過numpy()方法訪問真實值。python
若是你以爲range()方法不夠靈活,功能不夠強大,那麼你能夠嘗試使用from_generator()方法。from_generator()方法接收一個可調用的生成器函數最爲參數,在遍歷from_generator()方法返回的Dataset對象過程當中不斷生成新的數據,減小內存佔用,這在大數據集中頗有用。jquery
def count(stop):
i = 0
while i<stop:
print('第%s次調用……'%i)
yield i
i += 1
dataset2 = tf.data.Dataset.from_generator(count, args=[3], output_types=tf.int32, output_shapes = (), )
a = iter(dataset2)
next(a)
第0次調用……
<tf.Tensor: id=46, shape=(), dtype=int32, numpy=0>
next(a)
第1次調用……
<tf.Tensor: id=47, shape=(), dtype=int32, numpy=1>
for i in dataset2:
print(i)
print(i.numpy())
第0次調用…… tf.Tensor(0, shape=(), dtype=int32) 0 第1次調用…… tf.Tensor(1, shape=(), dtype=int32) 1 第2次調用…… tf.Tensor(2, shape=(), dtype=int32) 2
(2)經過接收其餘類型的集合類對象建立Dataset對象。這裏所說的集合類型對象包含Python內置的list、tuple,numpy中的ndarray等等。這種建立Dataset對象的方法大多經過from_tensors()和from_tensor_slices()兩個方法實現。這兩個方法很經常使用,重點說一說。linux
當接收參數爲list或Tensor對象時,返回的狀況是同樣的,由於TensorFlow內部會將list先轉爲Tensor對象,而後實例化一個Dataset對象:android
a = [0,1,2,3,4]
dataset1 = tf.data.Dataset.from_tensors(a)
dataset1_n = tf.data.Dataset.from_tensors(np.array(a))
dataset1_t = tf.data.Dataset.from_tensors(tf.constant(a))
dataset1,next(iter(dataset1))
(<TensorDataset shapes: (5,), types: tf.int32>, <tf.Tensor: id=67, shape=(5,), dtype=int32, numpy=array([0, 1, 2, 3, 4], dtype=int32)>)
dataset1_n,next(iter(dataset1_n))
(<TensorDataset shapes: (5,), types: tf.int64>, <tf.Tensor: id=73, shape=(5,), dtype=int64, numpy=array([0, 1, 2, 3, 4])>)
dataset1_t,next(iter(dataset1_t))
(<TensorDataset shapes: (5,), types: tf.int32>, <tf.Tensor: id=79, shape=(5,), dtype=int32, numpy=array([0, 1, 2, 3, 4], dtype=int32)>)
多維結構也是同樣的:
a = [0,1,2,3,4]
b = [5,6,7,8,9]
dataset2 = tf.data.Dataset.from_tensors([a,b])
dataset2_n = tf.data.Dataset.from_tensors(np.array([a,b]))
dataset2_t = tf.data.Dataset.from_tensors(tf.constant([a,b]))
dataset2,next(iter(dataset2))
(<TensorDataset shapes: (2, 5), types: tf.int32>, <tf.Tensor: id=91, shape=(2, 5), dtype=int32, numpy= array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]], dtype=int32)>)
dataset2_n,next(iter(dataset2_n))
(<TensorDataset shapes: (2, 5), types: tf.int64>, <tf.Tensor: id=97, shape=(2, 5), dtype=int64, numpy= array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]])>)
dataset2_t,next(iter(dataset2_t))
(<TensorDataset shapes: (2, 5), types: tf.int32>, <tf.Tensor: id=103, shape=(2, 5), dtype=int32, numpy= array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]], dtype=int32)>)
當接收參數爲數組就不同了,此時Dataset內部內容爲一個tuple,tuple的元素是原來tuple元素轉換爲的Tensor對象:
a = [0,1,2,3,4]
b = [5,6,7,8,9]
dataset3 = tf.data.Dataset.from_tensors((a,b))
for i in dataset3:
print(type(i))
print(i)
for j in i:
print(j)
<class 'tuple'> (<tf.Tensor: id=112, shape=(5,), dtype=int32, numpy=array([0, 1, 2, 3, 4], dtype=int32)>, <tf.Tensor: id=113, shape=(5,), dtype=int32, numpy=array([5, 6, 7, 8, 9], dtype=int32)>) tf.Tensor([0 1 2 3 4], shape=(5,), dtype=int32) tf.Tensor([5 6 7 8 9], shape=(5,), dtype=int32)
當傳入一個list時,時將list中元素逐個轉換爲Tensor對象而後依次放入Dataset中,因此Dataset中有多個Tensor對象:
a = [0,1,2,3,4]
dataset1 = tf.data.Dataset.from_tensor_slices(a)
dataset1
<TensorSliceDataset shapes: (), types: tf.int32>
for i,elem in enumerate(dataset1):
print(i, '-->', elem)
0 --> tf.Tensor(0, shape=(), dtype=int32) 1 --> tf.Tensor(1, shape=(), dtype=int32) 2 --> tf.Tensor(2, shape=(), dtype=int32) 3 --> tf.Tensor(3, shape=(), dtype=int32) 4 --> tf.Tensor(4, shape=(), dtype=int32)
a = [0,1,2,3,4]
b = [5,6,7,8,9]
dataset2 = tf.data.Dataset.from_tensor_slices([a,b])
dataset2
<TensorSliceDataset shapes: (5,), types: tf.int32>
for i,elem in enumerate(dataset2):
print(i, '-->', elem)
0 --> tf.Tensor([0 1 2 3 4], shape=(5,), dtype=int32) 1 --> tf.Tensor([5 6 7 8 9], shape=(5,), dtype=int32)
當傳入參數爲tuple時,會將tuple中各元素轉換爲Tensor對象,而後將第一維度對應位置的切片進行從新組合成一個tuple依次放入到Dataset中,因此在返回的Dataset中有多個tuple。這種形式在對訓練集和測試集進行從新組合是很是實用。
a = [0,1,2,3,4]
b = [5,6,7,8,9]
dataset1 = tf.data.Dataset.from_tensor_slices((a,b))
dataset1
<TensorSliceDataset shapes: ((), ()), types: (tf.int32, tf.int32)>
for i in dataset1:
print(i)
(<tf.Tensor: id=143, shape=(), dtype=int32, numpy=0>, <tf.Tensor: id=144, shape=(), dtype=int32, numpy=5>) (<tf.Tensor: id=145, shape=(), dtype=int32, numpy=1>, <tf.Tensor: id=146, shape=(), dtype=int32, numpy=6>) (<tf.Tensor: id=147, shape=(), dtype=int32, numpy=2>, <tf.Tensor: id=148, shape=(), dtype=int32, numpy=7>) (<tf.Tensor: id=149, shape=(), dtype=int32, numpy=3>, <tf.Tensor: id=150, shape=(), dtype=int32, numpy=8>) (<tf.Tensor: id=151, shape=(), dtype=int32, numpy=4>, <tf.Tensor: id=152, shape=(), dtype=int32, numpy=9>)
c = ['a','b','c','d','e']
dataset3 = tf.data.Dataset.from_tensor_slices((a,b,c))
dataset3
<TensorSliceDataset shapes: ((), (), ()), types: (tf.int32, tf.int32, tf.string)>
for i in dataset3:
print(i)
(<tf.Tensor: id=162, shape=(), dtype=int32, numpy=0>, <tf.Tensor: id=163, shape=(), dtype=int32, numpy=5>, <tf.Tensor: id=164, shape=(), dtype=string, numpy=b'a'>) (<tf.Tensor: id=165, shape=(), dtype=int32, numpy=1>, <tf.Tensor: id=166, shape=(), dtype=int32, numpy=6>, <tf.Tensor: id=167, shape=(), dtype=string, numpy=b'b'>) (<tf.Tensor: id=168, shape=(), dtype=int32, numpy=2>, <tf.Tensor: id=169, shape=(), dtype=int32, numpy=7>, <tf.Tensor: id=170, shape=(), dtype=string, numpy=b'c'>) (<tf.Tensor: id=171, shape=(), dtype=int32, numpy=3>, <tf.Tensor: id=172, shape=(), dtype=int32, numpy=8>, <tf.Tensor: id=173, shape=(), dtype=string, numpy=b'd'>) (<tf.Tensor: id=174, shape=(), dtype=int32, numpy=4>, <tf.Tensor: id=175, shape=(), dtype=int32, numpy=9>, <tf.Tensor: id=176, shape=(), dtype=string, numpy=b'e'>)
對比總結一下from_generator()、from_tensor()、from_tensor_slices()這三個方法:
(3)經過讀取磁盤中的文件(文本、圖片等等)來建立Dataset。tf.data中提供了TextLineDataset、TFRecordDataset等對象來實現此功能。這部份內容比較多,也比較重要,我打算後續用專門一篇博客來總結這部份內容。
(1)take()
功能:用於返回一個新的Dataset對象,新的Dataset對象包含的數據是原Dataset對象的子集。
參數:
dataset = tf.data.Dataset.range(10)
dataset_take = dataset.take(5)
for i in dataset_take:
print(i)
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)
(2)batch()
功能:將Dataset中連續的數據分割成批。
參數:
dataset = tf.data.Dataset.range(11)
dataset_batch = dataset.batch(3)
for i in dataset_batch:
print(i)
tf.Tensor([0 1 2], shape=(3,), dtype=int64) tf.Tensor([3 4 5], shape=(3,), dtype=int64) tf.Tensor([6 7 8], shape=(3,), dtype=int64) tf.Tensor([ 9 10], shape=(2,), dtype=int64)
dataset_batch = dataset.batch(3,drop_remainder=True)
for i in dataset_batch:
print(i)
tf.Tensor([0 1 2], shape=(3,), dtype=int64) tf.Tensor([3 4 5], shape=(3,), dtype=int64) tf.Tensor([6 7 8], shape=(3,), dtype=int64)
train_x = tf.random.uniform((10,3),maxval=100, dtype=tf.int32)
train_y = tf.range(10)
dataset = tf.data.Dataset.from_tensor_slices((train_x, train_y))
for i in dataset.take(3):
print(i)
(<tf.Tensor: id=236, shape=(3,), dtype=int32, numpy=array([81, 53, 85], dtype=int32)>, <tf.Tensor: id=237, shape=(), dtype=int32, numpy=0>) (<tf.Tensor: id=238, shape=(3,), dtype=int32, numpy=array([13, 7, 25], dtype=int32)>, <tf.Tensor: id=239, shape=(), dtype=int32, numpy=1>) (<tf.Tensor: id=240, shape=(3,), dtype=int32, numpy=array([83, 25, 55], dtype=int32)>, <tf.Tensor: id=241, shape=(), dtype=int32, numpy=2>)
dataset_batch = dataset.batch(4)
for i in dataset_batch:
print(i)
(<tf.Tensor: id=250, shape=(4, 3), dtype=int32, numpy= array([[81, 53, 85], [13, 7, 25], [83, 25, 55], [53, 41, 11]], dtype=int32)>, <tf.Tensor: id=251, shape=(4,), dtype=int32, numpy=array([0, 1, 2, 3], dtype=int32)>) (<tf.Tensor: id=252, shape=(4, 3), dtype=int32, numpy= array([[41, 58, 39], [44, 68, 55], [52, 34, 22], [66, 39, 5]], dtype=int32)>, <tf.Tensor: id=253, shape=(4,), dtype=int32, numpy=array([4, 5, 6, 7], dtype=int32)>) (<tf.Tensor: id=254, shape=(2, 3), dtype=int32, numpy= array([[73, 8, 20], [67, 71, 98]], dtype=int32)>, <tf.Tensor: id=255, shape=(2,), dtype=int32, numpy=array([8, 9], dtype=int32)>)
爲何在訓練模型時要將Dataset分割成一個個batch呢?
(3)padded_batch()
功能: batch()的進階版,能夠對shape不一致的連續元素進行分批。
參數:
dataset = tf.data.Dataset.range(10)
dataset = dataset.map(lambda x: tf.fill([tf.cast(x, tf.int32)], x))
dataset_padded = dataset.padded_batch(4, padded_shapes=(None,))
for batch in dataset_padded:
print(batch.numpy())
print('---------------------')
[[0 0 0] [1 0 0] [2 2 0] [3 3 3]] --------------------- [[4 4 4 4 0 0 0] [5 5 5 5 5 0 0] [6 6 6 6 6 6 0] [7 7 7 7 7 7 7]] --------------------- [[8 8 8 8 8 8 8 8 0] [9 9 9 9 9 9 9 9 9]] ---------------------
dataset_padded = dataset.padded_batch(4, padded_shapes=(10,),padding_values=tf.constant(9,dtype=tf.int64)) # 修改填充形狀和填充元素
for batch in dataset_padded:
print(batch.numpy())
print('---------------------')
[[9 9 9 9 9 9 9 9 9 9] [1 9 9 9 9 9 9 9 9 9] [2 2 9 9 9 9 9 9 9 9] [3 3 3 9 9 9 9 9 9 9]] --------------------- [[4 4 4 4 9 9 9 9 9 9] [5 5 5 5 5 9 9 9 9 9] [6 6 6 6 6 6 9 9 9 9] [7 7 7 7 7 7 7 9 9 9]] --------------------- [[8 8 8 8 8 8 8 8 9 9] [9 9 9 9 9 9 9 9 9 9]] ---------------------
(4)map()
功能: 以dataset中每一位元素爲參數執行pap_func()方法,這一功能在數據預處理中修改dataset中元素是很實用。
參數:
def change_dtype(t): # 將類型修改成int32
return tf.cast(t,dtype=tf.int32)
dataset = tf.data.Dataset.range(3)
for i in dataset:
print(i)
tf.Tensor(0, shape=(), dtype=int64) tf.Tensor(1, shape=(), dtype=int64) tf.Tensor(2, shape=(), dtype=int64)
dataset_map = dataset.map(change_dtype)
for i in dataset_map:
print(i)
tf.Tensor(0, shape=(), dtype=int32) tf.Tensor(1, shape=(), dtype=int32) tf.Tensor(2, shape=(), dtype=int32)
map_func的參數必須對應dataset中的元素類型,例如,若是dataset中元素是tuple,map_func能夠這麼定義:
def change_dtype_2(t1,t2):
return t1/10,tf.cast(t2,dtype=tf.int32)*(-1) # 第一位元素除以10,第二爲元素乘以-1
dataset = tf.data.Dataset.from_tensor_slices((tf.range(3),tf.range(3)))
dataset_map = dataset.map(change_dtype_2)
for i in dataset_map:
print(i)
(<tf.Tensor: id=347, shape=(), dtype=float64, numpy=0.0>, <tf.Tensor: id=348, shape=(), dtype=int32, numpy=0>) (<tf.Tensor: id=349, shape=(), dtype=float64, numpy=0.1>, <tf.Tensor: id=350, shape=(), dtype=int32, numpy=-1>) (<tf.Tensor: id=351, shape=(), dtype=float64, numpy=0.2>, <tf.Tensor: id=352, shape=(), dtype=int32, numpy=-2>)
(5)filter()
功能:對Dataset中每個執行指定過濾方法進行過濾,返回過濾後的Dataset對象
參數:
dataset = tf.data.Dataset.range(5)
def filter_func(t): # 過濾出值爲偶數的元素
if t % 2 == 0:
return True
else:
return False
dataset_filter = dataset.filter(filter_func)
for i in dataset_filter:
print(i)
tf.Tensor(0, shape=(), dtype=int64) tf.Tensor(2, shape=(), dtype=int64) tf.Tensor(4, shape=(), dtype=int64)
(6)shuffle()
功能:隨機打亂數據
參數:
dataset = tf.data.Dataset.range(5)
dataset_s = dataset.shuffle(1)
for i in dataset_s:
print(i)
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)
dataset_s = dataset.shuffle(5)
for i in dataset_s:
print(i)
tf.Tensor(0, shape=(), dtype=int64) tf.Tensor(4, shape=(), dtype=int64) tf.Tensor(1, shape=(), dtype=int64) tf.Tensor(2, shape=(), dtype=int64) tf.Tensor(3, shape=(), dtype=int64)
(7)repeat()
功能:對Dataset中的數據進行重複,以建立新的Dataset
參數:
dataset = tf.data.Dataset.range(3)
dataset_repeat = dataset.repeat(3)
for i in dataset_repeat:
print(i)
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)