Fine-tune保存的模型如何转化为一个PaddleHub Module

模型基本信息

本示例以模型ERNIE Tiny在数据集ChnSentiCorp上完成情感分类Fine-tune任务后保存的模型转化为一个PaddleHub Module,Module的基本信息如下:

  1. name: ernie_tiny_finetuned
  2. version: 1.0.0
  3. summary: ERNIE tiny which was fine-tuned on the chnsenticorp dataset.
  4. author: anonymous
  5. author_email:
  6. type: nlp/semantic_model

本示例代码可以参考finetuned_model_to_module

Module存在一个接口predict,用于接收带预测,并给出文本的情感倾向(正面/负面),支持python接口调用和命令行调用。

  1. import paddlehub as hub
  2. ernie_tiny_finetuned = hub.Module(name="ernie_tiny_finetuned")
  3. ernie_tiny_finetuned.predcit(data=[["这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般"], ["交通方便;环境很好;服务态度很好 房间较小"],
  4. ["19天硬盘就罢工了~~~算上运来的一周都没用上15天~~~可就是不能换了~~~唉~~~~你说这算什么事呀~~~"]])

Module创建

step 1. 创建必要的目录与文件

创建一个finetuned_model_to_module的目录,并在finetuned_model_to_module目录下分别创建__init__.py、module.py,其中

文件名用途
init.py空文件
module.py主模块,提供Module的实现代码
ckpt文件利用PaddleHub Fine-tune得到的ckpt文件夹,其中必须包含best_model文件
  1. tree finetuned_model_to_module
  2. finetuned_model_to_module/
  3. ├── __init__.py
  4. ├── ckpt_chnsenticorp
  5. ├── ***
  6. ├── best_model
  7. ├── ***
  8. └── module.py

step 2. 编写Module处理代码

module.py文件为Module的入口代码所在,我们需要在其中实现预测逻辑。

step 2_1. 引入必要的头文件

  1. import os
  2. import numpy as np
  3. from paddlehub.common.logger import logger
  4. from paddlehub.module.module import moduleinfo, serving
  5. import paddlehub as hub

step 2_2. 定义ERNIE_Tiny_Finetuned类

module.py中需要有一个继承了hub.Module的类存在,该类负责实现预测逻辑,并使用moduleinfo填写基本信息。当使用hub.Module(name=”ernie_tiny_finetuned”)加载Module时,PaddleHub会自动创建ERNIE_Tiny_Finetuned的对象并返回。

  1. @moduleinfo(
  2. name="ernie_tiny_finetuned",
  3. version="1.0.0",
  4. summary="ERNIE tiny which was fine-tuned on the chnsenticorp dataset.",
  5. author="anonymous",
  6. author_email="",
  7. type="nlp/semantic_model")
  8. class ERNIETinyFinetuned(hub.Module):
  9. ...

step 2_3. 执行必要的初始化

  1. def _initialize(self,
  2. ckpt_dir="ckpt_chnsenticorp",
  3. num_class=2,
  4. max_seq_len=128,
  5. use_gpu=False,
  6. batch_size=1):
  7. self.ckpt_dir = os.path.join(self.directory, ckpt_dir)
  8. self.num_class = num_class
  9. self.MAX_SEQ_LEN = max_seq_len
  10. self.params_path = os.path.join(self.ckpt_dir, 'best_model')
  11. if not os.path.exists(self.params_path):
  12. logger.error(
  13. "%s doesn't contain the best_model file which saves the best parameters as fietuning."
  14. )
  15. exit()
  16. # Load Paddlehub ERNIE Tiny pretrained model
  17. self.module = hub.Module(name="ernie_tiny")
  18. inputs, outputs, program = self.module.context(
  19. trainable=True, max_seq_len=max_seq_len)
  20. self.vocab_path = self.module.get_vocab_path()
  21. # Download dataset and use accuracy as metrics
  22. # Choose dataset: GLUE/XNLI/ChinesesGLUE/NLPCC-DBQA/LCQMC
  23. # metric should be acc, f1 or matthews
  24. metrics_choices = ["acc"]
  25. # For ernie_tiny, it use sub-word to tokenize chinese sentence
  26. # If not ernie tiny, sp_model_path and word_dict_path should be set None
  27. reader = hub.reader.ClassifyReader(
  28. vocab_path=self.module.get_vocab_path(),
  29. max_seq_len=max_seq_len,
  30. sp_model_path=self.module.get_spm_path(),
  31. word_dict_path=self.module.get_word_dict_path())
  32. # Construct transfer learning network
  33. # Use "pooled_output" for classification tasks on an entire sentence.
  34. # Use "sequence_output" for token-level output.
  35. pooled_output = outputs["pooled_output"]
  36. # Setup feed list for data feeder
  37. # Must feed all the tensor of module need
  38. feed_list = [
  39. inputs["input_ids"].name,
  40. inputs["position_ids"].name,
  41. inputs["segment_ids"].name,
  42. inputs["input_mask"].name,
  43. ]
  44. # Setup runing config for PaddleHub Finetune API
  45. config = hub.RunConfig(
  46. use_data_parallel=False,
  47. use_cuda=use_gpu,
  48. batch_size=batch_size,
  49. checkpoint_dir=self.ckpt_dir,
  50. strategy=hub.AdamWeightDecayStrategy())
  51. # Define a classfication finetune task by PaddleHub's API
  52. self.cls_task = hub.TextClassifierTask(
  53. data_reader=reader,
  54. feature=pooled_output,
  55. feed_list=feed_list,
  56. num_classes=self.num_class,
  57. config=config,
  58. metrics_choices=metrics_choices)

初始化过程即为Fine-tune时创建Task的过程。

NOTE:

  1. 执行类的初始化不能使用默认的__init__接口,而是应该重载实现_initialize接口。对象默认内置了directory属性,可以直接获取到Module所在路径。

  2. 使用Fine-tune保存的模型预测时,无需加载数据集Dataset,即Reader中的dataset参数可为None。

step 3_4. 完善预测逻辑

  1. def predict(self, data, return_result=False, accelerate_mode=True):
  2. """
  3. Get prediction results
  4. """
  5. run_states = self.cls_task.predict(
  6. data=data,
  7. return_result=return_result,
  8. accelerate_mode=accelerate_mode)
  9. results = [run_state.run_results for run_state in run_states]
  10. prediction = []
  11. for batch_result in results:
  12. # get predict index
  13. batch_result = np.argmax(batch_result, axis=2)[0]
  14. batch_result = batch_result.tolist()
  15. prediction += batch_result
  16. return prediction

step 3_5. 支持serving调用

如果希望Module可以支持PaddleHub Serving部署预测服务,则需要将预测接口predcit加上serving修饰(@serving),接口负责解析传入数据并进行预测,将结果返回。

如果不需要提供PaddleHub Serving部署预测服务,则可以不需要加上serving修饰。

  1. @serving
  2. def predict(self, data, return_result=False, accelerate_mode=True):
  3. """
  4. Get prediction results
  5. """
  6. run_states = self.cls_task.predict(
  7. data=data,
  8. return_result=return_result,
  9. accelerate_mode=accelerate_mode)
  10. results = [run_state.run_results for run_state in run_states]
  11. prediction = []
  12. for batch_result in results:
  13. # get predict index
  14. batch_result = np.argmax(batch_result[0], axis=1)
  15. batch_result = batch_result.tolist()
  16. prediction += batch_result
  17. return prediction

完整代码

NOTE: __init__.py是空文件

测试步骤

完成Module编写后,我们可以通过以下方式测试该Module

调用方法1

将Module安装到本机中,再通过Hub.Module(name=…)加载

  1. hub install finetuned_model_to_module

安装成功会显示Successfully installed ernie_tiny_finetuned

  1. import paddlehub as hub
  2. import numpy as np
  3. ernie_tiny = hub.Module(name="ernie_tiny_finetuned")
  4. # Data to be prdicted
  5. data = [["这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般"], ["交通方便;环境很好;服务态度很好 房间较小"],
  6. ["19天硬盘就罢工了~~~算上运来的一周都没用上15天~~~可就是不能换了~~~唉~~~~你说这算什么事呀~~~"]]
  7. predictions = ernie_tiny.predict(data=data)
  8. for index, text in enumerate(data):
  9. print("%s\tpredict=%s" % (data[index][0], predictions[index]))

调用方法2

直接通过Hub.Module(directory=…)加载

  1. import paddlehub as hub
  2. import numpy as np
  3. ernie_tiny_finetuned = hub.Module(directory="finetuned_model_to_module/")
  4. # Data to be prdicted
  5. data = [["这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般"], ["交通方便;环境很好;服务态度很好 房间较小"],
  6. ["19天硬盘就罢工了~~~算上运来的一周都没用上15天~~~可就是不能换了~~~唉~~~~你说这算什么事呀~~~"]]
  7. predictions = ernie_tiny.predict(data=data)
  8. for index, text in enumerate(data):
  9. print("%s\tpredict=%s" % (data[index][0], predictions[index]))

调用方法3

将finetuned_model_to_module作为路径加到环境变量中,直接加载ERNIETinyFinetuned对象

  1. export PYTHONPATH=finetuned_model_to_module:$PYTHONPATH
  1. from finetuned_model_to_module.module import ERNIETinyFinetuned
  2. import numpy as np
  3. # Data to be prdicted
  4. data = [["这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般"], ["交通方便;环境很好;服务态度很好 房间较小"],
  5. ["19天硬盘就罢工了~~~算上运来的一周都没用上15天~~~可就是不能换了~~~唉~~~~你说这算什么事呀~~~"]]
  6. predictions = ERNIETinyFinetuned.predict(data=data)
  7. for index, text in enumerate(data):
  8. print("%s\tpredict=%s" % (data[index][0], predictions[index]))

PaddleHub Serving调用方法

第一步:启动预测服务

  1. hub serving start -m ernie_tiny_finetuned

第二步:发送请求,获取预测结果

通过如下脚本既可以发送请求:

  1. # coding: utf8
  2. import requests
  3. import json
  4. # 待预测文本
  5. texts = [["这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般"], ["交通方便;环境很好;服务态度很好 房间较小"],
  6. ["19天硬盘就罢工了~~~算上运来的一周都没用上15天~~~可就是不能换了~~~唉~~~~你说这算什么事呀~~~"]]
  7. # key为'data', 对应着预测接口predict的参数data
  8. data = {'data': texts}
  9. # 指定模型为ernie_tiny_finetuned并发送post请求,且请求的headers为application/json方式
  10. url = "http://127.0.0.1:8866/predict/ernie_tiny_finetuned"
  11. headers = {"Content-Type": "application/json"}
  12. r = requests.post(url=url, headers=headers, data=json.dumps(data))
  13. # 打印预测结果
  14. print(json.dumps(r.json(), indent=4, ensure_ascii=False))

关与PaddleHub Serving更多信息参见Hub Serving教程以及Demo