本文是 tf.keras 系列教程的第六篇。介紹了 Keras 加載 Pandas DataFrame 格式的數據;keras 和 tf.data 加載圖片數據,並對比了二者的處理速度。
文章目錄
代碼環境:
python version: 3.7.6
tensorflow version: 2.1.0
安裝Pillow:
pip install Pillow
導入表要的包:
import tensorflow as tf
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os
import IPython.display as display
from PIL import Image # 需要先安裝 pip install Pillow
1. 讀取Pandas.DataFrame格式數據
1.1 準備數據
1.下載數據集用作測試:
csv_file = tf.keras.utils.get_file('heart.csv', 'https://storage.googleapis.com/applied-dl/heart.csv')
2.讀取數據:
# 使用pandas讀取爲 DataFrame格式
df = pd.read_csv(csv_file)
df.head()
3.查看數據類型:
df.dtypes
輸出:
age int64
sex int64
cp int64
trestbps int64
chol int64
fbs int64
restecg int64
thalach int64
exang int64
oldpeak float64
slope int64
ca int64
thal object
target int64
dtype: object
4.將thal列轉換爲標籤
df['thal'] = pd.Categorical(df['thal'])
df['thal'] = df.thal.cat.codes
df.head()
1.2 使用 tf.data.Dataset
加載數據
tf.data.Dataset.from_tensor_slices
可以從 Pandas.DataFrame
中讀取數據。 tf.data.Dataset
的優點是易於編寫使用,高效地讀取數據。
target = df.pop('target')
dataset = tf.data.Dataset.from_tensor_slices((df.values, target.values))
for feat, targ in dataset.take(5):
print ('Features: {}Target: {}'.format(feat, targ))
輸出:
Features: [ 63. 1. 1. 145. 233. 1. 2. 150. 0. 2.3 3. 0.
2. ]Target: 0
Features: [ 67. 1. 4. 160. 286. 0. 2. 108. 1. 1.5 2. 3.
3. ]Target: 1
Features: [ 67. 1. 4. 120. 229. 0. 2. 129. 1. 2.6 2. 2.
4. ]Target: 0
Features: [ 37. 1. 3. 130. 250. 0. 0. 187. 0. 3.5 3. 0.
3. ]Target: 0
Features: [ 41. 0. 2. 130. 204. 0. 2. 172. 0. 1.4 1. 0.
3. ]Target: 0
1.3 打亂數據並實現批處理
train_dataset = dataset.shuffle(len(df)).batch(1)
1.4 創新模型並訓練
def get_compiled_model():
model = tf.keras.Sequential([
tf.keras.layers.Dense(10, activation='relu'),
tf.keras.layers.Dense(10, activation='relu'),
tf.keras.layers.Dense(1)
])
model.compile(optimizer='adam',
loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
metrics=['accuracy'])
return model
訓練:
model = get_compiled_model()
model.fit(train_dataset, epochs=15)
2. 讀取圖片數據
2.1 準備數據
import pathlib
# # get_file() 默認情況下,URL中的文件origin下載到cache_dir ~/.keras,放在cache_subdir中datasets,並指定文件名fname。
# # 返回下載文件的路徑。
# data_dir = tf.keras.utils.get_file(fname='flower_photos', # 指定文件名
# origin='https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
# untar=True, #布爾值,是否應該解壓縮文件
# )
data_dir = 'C:/Users/34123/.keras/datasets/flower_photos'
data_dir = pathlib.Path(data_dir)
注意:get_file() 方法下載的數據集比較大時,下載速度很慢。直接點擊鏈接下載,然後放到指定緩存文件夾即可,要先解壓。
緩存文件默認路徑:
WindowsPath('C:/Users/34123/.keras/datasets/flower_photos')
統計圖片數量:
image_count = len(list(data_dir.glob('*/*.jpg')))
image_count # 3670
統計圖片類別:
CLASS_NAMES = np.array([item.name for item in data_dir.glob('*') if item.name != "LICENSE.txt"])
CLASS_NAMES
輸出:
array(['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips'],
dtype='<U10')
查看前三張圖片:
roses = list(data_dir.glob('roses/*'))
for image_path in roses[:3]:
display.display(Image.open(str(image_path)))
2.1 使用 tf.keras.preprocessing 加載數據
定義通用參數:
BATCH_SIZE = 32
IMG_HEIGHT = 224
IMG_WIDTH = 224
STEPS_PER_EPOCH = np.ceil(image_count/BATCH_SIZE)
1.壓縮圖片數據,將原始的uint8類型轉換爲[0,1]範圍內的float32類型:
image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255)
2.加載數據:
train_data_gen = image_generator.flow_from_directory(directory=str(data_dir),
batch_size=BATCH_SIZE,
shuffle=True,
target_size=(IMG_HEIGHT, IMG_WIDTH),
classes = list(CLASS_NAMES))
3.查看數據:
def show_batch(image_batch, label_batch):
plt.figure(figsize=(10,10))
for n in range(25):
ax = plt.subplot(5,5,n+1)
plt.imshow(image_batch[n])
plt.title(CLASS_NAMES[label_batch[n]==1][0].title())
plt.axis('off')
image_batch, label_batch = next(train_data_gen)
show_batch(image_batch, label_batch)
輸出:
2.2 使用 tf.data 加載數據
上面的keras.preprocessing方法很方便,但是有三個缺點:
- 加載速度慢。
- 缺乏細粒度的控制。
- 沒有與TensorFlow的其餘部分很好地集成。
1.鑑於以上缺點,使用tf.data加載數據。
list_ds = tf.data.Dataset.list_files(str(data_dir/'*/*'))
查看數據集合下的圖片路徑:
for f in list_ds.take(5):
print(f.numpy())
輸出:
b'C:\\Users\\34123\\.keras\\datasets\\flower_photos\\dandelion\\570127230_ce409f90f8_n.jpg'
b'C:\\Users\\34123\\.keras\\datasets\\flower_photos\\sunflowers\\7820398908_4316bbba45.jpg'
b'C:\\Users\\34123\\.keras\\datasets\\flower_photos\\tulips\\3238068295_b2a7b17f48_n.jpg'
b'C:\\Users\\34123\\.keras\\datasets\\flower_photos\\sunflowers\\20407896403_a50fef58ac_n.jpg'
b'C:\\Users\\34123\\.keras\\datasets\\flower_photos\\sunflowers\\14741812319_e1d32ffb84_n.jpg'
2.將數據轉換爲圖片和標籤一一對應的元組:
# 以下三個自定義函數實現將文件路徑轉換爲(img, label)元組
def get_label(file_path):
'''
該函數實現從根目錄獲取分類名
'''
# 將路徑按照定界符(sep,第二個參數)拆分爲組成路徑的元素,返回一個列表;
parts = tf.strings.split(file_path, os.path.sep)
# 圖片路徑格式爲:C:\Users\34123\.keras\datasets\flower_photos\daisy\...;
# 因此列表中導數第二個元素爲文件夾名稱,也是分類名稱;
return parts[-2] == CLASS_NAMES
def decode_img(img):
'''
該函數實現將圖片轉化爲3D tensor,並且調整圖片爲指定大小
'''
# 將JPEG編碼的圖像解碼爲3D uint8張量
img = tf.image.decode_jpeg(img, channels=3)
# 轉換image爲dtype,根據需要縮放其值到 [0,1] 範圍內.
img = tf.image.convert_image_dtype(img, tf.float32)
# 調整圖片到指定大小.
return tf.image.resize(img, [IMG_WIDTH, IMG_HEIGHT])
def process_path(file_path):
'''
該函數實現將指定路徑下的圖片組合爲(圖片,標籤)元組
'''
label = get_label(file_path)
# load the raw data from the file as a string
img = tf.io.read_file(file_path)
img = decode_img(img)
return img, label
說明:
-
Windows中文件路徑分隔符是 ‘’,Linux是 ‘/’。爲了讓代碼在不同的平臺上都能運行,os.sep 根據所處的平臺,自動採用相應的分隔符號。
-
tf.strings.split
:https://www.tensorflow.org/api_docs/python/tf/strings/split -
tf.io.decode_jpeg
:https://www.tensorflow.org/api_docs/python/tf/io/decode_jpeg -
tf.image.convert_image_dtype
:https://www.tensorflow.org/api_docs/python/tf/image/convert_image_dtype -
tf.data.Dataset.map()
:https://www.tensorflow.org/api_docs/python/tf/data/Dataset#map -
其參數:
num_parallel_calls
:tf.int32標量tf.Tensor,表示要並行異步處理的數字元素。如果未指定,則將按順序處理元素。如果設置爲tf.data.experimental.AUTOTUNE
,則根據可用的CPU動態設置並行調用的次數。
3.並行處理數據:
# 設置“ num_parallel_calls”,以便並行加載/處理多個圖像。
labeled_ds = list_ds.map(process_path, num_parallel_calls=tf.data.experimental.AUTOTUNE)
4.查看數據:
# tf.Dataset.take(count)方法實現從數據集中提取count個元素
for image, label in labeled_ds.take(1):
print("Image shape: ", image.numpy().shape)
print("Label: ", label.numpy())
5.打亂數據並實現批處理:
要使用此數據集訓練模型,需要對數據做如下處理:
- 隨機打亂數據(shuffle)
- 批處理數據(batch)
- 儘可能快的批處理數據
使用tf.data api可以輕鬆實現以上功能。
def prepare_for_training(ds, cache=True, shuffle_buffer_size=1000):
# 這是一個小數據集,只加載一次,並保存在內存中。
# 使用`.cache(filename)`緩存不適合內存的數據集 進行預處理。
if cache:
if isinstance(cache, str):
ds = ds.cache(cache)
else:
ds = ds.cache()
# 打亂數據
ds = ds.shuffle(buffer_size=shuffle_buffer_size)
# 重複數據
ds = ds.repeat()
# 每次去一個批次數據
ds = ds.batch(BATCH_SIZE)
#“prefetch”允許數據集在模型訓練時在後臺提取批。
ds = ds.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)
return ds
使用迭代器,實現批處理
train_ds = prepare_for_training(labeled_ds)
image_batch, label_batch = next(iter(train_ds))
6.查看批數據:
show_batch(image_batch.numpy(), label_batch.numpy())
3.加載性能對比
定義數據加載函數:
import time
default_timeit_steps = 1000
def timeit(ds, steps=default_timeit_steps):
start = time.time()
it = iter(ds)
for i in range(steps):
batch = next(it)
if i%10 == 0:
print('.',end='')
print()
end = time.time()
duration = end-start
print("{} batches: {} s".format(steps, duration))
print("{:0.5f} Images/s".format(BATCH_SIZE*steps/duration))
3.1 keras
# keras.preprocessing
timeit(train_data_gen)
輸出:
....................................................................................................
1000 batches: 95.04578709602356 s
336.67984 Images/s
3.2 tf.data
# tf.data
timeit(train_ds)
輸出:
....................................................................................................
1000 batches: 29.184619188308716 s
1096.46796 Images/s
3.3 .cache
# 性能提高的很大一部分來自使用.cache
uncached_ds = prepare_for_training(labeled_ds, cache=False)
timeit(uncached_ds)
輸出:
....................................................................................................
1000 batches: 52.8910551071167 s
605.01724 Images/s
這裏將 .cache
設置爲 False
,可以看出加載速度下降了 400 Images/s。
3.4 使用緩存加載數據
如果數據集無法一次加載到內存中,可使用緩存文件來保持性能。
filecache_ds = prepare_for_training(labeled_ds, cache="./flowers.tfcache")
timeit(filecache_ds)
輸出:
....................................................................................................
1000 batches: 102.5016496181488 s
312.19010 Images/s
參考:
https://www.tensorflow.org/tutorials/load_data/pandas_dataframe
https://www.tensorflow.org/tutorials/load_data/images