TensorFlow之tfrecords檔案詳細教程
歡迎關注“勇敢AI”公眾號,更多python學習、資料分析、機器學習、深度學習原創文章與大家分享,還有更多電子資源、教程、資料集下載。勇敢AI,一個專注於人工智慧AI的公眾號。
==================================================================================
全文摘要
當前是資料爆炸的時代,深度學習與大資料更是相輔相成,在使用TensorFlow構建深度學習模型的時候,可能會涉及到海量的資料,可能會用到數G、T甚至P級別的訓練資料,很顯然,要將如此龐大的資料一次性載入進記憶體,顯然當前的硬體條件還遠遠不能夠。幸好TensorFlow也提供了非常有好的大資料處理方式。
一、TF資料讀取的方式
1.1資料讀取方式
對於深度學習而言,因為資料量龐大,在提高運算能力的同時,更高效的處理資料I/O對於提高整體的效能也非常重要。在使用TensorFlow訓練模型的時候,有三種資料載入的方式
(1)使用Python程式碼為TensorFlow提供資料
(2)預先載入資料,將需要訓練的資料以變數的形式預先儲存在計算機的記憶體中
(3)利用管道從檔案中讀取資料
對於資料較小的情況,直接將資料載入到計算機記憶體,然後每次取一個batch放進網路裡面加以訓練,問題,但是對於大資料而言,一方面如果直接全部將資料放進記憶體肯定不可能;另一方面,我可以每次需要多少資料就從硬碟中讀取,但是這樣做的後果就是頻繁的I/O操作,使得執行效率大打折扣。
1.2 小資料的常用資料格式
對於比較小的資料,我們可以直接載入進記憶體,對於這種級別的數量,常用的一些資料格式有以下幾種:
CSV格式;
npy npz格式:這是numpy的資料儲存格式
pkl: 這是python的序列化儲存格式
hdf: 以HDF5為最新的系列
1.3 大資料的專用資料格式
對於大資料而言,TensorFlow推薦使用自帶的tfrcords檔案。tfrecords檔案是以二進位制進行儲存的,適合以序列的方式讀取大批量的資料。
對於訓練資料而言,我們可以編寫程式將普通的訓練資料儲存為tfrecords資料格式。
二、tfrecords檔案的建立
2.1 建立思路及步驟
tfrecords的建立很簡單,就是將每一組“樣本資料”組裝成一個Example物件,這個物件是遵循protocol buffer協議的;然後將這個Example物件序列化成字串;最後用tf.python_io.TFRecordWriter寫入相應的tfrecords檔案即可。大致步驟如下:
第一步:獲取原始資料,一般使用numpy或者是pandas進行一些處理
第二步:使用tf.python_io.TFRecordWriter類定義一個tfrecords檔案
第三步:將每一條樣本資料按照相應的特徵組織好,即將樣本資料組織成Example的過程,這是整個操作流程的核心部分,相對較複雜
第四步:將組織好的Example寫入進tfrecords檔案,並關閉tfrecords檔案即可
下面以titanic資料為例加以說明:因為titanic資料是一個CSV檔案,裡面有不少空餘的,本文只選擇前面的50條資料,並且已經填充了空格(資料的預處理)
2.2 titanic例項
import tensorflow as tf
import pandas as pd
#第一步:獲取原始資料
data=pd.read_csv('Titanic dataset/titanic_train_01.csv')
print(data.shape)
#第二步:定義record檔案
tfrecord_file='titanic_train.tfrecords'
writer=tf.python_io.TFRecordWriter(tfrecord_file)
#第三步:每一次寫入一條樣本記錄
for i in range(len(data)):
features=tf.train.Features(feature={'Age':tf.train.Feature(float_list=tf.train.FloatList(value=[data['age'][i]])),
'Sex':tf.train.Feature(int64_list=tf.train.Int64List(value=[1 if data['sex'][i]=='male' else 0])),
'Pclass':tf.train.Feature(int64_list=tf.train.Int64List(value=[data['pclass'][i]])),
'Parch':tf.train.Feature(int64_list=tf.train.Int64List(value=[data['parch'][i]])),
'Sibsp':tf.train.Feature(int64_list=tf.train.Int64List(value=[data['sibsp'][i]])),
'Fare':tf.train.Feature(float_list=tf.train.FloatList(value=[data['fare'][i]])),
'Survived':tf.train.Feature(int64_list=tf.train.Int64List(value=[data['survived'][i]]))
})
#每一條樣本的特徵,將一系列特徵組織成一條樣本
example=tf.train.Example(features=features)
#將每一條樣本寫入到tfrecord檔案
writer.write(example.SerializeToString())
#第四步:寫入後關閉檔案
writer.close()
print('寫入tfrecords檔案完畢!')
核心函式解析:
(1)Features()
features=tf.train.Features(feature={*****})
該函式傳入一個關鍵字引數feature,表示的是一系列的特徵。
(2)Fearture()
'Age':tf.train.Feature(float_list=tf.train.FloatList(value=[data['age'][i]]))
該函式是對應於一系列特徵中的每一個特徵,它有三個可選的關鍵字引數,float_list、int64_list、byteslist分別對應於取值為浮點數的特徵、整數的特徵、二進位制數的特徵。
(3)FloatList()、Int64List()、BytesList()
float_list=tf.train.FloatList(value=[data['age'][i]]))
這三個函式是將每個特徵進行轉化的函式,分別對應特徵的取值為浮點數、整數、二進位制數。這裡有一個注意事項,這三個函式都有一個命名引數value,這個引數的的賦值一定要使用value=【data】的方式,這裡的中括號不能丟哦!
(4)Example()
example=tf.train.Example(features=features)
這個函式就是核心,是將上面組織好的一系列特徵進行包裝,包裝成一個Example物件,然後將該物件寫入到tfrecords檔案,關閉該檔案即可。
Example補充:
使用TFRecord時,一般以tf.train.Example和tf.train.SequenceExample作為基本單位來進行資料讀取。
(1)Example()
example=tf.train.Example(features=features)
tf.train.Example一般用於數值、影象等有固定大小的資料,同時使用tf.train.Feature指定每個記錄各特徵的名稱和資料型別,用法如下:
tf.train.Example(features=tf.train.Features(feature={
'height': tf.train.Feature(int64_list=tf.train.Int64List(value=[height])),
'width' : tf.train.Feature(int64_list=tf.train.Int64List(value=[width])),
'depth' : tf.train.Feature(int64_list=tf.train.Int64List(value=[depth])),
'image' : tf.train.Feature(bytes_list=tf.train.BytesList(value=[image]))
}))
(2)SequenceExample()
tf.train.SequenceExample一般用於文字、時間序列等沒有固定長度大小的資料,用法如下:
example = tf.train.SequenceExample()
# 通過context來指定資料量的大小
example.context.feature["length"].int64_list.value.append(len(data))
# 通過feature_lists來載入資料
words_list = example.feature_lists.feature_list["words"]
for word in words:
words_list.feature.add().int64_list.value.append(word_id(word))
2.3 mnist例項
上面是對titanic的CSV檔案進行的操作,那如果是影象資料呢,難道是對影象的每一個畫素進行Feature的轉換嗎?那麼當一個圖片很大的時候,畫素很多,這樣顯然不合理,對於影象的操作這裡就關鍵用到了BytesList去實現,下面以影象為例加以說明
本文沒有選擇很多的圖片,僅僅以三張圖片為例,在百度圖片下載任意三張圖片,因為大小不一樣,使用Photoshop將三張圖片簡單更改為500x500大小。
import tensorflow as tf
import pandas as pd
from PIL import Image
import numpy as np
#第一步:獲取原始資料,此處為原始影象
img1=Image.open('picture dataset\img1.jpg')
img2=Image.open('picture dataset\img2.jpg')
img3=Image.open('picture dataset\img3.jpg')
images=[img1,img2,img3]
print(img1.size,img2.size,img3.size)
label1=np.array([1,0,0])
label2=np.array([0,1,0])
label3=np.array([0,0,1])
labels=[label1,label2,label3]
#第二步:定義record檔案
tfrecord_file='picture_train.tfrecords'
writer=tf.python_io.TFRecordWriter(tfrecord_file)
#第三步:每一次寫入一條樣本記錄
for i in range(len(images)):
features=tf.train.Features(feature={'Picture':tf.train.Feature(bytes_list=tf.train.BytesList(value=[images[i].tobytes()])),
'Label':tf.train.Feature(bytes_list=tf.train.BytesList(value=[labels[i].tobytes()]))
})
#每一條樣本的特徵,將一系列特徵組織成一條樣本
example=tf.train.Example(features=features)
#將每一條樣本寫入到tfrecord檔案
writer.write(example.SerializeToString())
writer.close()
print('寫入tfrecords檔案完畢!')
執行上面的程式碼,已經有一個2198KB大小的picture_train.tfrecords檔案。上面的程式碼可以看出,對於影象資料,規則基本上是一模一樣的,區別在於資料的初始化處理,另外,圖片資料不是一個一個畫素進行存取的,需要將圖片以及獨熱編碼的標籤轉化為原生的bytes資料格式即可。
2.4 存為多個tfrecords檔案
通過前面兩個方法,我們知道可以把你想要的檔案或者記錄通過或多或少的方法轉為TFRecord格式.
那麼資料量很大的時候,你會發現,單個TFRecord檔案是非常非常大的,這對於硬碟是不小的負擔,所以,可以通過儲存多個TFRecord檔案來解決問題.其實儲存為多個tfrecords檔案並沒有新的操作,完全和上面一樣,只不過因為資料量巨大,需要對樣本進行劃分,然後分別儲存在不同的tfrecords檔案裡面即可
比如一共有30000張圖片,即30000個樣本,前面10000個儲存在picture_01.tfrecords檔案裡,中間10000個樣本儲存在picture_02.tfrecords檔案裡,最後10000組樣本儲存在picture_03.tfrecords檔案裡。
2.3.1 通過配置檔案
matplotlibrc是matplotlib resource configurations的簡稱。matplotlib的圖形配置方式有很多,主要是從以下三個方面進行配置的。
(1)通過配置檔案進行配置——檢視+設定
(2)通過rcParams['引數名']動態配置——查 看+設定
(3)通過matplotlib.rc()函式配置
本節總結
從上面的幾個例子可以看出,建立tfrecords檔案的步驟是比較簡單的,按照固定的格式組織資料,然後寫入進tfrecords檔案即可,資料是分層組織的,可以有外向內一次看成,Examples—>Example—>Features—>Feature(int64、float、bytes)
Next
三、tfrecords檔案的讀取
3.1 tfrecords檔案的簡單預覽
我們可以簡單的檢視一下我們所儲存的tfrecords檔案是否符合我們的預期,我們可以使用tf.train.Example.FromString()進行簡單的檢視,程式碼如下:
import tensorflow as tf
#確認tfrecord的內容
ex=next(tf.python_io.tf_record_iterator('titanic_train.tfrecords'))
print(tf.train.Example.FromString(ex))
上面程式的執行結果如下:
features {
feature {
key: "Age"
value {
float_list {
value: 30.0
}
}
}
feature {
key: "Fare"
value {
float_list {
value: 7.73330020904541
}
}
}
feature {
key: "Parch"
value {
int64_list {
value: 0
}
}
}
feature {
key: "Pclass"
value {
int64_list {
value: 3
}
}
}
feature {
key: "Sex"
value {
int64_list {
value: 0
}
}
}
feature {
key: "Sibsp"
value {
int64_list {
value: 0
}
}
}
feature {
key: "Survived"
value {
int64_list {
value: 1
}
}
}
}
從上面返回的結果可以檢視到儲存的特徵,特徵的資料型別,第一組樣本的特徵取值。
3.2 tfrecords檔案的載入
tfrecords檔案的讀取和載入是相對比較複雜的,本文也總結了幾個固定的步驟:
第一步:定義一個reader物件,和定義tfrecords檔案從哪裡來。
filename_queue=tf.train.string_input_producer(['titanic_train.tfrecords'])
reader = tf.TFRecordReader()
後面會解析這兩句話的含義
第二步:從tfrecords檔案中解析儲存的樣本資料格式
第三步:從樣本資料中一次性讀取一個批次的資料,即填充滿一個batch。因為在深度學習進行訓練的時候,往往都是一次訓練多少組,以多少組為一個batch,所以需要包裝。
上面三個步驟的核心函式解析:
第一步:
filename_queue=tf.train.string_input_producer(['titanic_train.tfrecords'])
它告訴我們tfrecords檔案從哪裡來,注意,引數裡面的中括號不能丟!
reader = tf.TFRecordReader()
定義一個reader物件,該物件負責從tfrecords檔案中讀取。
_,serialized_example=reader.read(filename_queue)
它返回的是(key,value)的元祖形式。上面的serialized_example是無法直接檢視的,需要去按照特徵進行解析。
第二步:解析資料
featurestf.parse_single_example(serialized_example,features={...})
將資料的特徵解析出來
第三步:每次將資料包裝成一個batch。
tf.train.batch([age,sex,pclass,parch,sibsp,fare,label],
batch_size=16,
capacity=500)
第一個引數就是特徵的名稱,中括號不能掉,第二個是batch_size的大小,這個capacity後面會解釋到。
但是上面的步驟完成之後,我還只能夠看到每一個特徵的維度資訊,還不能夠獲取具體的數值,要想獲取具體的數值,依然需要在會話物件Session裡面進行檢視,而且步驟分為以下幾步(續接前面):
第四步:首先在session裡面建立Coordinator物件,他負責實現資料輸入執行緒的同步,實現如下
coord = tf.train.Coordinator()
第五步:啟動佇列
threads=tf.train.start_queue_runners(sess=sess, coord)
第六步:這裡就可以檢視樣本資料,將獲取的樣本資料“喂”給網路進行訓練。
第七步:執行緒同步
coord.request_stop()
coord.join(threads=threads)
3.3 獲取titanic中的age的資料
按照前面的步驟,程式碼如下:
import tensorflow as tf
#第一步:定義reader物件以及tfrecords檔案的輸入部分
filename_queue = tf.train.string_input_producer(['titanic_train.tfrecords'])
reader = tf.TFRecordReader()
#第二步:使用reader函式讀入tfrecords內容,它返回的是(key,value)
_, serialized_example = reader.read(filename_queue)
#print(serialized_example.shape)
#第三步:資料的解析parse
features = tf.parse_single_example(serialized_example,
features={'Age':tf.FixedLenFeature([],tf.float32),
'Sex':tf.FixedLenFeature([],tf.int64),
'Pclass':tf.FixedLenFeature([],tf.int64),
'Parch':tf.FixedLenFeature([],tf.int64),
'Sibsp':tf.FixedLenFeature([],tf.int64),
'Fare':tf.FixedLenFeature([],tf.float32),
'Survived':tf.FixedLenFeature([],tf.int64)
})
age=features['Age']
sex=features['Sex']
pclass=features['Pclass']
parch=features['Parch']
sibsp=features['Sibsp']
fare=features['Fare']
label=features['Survived']
#image = tf.reshape(image, [28, 28, 1])
#label = tf.reshape(label, [10])
#第三步:將樣本包裝成一個一個的batch
age,sex,pclass,parch,sibsp,fare,label = tf.train.batch([age,sex,pclass,parch,sibsp,fare,label],batch_size=16,capacity=500)
print(age.shape)#在這就可以檢視特徵的資料維度了,為(16,)因為batch_size為16
with tf.Session() as sess:
tf.global_variables_initializer().run()
#第四步
coord = tf.train.Coordinator()
#第五步:啟動佇列
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
'''第六步,這裡面就可以檢視資料,將資料“喂“給網路了 '''
age_=sess.run(age)
print(age_)
#第七步
coord.request_stop()
coord.join(threads=threads)
print('完結!')
所獲得的age_變數的結果為(16,)維度:
[30. 38. 30. 54. 40. 28. 19. 30. 22. 21. 27. 60. 56. 20. 16. 48.]
3.4 獲取儲存的圖片資料
import tensorflow as tf
import matplotlib.pyplot as plt
#第一步:定義reader物件以及tfrecords檔案的輸入部分
filename_queue = tf.train.string_input_producer(['picture_train.tfrecords'])
reader = tf.TFRecordReader()
#第二步:使用reader函式讀入tfrecords內容,它返回的是(key,value)
_, serialized_example = reader.read(filename_queue)
features = tf.parse_single_example(serialized_example,
features={'Picture':tf.FixedLenFeature([],tf.string),
'Label':tf.FixedLenFeature([],tf.string)
})
image = tf.decode_raw(features['Picture'], tf.float32) #需要解碼,因為不是單個的數值
label = tf.decode_raw(features['Label'], tf.float64)
image = tf.reshape(image, [500,500])
label = tf.reshape(label, [3])
#第三步:將樣本包裝成一個一個的batch
img,lab = tf.train.shuffle_batch([image,label], batch_size=3,capacity=32,min_after_dequeue=10)
print(img.shape) #形狀為(3,500,500)
print(lab.shape) #形狀為(3,3)
with tf.Session() as sess:
tf.global_variables_initializer().run()
coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
img_=sess.run(lab[1])
print(img_)
coord.request_stop()
coord.join(threads=threads)
print('完結!')
本節總結
tfrecords檔案的資料的讀取步驟基本上是大同小異的,上面給出了詳細的總結。需要注意的是,tfrecords檔案中的資料的檢視需要定義在session會話中。在session執行中,shuffle_batch和batch函式生成“一個batch的資料包”的過程是作為執行緒獨立執行的,資料輸入執行緒的掛起和執行時機由batch資料的生成函式控制的。shuffle函式指定記憶體儲存樣本數量的上限capacity和下限min_after_dequeue。當記憶體中的儲存的樣本數量大於上限capacity時,資料輸入執行緒掛起。反之,當樣本資料小於min_after_dequeue時,訓練程式掛起。函式start_queue_runners()開啟對應會話session的所有執行緒佇列並返回執行緒控制代碼。Coordinator類物件負責實現資料輸入執行緒的同步。當string_input_producer()函式產生無限迴圈佇列時,應取消資料輸入與訓練程式的執行緒同步。