這三天的實作內容在第三天才有結果
這真的跟我想的不太一樣啊啊啊啊啊!!!!
好不容易有的結果最主要還歸功於第三天的 果斷放棄使用TFRecord
什麼鬼? 好爛! 我怎麼能甘心接受呢?
俗話說的好,在哪裡跌倒在哪裡 哭 (喂!
今天來個抖M上身,直接裸身踩TFRecord這個坑
讓我們開踩吧 !
TFRecord是一種專為Tensorflow打造的儲存文件的格式,就像是.csv
、.md
、.pkl
、.npy
、.json
、...等等,他們會有自己的儲存方式,但這種儲存資料的檔案不外乎有一個特點,資料型態大致就那幾種 :
.csv
、.md
或是.json
都是這種,就是用我們人能夠讀懂的資料儲存.pkl
、.npy
、TFRecord
那麼上面有提到TFRecord屬於一種binary的儲存格式,那就一定要來比一比binary格式常使用於python的比較吧!
格式 | 儲存速度 | 讀取速度 | 檔案大小 |
---|---|---|---|
.npy |
最快 |
最快 |
最小 |
.pkl |
中 | 最慢 |
最大 |
.tfrecord |
最慢 |
中 | 中 |
說起這個.npy
的儲存格式,只適用於python的numpy來儲存和讀取,但非常的穩,也是我最喜歡的格式,簡單好用還非常穩定,看著統計圖果不其然是這三者中的佼佼者。反觀雖然.pkl
是python的特殊儲存格式,但無論各方面都輸.npy
。但有別於npy檔,pkl在儲存非array型的資料比numpy強,較不侷限。所以綜合來說,在對Numpy array熟悉的人來說,選擇.npy
是比較好的,但在特殊的情況下,還是可以用.pkl
。
以上就是簡單的比較。
等...等一下,是不是忘了什麼? 我們大TFRecord呢?
沒啥好講的啊,就很中規中矩啊。
不...不是吧? 他不是這裡面最困難使用的嗎? 而且還是儲存最慢的耶~
照你這麼講,它豈不比.pkl
還爛嗎?
別急,我們先來談談TFRecord的讀取方式
比較於.npy
,一般情況如.npy
一樣,是將整個資料讀進內存
.npy
要被讀入,你的內存空間就要至少30GB。
請原諒我的美術+小畫家,這不是重點
當然內部的細節交由Google來做,我們只負責使用。
所以接下來讓我們來看看要怎麼用吧!
feature
的方式儲存陣列資料,且只有以下三種格式
string
byte
float
double
bool
enum
int32
uint32
int64
uint64
.SerializeToString
序列化為binary-stringtf.Example
建構範例feature,即可避免冗長的程式碼
# The number of observations in the dataset.
n_observations = int(1e4)
# Boolean feature, encoded as False or True.
feature0 = np.random.choice([False, True], n_observations)
# Integer feature, random from 0 to 4.
feature1 = np.random.randint(0, 5, n_observations)
# String feature
strings = np.array([b'cat', b'dog', b'chicken', b'horse', b'goat'])
feature2 = strings[feature1]
# Float feature, from a standard normal distribution
feature3 = np.random.randn(n_observations)
def serialize_example(feature0, feature1, feature2, feature3):
"""
Creates a tf.Example message ready to be written to a file.
"""
# Create a dictionary mapping the feature name to the tf.Example-compatible
# data type.
feature = {
'feature0': _int64_feature(feature0),
'feature1': _int64_feature(feature1),
'feature2': _bytes_feature(feature2),
'feature3': _float_feature(feature3),
}
# Create a Features message using tf.train.Example.
example_proto = tf.train.Example(features=tf.train.Features(feature=feature))
return example_proto.SerializeToString()
example_observation = []
serialized_example = serialize_example(False, 4, b'goat', 0.9876)
serialized_example
tf.Example
,則可以參考下方程式碼
serialize_example
後直接儲存成檔# Write the `tf.Example` observations to the file.
with tf.io.TFRecordWriter(filename) as writer:
for i in range(n_observations):
example = serialize_example(feature0[i], feature1[i], feature2[i], feature3[i])
writer.write(example)
filenames = [filename]
raw_dataset = tf.data.TFRecordDataset(filenames)
raw_dataset
take
讀出for raw_record in raw_dataset.take(1):
example = tf.train.Example()
example.ParseFromString(raw_record.numpy())
print(example)
tf.data
API中的使用tf.data是一個tensorflow的高階API,其方便和強大在於訓練過程中對輸入資料的優化!!
from_tensor_slices
方法,就是告訴API接下來資料要從tensorf片一片一片讀進來features_dataset = tf.data.Dataset.from_tensor_slices((feature0, feature1, feature2, feature3))
features_dataset
take
一個一個拿出來for f0,f1,f2,f3 in features_dataset.take(1):
print(f0)
print(f1)
print(f2)
print(f3)
tf.data.Dataset.map
使用
graph mode
下的tensorflow才能使用若是在
eager
模式下,則要透過tf.py_function
讓它在python中計算def tf_serialize_example(f0,f1,f2,f3): tf_string = tf.py_function( serialize_example, (f0,f1,f2,f3), # pass these args to the above function. tf.string) # the return type is `tf.string`. return tf.reshape(tf_string, ()) # The result is a scalar
tf_serialize_example(f0,f1,f2,f3)
的輸出就是tf.Tensor
了
tf.Tensor
後使用tf.data.Dataset.map
serialized_features_dataset = features_dataset.map(serialize_example)
# serialized_features_dataset = features_dataset.map(tf_serialize_example)
serialized_features_dataset
TFRecord
filename = 'test.tfrecord'
writer = tf.data.experimental.TFRecordWriter(filename)
writer.write(serialized_features_dataset)
filenames = [filename]
raw_dataset = tf.data.TFRecordDataset(filenames)
raw_dataset
tf.data
API功能(consuming_tfrecord_data)
eager
模式下進行(reading_a_tfrecord_file)filenames = tf.placeholder(tf.string, shape=[None])
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...) # Parse the record into tensors.
dataset = dataset.repeat() # Repeat the input indefinitely.
dataset = dataset.batch(32)
iterator = dataset.make_initializable_iterator()
# You can feed the initializer with the appropriate filenames for the current
# phase of execution, e.g. training vs. validation.
# Initialize `iterator` with training data.
training_filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
sess.run(iterator.initializer, feed_dict={filenames: training_filenames})
# Initialize `iterator` with validation data.
validation_filenames = ["/var/data/validation1.tfrecord", ...]
sess.run(iterator.initializer, feed_dict={filenames: validation_filenames})
Dataset.map()
(preprocessing_data_with_datasetmap)我辛辛苦苦寫了許久,但你們肯定都只看這邊 嗚嗚嗚
python
tf.data
graph
modeeager
modetf.Example
序列副程式
(範例的serialize_example
function)TFRecord | python | tf.data eager | tf.data graph | 備註 |
---|---|---|---|---|
write | 用for透過序列副程式 寫入 |
透過tf.py_function 轉換為序列,map 導向序列副程式 ,透過yield 生成序列資料集,使用TFRecordWriter 寫入 |
map 導向序列副程式 ,透過yield 生成序列資料集,使用TFRecordWriter 寫入 |
|
read | 透過ParseFromString 解碼,使用for和take 讀取 |
TFRecordDataset 導入,直接透過take 讀取 |
TFRecordDataset 導入,map 導向_parse_function ,透過take 讀取 |
tf.data 只讀取:consuming_tfrecord_data、tf.data 讀取且要顯示:reading_a_tfrecord_file、多通道資料:preprocessing_data_with_datasetmap |
我前幾篇實作的確有踩到坑,總之我認為
tf.data
,最主要原因我們有時候希望shuffle資料或是一些設定。補充一下,表格中整理的tf.data
讀取資料都來自讀取且要顯示
那個網址,但實際貼的程式碼來自只讀取
的方法
我原本以為沒有實作的一天是很輕鬆的,但我錯了Orz
Using TFRecords and tf.Example
Importing Data