使用OpenVINO™ 部署PaddleSeg模型库中的DeepLabV3+模型

描述

 

 

 

01

概述

 

 

本文是OpenVINO 工具套件与百度飞桨PaddlePaddle模型转换/部署系列的第二部。这篇文章专注于展示如何将百度飞桨PaddelSeg项目下的DeepLabV3+路面语义分割模型转换为OpenVINO 工具套件的IR模型并且部署到CPU上。

 

为了使本文拥有更广的受众面,文章的目标部署平台选择了CPU和iGPU。关于如何部署到边缘设备例如Intel Movidius MyraidX VPU上,请参考第一篇文章:

Ubuntu20.04环境下使用OpenVINO部署BiSeNetV2模型

 

那么我们重述一下什么是语义分割:

 

语义分割(Semantic Segmentatio)是计算机视觉对现实世界理解的基础,大到自动驾驶,小到个人应用只要细心观察都可以发现语义分割的应用场所无处不在。其实语义分割相当于是图像分割+ 对分割区域的理解。

 

 

 


因此图像语义分割也称为图像语义标注,由于图像语义分割不仅要识别出对象还要标出每个对象的边界,所以相关的模型会具有像素级别(Pixel Level)的密集预测能力。
 

本篇文章的主要目的是展示如何把已有的百度飞桨Paddle模型一步一步的部署到Intel的Movidius Myriad X VPU上。同时本文也展示了一种思路:如何在浩瀚的Internet网络找到正确的信息,少走弯路。

 

1.1

OpenVINO 工具套件的重要性

 面对当下众多流行的AI框架,比如PaddlePaddle、PyTorch、Tensorflow等等,每个框架都有自己特殊的神经网络结构和文件类型,每个框架内的设计也尽不一样, 如此训练出来的模型则千差万别,这对软件开发和重用造成了一定的麻烦。

 

是否可以有一种工具可以使我们,不管选用什么AI框架来构建专属于自己的神经网络时,在最后部署到生产环境中的环节,都可以拥有统一的接口,可重用的代码呢?

 

答案是肯定的。OpenVINO 工具套件就是这样的一款工具,有了它的帮助,AI工程师可以在模型构建、训练阶段,选择自己熟悉的任何AI框架来搭建起符合要求的个性化神经网络, 而在后期使用OpenVINO 快速构建起专属的解决方案,提供统一的接口,并在Intel 的硬件上优化性能。

 

1.2

这篇文章的着重点和非着重点

正如前面提到的,,这篇文章的着重点在于一步一步演示怎样导出已经训练好的或者已有的百度飞桨PaddleSeg模型,并且怎样转换Paddle模型到ONNX格式,最后再转到OpenVINO 工具套件IR模型,直至部署到CPU上为止。在每一步我都会提供相应的官方网址,一步一步的把读者推向正确的官网文档,减少走弯路。

 

再来讲一下这篇文章不讲什么。这篇文章不讲解怎样安装Python,Anaconda,OpenVINO 工具套件这样的基础需求框架。以上几个产品的官方网站教程都做的非常详细,而且会实时更新,相信对于每个不同的技术,阅读相对应的官方文档可以省去很多麻烦,少走弯路。这篇文章更多的精力会用在讲解模型之间的转换,部署,以及排错。

 

1.3

Intel OpenVINO简介

OpenVINO 工具套件(以下简称OV)是Intel 发布的一个综合性工具套件,用于快速开发解决各种任务的应用程序和解决方案。它包括人类视觉,自动语音识别,自然语言处理,推荐系统等。该工具套件基于最新一代人工神经网络,包括卷积神经网络 (CNN)、Recurrent Network和基于注意力的网络,可跨英特尔 硬件扩展计算机视觉和非视觉工作负载,从而最大限度地提高性能。

 

1.4

百度飞桨

百度飞桨(以下简称Paddle)是百度旗下一个致力于让AI深度学习技术的创新与应用更加简单的工具集。其中包括,PaddleCV,PaddleSeg,PaddleClas等工具帮助您快速的搭建起AI应用程序,最快落地AI模型项目。对于有过Tensorflow, PyTorch经验的朋友来说, Paddle的角色和前面二者是一样的,都是高度集成的AI框架。目前Paddle有很活跃的开发者社区,有利于快速找到自己需要的答案和帮助。

 

02

概述面向的读者和需要的软件

 

 

2.1

面向的读者

本文面向的读者是具有一定编程能力和经验的开发人员,AI模型开发员,熟悉Python语言,并使用Anaconda,已有训练好的模型,期待能部署到边缘设备上来投入实际生产中。对于0基础的读者也是一个很好的机会通过一篇文章一并了解以上的几个技术以及怎样综合使用这些技术,让它们成为您得心应手的工具来帮助您最快的实现AI部署。

 

2.2

需要的软件

Anaconda,Python(创建Anaconda虚拟环境的时候会自带),OpenVINO 工具套件,Paddle,PaddleSeg,Paddle2Onnx,mamba。

 

03

安装PaddlePaddle & PaddleSeg

 

 

在介绍完以上内容或,现在可以正式动工啦。由于本文用到的BiSeNetV2路面分割模型是用PaddleSeg训练的,所以需要先安装PaddleSeg的基础库PaddlePaddle。然后再安装PaddelSeg。

在安装Paddle组件之前,请确保您已经安装好了Anaconda(地址:

https://docs.anaconda.com/anaconda/install/index.html)

 

第一步:创建一个conda 虚拟环境:

conda create -n "paddle" python=3.8.8 ipython

创建好环境后 别忘了激活环境:

conda activate paddle

 

第二步:安装GPU或者CPU版本的PaddlePaddle:

至于是选择GPU还是CPU版本的Paddle,主要是根据您的硬件配置。如果您有NVIDIA最近几年的显卡例如:RTX 1060,RTX 2070等,那么请选择安装GPU版本。查看CUDA对GPU支持的具体信息,请阅读NVIDIA官网的GPU Compute Capability(地址:

https://developer.nvidia.com/cuda-gpus)

 

首先安装NVIDIA的cudnn

conda install cudnn

安装的时候也可以把conda 换成mamba(地址:https://github.com/mamba-org/mamba),从而得到更快的下载速度。

图像分割

图四:使用Mamba安装cudnn

这里快速介绍一下mamba。它是Conda的C++实现。相比于Conda,它提供多线程下载,这也就意味着它可以比Conda更好的利用网络资源,下载的更快。同时它也能更快的解析依赖项。估计用Conda多的朋友应该会遇到过,Conda有时候查找依赖项非常慢、很耽误时间。Mamba是您的好朋友,以下教程种再看到conda的命令,大家都可以自动替换成mamba来提高速度, 让您的效率飞起来~!

 

安装PaddlePaddle的时候,Paddle的官网(https://www.paddlepaddle.org.cn/)是您的好朋友,(任何时候安装任何东西,找到官网一般就能获取最新的指南), 我以Ubuntu 20.04的系统为例(如果您用其他的系统,那么请选择相应的选项)

 

 

 

具体命令如下:

conda install paddlepaddle-gpu==2.1.2 cudatoolkit=11.2 -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/Paddle/ -c conda-forge

 

安装完底层框架之后,是时候安装PaddleSeg(地址:https://github.com/PaddlePaddle/PaddleSeg)啦。同理, 安装PaddleSeg的时候您的好朋友还是它的官网或者它的Github仓库。

pip install paddleseg

git clone https://github.com/PaddlePaddle/PaddleSeg

安装完PaddleSeg之后 我们就可以开始激动人心的步骤:导出已经训练好的模型~!

 

04

模型转换

 

 

模型的转换分为4个步骤:

1.     导出已经训练好的模型

2.     转换到ONNX模型

3.     通过ONNX模型转换到OpenVINO 工具套件的 IR模型

4.     最后编译IR模型成为.blob模型(只适用于Intel VPU,神经棒等, CPU不需要)

 

 

其中3和4都是可以在Intel Movidius Myriad X VPU上部署测试的。

 

4.1

导出已经训练好的模型

本文将会以PaddleSeg官方的DeepLabV3P模型(DeepLabV3+[2](地址:

https://github.com/PaddlePaddle/PaddleSeg/tree/release/2.2/configs/deeplabv3p)

是一款相对较新的神经网络构架,拥有不错的准确度和性能)为例(如果您有自己的模型,请替换掉下面例子中的模型,并且更新相应的配置即可)。本文选中的是以Pascol VOC 2012 + Aug 为数据集训练好的, ResNet50_OS8为骨干网络的DeepLabV3P模型。

命令的格式如下:

conda activate paddle

cd PaddleSeg

python export.py

--config / Github/PaddleSeg/configs/deeplabv3p/deeplabv3p_resnet50_os8_voc12aug_512x512_40k.yml

--model /Models/Paddle/Segmentation/deeplabv3p_pascalvoc2012.pdparams

如果一切运行正常您将会看到类似如下的信息:

W0922 2341.752403 25116 device_context.cc:404] Please NOTE: device: 0, GPU Compute Capability: 7.5, Driver API Version: 11.2, Runtime API Version: 11.2

W0922 2341.775831 25116 device_context.cc:422] device: 0, cuDNN Version: 8.1.

2021-09-22 2344 [INFO]  Loading pretrained model from https://bj.bcebos.com/paddleseg/dygraph/resnet50_vd_ssld_v2.tar.gz

Connecting to https://bj.bcebos.com/paddleseg/dygraph/resnet50_vd_ssld_v2.tar.gz

Downloading resnet50_vd_ssld_v2.tar.gz

[==================================================] 100.00%

Uncompress resnet50_vd_ssld_v2.tar.gz

[==================================================] 100.00%

2021-09-22 2304 [INFO]  There are 275/275 variables loaded into ResNet_vd.

2021-09-22 2305 [INFO]  Loaded trained params of model successfully.

/anaconda3/envs/paddle/lib/python3.8/site-packages/paddle/fluid/layers/utils.py DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.9 it will stop working

  return (isinstance(seq, collections.Sequence) and

2021-09-22 2307 [INFO]  Model is saved in ./output.

具体的模型deeplabv3p_resnet50_os8_voc12aug_512x512_40k.yml配置文件可以在PaddleSeg的官方Github阅读。

(地址:

https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.2/configs/deeplabv3p/deeplabv3p_resnet50_os8_voc12aug_512x512_40k.yml)

 

如果想知道更多参数,您的好朋友还是PaddleSeg的官方Github Repository:模型导出 (地址:

https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.2/docs/export/export/model_export.md)

 

来讲解一下这条命令。

--config 是用来指定模型配置参数的文件。在这个文件里它说明了您使用的模型叫什么名字, 比如在我的例子中,使用的模型叫做:BiSeNetV2, 您需要的分类有多少种,用了什么优化器,损失函数是什么,batch size是多少等等都在这个文件里面。

来看一下

deeplabv3p_resnet50_os8_voc12aug_512x512_40k.yml配置文件  地址:

https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.2/configs/deeplabv3p/deeplabv3p_resnet50_os8_voc12aug_512x512_40k.yml)的内容

_base_: '../_base_/pascal_voc12aug.yml'

model:

       type: DeepLabV3P

       backbone:

              type: ResNet50_vd

              output_stride: 8

              multi_grid: [1, 2, 4]

              pretrained: https://bj.bcebos.com/paddleseg/dygraph/resnet50_vd_ssld_v2.tar.gz

       backbone_indices: [0, 3]

       aspp_ratios: [1, 12, 24, 36]

       aspp_out_channels: 256

       align_corners: False

       pretrained: null

 

可以看出, 它只需要在本配置文件里面指出了和模板文件pascal_voc12aug.yml (地址:

https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.2/configs/_base_/pascal_voc12aug.yml)

不一样的参数.  而它的模板文件又参照了pascal_voc12.yml (地址:

https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.2/configs/_base_/pascal_voc12.yml)

作为更上一层的模板文件。

 

这样做的好处显而易见. 您在自己的模型配置文件里, 只需要做少许改动,指出和模板文件不同的配置便可以,大量的相同配置则被重复使用了。

 

一个小窍门就是,参考PaddleSeg项目里已有的模板 (例如您刚克隆的PaddleSeg代码下面的 deeplabv3p_resnet50_os8_voc12aug_512x512_40k.yml) (地址:

https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.2/configs/deeplabv3p/deeplabv3p_resnet50_os8_voc12aug_512x512_40k.yml)

一级一级的追查回到最底层的模板,您就差不多可以知道在xml文件里有哪些参数可以指定的了。同时也参考自己在训练AI模型的时候代码里用到了哪些参数,基本上都是要在这个config文件里面反映出来的。

 

--model 指向的是您已经训练好的模型文件。

 

4.2

转模型到ONNX: Paddle --> ONNX

模型导出后第一道转换现在开始了.Paddle提供了转换工具 Paddle2onnx. (地址:

https://github.com/PaddlePaddle/Paddle2ONNX)我们先来安装它:

pip install paddle2onnx

是时候转化模型到ONNX啦

paddle2onnx     --model_dir inference     --model_filename model.pdmodel     --params_filename model.pdiparams     --save_file road_seg.onnx     --opset_version 11

--enable_onnx_checker True

 

这里 model_dir, model_filename, 以及params_filename和Save_file替换成自己的文件路径就好.

--model_dir是转换后的模型要保存的目录

--enable_onnx_checker 把这个也启动,让转换程序帮我们检查模型

 

我当时遇到的问题:

Opset_version的默认值是9(地址:

https://github.com/PaddlePaddle/Paddle2ONNX#parameters),当我在转BiSeNetV2的时候一开始并没有指定这个,而且出错了,经过研究,发现是因为BiSeNetV2的框架比较新,需要把这个opset_version调高到11,更改到11后就好了。目前看到官网能稳定支持的是 11,但是也有看到过别人用12的,大家可以边试边用。

 

如果转换成功啦则会看到类似的如下信息:

2021-08-23 2233 [INFO] ONNX model generated is valid.

2021-08-23 2233 [INFO] ONNX model saved in /onnx_models/road_seg.onnx

 

4.3

转换ONNX模型到OpenVINO IR模型

铺垫了很久,终于来到了这一步。

先来快速介绍一下OpenVINO的IR模型。IR的全称叫做Intermediate Representation. IR格式的模型是由2个文件组成的,它们分别是 .xml 和 .bin.

 

来到这一步之前请确保您的Intel OpenVINO安装成功啦。怎样安装Intel OpenVINO呢?您的好朋友又要出现了:Intel OpenVINO官网安装教程(地址:

https://docs.openvinotoolkit.org/cn/latest/openvino_docs_install_guides_installing_openvino_linux.html#install-openvino),这里是Intel OpenVINO官方下载地址(https://software.seek.intel.com/openvino-toolkit)

 

Intel OpenVINO的安装包里3种安装选项分别是:

1.       图像界面GUI安装

2.       命令行安装

3.       命令行安装安静模式

对于新手,推荐用GUI安装,清清楚楚、明明白白。

 

4.3.1

设置外部软件依赖

安装成功后,记得把Install External Software Dependencies(地址:

https://docs.openvinotoolkit.org/cn/latest/openvino_docs_install_guides_installing_openvino_linux.html#install-external-dependencies)这个部分的要求也跟完这一步是需要的。

 

4.3.2

激活Intel OpenVINO环境变量

小提示:接下来要使用OV就要设置好它的环境变量。官方教程(地址:

https://docs.openvinotoolkit.org/cn/latest/openvino_docs_install_guides_installing_openvino_linux.html#set-the-environment-variables)要求把环境变量加载到您的 .bashrc文件里,这样每次打开任何命令行接口都可以自动加载OV的环境变量。但是我在实际使用过程中发现了一个问题。安装完OV后,我的一部分程序开始报错,出现了一个和Gstreamer相关的错误信息。经过研究发现原来OV的环境变量会和Anaconda的虚拟环境冲突,导致GStreamer出现问题。

 

其实解决方法也很简单。我们一般只会在模型转换的时候用到OV,那么就不要把OV的环境变量设置到.bashrc文件里面,只需要在使用OV之前,在命令行里激活OV的环境变量就行。激活环境变量的方法如下:

source /opt/intel/openvino_2021/bin/setupvars.sh

记住/opt/intel/openvino_2021/bin 是默认的OV安装路径,如果您改变了路径,请记得也随之改变这里的路径。

 

4.3.3

配置模型优化器

Model Optimizer(MO)

相信我同志们,我知道过程很长,但是曙光就在眼前啦~!这个就是开始转OpenVINO IR模型前要调整的最后一步,坚持住~!

 

MO是一个基于Python的命令行工具,可以用来从其他流行的人工智能框架例如Caffe,ONNX,TensorFlow等导入训练好的模型。没有用MO优化过的模型是不能用来在OV上做推理的。

 

在这一步可以只为您需要的环境比如ONNX,或者Tensorflow等做配置,但也可以一下配置好可以适用于各种人工智能框架的环境。我在这里选择了后者,毕竟路慢慢其修远 现在用ONNX 之后也有可能用到任何其他网络。

那么第一步先CD到MO设置的文件夹里面:

 

4.3.4

转ONNX模型到IR模式

cd /opt/intel/openvino_2021/deployment_tools/model_optimizer

python mo_onnx.py --input_model /inference/onnx_models/road_seg.onnx

--output_dir /openvino/FP16

--input_shape [1,3,224,224]

--data_type FP16

--scale_values [127.5,127.5,127.5]

--mean_values [127.5,127.5,127.5]

 

这里需要提一下怎样找出—input_shape 的参数值.

一般来说模型是自己训练的 那么您在训练的时候就要指定输入图片的尺寸大下, 那么这个512,512就是指图片的宽和高. 前面的1是batch size,3是图片的颜色通道数.

 

-data_type来指定模型的精度,

 

如果转换成功,您将会看到如下输出:

Model Optimizer arguments:

Common parameters:

       - Path to the Input Model:      /Models/Paddle/Segmentation/DeepLabV3+/Onnx/pascalvoc.onnx

       - Path for generated IR:        /Models/Paddle/Segmentation/DeepLabV3+/OpenVINO/

       - IR output name:      pascalvoc

       - Log level:  ERROR

       - Batch:        Not specified, inherited from the model

       - Input layers:     Not specified, inherited from the model

       - Output layers:  Not specified, inherited from the model

       - Input shapes:    [1,3,512,512]

       - Mean values:    Not specified

       - Scale values:    Not specified

       - Scale factor:     Not specified

       - Precision of IR:       FP16

       - Enable fusing:  True

       - Enable grouped convolutions fusing:  True

       - Move mean values to preprocess section:  None

       - Reverse input channels:        False

ONNX specific parameters:

       - Inference Engine found in:        /opt/Intel/OpenVINO_2021/python/python3.8/OpenVINO

Inference Engine version:       2021.4.0-3839-cd81789d294-releases/2021/4

Model Optimizer version:       2021.4.0-3839-cd81789d294-releases/2021/4

[ SUCCESS ] Generated IR version 10 model.

[ SUCCESS ] XML file: /Models/Paddle/Segmentation/DeepLabV3+/OpenVINO/pascalvoc.xml

[ SUCCESS ] BIN file: /Models/Paddle/Segmentation/DeepLabV3+/OpenVINO/pascalvoc.bin

[ SUCCESS ] Total execution time: 18.65 seconds.

[ SUCCESS ] Memory consumed: 640 MB.

 

4.4

验证转换后的IR模型

在继续下去之前我们应该先检验一下这个模型是否真的转换成功。

在运行如下代码之前,请换一个命令行窗口,并且启动之前创建的Anaconda 环境,这样做是为了确保OV的环境变量和Conda的不互相冲突,产生错误。

运行如下代码infer_deeplabv3p.py地址:https://github.com/franva/Intel-OpenVINO-Paddle/blob/main/infer_deeplabv3p.py

检测转换后的模型的正确性:

import cv2

import numpy as np

from OpenVINO.inference_engine import IENetwork, IECore

import paddleseg.transforms as T

 

from colors_pascalvoc import ColorMap_PASCALVOC

 

# Please update the pathes to xml and bin files respectively on your machine

model_xml = r'/Models/Paddle/Segmentation/DeepLabV3+/OpenVINO/pascalvoc.xml'

model_bin = r'/Models/Paddle/Segmentation/DeepLabV3+/OpenVINO/pascalvoc.bin'

 

 

ie = IECore()

 

# Read IR

net = IENetwork(model=model_xml, weights=model_bin)

input_blob = next(iter(net.inputs))

out_blob = next(iter(net.outputs))

exec_net = ie.load_network(network=net, device_name="CPU")

 

del net

 

transforms = [

            T.Resize(target_size=(512,512)),

            T.Normalize()

        ]

 

def show_img(img, window_name, channel_first=True):

    visual = img

    if channel_first:

        visual = img.transpose(1,2,0)

        visual = cv2.resize(visual, (600, 600))

    else:

        visual = cv2.resize(visual, (600,600))

    cv2.imshow(window_name, visual)

 

def save_img(img, img_fn):

    cv2.imwrite(img_fn, img)

 

 

# Run inference, replace this mp4 file with your own video

video = '/Testing Videos/mel_highway.mp4'

 

cap = cv2.VideoCapture(video)

read_successfully, frame = cap.read()

 

while read_successfully:   

 

    if read_successfully == False:

        continue

 

    resized_frame, tesrt = T.Compose([transforms[0]])(frame)

    img, _ = T.Compose(transforms)(frame)

 

    # add an new axis in front

    img_input = img[np.newaxis, :]

    result = exec_net.infer(inputs={input_blob: img_input})

 

    img_segmentation = result['save_infer_model/scale_0.tmp_1']

    img_segmentation = np.squeeze(img_segmentation)

    class_colors = ColorMap_PASCALVOC.COLORS

    class_colors = np.asarray(class_colors, dtype=np.uint8)

    img_mask = class_colors[img_segmentation]

 

    img_mask = img_mask.transpose(2, 0, 1)

 

    img_overlayed = cv2.addWeighted(resized_frame, 1, img_mask, 1, 0.5)

    img_overlayed = img_overlayed.transpose(1,2,0)

    img_overlayed = cv2.cvtColor(img_overlayed, cv2.COLOR_RGB2BGR)

    show_img(img_overlayed, 'overlayed', False)

    show_img(img_mask, 'mask', True)

 

    if cv2.waitKey(1) == ord('q'):

        break

 

    read_successfully, frame = cap.read()

 

cap.release()

cv2.destroyAllWindows()

 

4.5

模型性能和吞吐量

测试好模型之后,我们还可以检查一下模型的性能和吞吐量。幸运的是,Intel DevCloud已经提供了现有的工具来帮助我们快速完成这项工作。

 

我们任意挑选了几套硬件搭配,来看一下刚转换好的IR模型性能。

 

图像分割

图八:  Benchmark of DeepLabV3+模型

 

由上图可以看出,更好的GPU支持会带来更多的性能提升。同样大部分AI模型检测,分类,分割任务不需要很高的分辨率,所以适当降低输入图像的尺寸后也能大幅度提高模型的性能。

 

关于如何使用Intel DevCloud,更详细的步骤请参考: 

使用OpenVINO优化和部署DenseNet模型并在DevCloud上完成性能测试-上篇

使用OpenVINO 优化和部署DenseNet模型并在DevCloud上完成性能测试-下篇

 

至此,整个流程结束。恭喜大家成功的把模型落地,并且部署到了边缘设备上。期待看到你们各个精彩的应用啦!

最后贴上Github 的本文章的陪同代码库(地址:https://github.com/franva/Intel-OpenVINO-Paddle),欢迎大家提出宝贵的意见。

 

05

总结

 

 

本文一开始先介绍了图像分割和语义分割,阐述了用OpenVINO 工具套件部署模型的重要性。快速介绍了OpenVINO 工具套件以及百度的 PaddlePaddle框架。然后以一个训练好的百度飞桨Paddle模型为例开始,一步一步带着大家把模型转换到了OpenVINO 工具套件的IR格式,直到部署到CPU上面。对于不同的模型,只需要适量的改动,便可以快速独立的开发属于自己的AI应用程序。

编辑:jq


打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分