关于PaddleNLP的使用调研

近期组内新增了一些自然语言处理的需求,主要是中文分词和通用文本分类的需求。关于分词已经有很多成熟的方案了(例如利用 ElasticSearch 的分词器),但文本分类的需求就需要更专业的工具来处理,恰好发现百度的 PaddleNLP 是一个不错的选择。

PaddlePaddle 是百度开源的一个深度学习平台,集深度学习核心训练和推理框架、基础模型库、端到端开发套件和丰富的工具组件于一体。PaddleNLP 是一款简单易用且功能强大的自然语言处理开发库。聚合业界优质预训练模型并提供开箱即用的开发体验,覆盖NLP多场景的模型库,搭配产业实践范例,提供极致的训练与推理性能,可满足灵活定制的开发需求。

本文基于 Python 3.9.18 以及 PaddlePaddle 2.6 + PaddleNLP 2.7.2 在 Windows 系统下进行调研,不同版本可能有所差异。因为是预研测试,暂时安装 CPU 版本的 PaddlePaddle,生产环境可根据服务器配置选择 GPU 版本加快处理速度及训练。

安装 PaddleNLP

由于 Python 相同库的不同版本可能存在不兼容的问题,因此使用 conda 进行环境管理,方便项目管理。

  1. 安装 MiniConda
    Anaconda 商业化使用收费。可前往 MiniConda官网 下载安装

  2. 配置 conda 环境并安装 PaddleNLP
    打开 Miniconda Prompt ,创建一个新的环境并安装 PaddlePaddle 以及 PaddleNLP。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 创建名为 paddle_39 的环境
    conda create -n paddle_39 python=3.9
    # 切换到 paddle_39 环境
    conda activate paddle_39
    # 切换到清华源加速
    conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
    conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
    conda config --set show_channel_urls yes
    # 安装 PaddlePaddle
    conda install paddlepaddle==2.6.2 --channel https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/Paddle/
    # 安装 PaddleNLP
    conda install paddlenlp

使用 PaddleNLP

初次使用 PaddleNLP 的 Taskflow 功能可能需要下载官方预训练的模型,默认存储在 $HOME/.paddlenlp 目录下。如果服务器数据磁盘挂载有特殊设置或内网部署,则需要在调用时手动修改指定存储路径。

1
2
from paddlenlp import Taskflow
ner = Taskflow("ner", home_path="/workspace")
  1. 中文分词

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    (paddle_39) PS C:\Users\aofall> python
    Python 3.9.18 (main, Sep 11 2023, 14:09:26) [MSC v.1916 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from paddlenlp import Taskflow
    # 默认模式
    >>> seg = Taskflow("word_segmentation")
    >>> seg("近日国家卫健委发布第九版新型冠状病毒肺炎诊疗方案")
    ['近日', '国家卫健委', '发布', '第九版', '新型', '冠状病毒肺炎', '诊疗', '方案']
    # 基于jieba的快速模式
    >>> seg_fast = Taskflow("word_segmentation", mode="fast")
    >>> seg_fast("近日国家卫健委发布第九版新型冠状病毒肺炎诊疗方案")
    ['近日', '国家', '卫健委', '发布', '第九版', '新型', '冠状病毒', '肺炎', '诊疗', '方案']
    # 基于预训练模型的精确模式
    >>> seg_accurate = Taskflow("word_segmentation", mode="accurate")
    >>> seg_accurate("近日国家卫健委发布第九版新型冠状病毒肺炎诊疗方案")
    ['近日', '国家卫健委', '发布', '第九版', '新型冠状病毒肺炎', '诊疗', '方案']
  2. 零样本文本分类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    (paddle_39) PS C:\Users\aofall> python
    Python 3.9.18 (main, Sep 11 2023, 14:09:26) [MSC v.1916 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from paddlenlp import Taskflow
    >>> cls = Taskflow("zero_shot_text_classification", schema=["好评", "差评"])
    >>> cls("房间干净明亮,非常不错")
    [{'text_a': '房间干净明亮,非常不错', 'predictions': [{'label': '好评', 'score': 0.9982299185674333}]}]
    >>> cls("东西还可以,但是快递非常慢,下次不会再买这家了。")
    [{'text_a': '东西还可以,但是快递非常慢,下次不会再买这家了。', 'predictions': [{'label': '差评', 'score': 0.8738513034090186}]}]

接入 Java 应用

由于 PaddlePaddle 是基于 Python 的应用程序,无法直接与 Java 应用程序互通,则需要考虑接入方案,此处以零样本文本分类功能举例。

  1. 使用Java的 Runtime API 进行接入
    这种方案是最简单,但需要 Java 应用和 PaddlePaddle 部署在同一台服务器上,不方便扩展。

  2. 使用 Django/Flask 等 Web 框架提供 Restful API 接入或使用 GRPC

这里举例使用 Django 提供 Restful API。

myapp/urls.py 中添加 URL 指定路由 /process-cls

1
2
3
4
5
6
from django.urls import path
from .views import ProcessView

urlpatterns = [
path('process-cls/', ProcessView.as_view(), name='process'),
]

编写Controller逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from django.http import JsonResponse
from django.views import View
from paddlenlp import Taskflow
import json

class ProcessView(View):
def post(self, request, *args, **kwargs):
try:
# 获取请求中的 JSON 数据
data = json.loads(request.body)
schema = data.get('schema')
text_list = data.get('text')

cls = Taskflow("zero_shot_text_classification", schema)

# 构造响应数据
response_data = []
for text, label in zip(text_list, schema):
handle_data = cls(text)
// 取出处理后的数据并组装,此处省略...
// [{'text_a': '东西还可以,但是快递非常慢,下次不会再买这家了。', 'predictions': [{'label': '差评', 'score': 0.8738513034090186}]}]
response_data.append({
"text": text,
"label": label,
"score": score
})
return JsonResponse({"data": response_data})
except json.JSONDecodeError:
return JsonResponse({"error": "Invalid JSON format"}, status=400)
except Exception as e:
return JsonResponse({"error": str(e)}, status=500)
  1. 使用消息队列进行处理
    Java 应用侧在接收到请求后向消息队列中发送处理请求,Python 侧在拉取到请求处理,并将处理后的文本再次发回消息队列给 Java 应用侧消费。这个方案需要做好幂等控制以及消息丢失的超时处理。