AI框架中的数据处理
在构建深度学习模型时,数据处理是我们最先面临的挑战。任务开始之前,由于数据量受限,或者为了得到更好的结果,通常需要进行数据增强操作,来获得能使网络受益的数据输入。典型的训练数据处理流程如下所示:
指从各种异构存储中将训练数据加载到内存中,加载时涉及数据的访问、解析等处理。
训练一般是多个epoch,通过shuffle打乱数据集不同epoch的数据排序,防止训练过拟合。如果数据集支持随机访问,则只需按不同顺序随机选择数据就可以非常有效地进行混洗shuffle。如果数据集不支持随机访问,那么一个子集的数据可以加载到一个特殊的混洗缓冲区shufflebuffer中。
完成训练数据的处理,包括像类数据增强、Text类分词等处理。其中,数据增强是一种创造有着不同方向的“新”数据的方法,一是从有限数据中生成“更多数据”,二是防止过拟合。
训练时一般都是使用mini-batch的方式,即一个批次训练少量数据;batch算子负责构造一个批次的数据发送给训练。
可以通过repeat的方式增加训练的总数据量;一次repeat就是加载一遍整个训练集。
模型在进行推理时,同样会涉及到数据处理,典型的过程如下所示:
推理过程的数据处理,将一张片进行解码、缩放、中心截、归一化、通道变换等操作后,送入模型中进行推理并得到结果。与训练相比,推理时的数据转换基本一致,不同的是推理时一般加载单样本进行处理,而非数据集。本文将重点分析AI框架在数据处理时面临的挑战以及MindSpore的解决思路。
难点与挑战
1数据处理的高效性
当前各AI框架的数据处理主要利用CPU运算,训练则利用GPU/AI芯片,两者是并行的。理想情况下,应该在每轮迭代开始前,就准备好完成增强之后的数据,保持训练过程持续地进行。然而在实际的训练中,很多时候数据处理成为了阻碍性能提升的瓶颈:或是因为从存储中读取数据的速度不足,或是因为数据增强操作效率过低。
根据黄氏定律,GPU/AI芯片的算力每一年会提升一倍,相比于即将失效的摩尔定律,AI芯片的算力提升速度会远大于CPU。模型迭代计算效率的不断提升,对数据处理也提出了更高的要求:数据处理过程必须足够高效,才能够避免GPU/AI芯片因为等待训练数据而空闲。
2数据处理的灵活性
数据处理的灵活性挑战主要体现在以下两个方面:
数据集种类繁多,难以统一
目前已知常用的开源数据集有几百种,每种由不同的组织/机构来产生,有各自的格式与组织方式。根据深度学习任务的不同,每种类型的输入也有自身的特点。以像为例,其包含类型、长、宽、大小等属性信息,每张训练像被标记成某一个类别,这些像及其对应类别的列表数据被用来进行分类等训练;以音频为例,通过训练可以直接将语音转换为文字,进一步作为智能AI的输入,完成语义理解及指令性操作。
在数据集加载时,如何支持种类繁多的像、文本、音频、视频格式,屏蔽IO差异并将其映射到内存结构中进行下一步处理,是AI框架重点需要解决的问题。
数据增强算法非常灵活
例如CV类场景,像作为网络的输入,需要保证一定的一致性,也需要有一定的泛化能力,才能使训练得到的模型具有更好的精度。研究已经证明,采用不同的数据处理逻辑,训练得到的模型精度会有明显的不同。
以resnet50和ssd为例,数据处理过程对比如下:
其中既涉及到经典的数据处理逻辑,又包含了用户自定义的处理过程。AutoML和动态shape等场景,也对数据处理的灵活性提出了更高要求。提供更多、更灵活的数据处理机制,也是框架需要重点考虑的。
3端云统一
训练导出的模型,在推理时如何高效进行数据处理:
网络训练生成的模型文件中,记录了训练时的计算及权重信息,但是数据处理过程往往没有统一存储到模型中,这就导致AI工程师在使用模型进行推理时,需要重新编写数据处理代码,十分不便。
端侧资源受限,需要提供更轻量化的数据处理方式
在端侧场景下,CPU和内存资源往往比较少,在提供数据处理的能力时,要求API尽可能简单、轻便,以最少的资源占用获得最快的执行效率。需要AI框架提供的库尽可能的小。使用云化场景提供的数据处理机制就不是特别适用。如何在不同的场景下提供最合适的数据处理方法是AI框架面临的挑战。
典型的云侧场景与端侧场景资源对比如下:
MindSpore设计思考
1设计目标与思路
MindSpore的设计中,充分考虑了数据处理的高效性、灵活性以及在不同场景下的适配性。整个数据处理子系统分为以下模块:
API:数据处理过程在MindSpore中以的形式表示,称为“数据”。MindSpore对外提供PythonAPI来定义数据,内部实现优化和执行。
整个数据加载和预处理的流程实现为多步并行流水线,包括:
Adaptor:将上层语言构建的数据,转换为下层可执行的C++数据
Optimizer:数据优化器
Runtime:运行优化后Executiontree的执行引擎
数据集算子:Executiontree中的某个节点,对应数据处理流水线中的一步具体操作,比如从文件夹加载训练数据的ImageFolder算子,做各种数据增强的Map算子,Repeat算子等。
数据增强算子:也可称为tensor算子,是对某个tensor变换的,比如Decode,Resize,Crop,Pad等,通常是被datasetoperator中的Map算子调用。
2极致的处理性能
▽多段pipeline流水线
相比于业界其他框架,MindSpore采用了多段并行流水线的方式来构建数据处理pipeline。这种架构一方面可以更加细粒度地规划CPU等计算资源的使用,另一方面天然支持各段使用异构硬件进行流水处理,从而提高数据处理过程的吞吐量。如上所示,每个数据集算子都包含一个输出Connector:由一组阻塞队列和计数器组成的保序缓冲队列。每当一个数据集算子完成一块缓存数据的处理,这个算子会将这块缓存推送到自身的输出Connector。下游的数据集算子会从上游的输出Connector里取出缓存进行后续处理。这种机制的优势包括:
数据集加载、map、batch等操作以任务调度机制来驱动,每个操作的任务互相独立,上下文通过Connector来实现Pipeline;
每个操作均可以实现细粒度的多线程/多进程并行加速。数据框架为用户提供调整算子线程数/多进程处理的接口,可以灵活控制各个节点的处理速度,进而实现整个数据处理Pipeline性能最优;
Connector支持用户对其大小进行设置,在一定程度上可以有效的控制内存的使用率,适用于不同网络对于数据处理速度的要求。
在这种数据处理机制下,对输出数据进行保序处理是保证训练精度的关键。保序意味数据处理流水线运行时,同样顺序的原始数据输入,需要保证数据处理完成后,得到同样顺序的数据输出。MindSpore采用轮询算法来保证多个线程数据处理的有序性:
在上面的数据处理pipeline示例中,保序操作发生在下游map操作的取出操作中,其以轮询的方式取出上游队列中的数据。Connector内部有两个计数器expect_consumer_记录已经有多少个consumer从queues_中取了数据,pop_from_记录了哪个内部阻塞队列将要进行下一次取出。expect_consumer_以consumer个数取余,而pop_from_以producer个数取余。在expect_consumer_再次为0时,说明所有的local_queues_已经都处理上一批任务,然后继续下一批任务分配及处理,进而实现了上游至下游map操作的多并发保序处理。
▽数据处理与计算过程pipeline
数据pipeline会不断地进行数据处理,并把处理后的数据发送到device侧的缓存;当一个step执行结束后,会直接从device的缓存中读取下一个step的数据。
数据处理:负责将数据集处理成网络需要的输入,并传递给发送队列中,保证数据处理的高效取可;
发送队列Queue:维护数据列队,保证数据处理与网络计算过程互不影响,实现桥梁的作用;
网络计算:从发送队列中获取数据,用于迭代训练。
以上三者各司其职,相互独立,构筑整个训练过程中Pipeline。这样,只要数据队列不为空,模型训练就不会因为等待训练数据而产生瓶颈。
▽缓存技术
当数据集的size较大时,无法全部加载到memorycache,此时训练中的部分数据需要从磁盘读取,可能会遇到I/O瓶颈,增大每个epoch的cache命中率就显得尤为关键。传统的缓存管理策略采用LRU策略,没有考虑深度学习数据的读取特点:在不同的epoch之间数据是重复读取的,而在同一个epoch中随机读取。每条数据的读取概率都是相同的,因此哪个数据被缓存并不是最重要的,已经cache的数据在被使用之前不被换出更加重要。针对这个特点,我们使用了一个简单高效的缓存算法:数据一旦被缓存,就不会从cache中被换出。
在数据优化的过程中,会根据流水线结构自动生成缓存算子,既可以缓存原始数据集,也可以缓存数据增强处理后的结果。
3灵活的定制能力
整个数据处理pipeline中,用户往往需要特定的处理逻辑,这些处理逻辑有其特殊性,无法固化到框架中。框架需要具备开放的能力,让用户能够定制不同的数据处理逻辑。MindSpore提供了灵活的数据集加载方式、丰富的数据处理算子、自动数据增强、数据动态Shape、数据处理Callback等机制等供开发人员在各种场景使用。
▽灵活的数据集加载方式
针对数据集种类繁多、格式与组织各异的难题,MindSpore提供了三种不同的数据集加载方式:
如果用户使用常用数据集,那么可以使用MindSpore内置的API接口直接进行加载。MindSpore实现了包括CelebADataset、Cifar10Dataset、CocoDataset、ImageFolderDataset、MnistDataset、VOCDataset等数据集的C++IOReader加载,在保证性能的同时实现了数据集的开箱即用。
用户将数据集转换为MindSpore数据格式,即MindRecord,然后通过MindSpore的API进行加载。MindRecord可以将不同的数据集格式归一化,有聚合存储、高效读取、快速编解码、灵活控制分区大小等多种优势。
▽通过Python层自定义、C层插件的方式支持更多算子
MindSpore内置了丰富的数据处理算子。这些算子可以分为C层以及Python层,C层算子能提供较高的执行性能;Python层算子可以很方便集成第三方包完成数据处理的功能,但是性能较低,好处是易开发易使用。MindSpore支持用户扩展自定义的数据处理算子,用户可以开发C层算子代码,编译后以插件的形式注册到MindSpore的数据处理中进行调用。
▽支持自动数据增强策略
MindSpore提供了基于特定策略自动对像进行数据增强处理的机制:通过基于概率或者回调参数的数据增强策略,可以实现算子自动选择执行,达到训练精度提升的目的。
例如对ImageNet数据集,自动数据增强最终搜索出的方案包含25个子策略组合,每个子策略包含两种变换,针对每幅像都随机的挑选一个子策略组合,然后以一定的概率来决定是否执行子策略中的每种变换。
这些策略包括:
RandomSelectSubpolicy-多批概率算子,随机选择其中一批执行
RandomChoice-多个算子选择其中一个执行
RandomApply-基于某个概率执行这批算子
通过自动数据增强操作,在ImageNet数据集上可以提升1%左右的训练精度。
▽支持动态shape
MindSpore通过per_batch_map支持用户自定义控制输出不同Shape的训练数据,满足网络需要基于不同场景调整数据Shape的诉求。
用户自定义数据Shape生成逻辑udf,如:第n个step生成shape为的数据;
通过batch将第一步定义的逻辑生效,最终得到不同Shape训练数据。
▽Callback机制让数据处理更加灵活
通过callback机制实现根据训练结果动态调整数据增强的逻辑,为数据增强过程提供更灵活的操作。
MindSpore支持用户基于数据处理提供的DSCallback实现自己的数据增强逻辑UDF,并将其添加至map操作中,以实现更灵活的数据增强操作。
4端云统一架构
▽数据与计算的统一
MindIR是MindSpore基于表示的函数式IR,其最核心的目的是服务于自动微分变换。自动微分采用的是基于函数式编程框架的变换方法,因此IR采用了接近于ANF函数式的语义。
推理数据典型的场景为:数据集样本大小缩放、中间截、归一化、通道变换。
我们将推理数据以子的方式保存至生成的模型文件中,那么在推理时,可以通过统一的接口加载模型中数据处理流程,自动进行数据处理得到模型需要的输入数据,简化用户的操作,提升易用性。
▽轻量化的数据处理
云侧训练时,可以被使用的资源往往比较充裕,数据处理Pipeline在运行过程中是会占用比较多系统资源,以ImageNet为例,训练过程中CPU占用20%,内存占用30-50G,这显然在端侧是不可被接收的,并且数据处理Pipeline在初始化时启动会慢些,这也不适用于端侧需要快速启动、多次训练/推理的前提条件。故:MindSpore基于现有数据处理算子,提供一套更轻量化、更适用于端侧的API,解决云化场景数据处理Pipeline不适用于端侧的问题。
MindSpore基于Pipeline调整架构,支持数据处理单算子独立使用,支持各种场景推理,提供给AI开发人员更大的灵活性;将Pipeline轻量化,实现基于PullBase的轻量化pipeline,减少资源占用并且处理速度快。
通过上述两种方法,MindSpore保证了统一的数据处理架构支撑多种不同的应用场景。
未来计划
在未来,MindSpore将继续完善数据处理的易用性,提供更丰富的算子和内置数据集。与此探索大规模数据处理的加速技术,包括:
资源自适应分配
当前各数据增强算子使用的处理线程数目由用户手工配置,对用户的调优经验要求极高。通过自适应判断Pipeline瓶颈,由框架给各个数据增强算子合理分配CPU资源,可以在训练过程中动态优化数据处理性能,免去用户繁琐的调优过程。
异构硬件加速
当前的数据处理Pipeline操作在CPU执行,一旦出现瓶颈,带来AI芯片/GPU等待空闲,用户无法充分利用所有硬件的计算能力。MindSpore期望构建用户无感知的异构硬件资源调度能力:通过监测硬件资源使用,完善Ascend/GPU上的数据处理算子,采用代价模型自适应地将数据处理任务调度至合适的资源,实现异构硬件的充分利用。
分布式缓存
当前如GPT-3等网络的训练需要使用超大规模数据集,这些数据难以在本地存储,直接从OBS读取会受网络IO限制而影响性能。与此在AutoML场景下,用户经常在集群中运行同一类模型的多个作业,每个作业独立进行数据加载和处理效率极低。MindSpore期望构建分布式缓存能力,加速这些场景下的数据处理。
MindSpore官方资料
官方QQ群:486831414
文章为作者独立观点,不代表 股票程序化软件自动交易接口观点