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