1. Tensorflow高效流水线Pipeline

本文探讨了如何在Tensorflow中构建高效的数据处理流水线,包括使用Dataset和Iterator,生成TFRecord,以及Estimator的实践原理。重点介绍了tf.data API的功能,及在不同模型和加速器上构建高性能输入流水线的最佳实践。
1. Tensorflow高效流水线Pipeline
2. Tensorflow的数据处理中的Dataset和Iterator
3. Tensorflow生成TFRecord
4. Tensorflow的Estimator实践原理

1. 前言

GPU和TPU可以显著缩短执行单个训练步所需的时间。实现最高性能需要高效的输入流水线,以在当前时间步完成之前为下一步提供数据。tf.data API可以帮助我们构建灵活高效的输入流水线。本文档介绍了 tf.data API的功能,以及在各种模型和加速器上构建高性能TensorFlow输入流水线的最佳做法

2. Pipeline Structure输入流水线结构

我们可以将典型的 TensorFlow 训练输入流水线视为 ETL 流程:

  1. Extract:从永久性存储(可以是 HDD 或 SSD 等本地存储或 GCS 或 HDFS 等远程存储)读取数据。
  2. Transform:使用CPU核心解析数据并对其执行预处理操作,例如图像解压缩、数据增强转换(例如随机裁剪、翻转和颜色失真)、重排和批处理。
  3. Load:将转换后的数据加载到执行机器学习模型的加速器设备(例如,GPU 或 TPU)上。

这种模式可高效利用 CPU,同时预留加速器来完成对模型进行训练的繁重工作。此外,将输入流水线视为 ETL 流程可提供便于应用性能优化的结构。

使用 tf.estimator.Estimator API 时,前两个阶段(提取和转换)是在 input_fn(传递给 tf.estimator.Estimator.train)中捕获的。代码可能如以下(简单序列)实现所示:

def parse_fn(example):
  "Parse TFExample records and perform simple data augmentation."
  example_fmt = {
    "image": tf.FixedLengthFeature((), tf.string, ""),
    "label": tf.FixedLengthFeature((), tf.int64, -1)
  }
  parsed = tf.parse_single_example(example, example_fmt)
  image = tf.image.decode_image(parsed["image"])
  image = _augment_helper(image)  # augments image using slice, reshape, resize_bilinear
  return image, parsed["label"]

def input_fn():
  files = tf.data.Dataset.list_files("/path/to/dataset/train-*.tfrecord")
  dataset = files.interleave(tf.data.TFRecordDataset)
  dataset = dataset.shuffle(buffer_size=FLAGS.shuffle_buffer_size)
  dataset = dataset.map(map_func=parse_fn)
  dataset = dataset.batch(batch_size=FLAGS.batch_size)
  return dataset

2.1 最佳Pipeline步骤

在这里先给出最佳做法,如果同学们只想知道怎么做,直接参考这里就可以啦。

下面的内容是针对每一点优化的原理。

3. 优化性能

由于新型计算设备(例如 GPU 和 TPU)可以不断提高神经网络的训练速度,因此,CPU 处理很容易成为瓶颈。tf.data API 为用户提供构建块来设计可高效利用 CPU 的输入流水线,并优化 ETL 流程的每个步骤。

3.1 prefetch预取数据

要执行训练步骤,您必须首先提取并转换训练数据,然后将其提供给在加速器上运行的模型。但是,在一个简单的同步实现中,当 CPU 正在准备数据时,加速器处于空闲状态。相反,当加速器正在训练模型时,CPU 处于空闲状态。因此,训练步的用时是 CPU 预处理时间和加速器训练时间的总和。

流水线将训练步骤的预处理和模型执行过程重叠到一起。当加速器正在执行第 N 个训练步时,CPU 正在准备第 N+1 步的数据。这样做不仅可以最大限度地缩短训练的单步用时(而不是总用时),而且可以缩短提取和转换数据所需的时间。

如果不使用流水线,CPU 和 GPU/TPU 在大部分时间都处于空闲状态:

image

使用流水线可显著减少空闲时间:
image

tf.data API 通过 tf.data.Dataset.prefetch 转换提供了一种软件流水线机制,该机制可用于将生成数据的时间和使用数据的时间分离开。具体而言,该转换使用后台线程和内部缓冲区,以便在请求元素之前从输入数据集中预取这些元素。因此,为了实现上图所示的流水线效果,您可以将 prefetch() 作为最终转换添加到数据集流水线中(如果单步训练使用 n 个元素,则添加 prefetch(n))。

要将此项更改应用于我们正在运行的示例,请将:

dataset = dataset.batch(batch_size=FLAGS.batch_size)
return dataset

更改为:

dataset = dataset.batch(batch_size=FLAGS.batch_size)
dataset = dataset.prefetch(buffer_size=FLAGS.prefetch_buffer_size)
return dataset

3.2 map并行处理数据转换

准备批次数据时,可能需要预处理输入元素。为此,tf.data API 提供了 tf.data.Dataset.map 转换,以将用户定义的函数(例如,正在运行的示例的 parse_fn)应用于输入数据集的每个元素。由于输入元素彼此独立,因此可以跨多个 CPU 核心并行执行预处理。为实现这一点,map 转换提供了 num_parallel_calls 参数来指定并行处理级别。例如,下图说明了将 num_parallel_calls=2 设置为 map 转换的效果:

image

并行后,由于数据预处理的时间缩短,整体的时间也减少了。如何为 num_parallel_calls 参数选择最佳值取决于硬件、训练数据的特征(例如其大小和形状)、映射函数的成本以及同时在 CPU 上进行的其他处理;一个简单的启发法是设为可用 CPU 核心的数量。例如,如果执行以上示例的机器有 4 个核心,则设置 num_parallel_calls=4 会更高效。另一方面,将 num_parallel_calls 设置为远大于可用 CPU 数量的值可能会导致调度效率低下,进而减慢速度。

要将此项更改应用于我们正在运行的示例,请将:

dataset = dataset.map(map_func=parse_fn)

更改为:

dataset = dataset.map(map_func=parse_fn, num_parallel_calls=FLAGS.num_parallel_calls)

此外,如果批次大小为数百或数千,那么并行处理批次创建过程还可能给流水线带来更大的优势。为此,tf.data API 提供了 tf.contrib.data.map_and_batch 转换,它可以有效地将映射和批次转换“混合”在一起。

要将此项更改应用于我们正在运行的示例,请将:

dataset = dataset.map(map_func=parse_fn, num_parallel_calls=FLAGS.num_parallel_calls)
dataset = dataset.batch(batch_size=FLAGS.batch_size)

更改为:

dataset = dataset.apply(tf.contrib.data.map_and_batch(
    map_func=parse_fn, batch_size=FLAGS.batch_size))

3.3 并行处理远程数据提取

在实际设置中,输入数据可能会远程存储(例如,GCS 或 HDFS),这是因为输入数据不适合本地存储,或因为训练是分布式训练,因此在每台机器上复制输入数据没有意义。非常适合在本地读取数据的数据集流水线在远程读取数据时可能会遇到 I/O 瓶颈,这是因为本地存储和远程存储之间存在以下差异:

  • 首字节时间:与本地存储相比,从远程存储读取文件的首字节所用时间可能要多出几个数量级。
  • 读取吞吐量:虽然远程存储通常可提供较大的聚合带宽,但读取单个文件可能只能利用此带宽的一小部分。

此外,将原始字节读入内存中后,可能还需要对数据进行反序列化或解密(例如,protobuf),这会带来额外的开销。无论数据是在本地还是远程存储,都存在这种开销,但如果未有效预取数据,则在远程存储的情况下可能更糟。

为了降低各种数据提取开销的影响,tf.data API 提供了 tf.contrib.data.parallel_interleave 转换。使用此转换可以并行执行其他数据集(例如数据文件读取器)并交错这些数据集的内容。可以通过 cycle_length 参数指定要重叠的数据集的数量。

下图说明了为 parallel_interleave 转换提供 cycle_length=2 的效果:

image
要将此项更改应用于我们正在运行的示例,请将:

dataset = files.interleave(tf.data.TFRecordDataset)

更改为:

dataset = files.apply(tf.contrib.data.parallel_interleave(
    tf.data.TFRecordDataset, cycle_length=FLAGS.num_parallel_readers))

由于负载或网络事件,远程存储系统的吞吐量可能会随时间而变化。鉴于这种差异,parallel_interleave 转换可以选择使用预取(如需了解详情,请参阅 tf.contrib.data.parallel_interleave)。

默认情况下,parallel_interleave 转换可提供元素的确定性排序以帮助实现可再现性。作为预取的替代方案(在某些情况下可能效率低下),parallel_interleave 转换还提供了一个可提升性能但无法保证排序的选项。特别是,如果 sloppy 参数设为 true,则该转换可在系统请求下一个元素时暂时跳过其元素不可用的文件,从而放弃该转换的确定性排序。

4. 性能考虑因素

tf.data API 围绕可组合转换而设计,旨在为用户提供灵活性。虽然这些转换中有很多都是可以交替的,但某些转换的顺序会对性能产生影响。

4.1 map映射和batch批次

调用传递给 map 转换的用户定义函数具有与调度和执行用户定义函数相关的开销。通常,与函数执行的计算量相比,这种开销很小。但是,如果 map 几乎不起作用,那么这种开销可能会占总成本的很大一部分。在这种情况下,建议向量化用户定义的函数(即,让该函数一次对一批输入进行操作),并在 map 转换之前先应用 batch 转换
或者直接更改为如下代码:

dataset = dataset.apply(tf.contrib.data.map_and_batch(
    map_func=parse_fn, batch_size=FLAGS.batch_size))

4.2 map映射和cache缓存

tf.data.Dataset.cache 转换可以在内存或本地存储中缓存数据集。如果传递给 map 转换的用户定义函数代价很高,则只要内存或本地存储仍可以容纳生成的数据集,就可以在映射转换后应用缓存转换。如果用户定义的函数会增加存储数据集所需的空间,并超出缓存容量,请考虑在训练作业之前预处理数据以减少资源消耗量。

4.3 map映射和interleave交错/prefetch预取/shuffle重排

许多转换(包括map interleave、prefetch 和 shuffle)都维持一个内部元素缓冲区。如果传递给 map 转换的用户定义函数改变了元素的大小,那么映射转换的顺序和缓冲元素的转换会影响内存使用量。通常,我们建议选择可以减少内存占用的顺序,除非为了提高性能而需要采用不同的顺序(例如,为了混合映射和批次转换)。

4.4 repeat重复和shuffle重排

tf.data.Dataset.repeat 转换会将输入数据重复有限(或无限)次;每次数据重复通常称为一个周期。tf.data.Dataset.shuffle 转换会随机化数据集样本的顺序。

如果在 shuffle 转换之前应用 repeat 转换,则系统会对周期边界进行模糊处理。也就是说,某些元素可以在其他元素出现之前重复出现。另一方面,如果在重复转换之前应用 shuffle 转换,那么在每个周期开始时性能可能会降低,因为需要初始化 shuffle 转换的内部状态。换言之,前者(repeat 在 shuffle 之前)可提供更好的性能,而后者(repeat 在 shuffle 之前)可提供更强的排序保证。

如果可能,建议您使用 tf.contrib.data.shuffle_and_repeat 混合转换,这样可以达到两全其美的效果(良好的性能和强大的排序保证)。否则,我们建议在repeat重复之前进行shuffle重排

转载于:https://www.cnblogs.com/huangyc/p/10340766.html

### PaddlePaddle Pipeline 使用指南及实现方式 PaddleNLP 是 PaddlePaddle 提供的一个自然语言处理(NLP)工具包,其中 Pipelines 框架提供了一种端到端的流水线机制,用于快速构建和部署强的 NLP 系统。以下是关于 PaddleNLP Pipelines 的详细说明和使用方法[^2]。 #### 1. PaddleNLP Pipelines 的核心功能 Pipelines 框架的核心目标是将复杂的 NLP 任务抽象为多个模块化的组件,并通过流水线的方式串联起来。这些组件通常包括以下几个部分: - **Preprocessor**: 数据预处理模块,负责将原始输入转换为模型可以接受的格式。 - **Model**: 核心模型模块,执行具体的推理或预测任务。 - **Postprocessor**: 后处理模块,负责将模型输出转换为用户可理解的结果。 #### 2. 快速开始:使用 Pipelines 部署 NLP 服务 以下是一个简单的示例,展示如何使用 PaddleNLP Pipelines 构建一个问答系统并将其部署为 Web 服务。 ##### (1) 安装依赖 首先需要安装必要的依赖库,包括 `streamlit` 和 `paddlenlp`: ```bash pip install streamlit paddlenlp ``` ##### (2) 创建流水线 在 Python 脚本中定义一个流水线,例如 `deploy/web/streamlit_demo.py`: ```python from paddlenlp import Taskflow # 初始化流水线 qa_pipeline = Taskflow("question_answering") def run_qa(query): result = qa_pipeline(query) return result ``` ##### (3) 部署为 Web 服务 使用 Streamlit 快速部署 Web 服务: ```python import streamlit as st from deploy.web.streamlit_demo import run_qa st.title("PaddleNLP QA System") query = st.text_input("请输入问题:") if query: result = run_qa(query) st.write("答案:", result) ``` 运行以下命令启动 Web 服务: ```bash streamlit run deploy/web/streamlit_demo.py ``` ##### (4) 测试 API 接口 如果需要通过 API 接口访问流水线,可以参考以下 `curl` 请求: ```bash curl -X POST -k \ http://localhost:8891/query \ -H 'Content-Type: application/json' \ -d '{"query": "北京市有多少个行政区?","params": {"Retriever": {"top_k": 5}, "Ranker":{"top_k": 5}}}' ``` #### 3. Pipelines 的高级用法 Pipelines 支持多种任务类型,例如文本分类、命名实体识别、情感分析等。以下是一些常见的任务及其对应的初始化代码: ##### (1) 文本分类 ```python text_classification = Taskflow("sentiment_analysis") result = text_classification("这部电影非常好看!") ``` ##### (2) 命名实体识别 ```python ner_pipeline = Taskflow("ner") result = ner_pipeline("北京是中国的首都。") ``` ##### (3) 情感分析 ```python sentiment_analysis = Taskflow("sentiment_analysis") result = sentiment_analysis("今天的天气真好!") ``` #### 4. Pipelines 与 Hugging Face Transformers 的区别 虽然 PaddleNLP Pipelines 和 Hugging Face Transformers 都提供了类似的流水线机制,但两者存在一些关键差异[^2]: - **框架支持**:PaddleNLP Pipelines 基于 PaddlePaddle 框架,而 Hugging Face Transformers 基于 PyTorch 或 TensorFlow。 - **性能优化**:PaddleNLP Pipelines 在中文场景下进行了量优化,适合处理中文相关的 NLP 任务。 - **生态支持**:Hugging Face Transformers 提供了更广泛的预训练模型支持,而 PaddleNLP 更注重工业级应用。 #### 5. 总结 PaddleNLP Pipelines 是一个强的工具,能够帮助开发者快速构建和部署 NLP 系统。无论是通过 Streamlit 部署 Web 服务还是通过 API 接口访问,Pipelines 都能提供灵活且高效的解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值