【tf.keras】basic 06: Load Pandas.DataFrame and Image Data Set

本文是 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

說明:

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章