TensorFlow深度学习:5.1.数据管道Dataset

数据管道Dataset

  • 1.构建数据管道
    • 1.1.从Numpy array构建数据管道
    • 1.2.从 Pandas DataFrame构建数据管道
    • 1.3.从Python generator构建数据管道
    • 1.4.从csv文件构建数据管道
    • 1.5 从文本文件构建数据管道
    • 1.6.从文件路径构建数据管道
    • 1.7.从tfrecords文件构建数据管道
  • 2.应用数据转换
    • 2.1.map和flat_map:
    • 2.2.interleave() :
    • 2.3.filter过滤掉某些元素:
    • 2.4.zip和condatenate
    • 2.5.reduce
    • 2.6.batch和padded_batch
    • 2.7.shuffle
    • 2.8.repeat
    • 2.9.shard
    • 2.10.take
  • 3.提升管道性能
    • 3.1.prefetch
    • 3.2.interleave
    • 3.3.map
    • 3.4.cache
    • 3.5.map-batch

1.构建数据管道

可以从 Numpy array, Pandas DataFrame, Python generator, csv文件, 文本文件, 文件路径, tfrecords文件等方式构建数据管道。
其中通过Numpy array, Pandas DataFrame, 文件路径构建数据管道是最常用的方法

1.1.从Numpy array构建数据管道

import tensorflow as tf
import numpy as np 
from sklearn import datasets 
iris = datasets.load_iris()

ds1 = tf.data.Dataset.from_tensor_slices((iris["data"],iris["target"]))

1.2.从 Pandas DataFrame构建数据管道

import tensorflow as tf
from sklearn import datasets 
import pandas as pd
iris = datasets.load_iris()
#pandas.DataFrame( data, index, columns, dtype, copy)
#data源数据,index每一行得索引名称,columns每一列得索引名称
dfiris = pd.DataFrame(iris["data"],columns = iris.feature_names)
#to_dict 可以对DataFrame类型的数据进行转换
ds2 = tf.data.Dataset.from_tensor_slices((dfiris.to_dict("list"),iris["target"]))

1.3.从Python generator构建数据管道

import tensorflow as tf
from matplotlib import pyplot as plt 
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# 定义一个从文件中读取图片的generator
#class_mode:"categorical"会返回2D的one-hot编码标签
#"binary"返回1D的二值标签."sparse"返回1D的整数标签
image_generator = ImageDataGenerator(rescale=1.0/255).flow_from_directory(
                    "./data/cifar2/test/",
                    target_size=(32, 32),
                    batch_size=20,
                    class_mode='binary')
#classes: 为子文件夹的列表;class_indices可获得文件夹名与类的序号的对应字典
classdict = image_generator.class_indices
print(classdict)

def generator():
    for features,label in image_generator:
        yield (features,label)

ds3 = tf.data.Dataset.from_generator(generator,
    output_types=(tf.float32,tf.int32))

1.4.从csv文件构建数据管道

ds4 = tf.data.experimental.make_csv_dataset(
      file_pattern = ["./data/titanic/train.csv","./data/titanic/test.csv"],
      batch_size=3, 
      label_name="Survived",
      na_value="",
      num_epochs=1,
      ignore_errors=True)
  • CSV 文件的每列都会有一个列名。dataset 的构造函数会自动识别这些列名。如果文件的第一行不包含列名,那么需要将列名通过字符串列表传给函数中得column_names=
  • 如果我们要有选择地加载csv文件中地某些列作为训练地属性,可以使用make_cvs_dataset函数中地select_columns参数进行指定:select_columns = columns_to_use
  • 如果cvs文件列中包含我们预测地类别属性,需要使用make_cvs_dataset函数中地label_name进行指定

1.5 从文本文件构建数据管道

ds5 = tf.data.TextLineDataset(
    filenames = ["./data/titanic/train.csv","./data/titanic/test.csv"]
    ).skip(1) #略去第一行header

1.6.从文件路径构建数据管道

ds6 = tf.data.Dataset.list_files("./data/cifar2/train/*/*.jpg")

1.7.从tfrecords文件构建数据管道

import os
import numpy as np

# inpath:原始数据路径 outpath:TFRecord文件输出路径
def create_tfrecords(inpath,outpath): 
    writer = tf.io.TFRecordWriter(outpath)
    dirs = os.listdir(inpath)
    for index, name in enumerate(dirs):
        class_path = inpath +"/"+ name+"/"
        for img_name in os.listdir(class_path):
            img_path = class_path + img_name
            img = tf.io.read_file(img_path)
            #img = tf.image.decode_image(img)
            #img = tf.image.encode_jpeg(img) #统一成jpeg格式压缩
            example = tf.train.Example(
               features=tf.train.Features(feature={
                    'label': tf.train.Feature(int64_list=tf.train.Int64List(value=[index])),
                    'img_raw': tf.train.Feature(bytes_list=tf.train.BytesList(value=[img.numpy()]))
               }))
            writer.write(example.SerializeToString())
    writer.close()
    
create_tfrecords("./data/cifar2/test/","./data/cifar2_test.tfrecords/")

2.应用数据转换

Dataset数据结构应用非常灵活,因为它本质上是一个Sequece序列,其每个元素可以是各种类型,例如可以是张量,列表,字典,也可以是Dataset。

  • Dataset包含了非常丰富的数据转换功能。
  • map: 将转换函数映射到数据集每一个元素。
  • flat_map: 将转换函数映射到数据集的每一个元素,并将嵌套的Dataset压平。
  • interleave: 效果类似flat_map,但可以将不同来源的数据夹在一起。
  • filter: 过滤掉某些元素。
  • zip: 将两个长度相同的Dataset横向铰合。
  • concatenate: 将两个Dataset纵向连接。
  • reduce: 执行归并操作。
  • batch : 构建批次,每次放一个批次。比原始数据增加一个维度。 其逆操作为unbatch。
  • padded_batch: 构建批次,类似batch, 但可以填充到相同的形状。
  • window :构建滑动窗口,返回Dataset of Dataset.
  • shuffle: 数据顺序洗牌。
  • repeat: 重复数据若干次,不带参数时,重复无数次。
  • shard: 采样,从某个位置开始隔固定距离采样一个元素。
  • take: 采样,从开始位置取前几个元素。

2.1.map和flat_map:

map(
    map_func, num_parallel_calls=None, deterministic=None
)
  • map_func:函数映射到另一数据集元素的数据集的元素。
  • num_parallel_calls:(可选)。A tf.int32标量tf.Tensor ,代表数量的元件以并行异步处理。如果未指定,元件将被顺序地处理。如果该值tf.data.experimental.AUTOTUNE被使用,然后并行的呼叫的数目被设置动态地基于可用的CPU上。
ds = tf.data.Dataset.from_tensor_slices(["hello world","hello China","hello Beijing"])
ds_map = ds.map(lambda x:tf.strings.split(x," "))
for x in ds_map:
    print(x)
》》tf.Tensor([b'hello' b'world'], shape=(2,), dtype=string)
    tf.Tensor([b'hello' b'China'], shape=(2,), dtype=string)
    tf.Tensor([b'hello' b'Beijing'], shape=(2,), dtype=string)

ds = tf.data.Dataset.from_tensor_slices(["hello world","hello China","hello Beijing"])
ds_flatmap = ds.flat_map(lambda x:tf.data.Dataset.from_tensor_slices(tf.strings.split(x," ")))
for x in ds_flatmap:
    print(x)
》》tf.Tensor(b'hello', shape=(), dtype=string)
    tf.Tensor(b'world', shape=(), dtype=string)
    tf.Tensor(b'hello', shape=(), dtype=string)
    tf.Tensor(b'China', shape=(), dtype=string)
    tf.Tensor(b'hello', shape=(), dtype=string)
    tf.Tensor(b'Beijing', shape=(), dtype=string)

2.2.interleave() :

interleave(
    map_func, cycle_length=None, block_length=None, num_parallel_calls=None,
    deterministic=None
)
  • 假定我们现在有一个Dataset——A
  • 从该A中取出cycle_length个element,然后对这些element apply map_func,得到cycle_length个新的Dataset对象。
  • 然后从这些新生成的Dataset对象中取数据,取数逻辑为轮流从每个对象里面取数据,每次取block_length个数据
  • 当这些新生成的某个Dataset的对象取尽时,从原Dataset中再取cycle_length个element,,然后apply map_func,以此类推。
    可以使用Dataset.interleave()可以同时处理多个输入文件,有点像扩展的flat_map,flat_map只能处理一份数据x,但是interleave可以多线程处理多个数据:
filenames = ["/var/data/file1.txt", "/var/data/file2.txt",
             "/var/data/file3.txt", "/var/data/file4.txt"]
dataset = tf.data.Dataset.from_tensor_slices(filenames)
dataset = dataset.interleave(lambda x: tf.data.TFRecordDataset(x),
    cycle_length=4, num_parallel_calls=tf.data.experimental.AUTOTUNE,
    deterministic=False)

2.3.filter过滤掉某些元素:

dataset = tf.data.Dataset.from_tensor_slices([1, 2, 3])
dataset = dataset.filter(lambda x: x < 3)
list(dataset.as_numpy_iterator())
》》[1,2]
# `tf.math.equal(x, y)` is required for equality comparison
def filter_fn(x):
  return tf.math.equal(x, 1)
dataset = dataset.filter(filter_fn)
list(dataset.as_numpy_iterator())
》》[1]

2.4.zip和condatenate

ds1 = tf.data.Dataset.range(0,3)
ds2 = tf.data.Dataset.range(3,6)
ds3 = tf.data.Dataset.range(6,9)
ds_zip = tf.data.Dataset.zip((ds1,ds2,ds3))
for x,y,z in ds_zip:
    print(x.numpy(),y.numpy(),z.numpy())
》》0 3 6
	1 4 7
	2 5 8
 list(ds_zip.as_numpy_iterator())
[(0, 3,6), (1, 4,7), (2, 5,8)]
ds_concat = tf.data.Dataset.concatenate(ds1,ds2)
for x in ds_concat:
    print(x)
》》tf.Tensor(0, shape=(), dtype=int64)
	tf.Tensor(1, shape=(), dtype=int64)
	tf.Tensor(2, shape=(), dtype=int64)
	tf.Tensor(3, shape=(), dtype=int64)
	tf.Tensor(4, shape=(), dtype=int64)
	tf.Tensor(5, shape=(), dtype=int64)

2.5.reduce

reduce( initial_state, reduce_func )
  • initial_state:表示变换的初始状态的元件。
  • reduce_func:函数映射(old_state, input_element)到new_state 。
    ds =
tf.data.Dataset.from_tensor_slices([1,2,3,4,5.0])
result = ds.reduce(0.0,lambda x,y:tf.add(x,y))
result
》》<tf.Tensor: shape=(), dtype=float32, numpy=15.0>

2.6.batch和padded_batch

ds = tf.data.Dataset.range(12)
ds_batch = ds.batch(4)
for x in ds_batch:
    print(x)
》》tf.Tensor([0 1 2 3], shape=(4,), dtype=int64)
    tf.Tensor([4 5 6 7], shape=(4,), dtype=int64)
    tf.Tensor([ 8  9 10 11], shape=(4,), dtype=int64)

padded_batch:构建批次,类似batch, 但可以填充到相同的形状:

padded_batch(
    batch_size, padded_shapes=None, padding_values=None, drop_remainder=False
)
  • 如果尺寸是一个常数,该组件将被填充出到该维度该长度。
  • 如果尺寸是未知的,则该组件将被填充出到该维度的所有元素的最大长度。
elements = [[1, 2],[3, 4, 5],[6, 7],[8]]
ds = tf.data.Dataset.from_generator(lambda: iter(elements), tf.int32)

ds_padded_batch = ds.padded_batch(2,padded_shapes = [4,])
for x in ds_padded_batch:
    print(x)    
》》tf.Tensor(
	[[1 2 0 0]
	 [3 4 5 0]], shape=(2, 4), dtype=int32)
	tf.Tensor(
	[[6 7 0 0]
 	[8 0 0 0]], shape=(2, 4), dtype=int32)

2.7.shuffle

数据顺序洗牌。

ds = tf.data.Dataset.range(12)
ds_shuffle = ds.shuffle(buffer_size = 5)

2.8.repeat

重复数据若干次,不带参数时,重复无数次。

ds = tf.data.Dataset.range(3)
ds_repeat = ds.repeat(3)

2.9.shard

采样,从某个位置开始隔固定距离采样一个元素。

ds = tf.data.Dataset.range(12)
ds_shard = ds.shard(3,index = 1)

2.10.take

采样,从开始位置取前几个元素。

ds = tf.data.Dataset.range(12)
ds_take = ds.take(3)

3.提升管道性能

数据准备过程的耗时则可以通过构建高效的数据管道进行提升,以下是一些构建高效数据管道的建议:

  • 使用 prefetch 方法让数据准备和参数迭代两个过程相互并行。
  • 使用 interleave 方法可以让数据读取过程多进程执行,并将不同来源数据夹在一起。
  • 使用 map 时设置num_parallel_calls 让数据转换过程多进程执行。
  • 使用 cache 方法让数据在第一个epoch后缓存到内存中,仅限于数据集不大情形。
  • 使用 map转换时,先batch, 然后采用向量化的转换方法对每个batch进行转换。

3.1.prefetch

ds = tf.data.Dataset.from_generator(generator,output_types = (tf.int32))
# 使用 prefetch 方法让数据准备和参数迭代两个过程相互并行。
for x in ds.prefetch(buffer_size = tf.data.experimental.AUTOTUNE):
    train_step()  

3.2.interleave

让数据读取过程多进程执行,并将不同来源数据夹在一起。

ds_files = tf.data.Dataset.list_files("./data/titanic/*.csv")
ds = ds_files.flat_map(lambda x:tf.data.TextLineDataset(x).skip(1))
ds = ds_files.interleave(lambda x:tf.data.TextLineDataset(x).skip(1))

使用interleave取代flat_map

3.3.map

设置num_parallel_calls 让数据转换过程多进行执行

ds = tf.data.Dataset.list_files("./data/cifar2/train/*/*.jpg")
#单进程
ds_map = ds.map(load_image)
#多进程
ds_map_parallel = ds.map(load_image,num_parallel_calls = tf.data.experimental.AUTOTUNE)

3.4.cache

让数据在第一个epoch后缓存到内存中,仅限于数据集不大情形。

ds = tf.data.Dataset.from_generator(generator,output_types = (tf.int32))
# 使用 cache 方法让数据在第一个epoch后缓存到内存中,仅限于数据集不大情形。
ds = tf.data.Dataset.from_generator(generator,output_types = (tf.int32)).cache()

3.5.map-batch

使用 map转换时,先batch, 然后采用向量化的转换方法对每个batch进行转换。

#先map后batch
ds = tf.data.Dataset.range(100000)
ds_map_batch = ds.map(lambda x:x**2).batch(20)
#先batch后map
ds = tf.data.Dataset.range(100000)
ds_batch_map = ds.batch(20).map(lambda x:x**2)

你可能感兴趣的:(tensorflow,深度学习,机器学习)