【TensorFlow-windows】keras接口——利用tensorflow的方法加载数据
前言
之前使用tensorflow和keras的時候,都各自有一套數據讀取方法,但是遇到一個問題就是,在訓練的時候,GPU的利用率忽高忽低,極大可能是由于訓練過程中讀取每個batch數據造成的,所以又看了tensorflow官方的加載數據方法。主要是利用了tf.data.Dataset這里面的一系列操作。
國際慣例,參考博客:
tensorflow官方加載數據集方法
官方文檔對應的代碼images.ipynb
官方文檔對應的代碼tf_records.ipynb
Tensorflow中創建自己的TFRecord格式數據集
TensorFlow全新的數據讀取方式:Dataset API入門教程
以tf.data優化訓練數據 Google開發者大會2018
Tensorflow數據預處理之tf.data.TFRecordDataset—TFRecords詳解\TFRecords圖像預處理
buffer_size的含義——Dataset.map , Dataset.prefetch and Dataset.shuffle
tensorflow 數據讀取總結—(直接供給數據(feeding) 從文件中以管線形式讀取數據 預加載數據)
復習
先復習一下之前博客中tensorflow和keras加載數據的方法
之前采用的tensorflow加載數據方法
詳細查看之前的這篇博客:
IMG_HEIGHT = 28 # 高 IMG_WIDTH = 28 # 寬 CHANNELS = 3 # 通道數 def read_images(dataset_path, batch_size):imagepaths, labels = list(), list()data = open(dataset_path, 'r').read().splitlines()for d in data:imagepaths.append(d.split(' ')[0])labels.append(int(d.split(' ')[1])) # 轉換為張量imagepaths = tf.convert_to_tensor(imagepaths, dtype=tf.string)labels = tf.convert_to_tensor(labels, dtype=tf.int32)# 建立TF隊列,打亂數據image, label = tf.train.slice_input_producer([imagepaths, labels],shuffle=True)# 讀取數據image = tf.read_file(image)image = tf.image.decode_jpeg(image, channels=CHANNELS)# 將圖像resize成規定大小image = tf.image.resize_images(image, [IMG_HEIGHT, IMG_WIDTH])# 手動歸一化image = image * 1.0/127.5 - 1.0# 創建batchinputX, inputY = tf.train.batch([image, label], batch_size=batch_size,capacity=batch_size * 8,num_threads=4)return inputX, inputY主要使用tf.train中的一系列操作
keras中自帶的數據加載方法
直接看官方文檔即可,我比較喜歡用下面這一系列方法從文件夾中讀取數據:
train_datagen = ImageDataGenerator(rescale=1./255,shear_range=0.2,zoom_range=0.2,horizontal_flip=True) test_datagen = ImageDataGenerator(rescale=1./255) train_generator = train_datagen.flow_from_directory('data/train',target_size=(150, 150),batch_size=32,class_mode='binary')validation_generator = test_datagen.flow_from_directory('data/validation',target_size=(150, 150),batch_size=32,class_mode='binary')只要使用flow_from_directory自動從文件夾中讀取數據。
利用tf.data直接讀取數據
數據集準備
下列所有實驗的數據都基于tensorflow提供的flower_photos數據集,才220M左右,下載地址戳這里。
我也上傳到網盤了:
鏈接:https://pan.baidu.com/s/13esPlx-fkKlXaegJNROPyw
提取碼:nv64
解壓后,得到五個文件夾,每個文件夾一類花朵。
代碼
首先引入必要的包:
import os import tensorflow as tf import pathlib import random import numpy as np讀取圖片數據
-
首先找到所有圖片和對應的路徑:
data_root = pathlib.Path('./dataset/flower_photos/') all_image_paths = list(data_root.glob('*/*')) all_image_paths = [str(path) for path in all_image_paths] random.shuffle(all_image_paths) image_count = len(all_image_paths) print('total image num:',image_count)#total image num: 3670 -
數據預處理:
class Process_img:def __init__(self,img_size):self.img_size = img_sizedef load_and_preprocess_image(self,img_path):image = tf.read_file(img_path)image = tf.image.decode_jpeg(image,channels=3)#進行各種圖像處理:裁剪、縮放、旋轉、亮度調整等image=tf.image.resize_images(image,self.img_size) #此處嚴格按照API文檔調用,tensorflow 版本不同使用的方法不同image /= 255.0return image -
數據預處理必須使用如下流程,先from_tensor_slice轉換成Dataset格式,然后使用map將數據丟到預處理函數中:
path_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)a=Process_img(img_size=[192,192]) image_ds = path_ds.map(a.load_and_preprocess_image,num_parallel_calls=tf.contrib.data.AUTOTUNE)這里有一個小技巧就是:本人不清楚map函數如何接受預處理函數所需傳遞的方法,有一個方法就是將預處理參數,比如image_size也丟到Dataset里面去,但是有點麻煩,這樣做from_tensor_slice里面的參數有點長,還不如初始化一個對象,存儲預處理所需參數了,清晰易懂。
讀取標簽數據
-
先獲取標簽,因為路徑中文件名的上級文件夾就是標簽,所以可以:
label_names = sorted(item.name for item in data_root.glob('*/') if item.is_dir()) print('label names:',label_names) #label names: ['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips'] -
再將標簽轉換為int型索引:
#將標簽轉換為索引值 label_to_index = dict((name,index) for index,name in enumerate(label_names)) print('label corresponding index:',label_to_index) -
然后獲取到所有圖像對應的標簽:
all_image_labels = [label_to_index[pathlib.Path(path).parent.name] for path in all_image_paths]驗證一下看看:
print('first 10 sample path and labels:') for i in range(0,10):print('{0}\t {1}'.format(all_image_paths[i],all_image_labels[i])) ''' first 10 sample path and labels: dataset\flower_photos\roses\5799616059_0ffda02e54.jpg 2 dataset\flower_photos\roses\22385375599_1faf334f5d_n.jpg 2 dataset\flower_photos\sunflowers\6627521877_6e43fb3c49_m.jpg 3 dataset\flower_photos\dandelion\3465599902_14729e2b1b_n.jpg 1 dataset\flower_photos\roses\4267024012_295e7141a3_n.jpg 2 dataset\flower_photos\dandelion\23414449869_ee849a80d4.jpg 1 dataset\flower_photos\tulips\4418204816_018375acd0_m.jpg 4 dataset\flower_photos\dandelion\9517326597_5d116a0166.jpg 1 dataset\flower_photos\dandelion\7197581386_8a51f1bb12_n.jpg 1 dataset\flower_photos\dandelion\425800274_27dba84fac_n.jpg 1 ''' -
同樣將標簽也轉換成Dataset格式:
label_ds = tf.data.Dataset.from_tensor_slices(tf.cast(all_image_labels,tf.int64))
組合數據
因為后續需要打亂和分批,所以需要將圖像與標簽對應打包對應好,后面一起變換
img_label_ds = tf.data.Dataset.zip((image_ds,label_ds))打亂、重復數據、分批,詳細解釋可以查看這里,我們只看如何使用:
batch_size = 32 ds = ds.cache() ds=img_label_ds.shuffle(buffer_size=image_count) ds=ds.repeat() ds=ds.batch(batch_size) ds = ds.prefetch(buffer_size=tf.contrib.data.AUTOTUNE)【注】不加cache也行,但是官方文檔說加了能提高數據喂進內存的性能。prefetch在官方文檔中說的是在訓練時將數據喂到batch里面。
訓練
使用tf.keras里面的mobileNetV2模型微調
預處理
因為mobileNetV2要求輸入數據范圍在(?1,?1)(-1,-1)(?1,?1),所以我們還要做一次預處理:
# 把數據由(0,1)轉換為(-1,1) def change_range(image,label):return 2*image-1,label keras_ds=ds.map(change_range)【注】可以發現,數據變成Dataset格式以后,各種預處理都得用map映射到處理函數。
載入模型并訓練
去掉mobileNet的尾巴,是否使用imagenet的權重與訓練,取決于weights是否None
mobile_net = tf.keras.applications.MobileNetV2(input_shape=(192,192,3),include_top=False,weights=None)接個全連接做分類:
model = tf.keras.Sequential([mobile_net,tf.keras.layers.GlobalAveragePooling2D(),tf.keras.layers.Dense(5,activation='softmax') ])看看網絡結構
model.summary() ''' _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= mobilenetv2_1.00_192 (Model) (None, 6, 6, 1280) 2257984 _________________________________________________________________ global_average_pooling2d (Gl (None, 1280) 0 _________________________________________________________________ dense (Dense) (None, 5) 6405 ================================================================= Total params: 2,264,389 Trainable params: 2,230,277 Non-trainable params: 34,112 _________________________________________________________________ '''編譯模型:
model.compile(optimizer=tf.train.AdamOptimizer(),loss=tf.keras.losses.sparse_categorical_crossentropy,metrics=["accuracy"])開始訓練
steps_per_epoch = int(np.ceil(len(all_image_paths)/batch_size)) model.fit(keras_ds,epochs=1000,steps_per_epoch=steps_per_epoch) ''' Epoch 1/1000 115/115 [==============================] - 172s 1s/step - loss: 1.3721 - acc: 0.4427 Epoch 2/1000 115/115 [==============================] - 155s 1s/step - loss: 1.1061 - acc: 0.5582 Epoch 3/1000 115/115 [==============================] - 150s 1s/step - loss: 0.9562 - acc: 0.6190 Epoch 4/1000 115/115 [==============================] - 148s 1s/step - loss: 0.8750 - acc: 0.6617 Epoch 5/1000 115/115 [==============================] - 223s 2s/step - loss: 0.8136 - acc: 0.6927 Epoch 6/1000 115/115 [==============================] - 148s 1s/step - loss: 0.7368 - acc: 0.7201 Epoch 7/1000 115/115 [==============================] - 148s 1s/step - loss: 0.6718 - acc: 0.7582 Epoch 8/1000 115/115 [==============================] - 148s 1s/step - loss: 0.6206 - acc: 0.7682 Epoch 9/1000 115/115 [==============================] - 148s 1s/step - loss: 0.5699 - acc: 0.7905 Epoch 10/1000 115/115 [==============================] - 147s 1s/step - loss: 0.5368 - acc: 0.8041 Epoch 11/1000 115/115 [==============================] - 147s 1s/step - loss: 0.4938 - acc: 0.8190 Epoch 12/1000 115/115 [==============================] - 148s 1s/step - loss: 0.4456 - acc: 0.8372 Epoch 13/1000 115/115 [==============================] - 147s 1s/step - loss: 0.4257 - acc: 0.8429 Epoch 14/1000 115/115 [==============================] - 149s 1s/step - loss: 0.3856 - acc: 0.8573 ....... '''利用tf.data轉成tfrecord再載入
比較喜歡的方法就是跟caffe一樣,先做數據集,訓練的時候讀取,tensorflow中建議的存儲格式就是tfrecord
制作數據集
導入對應包:
import os import tensorflow as tf import pathlib import random import numpy as np獲取圖像數據路徑:
data_root = pathlib.Path('./dataset/flower_photos/') all_image_paths = list(data_root.glob('*/*')) all_image_paths = [str(path) for path in all_image_paths] random.shuffle(all_image_paths) image_count = len(all_image_paths) print('total image num:',image_count)獲取圖像對應標簽:
label_names = sorted(item.name for item in data_root.glob('*/') if item.is_dir()) label_to_index = dict((name,index) for index,name in enumerate(label_names)) all_image_labels = [label_to_index[pathlib.Path(path).parent.name] for path in all_image_paths]把圖像與標簽打包:
image_labels = zip(all_image_paths,all_image_labels)按照tensorflow的方法將圖像和標簽做成tfrecord格式數據集:
def _bytes_feature(value):"""Returns a bytes_list from a string / byte."""return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))def _float_feature(value):"""Returns a float_list from a float / double."""return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))def _int64_feature(value):"""Returns an int64_list from a bool / enum / int / uint."""return tf.train.Feature(int64_list=tf.train.Int64List(value=[value])) def image_example(image_string, label):feature = {'label': _int64_feature(label),'image_raw': _bytes_feature(image_string)}return tf.train.Example(features=tf.train.Features(feature=feature)) with tf.python_io.TFRecordWriter('images.tfrecords') as writer:for filename, label in image_labels:image_string = open(filename, 'rb').read()tf_example = image_example(image_string, label)writer.write(tf_example.SerializeToString())上面需要注意的就是image_example里面的feature里面存的內容,你可以自己定義一些其它的,比如圖像寬高之類的,后續讀取的時候可以通過鍵值獲取對應值,這里只存了必須的圖像字節和標簽。其余的函數干啥的,別問,用之就對了。
讀取數據
讀取必要包:
import os import tensorflow as tf import pathlib import random import numpy as np讀取tfrecord對應的數據:
raw_image_dataset = tf.data.TFRecordDataset('images.tfrecords')# Create a dictionary describing the features. image_feature_description = {'label': tf.FixedLenFeature([], tf.int64),'image_raw': tf.FixedLenFeature([], tf.string), }def _parse_image_function(example_proto):# Parse the input tf.Example proto using the dictionary above.example = tf.parse_single_example(example_proto, image_feature_description)image = tf.image.resize_images(tf.image.decode_jpeg(example['image_raw'],channels=3),[192,192])image/=255.0label = example['label']return image,labelparsed_image_dataset = raw_image_dataset.map(_parse_image_function) parsed_image_dataset流程基本就是使用tf.data.TFRecordDataset載入tfrecord數據,然后取對應存儲的信息,如圖像與標簽。還可以來一波預處理,當然還是利用map將數據丟到預處理函數中。
接著就是打亂、分批、重復
train_data = parsed_image_dataset.shuffle(buffer_size=100) train_data = train_data.batch(8) train_data = train_data.repeat() train_data = train_data.prefetch(buffer_size=tf.contrib.data.AUTOTUNE) print(train_data)關于buffer_size的說明,戳這里
訓練
跟前面沒啥區別:
mobile_net = tf.keras.applications.MobileNetV2(input_shape=(192,192,3),include_top=False,weights=None)model = tf.keras.Sequential([mobile_net,tf.keras.layers.GlobalAveragePooling2D(),tf.keras.layers.Dense(5,activation='softmax') ])model.compile(optimizer=tf.train.AdamOptimizer(),loss=tf.keras.losses.sparse_categorical_crossentropy,metrics=["accuracy"])model.fit(train_data,epochs=1000,steps_per_epoch=1000) ''' Epoch 1/1000 1000/1000 [==============================] - 356s 356ms/step - loss: 1.2820 - acc: 0.4885 Epoch 2/1000 1000/1000 [==============================] - 358s 358ms/step - loss: 1.0426 - acc: 0.5928 Epoch 3/1000 1000/1000 [==============================] - 355s 355ms/step - loss: 0.9630 - acc: 0.6330 Epoch 4/1000 1000/1000 [==============================] - 354s 354ms/step - loss: 0.9110 - acc: 0.6524 Epoch 5/1000 1000/1000 [==============================] - 354s 354ms/step - loss: 0.8589 - acc: 0.6771 Epoch 6/1000 1000/1000 [==============================] - 355s 355ms/step - loss: 0.7635 - acc: 0.7152 Epoch 7/1000 1000/1000 [==============================] - 355s 355ms/step - loss: 0.6983 - acc: 0.7406 Epoch 8/1000 1000/1000 [==============================] - 354s 354ms/step - loss: 0.6482 - acc: 0.7632 Epoch 9/1000 1000/1000 [==============================] - 354s 354ms/step - loss: 0.5834 - acc: 0.7769 Epoch 10/1000 1000/1000 [==============================] - 357s 357ms/step - loss: 0.5439 - acc: 0.7995 '''訓練結果和上面直接從文件夾讀取的結果差不多,說明流程沒問題。
有個坑
讀取tfrecord數據集的這句話:
raw_image_dataset = tf.data.TFRecordDataset('images.tfrecords')丫的竟然不核對這個tfrecords文件是否存在,或者是否為空數據,不信你隨便改個名,這句話還能運行,真的是醉了。程序model.fit會直接進入死機狀態,你也不知道它是在讀數據,還是崩了。
所以我們在進行下列一頓操作以后:
raw_image_dataset = tf.data.TFRecordDataset('images11.tfrecords')# Create a dictionary describing the features. image_feature_description = {'label': tf.FixedLenFeature([], tf.int64),'image_raw': tf.FixedLenFeature([], tf.string), }def _parse_image_function(example_proto):# Parse the input tf.Example proto using the dictionary above.example = tf.parse_single_example(example_proto, image_feature_description)image = tf.image.resize_images(tf.image.decode_jpeg(example['image_raw'],channels=3),[192,192])image/=255.0label = example['label']return image,labelparsed_image_dataset = raw_image_dataset.map(_parse_image_function) parsed_image_datasettrain_data = parsed_image_dataset.shuffle(buffer_size=100) train_data = train_data.batch(8) train_data = train_data.repeat() train_data = train_data.prefetch(buffer_size=tf.contrib.data.AUTOTUNE) print(train_data)必須得驗證一下這個train_data里面是不是有數據,圖片與標簽是否對應。
驗證方法,是迭代輸出
iterator = train_data.make_one_shot_iterator() one_element = iterator.get_next() with tf.Session() as sess:try:while True:a=sess.run(one_element)print(a[0].shape)#(8, 192, 192, 3)print(a[1].shape)#(8,)breakexcept tf.errors.OutOfRangeError:print('end!')我們把數據保存在a里面,同時從a的shape可以看出來,存了圖片和標簽,而且存儲的是一個batch_size大小的數據。接下來顯示一下:
label_name=['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips'] print((a[0][0]).shape) show_idx = 0 plt.imshow(a[0][show_idx]) plt.title(label_name[a[1][show_idx]])有圖片輸出就說明沒問題了。
后記
可以發現這一系列的數據讀取操作是可以封裝在一起的,這里先將實驗驗證用的ipynb放出來:
- 直接使用tf.data遍歷文件夾訓練:
鏈接:https://pan.baidu.com/s/1YSWLVfmfU2brnLI0uRyljg
提取碼:n2ht - 制作tfrecord數據集:
鏈接:https://pan.baidu.com/s/1HGH66klAl5zECEznRPhV7g
提取碼:w6ss - 讀取tfrecord數據集并訓練:
鏈接:https://pan.baidu.com/s/1Jyyu2u96xLkomJhKT-AgIA
提取碼:mnkj
為了方便后續使用,直接寫一個現成的Python腳本,以后直接傳入路徑,輸出可以直接訓練的數據參數。
-
直接使用tf.data遍歷文件夾訓練:
鏈接:https://pan.baidu.com/s/1yb0EoBXzhyQEA-BO3i1Fcg
提取碼:2706 -
制作tfrecord數據集:
鏈接:https://pan.baidu.com/s/1Jw2LDKGeTrMKaItDHqe3dA
提取碼:znfy -
讀取tfrecord數據集訓練:鏈接:https://pan.baidu.com/s/1rRKx9tP8jrAZhzXjIZNNTQ
提取碼:sa2t
總結
以上是生活随笔為你收集整理的【TensorFlow-windows】keras接口——利用tensorflow的方法加载数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 神回避2第19关怎么过 神回避2第19关
- 下一篇: 王者荣耀最新墨子大神出装 墨子高胜率六神