2百度开源的AI计算芯片PaddleChip介绍

作者:禅与计算机程序设计艺术

1.简介

  自然语言处理(NLP)、计算机视觉(CV)、图形处理(Graphics)等领域都在不断创新中取得重大突破。随着硬件的不断更新换代升级,目前国内外诸多知名企业纷纷布局AI芯片研究开发领域。近几年,谷歌、微软、苹果、亚马逊等公司都推出了自己的自研AI芯片产品,并于2019年底完成了4nm工艺的第一代CMOS芯片:Google Coral Edge TPU、Apple Neural Engine、Apple M1、Amazon EC2。近日,百度宣布启动PaddlePaddle基金会,旨在通过开放共享的协同创新,打造一套统一的AI生态系统,包括AI芯片、算力平台、训练技术、应用工具、服务平台等。本文将基于PaddlePaddle开源框架介绍其开源的基础AI芯片Paddle-Lite,这是百度首个开源的面向视觉、音频、自然语言、强化学习等AI领域的端侧计算平台,能够实现深度学习模型快速部署、高效执行。

2.背景介绍

  随着AI技术的飞速发展,各行各业纷纷布局用AI解决实际问题。作为AI的先驱者之一,百度自然语言处理部门创始人余凯龙曾经这样描述他对AI的看法:“AI是人的潜意识,是让机器自己思考、自己学习、自己适应环境的能力。它的出现,使得我们可以用更高效、更低廉的方式处理海量数据,同时还可以在某些时候取代人类成为主流。”虽然AI的潜能无限,但是,在落地应用时,仍需不断深耕细作,提升模型的效果。因此,如何快速部署AI模型,缩短模型上线时间,成为每个企业关注的焦点。

  华为技术有限公司一直以来致力于加快人工智能产业的发展,秉承“预见、创新、投入”的精神,创立了全球领先的人工智能开源组织——华为开源软件中心,致力于开源人工智能相关技术,目前已经拥有十多个开源项目,覆盖数据处理、人工智能算法、机器学习框架、工具软件等多个方向。在人工智能研究的道路上,华为一直秉持开放、共享的价值观,并且积极参与到开源社区的建设中。华为开源软件中心今后将通过建立AI社区、技术交流、成果转化等方式助力企业产业化发展,推动AI技术的迅速发展。此外,百度也与华为等大厂保持密切合作关系,共同推进AI理论、技术、产业的创新和进步。

  百度自研的AI芯片主要由Paddle-Lite团队开发,采用PaddlePaddle开源框架进行模型转换、优化、部署。当前,该芯片已经开源,可供开发者使用。Paddle-Lite是一个轻量级、灵活性强、易扩展的AI计算库,它针对ARM Cortex-M系列MCU和PaddlePaddle框架优化后生成的一套硬件加速库。Paddle-Lite在功能、性能及功耗方面均有大幅优于商用硬件产品的优势,可广泛用于嵌入式设备、移动终端、服务器端等场景,帮助客户迅速完成从海量数据的采集到实时应用的落地。其开源架构如下图所示:

3.核心概念术语说明

3.1 PaddlePaddle

  PaddlePaddle是百度开源的AI计算平台,是一种模块化的、支持多种硬件的深度学习框架。它在深度学习领域具有领先地位,可以进行模型训练和预测,支持多种编程语言,如Python、C++、Java,同时支持分布式多卡训练与超参数搜索。它具有丰富的预训练模型、强大的生态系统、完备的文档、示例和教程,被越来越多的公司、机构和个人所使用。

3.2 ARM CPU架构

  ARM是以Cortex-A、Cortex-R为代表的CPU架构系列。这些架构集成了用于图像处理、视频处理、机器学习等任务的传感器与处理单元,具有极高的算力和可靠性。目前,百度的AI芯片使用的ARM CPU架构为Cortex-M7。

3.3 FPGA芯片

  FPGA(Field Programmable Gate Array),即可编程逻辑门阵列,是一种基于门阵列结构的可编程的数字集成电路,可以高度集成、灵活、可编程。百度的AI芯片使用的FPGA芯片为赛灵思MYRIAD X。

3.4 MicroTVM

  MicroTVM(Micro Tuning and Vetting Framework),一种微型自动调参工具,可以帮助开发者在边缘设备上做轻量化和定制化的模型优化工作。可以减少模型运行时长,提升推理性能。百度的AI芯片中的MicroTVM工具对模型进行自动优化,得到比手工优化更好的结果。

4.核心算法原理和具体操作步骤以及数学公式讲解

  PaddlePaddle是一个开源框架,Paddle-Lite是一个开源的端侧AI计算芯片,下面将详细介绍PaddlePaddle框架。

PaddlePaddle概述

  首先,介绍一下PaddlePaddle。PaddlePaddle是一个模块化、支持多种硬件的深度学习框架。它在深度学习领域具有领先地位,可以进行模型训练和预测,支持多种编程语言,如Python、C++、Java,同时支持分布式多卡训练与超参数搜索。其特点如下:

  1. 模块化:PaddlePaddle框架是一个模块化的深度学习框架,不同类型的网络可以根据需求组装到一起,用户只需要关注他们想要的组件即可。

  2. 支持多种硬件:PaddlePaddle框架支持多种硬件,包括CPU、GPU、FPGA和其他IPU。它可以自动根据安装的硬件资源,自动选择最佳的算法配置,自动进行模型调度。

  3. 深度学习框架:PaddlePaddle框架基于其独特的设计理念和技术,可以实现非常复杂的神经网络模型,并且通过灵活的参数组合,可以达到极高的准确率。

  4. 可扩展性:PaddlePaddle框架可以方便地进行扩展,包括自定义算子、自定义层、自定义数据读取方法、自定义优化算法等。

  5. 文档和教程:PaddlePaddle框架提供了详尽的文档和示例,还有大量的教程可以帮助开发者快速掌握其API和使用方式。

如何使用PaddlePaddle框架

安装

  首先,下载最新版的PaddlePaddle框架,并根据需求进行安装。我们推荐通过pip命令安装PaddlePaddle,pip install paddlepaddle。

pip install paddlepaddle

PaddlePaddle可以通过两种方式使用,第一种方式是在代码中直接导入PaddlePaddle。第二种方式则是通过命令行工具paddlerec。

命令行工具paddlerec

  Paddlerec是百度开源的高性能、灵活的模型训练和评估工具。它可以帮助开发者快速实现模型的训练、评估、部署。通过paddlerec,用户只需要指定模型类型、数据集路径、超参数、训练轮次等信息,就可以开始训练与评估过程。其特色如下:

  1. 高性能:Paddlerec能够充分利用集群资源,进行超参数搜索、训练的并行化、高效的数据加载等策略,对模型训练过程进行优化。

  2. 灵活的接口:Paddlerec提供了丰富的模型接口,包括深度学习框架原生的API接口和PaddleX、PaddleDetection等高级模型接口。

  3. 完整的生态系统:Paddlerec提供了一个完整的生态系统,包括模型库、数据集库、评估指标库,可提供模型开发、调试、部署等一站式服务。

使用方法

  下面,以一个分类任务为例,介绍如何使用PaddlePaddle框架。假设我们要训练一个图片分类模型。首先,我们需要准备好数据集。然后,编写配置文件config.yaml。配置文件内容如下:

runner:
  train_data_dir: /path/to/train_dataset
  valid_data_dir: /path/to/valid_dataset
  batch_size: 64
model:
  class: ResNet50
  num_classes: 10
  image_shape: [3, 224, 224]
optimizer:
  class: Adam
  learning_rate: 0.001
total_epochs: 120

这里,runner表示模型运行的配置;model表示模型结构的配置;optimizer表示优化器的配置;total_epochs表示总的训练轮次。

接下来,编写脚本train.py:

import paddle
from paddlenlp import Taskflow
from paddlenlp.datasets import load_dataset
from config import config


def main():
    # 获取分类数据集
    train_ds = load_dataset('clue', name='chnsenticorp', splits='train')
    dev_ds = load_dataset('clue', name='chnsenticorp', splits='dev')

    model = Taskflow("sentiment_analysis")
    inputs, labels = model.inputs["text"], model.labels

    trans_func = model.transforms()

    train_ds = train_ds.map(trans_func)
    dev_ds = dev_ds.map(trans_func)

    data_loader = model.create_data_loader(
        mode="train", dataset=train_ds, batch_size=config['batch_size'])

    metrics = {"acc": model.metrics["accuracy"]}

    model.fit(
        data_loader,
        epochs=config['total_epochs'],
        eval_data_loader=dev_ds,
        save_best_model=True,
        metrics=metrics,
        verbose=1)


if __name__ == "__main__":
    main()

这里,Taskflow表示分类模型,inputs表示输入文本,labels表示标签。transform函数负责对数据进行预处理。create_data_loader函数创建一个数据迭代器。metrics表示评估指标,acc表示准确率。fit函数用来训练模型。

最后,执行命令python train.py,即可开始训练模型。

Paddle-Lite概述

  Paddle-Lite是百度开源的端侧AI计算芯片,是一种支持多种硬件的框架,可以快速部署基于深度学习框架的AI模型。其核心组件包括优化器、运行时、模型加载器等。下面将介绍Paddle-Lite的组成。

Optimizer

  Optimizer是Paddle-Lite的优化器,它负责模型的优化。Paddle-Lite在优化阶段,首先分析模型结构,并自动进行调度、合并、融合等操作,得到高效的计算图。其特点包括自动调度、自动优化、自动并行、自动混合精度。

Runtime

  Runtime是Paddle-Lite的运行时,它负责模型的运行。Paddle-Lite在运行阶段,通过驱动接口将计算图映射到相应的IP核上,执行模型的推理。目前,Paddle-Lite支持CPU、GPU、NPU和其他IPU等硬件,且支持动态、静态两种运行模式。

Model Loader

  Model Loader是Paddle-Lite的模型加载器,它负责加载模型文件,并对其进行编译、优化等操作,得到可在端侧设备上执行的模型。其特点包括跨平台部署和集成化开发等。

5.具体代码实例和解释说明

本节展示几个典型案例,带领读者了解PaddlePaddle和Paddle-Lite框架的更多特性。

数据处理与模型训练

数据处理

当我们使用PaddlePaddle框架处理图像数据时,我们可以使用相关API,例如paddle.vision.transforms。当我们处理文本数据时,我们可以使用Related API,例如paddle.text.transforms。

import paddle.vision.transforms as T

# 对图像数据进行预处理
train_transform = T.Compose([
    T.Resize((224, 224)),
    T.ToTensor(),
    T.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
])

val_transform = T.Compose([
    T.Resize((224, 224)),
    T.ToTensor(),
    T.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
])

# 对文本数据进行预处理
def preprocess_function(examples):
    examples['sentence'] = examples['sentence'].apply(lambda x:''.join(['[CLS]', x, '[SEP]']))
    return examples

train_ds = load_dataset('imdb', split='train').map(preprocess_function).map(train_transform)
val_ds = load_dataset('imdb', split='test').map(preprocess_function).map(val_transform)
模型训练

当我们使用PaddlePaddle框架训练分类模型时,我们可以调用Sequential或Layer封装模型结构。然后,设置优化器和损失函数,进行模型训练。

import paddle.nn as nn

class SimpleNet(nn.Layer):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(768, 256)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(256, 10)

    def forward(self, x):
        out = self.fc1(x)
        out = self.relu(out)
        out = self.fc2(out)
        return out

net = SimpleNet()
criterion = nn.CrossEntropyLoss()
opt = paddle.optimizer.Adam(learning_rate=0.001, parameters=net.parameters())

for epoch in range(num_epoch):
    for step, (x, y) in enumerate(train_dataloader()):
        out = net(x)
        loss = criterion(out, y)

        loss.backward()
        opt.step()
        opt.clear_grad()

    print("Epoch {}: Loss={:.6f}".format(epoch+1, np.mean(losses)))

模型部署

模型压缩

当我们部署模型时,我们通常需要考虑模型大小的问题。为了降低模型大小,我们可以进行模型压缩。Paddle-Lite提供了两种模型压缩方案,量化和裁剪。

量化

量化是指将浮点运算转换为整数运算,其目的是为了降低模型大小,提升模型计算速度。而在Paddle-Lite中,我们可以使用不同的量化方案,包括均匀量化和非均匀量化。其中,均匀量化将权重按一定范围划分,每个区间代表一个量化级别。而非均匀量化是指,按照权重的绝对值大小划分等份,每个区间代表一个量化级别。

import paddlelite.lite as lite

place = lite.Place(lite.TargetType.kARM, lite.PrecisionType.kFloat)
quantize = True

converter = lite.Converter()
converter.optimizers = ["conv_transpose_eltwisecut_quant"]
converter.set_param_prefix("")
converter.target_type = place.target
converter.set_device_type(int(str(place.target)[-1]))
converter.precision_type = lite.PrecisionType.kInt8
converter.convert(model_file_path, params_file_path, quantize=quantize)

裁剪

裁剪是指在训练过程中,根据权重大小删除一些不重要的节点,以降低模型大小。Paddle-Lite提供了三种裁剪策略,包括通道裁剪、特征裁剪、结构裁剪。其中,通道裁剪是指,对于卷积层的输出通道数较多的情况,通过剔除不重要的输出通道来减小模型大小。特征裁剪是指,对于卷积层输出特征图较多的情况,通过剔除不重要的特征点来减小模型大小。结构裁剪是指,对于卷积层输出尺寸较大的情况,通过剔除不重要的层来减小模型大小。

import paddlelite.lite as lite

place = lite.Place(lite.TargetType.kARM, lite.PrecisionType.kFloat)

clipper = lite.ULQClipper()
clipper.clip_model(model_file_path, params_file_path, save_dir, sample_generator, calib_table_path)
模型预测

当我们把模型部署到目标设备上时,我们可能需要考虑延迟和内存占用的问题。为了缓解延迟,我们可以采用异步预测方案。异步预测是指,把模型预测请求发送给后端引擎,并不等待结果返回,而是继续接收新的请求。Paddle-Lite提供了异步预测接口,用户可以把预测请求放入队列,并立刻返回。

#include <paddlelite/lite.h>

using namespace paddle::lite_api;

std::unique_ptr<PaddlePredictor> CreatePaddlePredictor(const char* model_buffer, size_t buffer_size) {
    MobileConfig config;
    config.set_model_from_memory(model_buffer, buffer_size);
    config.threads = 1; // 设置线程数为1,开启异步预测
    auto predictor = CreatePaddlePredictor(config);
    return predictor;
}

void RunAsyncInference(std::shared_future<std::vector<PaddleLiteTensor>>& future, int id) {
    try {
        auto results = future.get();

        // do something with the prediction result...
    } catch (...) {
        printf("Exception caught while running async inference on thread %d\n", id);
    }
}

void MakePrediction(std::unique_ptr<PaddlePredictor>& predictor, const cv::Mat& img) {
    int width = img.cols;
    int height = img.rows;
    auto input_tensor = predictor->GetInput(0);
    input_tensor->Resize({1, 3, height, width});
    float* data = input_tensor->mutable_data<float>();
    cv::cvtColor(img, img, cv::COLOR_BGR2RGB);
    int area = width * height;
    memcpy(data, img.data, area * 3 * sizeof(float));

    std::shared_future<std::vector<PaddleLiteTensor>> future = (*predictor)(); // 同步预测
    std::thread t([&]() { RunAsyncInference(future, 0); });    // 开启新的线程处理异步预测结果
    t.detach();                                            // 不阻塞当前线程
}

猜你喜欢

转载自blog.csdn.net/universsky2015/article/details/132843815