Categories
程式開發

對PyTorch BERT 模型進行微調,並將其部署到Amazon SageMaker 上的Amazon Elastic Inference


原始網址: https://aws.amazon.com/cn/blogs/machine-learning/fine-tuning-a-pytorch-bert-model-and-deploying-it-with-amazon-elastic-inference-on-amazon-sagemaker/

文本分類,是一種將不同文本內容劃分到對應類別的技術,其擁有廣泛的應用範圍:電子郵件服務商通過文本分類檢測垃圾郵件,營銷機構藉此對客戶評論進行情感分析,論壇版主則藉此檢測不當發帖等等。

以往,數據科學家使用 tf-idfword2vec詞袋(BOW)等方法,生成用於訓練分類模型的特徵。 儘管這些技術在諸多自然語言處理(NLP)任務中獲得了巨大成功,但在不同的上下文背景之下,其往往無法準確地捕捉單詞含義。 最近,隨著基於Transformers的雙向編碼器表示(BERT)技術在結合實際上下文準確實現單詞含義編碼方面帶來的突出表現,人們也希望藉助BERT的力量在文本分類任務當中獲得更理想的結果。

亞馬遜SageMaker 是一項全託管服務,能夠為開發人員及數據科學家提供快速構建、訓練並部署機器學習(ML)模型的能力。 Amazon SageMaker消除了ML流程中各個步驟帶來的繁重工作,極大降低了高質量模型的開發門檻。 Amazon SageMaker Python SDK還提供開源API與容器,允許您更輕鬆地在Amazon SageMaker中使用多種不同ML與深度學習框架,實現模型的訓練與部署作業。

我們的客戶經常需要快速調優並輕鬆部署NLP模型。 此外,客戶也希望盡可能降低推理延遲與模型推理成本。亞馬遜彈性推理能够将GPU推理加速能力附加至CPU类型的终端端点当中,可以在不牺牲性能的前提下显著降低深度学习的推理成本。

本文將介紹如何使用Amazon SageMaker對PyTorch BERT模型進行微調,並將其部署在應用了Elastic Inference的SageMaker終端節點上。 本文中使用的全部代碼皆發佈在GitHub回購之上。 關於BERT微調的更多詳細信息,請參閱PyTorch BERT調優教程

BERT是什麼?

BERT最初發佈於2018年11月,這是一種革命性的模型,能夠主動屏蔽句子中的一個或者多個單詞。 BERT將屏蔽過單詞的句子作為輸入,藉此自我訓練以預測被屏蔽的單詞內容。 此外,BERT還能夠應用於預測下一句的任務。

BERT代表著一項重大突破,已經幫助業界研究人員及數據工程師在眾多NLP任務中取得重大成果。 BERT提供的各個單詞的表徵能夠切實與所處上下文(即句子中的其餘部分)相匹配。 關於BERT的更多詳細信息,請參閱BERT:用於語言理解的深度雙向Transformers預訓練模型

BERT調優

數據科學家在NLP項目當中面臨的最大挑戰之一,在於缺乏訓練數據。 大家往往只能獲得幾千條帶有人工標記的文本數據,用於模型訓練。 但是,現代深度學習NLP任務又需要大量標記數據,而解決此難題的一大重要方法,就是使用遷移學習技術。

遷移學習是一種ML方法,旨在將預訓練完成的模型(比如用於圖像分類的預訓練ResNet模型)重新用作另一不同、但具有相關性的問題。 通過復用預訓練模型中的參數,我們可以節約大量的訓練時間與成本。

BERT是基於BookCorpus與英文維基百科的數據進行訓練,二者分別包含8億以及25億個單詞[1]。 從零開始訓練BERT的成本極為高昂,但通過遷移學習,大家可以面對新的場景用例時使用相關少量的訓練數據對BERT進行快速微調,藉此實現常見NLP任務(例如文本分類與問題解答)的高質量預測結果。

解決方案概述

在本文中,我們將分步介紹數據集、訓練流程以及最終的模型部署環節。

我們使用Amazon SageMaker notebook實例用於代碼運行。 關於在Amazon SageMaker上使用Jupyter notebooks的更多詳細信息,請參閱使用Amazon SageMaker notebook實例,或者Amazon SageMaker Studio入門指南

本文中的notebook與代碼皆發佈於 的GitHub之上。 您可以克隆 GitHub回購並打開Jupyter notebook文件

問題與數據集

在本文中,我們使用語言可接受性語料庫(CoLA),這是一套對從已出版語言學文獻中收集到的10657個英語句子進行符合語法與不符合語法標記的數據集。 在我們的notebook中,將使用以下代碼下載並解壓這些數據:

蟒蛇

if not os.path.exists("./cola_public_1.1.zip"):
    !curl -o ./cola_public_1.1.zip https://nyu-mll.github.io/CoLA/cola_public_1.1.zip
if not os.path.exists("./cola_public/"):
    !unzip cola_public_1.1.zip

在訓練數據中,我們只需要其中兩列——句子本體及其標籤:

蟒蛇

df = pd.read_csv(
    "./cola_public/raw/in_domain_train.csv",
    sep="t",
    header=None,
    usecols=[1, 3],
    names=["label", "sentence"],
)
sentences = df.sentence.values
labels = df.label.values

如果我們輸出部分句子,即可看到該數據集如何根據句子語法的完整性進行句子標記。 具體參見以下代碼:

蟒蛇

print(sentences[20:25])
print(labels[20:25])

["The professor talked us." "We yelled ourselves hoarse."
 "We yelled ourselves." "We yelled Harry hoarse."
 "Harry coughed himself into a fit."]
[0 1 0 0 1]

接下來,我們對數據集進行拆分以進行訓練與測試,而後將其上傳至Amazon S3以供後續使用。 SageMaker Python SDK可幫助我們快速完成上傳操作:

蟒蛇

from sagemaker.session import Session
from sklearn.model_selection import train_test_split

train, test = train_test_split(df)
train.to_csv("./cola_public/train.csv", index=False)
test.to_csv("./cola_public/test.csv", index=False)

session = Session()
inputs_train = session.upload_data("./cola_public/train.csv", key_prefix="sagemaker-bert/training/data")
inputs_test = session.upload_data("./cola_public/test.csv", key_prefix="sagemaker-bert/testing/data")

訓練腳本

在本文中,我們使用 PyTorch變形金剛庫。 此庫中包含用於BERT等多種NLP模型的PyTorch實現與預訓練模型權重。 詳見以下代碼:

蟒蛇

model = BertForSequenceClassification.from_pretrained(
    "bert-base-uncased",  # Use the 12-layer BERT model, with an uncased vocab.
    num_labels=2,  # The number of output labels--2 for binary classification.
    output_attentions=False,  # Whether the model returns attentions weights.
    output_hidden_states=False,  # Whether the model returns all hidden-states.
)

根據SageMaker PyTorch鏡像的規定,我們的訓練腳本應將在訓練過程中學習到的模型文件保存至文件路徑model_dir。 訓練完成之後,Amazon SageMaker將保存在model_dir中的模型文件上傳至Amazon S3以進行下一步部署。 腳本將使用以下代碼保存訓練得出的模型工件:

蟒蛇

model_2_save = model.module if hasattr(model, "module") else model
model_2_save.save_pretrained(save_directory=args.model_dir)

我們將此腳本保存為 train_deploy.py文件,並將該文件放置在名為code/的目錄當中。 大家可以在該目錄中查看完整的訓練腳本。

由於PyTorch-Transformer本身並不包含在Amazon SageMaker PyTorch鏡像當中,因此我們需要提供對應的requirements.txt文件,保證Amazon SageMaker能夠安裝該庫以進行訓練與推理。 requirements.txt文件屬於文本文件,其中包含使用pip install進行安裝的條目列表。 您也可以指定需要安裝的各條目的具體版本。 要安裝PyTorch-Transformer,我們需要將以下行添加至requirements.txt文件當中。

蟒蛇

transformers==2.3.0

您可以在 GitHub回購上查看完整文件,也可以通過 code/目錄進行查看。 關於requirements.txt文件的更多詳細信息,請參閱Requirements文件

在Amazon SageMaker上執行訓練

我們使用Amazon SageMaker對我們的自定義PyTorch代碼執行模型訓練與部署。 Amazon SageMaker Python SDK能夠極大降低在Amazon SageMaker中運行PyTorch腳本的難度。 接下來,我們可以使用SageMaker Python SDK對經過訓練的模型加以部署,並運行實際預測。 關於將SDK與PyTorch配合使用的更多詳細信息,請參閱將PyTorch與SageMaker Python SDK配合使用

首先,我們使用PyTorch estimator進行模型訓練。 在創建此estimator 時,請注意明確指定以下內容:

  • 入口點 – PyTorch腳本的名稱
  • source_dir – 訓練腳本與 requirements.txt文件的位置
  • framework_version : 我們希望使用的PyTorch版本

PyTorch estimator支持多機分佈式PyTorch訓練。 要使用此功能,我們只需將train_instance_count的值設定為大於1即可。 我們的訓練腳本僅支持面向GP​​U實例進行分佈式訓練。

在估計器創建完成之後,我們調用fit()以啟動一項訓練作業。 接下來,我們使用之前上傳訓練數據時獲得的Amazon S3 URI,詳見以下代碼:

蟒蛇

from sagemaker.pytorch import PyTorch

estimator = PyTorch(
    entry_point="train_deploy.py",
    source_dir="code",
    role=role,
    framework_version="1.3.1",
    py_version="py3",
    train_instance_count=2,
    train_instance_type="ml.p3.2xlarge",
    hyperparameters={
        "epochs": 1,
        "num_labels": 2,
        "backend": "gloo",
    }
)
estimator.fit({"training": inputs_train, "testing": inputs_test})

在訓練開始之後,Amazon SageMaker會顯示訓練進度(如以下代碼所示),具體包括輪次、訓練損失以及測試數據精度:

蟒蛇

2020-06-10 01:00:41 Starting - Starting the training job...
2020-06-10 01:00:44 Starting - Launching requested ML instances......
2020-06-10 01:02:04 Starting - Preparing the instances for training............
2020-06-10 01:03:48 Downloading - Downloading input data...
2020-06-10 01:04:15 Training - Downloading the training image..
2020-06-10 01:05:03 Training - Training image download completed. Training in progress.
...
Train Epoch: 1 [0/3207 (0%)] Loss: 0.626472
Train Epoch: 1 [350/3207 (98%)] Loss: 0.241283
Average training loss: 0.5248292144022736
Test set: Accuracy: 0.782608695652174
...

我們可以監控訓練進度,請保證在繼續進行notebook中的後續部分之前,確認訓練流程已經成功完成。

部門腳本

在模型訓練完成之後,我們通過在PyTorch estimator上調用 deploy 將模型託管在Amazon SageMaker終端節點之上。 該終端節點將運行一套Amazon SageMaker PyTorch模型服務器。 我們需要對此服務器中的兩項組件加以配置:模型加載與模型服務。 這兩個組件的實現通過推理腳本train_deploy.py完成,完整文件可通過 GitHub回購獲取。

model_fn()函數用於加載已保存模型,並返回一個模型對像以供模型服務組件使用。 SageMaker PyTorch模型服務器通過調用model_fn加載我們的模型:

蟒蛇

def model_fn(model_dir):
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    model = BertForSequenceClassification.from_pretrained(model_dir)
    return model.to(device)

input_fn() 對預測輸入進行反序列化與數據轉換。 在本用例中,我們的請求正文將首先被序列化為JSON格式,而後發送至模型服務端點。 接下來,我們首先在 input_fn()中對JSON格式的請求正文進行反序列化,然後根據BERT的要求將輸入以 torch.tensor的形式返回:

蟒蛇

def input_fn(request_body, request_content_type):
    if request_content_type == "application/json":
        sentence = json.loads(request_body)
    
        input_ids = []
        encoded_sent = tokenizer.encode(sentence,add_special_tokens = True)
        input_ids.append(encoded_sent)
    
        # pad shorter sentences
        input_ids_padded =[]
        for i in input_ids:
            while len(i)  0) for token_id in sent] for sent in input_ids

        # convert to PyTorch data types.
        train_inputs = torch.tensor(input_ids)
        train_masks = torch.tensor(attention_masks)
    
        # train_data = TensorDataset(train_inputs, train_masks)
        return train_inputs, train_masks

predict_fn() 執行預測並返回結果。 詳見以下代碼:

蟒蛇

def predict_fn(input_data, model):
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    model.to(device)
    model.eval()
    input_id, input_mask = input_data
    input_id.to(device)
    input_mask.to(device)
    with torch.no_grad():
        return model(input_id, token_type_ids=None,attention_mask=input_mask)[0]

預構建的Amazon SageMaker PyTorch鏡像中的默認支持對預測結果進行序列化。

部署端點

要部署我們的端點,需要在PyTorch estimator對像上調用deploy(),並提供所需數量的實例與實例類型:

蟒蛇

predictor = estimator.deploy(initial_instance_count=1, instance_type="ml.m4.xlarge")

接下來,我們通過配置讓預測變量使用"application/json"作為內容類型,而後將請求發送至我們的端點:

蟒蛇

from sagemaker.predictor import json_deserializer, json_serializer

predictor.content_type = "application/json"
predictor.accept = "application/json"
predictor.serializer = json_serializer
predictor.deserializer = json_deserializer

最後,我們使用預測變量對像以調用該端點:

蟒蛇

result = predictor.predict("Somebody just left - guess who.")
print(np.argmax(result, axis=1))

[1]

預測出的類別為 1,符合我們的預期,因為用於測試的句子確實擁有正確的語法表達。

使用Elastic Inference部署端點

要為推理任務選擇正確的實例類型,我們需要在不同數量的GPU、CPU以及內存資源之間做出權衡。 在獨立GPU實例上針對其中某一種資源進行優化,往往會導致其他資源得不到充分利用。 Elastic Inference則能夠為特定端點提供適量的GPU驅動型推理加速資源,解決了這一難題。 自2020年3月起,用戶已經可以在Amazon SageMaker與Amazon EC2上獲得Elastic Inference對PyTorch的支持能力。

要使用Elastic Inference,我們需要首先將訓練完成的模型轉換為TorchScript。 關於更多詳細信息,請參閱使用Amazon Elastic Inference在Amazon SageMaker for PyTorch模型上降低ML推理成本

我們首先從Amazon S3處下載訓練完成的模型文件。 模型文件的位置為estimator.model_data。 接下來,我們使用以下代碼將模型轉換為TorchScript:

蟒蛇

model_torchScript = BertForSequenceClassification.from_pretrained("model/", torchscript=True)
device = "cpu"
for_jit_trace_input_ids = [0] * 64
for_jit_trace_attention_masks = [0] * 64
for_jit_trace_input = torch.tensor([for_jit_trace_input_ids])
for_jit_trace_masks = torch.tensor([for_jit_trace_input_ids])

traced_model = torch.jit.trace(
    model_torchScript, [for_jit_trace_input.to(device), for_jit_trace_masks.to(device)]
)
torch.jit.save(traced_model, "traced_bert.pt")

subprocess.call(["tar", "-czvf", "traced_bert.tar.gz", "traced_bert.pt"])

要加載TorchScript模型並將其應用於實際預測,我們還需要對模型的加載與預測函數做出些許調整。 我們需要創建一個新的腳本 deploy_ei.py,其內容與train_deploy.py腳本略有不同。

要加載模型,我們使用 torch.jit.load替代之前使用的 BertForSequenceClassification.from_pretrained調用:

蟒蛇

loaded_model = torch.jit.load(os.path.join(model_dir, "traced_bert.pt"))

要進行預測,我們在最終return語句當中使用torch.jit.optimized_execution

蟒蛇

with torch.no_grad():
    with torch.jit.optimized_execution(True, {"target_device": "eia:0"}):
        return model(input_id,attention_mask=input_mask)[0]

完整的 deploy_ei.py腳本可通過 GitHub回購獲取。 使用這套腳本,我們即可通過Elastic Inference進行模型部署:

蟒蛇

predictor = pytorch.deploy(
    initial_instance_count=1, 
    instance_type="ml.m5.large",
    accelerator_type="ml.eia2.xlarge"
)

通過使用 accelerator_type="ml.eia2.xlarge"參數,我們即可將Elastic Inference加速器附加至終端節點當中。

資源清理

在實驗完成之後,請及時刪除期間創建的Amazon SageMaker端點以及Amazon SageMaker notebook實例,以避免產生不必要的費用。 具體參見以下代碼:

蟒蛇

predictor.delete_endpoint()

總結

在本文中,我們使用Amazon SageMaker以BERT為起點,訓練出一套能夠標記句子語法完整性的模型。 接下來,我們將模型分別部署在使用Elastic Inference與不使用Elastic Inference的Amazon SageMaker終端節點。 您也可以使用這套解決方案對BERT做其他方向的微調,或者使用PyTorch-Transformers提供的其他預訓練模型。 關於將PyTorch與Amazon SageMaker配合使用的更多詳細信息,請參閱將PyTorch與Amazon SageMaker配合使用

參考文獻

[1] Yukun Zhu, Ryan Kiros, Rich Zemel, Ruslan Salakhutdinov, Raquel Urtasun, Antonio Torralba以及Sanja Fidler。 2015年。 《書籍與電影的映射:在觀看電影與閱讀書籍中實現相似故事的視覺解釋》,IEEE國際計算機視覺會議論文集,第19至27頁。

作者介紹

本篇作者

對PyTorch BERT 模型進行微調,並將其部署到Amazon SageMaker 上的Amazon Elastic Inference 1

Qingwei Li

Amazon Web Services機器學習專家。 在超出研究補助預算卻未能成功拿下預想中的諾貝爾獎之後,他開始轉向運籌學領域。 目前,他幫助金融服務與保險業客戶在AWS上構建機器學習解決方案。 在業餘時間,他喜歡閱讀和教學。

平大衛

AWS首席解決方案架構師。 他與我們的客戶一道使用AWS構建雲與機器學習解決方案。 他住在紐約都會區,喜歡學習各類最新的機器學習技術。

Lauren Yu

Amazon SageMaker軟件開發工程師。 她主要研究SageMaker Python SDK,以及用於將PyTorch、TensorFlow、MXNet同Amazon SageMaker整合起來的工具包解決方案。 業餘時間,她喜歡在Amazon交響樂團與Doppler Quartet樂隊中演奏中提琴。

本文轉載自亞馬遜AWS官方博客。

原文鏈接

對PyTorch BERT 模型進行微調,並將其部署到Amazon SageMaker 上的Amazon Elastic Inference