兩行程式碼玩轉Google BERT句向量詞向量
關於作者: 肖涵博士,bert-as-service 作者。現為騰訊 AI Lab 高階科學家、德中人工智慧協會主席。 肖涵的 Fashion-MNIST 資料集已成為機器學習基準集,在 Github 上超過 4.4K 星,一年來其學術引用數超過 300 篇。
肖涵在德國慕尼黑工業大學計算機系取得了計算機博士(2014)和碩士學位(2011),在北郵取得了資訊通訊學士學位(2009)。他曾於 2014-2018 年初就職於位於德國柏林的 Zalando 電商,先後在其推薦組、搜尋組和 Research 組擔任高階資料科學家。肖涵所創辦的德中人工智慧協會(GCAAI)如今擁有 400 餘名會員,致力於德中兩國 AI 領域的合作與交流,是德國最具影響力的新型團體之一。
WeChat: hxiao1987
Blog: ofollow,noindex"> https:// hanxiao.github.io 德中人工智慧協會: https:// gcaai.org
Google AI 幾周前釋出的 BERT (Bidirectional Encoder Representations from Transformers) 模型在 NLP 圈掀起了軒然大波, 其使用超多層 Transformer + 雙任務預訓練 + 後期微調的訓練策略,在 11 個不同型別的 NLP 任務上重新整理了紀錄。
Google 隨後在 Github 上開源了 BERT 的程式碼 ,並提供了在維基百科語料上使用 TPU 預訓練好的模型供大家下載。這其中也包括了基於字元級別的中文 BERT 預訓練模型。

bert-as-service 能讓你簡單通過兩行程式碼,即可使用預訓練好的模型生成句向量和 ELMo 風格的詞向量:

你可以將 bert-as-service 作為公共基礎設施的一部分,部署在一臺 GPU 伺服器上,使用多臺機器從遠端同時連線實時獲取向量,當做特徵資訊輸入到下游模型。
回顧:BERT的訓練機制
BERT 模型的訓練分為預訓練(Pre-training)和微調(Fine-tunning)兩步。 預訓練和下游任務無關,卻是一個非常耗時耗錢的過程。Google 坦言,對 BERT 的預訓練一般需要 4 到 16 塊 TPU 和一週的時間,才可以訓練完成。
慶幸的是,大部分 NLP 研究者只需使用 Google 釋出的預訓練模型,而不需要重複這一過程。你可以把預訓練模型想象成一個 Prior,是對語言的先驗知識,一旦擁有就不需要重複構造。
微調取決於下游的具體任務。 不同的下游任務意味著不同的網路擴充套件結構:比如一個對句子進行情感分類的任務,只需要在 BERT 的輸出層句向量上接入幾個 Dense 層,走個 softmax。而對於 SQuAD 上的閱讀理解任務,需要對 BERT 輸出的詞向量增加 match 層和 softmax。
總體來說,對 BERT 的微調是一個輕量級任務,微調主要調整的是擴充套件網路而非 BERT 本身。 換句話說,我們完全可以固定住 BERT 的引數,把 BERT 輸出的向量編碼當做一個特徵(feature)資訊,用於各種下游任務。
無論下游是什麼任務,對於 NLP 研究者來說,最重要的就是獲取一段文字或一個句子的定長向量表示,而將變長的句子編碼成定長向量的這一過程叫做 sentence encoding/embedding。
bert-as-service 正是出於此設計理念,將預訓練好的 BERT 模型作為一個服務獨立執行,客戶端僅需通過簡單的 API 即可呼叫服務獲取句子、詞級別上的向量。在實現下游任務時,無需將整個 BERT 載入到 tf.graph 中,甚至不需要 TensorFlow 也不需要 GPU,就可以在 scikit-learn, PyTorch, Numpy 中直接使用 BERT。
bert-as-service
bert-as-service 將 BERT模型作為一個獨立的句子編碼(sequence encoding/embedding)服務, 在客戶端僅用兩行程式碼就可以對句子進行高效編碼 。其主要特色如下:
- state-of-the-art: 基於 Google 最新發布的 BERT 模型;
- 易用: 客戶端僅需簡單兩行程式碼即可呼叫;
- 快速: 每秒 780 個句子(見 詳細評測 );
- 併發性: 自動擴充套件到多塊 GPU,多客戶端,高效任務排程,無延遲(見 針對多客戶端併發的評測 )。
使用方法
1. 下載 Google 釋出的預訓練 BERT 模型
從下方連結下載 Google 釋出的 預訓練模型 ,解壓到某個路徑下,比如: /tmp/english_L-12_H-768_A-12/
你可以使用包括 BERT-Base, Multilingual 和 BERT-Base, Chinese 在內的任意模型。
2. 開啟 BERT 服務
python app.py -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=4
這個程式碼將開啟一個 4 程序的 BERT 服務,意味著它可以最高處理來自 4 個客戶端的併發請求。雖然同一時刻連線服務的客戶端數量沒有限制,但在某時刻多餘 4 個的併發請求將被暫時放到一個負載均衡中,等待執行。有關 bert-as-service 背後的架構可以參考 FAQ 和併發客戶端效能評測。

3. 使用客戶端獲取句子向量編碼
對於客戶端來說,你唯一需要的檔案就是 service/client.py ,因為我們需要從中匯入 BertClient。
from service.client import BertClient bc = BertClient() bc.encode(['First do it', 'then do it right', 'then do it better'])
這會返回一個 3 x 768 的 ndarray 結構,每一行代表了一句話的向量編碼。你也可以通過設定,讓其返回 Python 型別的 List[List[float]] 。
在另一臺機器上使用 BERT 服務
客戶端也可以從另一臺機器上連線 BERT 服務,只需要一個 IP 地址和埠號:
# on another CPU machine from service.client import BertClient bc = BertClient(ip='xx.xx.xx.xx', port=5555)# ip address of the GPU machine bc.encode(['First do it', 'then do it right', 'then do it better'])
你還可以把服務架設在 docker container 中使用,詳情可以參考專案的 README.md。 bert-as-service 所支援的 C/S 模式可以用下圖總結:

效能評測
作為一個基礎服務,速度和伸縮性(scalability)非常關鍵。只有當下遊的模型能夠通過其快速流暢地獲取資料時,該服務的意義才能得到最大體現。BERT 的網路複雜度眾所周知, 那麼 bert-as-service 能否達到工程級別的速度? 為了驗證這一點,我們做了如下方面的評測。
max_seq_len 對速度的影響
max_seq_len 是服務端的一個引數,用來控制 BERT 模型所接受的最大序列長度。當輸入的序列長度長於 max_seq_len 時,右側多餘字元將被直接截斷。所以如果你想處理很長的句子,伺服器端正確設定 max_seq_len 是其中一個關鍵指標。而從效能上來講,過大的 max_seq_len 會拖慢計算速度,並很有可能造成記憶體 OOM。


client_batch_size 對速度的影響
client_batch_size 是指每一次客戶端呼叫 encode() 時所傳給伺服器 List 的大小。出於效能考慮,請儘可能每次傳入較多的句子而非一次只傳一個。比如,使用下列方法呼叫:
# prepare your sent in advance bc = BertClient() my_sentences = [s for s in my_corpus.iter()] # doing encoding in one-shot vec = bc.encode(my_sentences)
而不要使用:
bc = BertClient() vec = [] for s in my_corpus.iter(): vec.append(bc.encode(s))
如果你把 bc = BertClient() 放在了迴圈之內,則效能會更差。當然在一些時候,一次僅傳入一個句子無法避免,尤其是在小流量線上環境中。


num_client 對併發性和速度的影響
num_client 指同時連線服務的客戶端數量。當把 bert-as-service 作為公共基礎設施時,可能會同時有多個客戶端連線到服務獲取向量。


可以看到一個客戶端、一塊 GPU 的處理速度是每秒 381 個句子(句子的長度為 40),兩個客戶端、兩個 GPU 是每秒 402 個,四個客戶端、四個 GPU 的速度是每秒 413 個。這體現了 bert-as-service 良好的伸縮性:當 GPU 的數量增多時,服務對每個客戶端請求的處理速度保持穩定甚至略有增高(因為空隙時刻被更有效地利用)。
其它常見問題列表和詳細指南
參見: https:// github.com/hanxiao/bert -as-service
#投 稿 通 道#
如何才能讓更多的優質內容以更短路徑到達讀者群體,縮短讀者尋找優質內容的成本呢? 答案就是:你不認識的人。
總有一些你不認識的人,知道你想知道的東西。PaperWeekly 或許可以成為一座橋樑,促使不同背景、不同方向的學者和學術靈感相互碰撞,迸發出更多的可能性。
PaperWeekly 鼓勵高校實驗室或個人,在我們的平臺上分享各類優質內容,可以是 最新論文解讀 ,也可以是 學習心得 或 技術乾貨 。我們的目的只有一個,讓知識真正流動起來。
:memo: 來稿標準:
• 稿件確係個人 原創作品 ,來稿需註明作者個人資訊(姓名+學校/工作單位+學歷/職位+研究方向)
• 如果文章並非首發,請在投稿時提醒並附上所有已釋出連結
• PaperWeekly 預設每篇文章都是首發,均會新增“原創”標誌
:mailbox_with_mail: 投稿方式:
• 方法一:在PaperWeekly知乎專欄頁面點選“投稿”,即可遞交文章
• 方法二:傳送郵件至: [email protected] ,所有文章配圖,請單獨在附件中傳送
• 請留下即時聯絡方式(微信或手機),以便我們在編輯釋出時和作者溝通
關於PaperWeekly
PaperWeekly 是一個推薦、解讀、討論、報道人工智慧前沿論文成果的學術平臺。如果你研究或從事 AI 領域,歡迎在公眾號後臺點選 「交流群」 ,小助手將把你帶入 PaperWeekly 的交流群裡。
加入社群: http:// paperweek.ly
微信公眾號:PaperWeekly