【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

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