ERNIE | 「最棒」的語義理解框架,AWS 天然全力支持

專屬圖1.png

關注機器學習技術的童鞋可能已經據說了,最近舉辦的全球最大規模語義評測比賽 SemEval 2020中,語義理解框架 ERNIE 斬獲了包括視覺媒體的關鍵文本片斷挖掘、多語攻擊性語言檢測和混合語種的情感分析等在內的5項世界冠軍!html

ERNIE(Enhanced Representation through Knowledge Integration)旨在使用純文本捕獲語義模式的基礎之上,進一步經過知識加強的方式,結合語義中的知識關係來提供更加豐富的結構化表現。所提出的知識加強語義表示模型,以及2.0版本構建的持續學習語義理解框架,在中英文等多個任務上超越業界最好模型。尤爲在多項中文 NLP 任務中,ERNIE 的結果都能與 BERT 持平或有所提高。python

也許有童鞋還不知道,ERNIE 1.0在發佈之初,針對 BERT(Pre-training of Deep Bidirectional Transformers for Language Understanding)採用了以字爲單位的建模方式,但這種方式沒法學習到知識單元的完整語義表示。後來曾有人提出使用知識實體來學習完整概念的語義表示,並引入多源的數據語料進行訓。於是從2.0版開始,ERNIE 提出了一種預訓練框架,經過連續學習(Continual Learning)方式,使用多個不一樣的任務順序訓練一個模型,這樣,下個任務就會利用到前面任務的學習成果,不斷積累新的知識。面對新任務,能夠直接使用歷史任務學習到的參數來初始化模型,以獲取更好的訓練效果。git

接下來是一個更棒的消息:AWS,尤爲是 Amazon SageMaker 服務已經提供了針對ENRIE 2.0的全面支持!github

Amazon SageMaker 是亞馬遜雲計算(Amazon Web Service)的一項徹底託管的機器學習平臺服務,算法工程師和數據科學家能夠基於此平臺快速構建、訓練和部署機器學習(ML)模型,而無需關注底層資源的管理和運維工做。它做爲一個工具集,提供了用於機器學習的端到端的全部組件,包括數據標記、數據處理、算法設計、模型訓練、訓練調試、超參調優、模型部署、模型監控等,使得機器學習變得更爲簡單和輕鬆;同時,它依託於 AWS 強大的底層資源,提供了高性能 CPU、GPU、彈性推理加速卡等豐富的計算資源和充足的算力,使得模型研發和部署更爲輕鬆和高效。算法

ERNIE 是基於開源深度學習平臺 —— 飛槳(PaddlePaddle)來設計和實現的,本文會重點介紹如何經過 Amazon SageMaker 來實現基於此類第三方框架和自定義算法的模型預訓練、增量訓練和推理部署等機器學習任務,實現的技術細節和操做方法一樣適用於其餘相似的使用場景。docker

本文首先會重點介紹在 Amazon SageMaker 中使用 ERNIE 來進行模型預訓練(pretraining)任務的方法。json

Amazon SageMaker 模型訓練介紹

Amazon SageMaker 爲機器學習任務提供了基礎算力和操做平臺,在該平臺上啓動模型訓練任務,包括以下過程:從 Amazon SageMaker 的計算集羣中啓用指定算力的機器實例,實例加載包含了算法和框架在內的容器鏡像,而且加載存放於外部存儲(如 S3)的訓練、驗證、測試等數據集,啓動訓練腳本,完成模型迭代,保存模型等。而用戶僅需作好相應參數配置,便可經過控制檯點擊或 API 調用方式完成。session

訓練任務的核心在於選擇包含算法和框架在內的容器鏡像,關於訓練鏡像的來源,咱們既能夠選擇由 Amazon SageMaker 提供的多種內置算法鏡像,也能夠選擇基於 Amazon SageMaker 內置框架(TensorFlow、Apache MXNet、PyTorch、Scikit-learn、XGBoost、Chainer)鏡像,結合本身的代碼來完成訓練。若是須要使用本身的訓練代碼,並基於其餘第三方框架或自帶框架來完成模型訓練,也能夠經過自帶容器的方法基於 Amazon SageMaker 來實現。app

本文會針對最後一種狀況,重點介紹在 Amazon SageMaker 上如何基於 PaddlePaddle 框架,結合自定義算法來完成 ERNIE 模型的預訓練任務。該方法一樣適用於其餘自定義框架和算法的相似任務的實現。框架

經過自帶容器方法實現自定義算法的模型預訓練

關於 ERNIE 模型的預訓練任務請參見此連接中的「預訓練 (ERNIE 1.0)」章節。

若是在本地機器運行該預訓練任務,須要完成如下兩項工做:

  • 本地安裝 PaddlePaddle 框架
  • 本地執行訓練腳本

爲將該任務從本地機器遷移到 Amazon SageMaker,咱們須要針對這兩項工做進行以下調整:
1.本地安裝 PaddlePaddle 框架 -> 構建適配於 SageMaker Container 的 PaddlePaddle 容器
1) Amazon SageMaker 是基於容器機制來實現機器學習任務的,對於自帶框架和算法的場景,須要自行構建包含目標框架及相關依賴項在內的容器,並推送到 Amazon Elastic Container Registry(ECR)鏡像註冊表中,供 SageMaker 進行拉取和調用。
2) 在本文示例中,該部分工做的工程目錄以下,該工程能夠部署在 SageMaker Jupyter Notebook(推薦)或其餘計算資源上。

/<for-docker-directory>/
├── ernie
│   ├── …
├── Dockerfile
├── requirements.txt
└── docker-actions.ipynb

說明:

  • ernie:爲源碼中 ernie 框架實現代碼,文件目錄中的內容也進行了一些調整,主要包括:

    ~ 將根目錄下的 config 文件夾拷貝到此目錄下;
    ~ 爲適配接口的調用,修正 pretrain_args.py、train.py 和 pretraining.py 三個文件中的部分代碼,具體細節請見下文描述。

  • Dockerfile:Docker 描述文件
  • requirements.txt:爲源碼工程根目錄下依賴項描述文件,這裏的requirements.txt 去掉 paddlepaddle-gpu==1.6.3.post107 一項;
  • docker-actions.ipynb:容器打包和上傳代碼,以筆記本方式調用。

3)編寫 Dockerfile,本文示例的 Dockerfile 及說明以下:

# 拉取預安裝 PaddlePaddle 的鏡像,具體鏡像參考https://www.paddlepaddle.org.cn/documentation/docs/zh/1.7/install/install_Docker.html#docker
 
FROM paddlepaddle/paddle:1.6.3-gpu-cuda10.0-cudnn7
 
# 標記維護者信息
 
MAINTAINER Amazon AI <sage-learner@amazon.com>
 
# 拷貝requirements.txt到容器代碼目錄
 
COPY requirements.txt /opt/ml/code/requirements.txt
 
# 安裝依賴項
 
RUN pip install -r /opt/ml/code/requirements.txt
 
# 安裝 SageMaker Container
 
RUN pip install sagemaker-containers==2.6.1
 
# 將git工程中的ernie文件夾拷貝到容器的/opt/ml/code目錄下
 
COPY ernie /opt/ml/code
 
# 定義train.py爲訓練任務腳本
 
ENV SAGEMAKER_PROGRAM train.py

說明:

  • SageMaker Container 做爲一個庫,它能夠運行腳本、訓練算法或部署與 Amazon SageMaker 兼容的模型,它定義了咱們的安裝代碼、數據等資源在容器中的存儲位置,咱們須要經過 Dockerfile 將要運行的代碼保存在 SageMaker 容器所指望的位置(/opt/ml/code),具體內容請參見這裏和這裏。
  • ernie 目錄也按照 SageMaker Container 目錄要求拷貝到/opt/ml/code;
  • 設置環境變量 SAGEMAKER_PROGRAM,定義 train.py 爲訓練任務腳本。

4)執行 docker-actions.ipynb 內的容器打包和上傳代碼:

# 設置權限並構建容器
 
!chmod +x train.py
 
!docker build -t pd-ernie-pretrain:v1 .
 
# 獲取ECR login
 
!$(aws ecr get-login --no-include-email --region us-west-2)
 
# 容器打標籤
 
!docker tag pd-ernie-pretrain:v1 <your-aws-account>.dkr.ecr.<region>.amazonaws.com/<    
 
repository>:pd-ernie-pretrain
 
# 將容器推送到ECR
 
!docker push "<your-aws-account>.dkr.ecr.<region>.amazonaws.com/<         
 
repository>:pd-ernie-pretrain"

這樣,在咱們的 AWS 帳號 ECR 服務的指定存儲庫中就包含了一個集成 PaddlePaddle 框架和 ERNIE 工程的模型訓練容器。請記住該容器在存儲庫中的名稱」<your-aws-account>.dkr.ecr.<region>.amazonaws.com/<repository>:pd-ernie-pretrain」,供 Amazon SageMaker 後續調用。

2. 本地執行訓練腳本 -> 經過 API 方式在 Amazon SageMaker 上啓動訓練任務
本地執行訓練 ERNIE 預訓練的腳本爲 script/zh_task/pretrain.sh,其中核心的訓練命令爲:

python ./ernie/train.py --use_cuda True \
 
                --is_distributed False\
 
                --use_fast_executor True \
 
                --weight_sharing True \
 
                --in_tokens true \
 
                --batch_size 8192 \
 
                --vocab_path ./config/vocab.txt \
 
                --train_filelist ./data/train_filelist \
 
                --valid_filelist ./data/valid_filelist \
 
                --validation_steps 100 \
 
                --num_train_steps 1000000 \
 
                --checkpoints ./checkpoints \
 
                --save_steps 10000 \
 
                --ernie_config_path ./config/ernie_config.json \
 
                --learning_rate 1e-4 \
 
                --use_fp16 false \
 
                --weight_decay 0.01 \
 
                --max_seq_len 512 \
 
                --skip_steps 10

該命令形式也是咱們在本地機器上執行訓練任務的常規方式,如今咱們一塊兒看一看如何將該命令轉化爲 API 參數在 Amazon SageMaker 上啓動訓練任務。

該命令的參數主要包括3個部分:基礎配置項(如 use_cuda)、輸入數據路徑(如 vocab_path)以及算法超參(如 learning_rate)。其中輸入數據路徑又分爲基礎配置數據路徑(vocab_path、ernie_config_path)和數據集路徑(train_filelist、valid_filelist)。在使用 Amazon SageMaker 進行模型訓練過程當中,最佳實踐是將數據集存放於外部的存儲服務中,如 Amazon Simple Storage Service (S3)、Amazon Elastic File System (EFS)、Amazon FSx,通知 SageMaker 拉取相應數據集進行計算任務。所以,這裏的train_filelist和valid_filelist 不會經過此參數傳遞方式將一個本地路徑輸入至訓練腳本,而是經過 SageMaker 的 data channel方式指定 S3 中的數據集的位置。去掉 train_filelist 和 valid_filelist 兩個參數後,將剩餘參數構造以下_hyperparameters 的字典對象:

_hyperparameters = {
                            "use_cuda": True,
                            "is_distributed":False,
                            "use_fast_executor":True,
                            "weight_sharing":True,
                            "in_tokens":True,
                            "batch_size":8192,
                            "vocab_path":"./config/vocab.txt",
                            "num_train_steps":10000,
                            "checkpoints":"./checkpoints",
                            "save_steps":1000,
                            "ernie_config_path":"./config/ernie_config.json",
                            "learning_rate":"0.0001",
                            "use_fp16":False,
                            "weight_decay":0.01,
                            "max_seq_len":512,
                            "skip_steps":10,
                            "validation_steps": 100
              }

對於 train_filelist 和 valid_filelist 兩個文件列表文件,咱們首先看一下 data 路徑下的數據組成:

ERNIE/data/
├── demo_train_set.gz
├── demo_valid_set.gz
├── train_filelist
└── valid_filelist

其中 demo_train_set.gz 和 demo_valid_set.gz 爲編碼後的數據文件,train_filelist 爲數據文件列表描述:

./data/demo_train_set.gz        1.1

在本文示例中,咱們擴展數據集及並修正文件列表以下:

ERNIE/data/
├── demo_train_set.gz
├── demo_train_set2.gz
├── demo_train_set3.gz
├── demo_train_set4.gz
├── demo_train_set5.gz
├── demo_valid_set.gz
├── demo_valid_set2.gz
├── train_filelist
└── valid_filelist
train_filelist:
 
demo_train_set.gz      1.1
 
demo_train_set2.gz     1.1
 
demo_train_set3.gz     1.1
demo_train_set4.gz     1.1
 
demo_train_set5.gz     1.1
valid_filelist:
 
demo_valid_set.gz      1.1
 
demo_valid_set2.gz     1.1

修正後,將數據集文件及文件列表上傳至 S3:

<your-S3-bucket>/ernie/
├── train
│   ├── demo_train_set.gz
│   ├── demo_train_set2.gz
│   ├── demo_train_set3.gz
│   ├── demo_train_set4.gz
│   ├── demo_train_set5.gz
│   ├── train_filelist
├── valid
│   ├── demo_valid_set.gz
│   ├── demo_valid_set2.gz
└─ ├── valid_filelist

上傳後,咱們構建一個數據通道字典,描述對應數據集在 S3 的存儲位置:

_train_data = 'S3://{}/{}/{}'.format(bucket, folder, 'train')
_valid_data = 'S3://{}/{}/{}'.format(bucket,  folder, 'valid')
_data_channels = {'train': sagemaker.session.S3_input(_train_data),
               'valid': sagemaker.session.S3_input(_valid_data)}

SageMaker 收到此 data_channel 後會自動從 S3 對應位置拉取數據,下載到容器/opt/ml/data/<channel_name>/路徑下,這裏的< channel_name>對應的是字典兩個 Key:train 和 valid。

所以,爲指導訓練腳本從/opt/ml/data/<channel_name>/路徑下讀取訓練數據,須要對pretrain_args.py、train.py和pretraining.py進行一些修正(源工程是基於參數中的–train_filelist和–valid_filelist路徑進行數據讀取的),修正方式以下:

pretrain_args.py
 
data_g.add_arg("train_filelist",           str,  "",  "Path to training filelist.")
data_g.add_arg("valid_filelist",           str,  "",  "Path to valid filelist.")

修改成

data_g.add_arg("train_filelist",           str,  os.environ['SM_CHANNEL_TRAIN'] + "/train_filelist",  "Path to training filelist.")
data_g.add_arg("valid_filelist",           str,  os.environ['SM_CHANNEL_VALID'] + "/valid_filelist",  "Path to valid filelist.")

其中環境變量os.environ [‘SM_CHANNEL_TRAIN’]即/opt/ml/data/train,os.environ [‘SM_CHANNEL_VALID’]即/opt/ml/data/valid,對應數據集在容器中存儲的路徑。

Pretraining.py
 
 
class ErnieDataReader(object):
 
    def __init__(self,
 
                 filelist,
 
                 vocab_path,
 
                 batch_size=4096,
 
                 in_tokens=True,
 
                 max_seq_len=512,
 
                 shuffle_files=True,
 
                 random_seed=1,
 
                 epoch=100,
 
                 voc_size=0,
 
                 is_test=False,
 
                 generate_neg_sample=False):

增長一個參數:data_tag,修改成

class ErnieDataReader(object):
 
    def __init__(self,
 
                 data_tag,
 
                 filelist,
 
                 vocab_path,
 
                 batch_size=4096,
 
                 in_tokens=True,
 
                 max_seq_len=512,
 
                 shuffle_files=True,
 
                 random_seed=1,
 
                 epoch=100,
 
                 voc_size=0,
 
                 is_test=False,
 
                 generate_neg_sample=False):

類內實現增長變量:

self.data_tag = data_tag

方法data_generator增長:

def data_generator(self):
 
        """
 
        data_generator
 
        """
 
        def wrapper():
 
            def reader():
 
                for epoch in range(self.epoch):
 
                    self.current_epoch = epoch + 1
 
                    files = self.files
 
                    #during training, data are sliced by trainers
 
                    if self.shuffle_files:
 
                        start = epoch * self.total_file
 
                        end = start + self.total_file
 
                        files = [file_ for index, file_ in enumerate(self.files[start:end]) \
 
                            if index % self.trainer_nums == self.trainer_id]
 
                   
 
                    for index, file_ in enumerate(files):
 
                        file_, mask_word_prob = file_.strip().split("\t")
 
                        mask_word = (np.random.random() < float(mask_word_prob))
 
                        self.current_file_index = (index + 1) * self.trainer_nums
 
                        self.current_file = file_
 
                        ############ Modify - Start ############
 
                        env_str = 'SM_CHANNEL_' + self.data_tag.upper()
 
file_ = os.environ[env_str] + '/' + file_
 
                        ############ Modify – End ############
 
                        if mask_word:
 
                            self.mask_type = "mask_word"
 
                        else:
 
                            self.mask_type = "mask_char"

train.py 修正
方法 predict_wrapper:

filelist = args.test_filelist if args.do_test else args.valid_filelist
############ Modify - Start ############
tag = 'test' if args.do_test else 'valid
data_reader = ErnieDataReader(
        tag,
        filelist,
        vocab_path=args.vocab_path,
        batch_size=args.batch_size,
        voc_size=ernie_config['vocab_size'],
        shuffle_files=False,
        epoch=1,
        max_seq_len=args.max_seq_len,
        is_test=True)
############ Modify – End ############

方法train

############ Modify - Start ############
data_reader = ErnieDataReader(
        data_tag = 'train',
        filelist=args.train_filelist,
        batch_size=args.batch_size,
        vocab_path=args.vocab_path,
        voc_size=ernie_config['vocab_size'],
        epoch=args.epoch,
        max_seq_len=args.max_seq_len,
        generate_neg_sample=args.generate_neg_sample)
############ Modify – End ############

在訓練完成後,因爲 Amazon SageMaker 會自動回收啓動的計算資源,所以須要將模型保存至環境變量「SM_MODEL_DIR」對應的目錄(/opt/ml/model/)下,SageMaker會在回收資源以前自動將該目錄下的模型文件上傳至S3的指定目錄中。

代碼修改以下所示:
train.py,在方法train中,添加:

def train(args):
    …
    fluid.io.save_inference_model(dirname=os.environ['SM_MODEL_DIR'],
                                  feeded_var_names=['1','2','3','4'],
                                  target_vars=[next_sent_acc],
                                  executor=exe,
                                  main_program=train_program)

在模型訓練的過程當中,爲觀察訓練指標的變化,可經過 API 參數設置指標提取方式,SageMaker 會自動過濾指標數值,寫入到 Amazon CloudWatch 監控平臺中,咱們能夠在訓練過程當中及結束後觀察指標的變化狀況。

指標字典列表配置示例以下:

_metric_definitions = [{'Name': 'Training-loss' , 'Regex': 'loss: ([0-9\.]+)'}]

準備好上述相應參數對象後,便可經過 API 啓動 SageMaker 訓練任務,該部分工做推薦在 SageMaker Jupyter Notebook 中完成:

# 導入Sagemaker庫及Python SDK
import sagemaker as sage
from sagemaker import get_execution_role
import boto3
 
# 獲取會話及相關信息
sess = sage.Session()
client = boto3.client('sts')
account = client.get_caller_identity()['Account']
role = get_execution_role()
 
# 獲取S3存儲桶及路徑
bucket = sess.default_bucket()
folder = 'ernie'
 
# 獲取區域代碼
my_session = boto3.session.Session()
region = my_session.region_name
 
# 獲取存放在ECR中的容器鏡像名稱
ecr_image = account + ".dkr.ecr." + region + ".amazonaws.com/sm-byo:pd-ernie-pretrain"
 
from sagemaker.estimator import Estimator
# 輸入參數字典
_hyperparameters = {"use_cuda": True,
                    "is_distributed":False,
                    "use_fast_executor":True,
                    "weight_sharing":True,
                    "in_tokens":True,
                    "batch_size":8192,
                    "vocab_path":"./config/vocab.txt",
                    "num_train_steps":30,
                    "checkpoints":"./checkpoints",
                    "save_steps":10,
                    "ernie_config_path":"./config/ernie_config.json",
                    "learning_rate":"0.0001",
                    "use_fp16":False,
                    "weight_decay":0.01,
                    "max_seq_len":512,
                    "skip_steps":5,
                    "validation_steps": 20}
# 指標提取列表
_metric_definitions = [{'Name': 'Training-loss','Regex': 'loss: ([0-9\.]+)'}]
# 構建SageMaker Estimator
estimator = Estimator(image_name=ecr_image, # 容器鏡像
                      role=role,# 角色
                      train_instance_type='ml.p3.2xlarge', # 當前訓練機型
                      train_instance_count=1, # 訓練機器數量
                      hyperparameters = _hyperparameters, # 參數傳遞
                      metric_definitions = _metric_definitions, # 指標提取
                      output_path = 'S3://{}/{}/'.format(bucket,folder)) # 輸出模型存放在S3的路徑
 
# 輸入數據在S3路徑,構建數據通道字典
train_data = 'S3://{}/{}/{}'.format(bucket, folder, 'train')
valid_data = 'S3://{}/{}/{}'.format(bucket,  folder, 'valid')
data_channels = {'train': sage.session.S3_input(train_data),
                'valid': sage.session.S3_input(valid_data)}
 
# 啓動訓練
estimator.fit(inputs=data_channels,  logs=True)

說明

本示例選用單臺 ml.p3.2xlarge 機型承載訓練任務,該機器包含單張 Nvidia V100顯卡、8核 CPU、61GB 內存。更多計算實例類型請參見這裏。

訓練過程當中,能夠經過 Amazon CloudWatch 實時可視化觀察指標變化狀況,點擊 SageMaker 控制檯對應的訓練任務:

complete-ernie-machine-learning-tasks-based-on-amazon-sagemaker2.png

下拉至監控界面,能夠點擊查看算法指標、輸出日誌和實例指標。圖中框選的 Trainig-loss 即爲手動輸出至 Amazon CloudWatch 的指標對象。

complete-ernie-machine-learning-tasks-based-on-amazon-sagemaker3.png

訓練任務結束後,SageMaker 自動將模型文件保存至S3指定路徑上,用於後續部署或迭代。

complete-ernie-machine-learning-tasks-based-on-amazon-sagemaker1.png

以上就是基於 Amazon SageMaker 經過自帶容器方法實現自定義算法的模型預訓練任務的基本介紹,該方法一樣適用於基於其餘第三方或自帶框架的自定義任務的實現,關於經過自帶容器方式實現模型優化和部署,敬請關注該系列文章的後續內容。

底圖2.png

相關文章
相關標籤/搜索