1. 程式人生 > >TensorFlow之tfrecords檔案詳細教程

TensorFlow之tfrecords檔案詳細教程

1537250305768859.png

 

1537520241840992.png

歡迎關注“勇敢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()函式產生無限迴圈佇列時,應取消資料輸入與訓練程式的執行緒同步。

 

草樣年華.jpg