谷歌官宣:全面超越人類的最強 NLP 預訓練模型 BERT 開源了!

來源 | Google Research GitHub
編譯 | 無明、Natalie
編輯 | Nataliehtml

AI 前線導讀: 近日,谷歌 AI 的一篇 NLP 論文引發了社區極大的關注與討論,被認爲是 NLP 領域的極大突破。谷歌大腦研究科學家 Thang Luong Twitter 表示,這項研究開啓了 NLP 領域的新時代。該論文介紹了一種新的語言表徵模型 BERT——來自 Transformer 的雙向編碼器表徵。BERT 是首個在大批句子層面和 token 層面任務中取得當前最優性能的基於微調的表徵模型,其性能超越許多使用任務特定架構的系統,刷新了 11 項 NLP 任務的當前最優性能記錄。
剛剛,谷歌正式將其開源!這意味着全部NLP從業者均可以試用這個強大的NLP預訓練模型並結合到本身的工做中。
更多優質內容請關注微信公衆號「AI 前線」(ID:ai-front)python

首先附上開源代碼傳送門:git

github.com/google-rese…github

原論文連接:web

arxiv.org/abs/1810.04…json

該開源項目亮點以下api

  1. 獨立的 TensorFlow 代碼,有簡單的 API 且無依賴關係。bash

  2. 連接到論文中的 BERT-Base 和 BERT-Large 預訓練版本。微信

  3. 一鍵複製論文中的 MultiNLI 和 SQuAD v1.1 結果。網絡

  4. 包含預訓練數據生成和訓練的代碼。

  5. 能夠連接到 Colab,從而使用免費的雲端 TPU 運行 BERT。

幾個常見問題解答

  1. 咱們計劃很快發佈一個多語言模型(在 60 種語言上訓練的大型共享 WordPiece 詞彙,並對中文作了特殊處理)。

  2. 現有的基於 PyTorch(或其餘框架)的版本沒法與檢查點兼容(由於若是沒有咱們的代碼,這是不可能作到的)。咱們但願有人可以建立一個 op-for-op 從新實現 modeling.py,以便建立一個與咱們的檢查點兼容的 PyTorch 模型,尤爲是咱們計劃在未來發布更多的檢查點(例如,多語言模型)。

  3. 咱們尚未在 SQuAD 2.0 上運行過這個模型,咱們想把它做爲一項練習留給讀者來完成:)

  4. 你不必定要在雲端 TPU 上進行訓練,可是在 GPU 上訓練 BERT-Large 模型可能會出現嚴重的內存不足問題。在 GPU 上運行 BERT-Base 一般能夠正常工做(與咱們在論文中使用的相比,你可能須要下降 Batch Size,但若是你同時也對學習率作了調整,那麼最終結果應該是相似的)。咱們正在嘗試找出在 GPU 上運行 BERT-Large 的最佳解決方法。

如下內容編譯自 BERT 開源項目 Readme 文件(略有精簡)。

什麼是 BERT?

BERT 是預訓練語言表示的方法,也即咱們基於大型文本語料庫(如維基百科)訓練通用的「語言理解」模型,而後將模型用於下游的 NLP 任務(如問答) 。BERT 比以前的方法更優,由於它是第一個用於預訓練 NLP 的無監督、深度雙向系統。

無監督意味着 BERT 只使用純文本語料庫進行訓練,這點很重要,由於網絡上有不少公開的純文本數據。

預訓練表示也能夠是無上下文或有上下文的,有上下文的表示又能夠是單向或雙向的。word2vec 或 GloVe 這類無上下文模型爲詞彙表中的每一個單詞生成單個「詞袋」表示,所以「bank」與「bank deposit」和「river bank」具備相同的表示。相反,上下文模型基於句子中其餘單詞生成每一個單詞的表示。

BERT 創建在最近的預訓練上下文表示工做的基礎之上,包括半監督序列學習、生成預訓練、ELMo 和 ULMFit,這些模型都是單向或淺雙向的。也就是說,每一個單詞僅使用左側(或右側)的單詞進行語境化。例如,在「I made a bank deposit」這個句子中,「bank」的單向表示基於「I made a」而不是「deposit」。以前的一些工做以「淺層」的方式未來自左上下文和右上下文模型的表示結合在一塊兒,而 BERT 使用左右上下文來表示「bank」——從深度神經網絡的最底部開始,因此它是深度雙向的。

BERT 使用一種簡單的方法:咱們將輸入的 15%的單詞遮蔽掉,讓整個序列經過深度雙向 Transformer 編碼器,而後僅預測被遮蔽的單詞。例如:

Input: the man went to the [MASK1] . he bought a [MASK2] of milk.
Labels: [MASK1] = store; [MASK2] = gallon
複製代碼

爲了學習句子之間的關係,咱們還訓練一個簡單的任務:給定兩個句子 A 和 B,那麼 B 是 A 的下一個句子仍是隻是語料庫中的一個隨機句子?

Sentence A: the man went to the store .
Sentence B: he bought a gallon of milk .
Label: IsNextSentence
複製代碼
Sentence A: the man went to the store .
Sentence B: penguins are flightless .
Label: NotNextSentence
複製代碼

而後,咱們基於大型語料庫(Wikipedia + BookCorpus)訓練了一個模型(12 層到 24 層 Transformer),花了很長一段時間(1 百萬個更新步驟),那就是 BERT。

使用 BERT 須要兩個階段:預訓練和微調。

預訓練的成本至關高(在 4 到 16 個 Cloud TPU 上訓練須要 4 天時間),並且對於每一種語言,都是一次性的程序(目前的模型僅限英語,更多語言模型將在不久的未來發布)。咱們正在發佈一些預訓練的模型,這些模型是在 Google 上預先訓練過的。大多數 NLP 研究人員不須要從頭開始訓練本身的模型。

微調的成本較低。論文中提到的全部結果均可以在單個 Cloud TPU 上進行訓練,最多花 1 個小時,或者在 GPU 上花幾個小時便可。

預訓練模型 咱們在論文中發佈了 BERT-Base 和 BERT-Large 模型。Uncased 是指文本在 WordPiece 標記化以前已經轉換成小寫,例如「John Smith」轉換成「john smith」。Uncased 模型還移除了重音標記。Cased 是指保留真實的大小寫和重音標記。一般,除非你的任務須要大小寫(例如,命名實體識別或詞性標註),不然 Uncased 模型會更好。

這些模型都是基於 Apache 2.0 許可進行發行。

模型連接:

每一個.zip 文件包含三個項目:

  • 包含預訓練的權重(其實是 3 個文件)的 TensorFlow 檢查點(bert_model.ckpt)。

  • 用於將 WordPiece 映射到 word id 的詞彙文件(vocab.txt)。

  • 配置文件(bert_config.json),指定模型的超參數。

使用 BERT 進行微調

微調示例使用了 BERT-Base,它應該可以使用給定的超參數在配備至少 12GB RAM 的 GPU 上運行。

在 Cloud TPU 上進行微調

下面的大多數示例都假設你將使用 Titan X 或 GTX 1080 這樣的 GPU 在本地計算機上運行訓練 / 評估。

不過,若是你能夠訪問 Cloud TPU,只需將如下標誌添加到 run_classifier.py 或 run_squad.py:

--use_tpu=True \
 --tpu_name=$TPU_NAME
複製代碼

在 Cloud TPU 上,預訓練模型和輸出目錄須要在 Google Cloud Storage 上。例如,若是你有一個名爲 some_bucket 的桶,則可使用如下標誌:

--output_dir=gs://some_bucket/my_output_dir/
複製代碼

解壓縮的預訓練模型文件也能夠在 Google Cloud Storage 文件夾 gs://bert_models/2018_10_18 中找到。例如:

export BERT_BASE_DIR=gs://bert_models/2018_10_18/uncased_L-12_H-768_A-12
複製代碼

句子(和句子對)分類任務

在運行這個示例以前,你必須經過這個腳本( gist.github.com/W4ngatang/6… ) 下載 GLUE 數據 ( gluebenchmark.com/tasks ),並將其解壓縮到一個目錄中( 目錄變量能夠設置爲 $GLUE_DIR )。接下來,下載 BERT-Base 檢查點並將其解壓縮到另外一個目錄中( 目錄變量能夠設置爲 $BERT_BASE_DIR)。

這個示例針對微軟 Research Paraphrase Corpus(MRPC)語料庫對 BERT-Base 進行微調,這個語料庫僅包含 3,600 個樣本,在大多數 GPU 上只須要幾分鐘進行微調。

export BERT_BASE_DIR=/path/to/bert/uncased_L-12_H-768_A-12
export GLUE_DIR=/path/to/glue

python run_classifier.py \
 --task_name=MRPC \
 --do_train=true \
 --do_eval=true \
 --data_dir=$GLUE_DIR/MRPC \
 --vocab_file=$BERT_BASE_DIR/vocab.txt \
 --bert_config_file=$BERT_BASE_DIR/bert_config.json \
 --init_checkpoint=$BERT_BASE_DIR/bert_model.ckpt \
 --max_seq_length=128 \
 --train_batch_size=32 \
 --learning_rate=2e-5 \
 --num_train_epochs=3.0 \
 --output_dir=/tmp/mrpc_output/
複製代碼

你應該能夠看到這樣的輸出:

***** Eval results *****
 eval_accuracy = 0.845588
 eval_loss = 0.505248
 global_step = 343
 loss = 0.505248
複製代碼

dev 集的準確率爲 84.55%。MRPC 在 dev 集準確率方面有很大的差別,即便是從相同的預訓練檢查點開始。若是從新運行幾回(確保要指向不一樣的 output_dir),你應該會看到結果在 84%到 88%之間。

其餘一些預訓練模型是在 run_classifier.py 中實現的,因此應該能夠直接按照這些示例將 BERT 用於任何單句或句子對分類任務。

SQuAD

斯坦福問答數據集(SQuAD)是一個很是流行的問答基準數據集。BERT(在發佈時)在 SQuAD 上得到了最好的結果,幾乎沒有進行特定任務的網絡架構修改或數據加強。不過,它確實須要半複雜數據預處理和後處理來處理 SQUAD 上下文段落的可變長度性質,以及用於 SQuAD 訓練的字符級答案註解。run_squad.py 實現並記錄了處理過程。

要在 SQuAD 上運行訓練,首先須要下載這個數據集。SQuAD 網站(rajpurkar.github.io/SQuAD-explo… v1.1 數據集的連接,一些必要的文件能夠在這裏找到:

將這些下載到某個目錄(變量能夠設置爲 $SQUAD_DIR)。

因爲內存限制,目前沒法在 12GB-16GB 的 GPU 上再現最好的 SQuAD 結果。可是,可使用下面這些超參數在 GPU 上訓練 BERT-Base 模型:

python run_squad.py \
 --vocab_file=$BERT_BASE_DIR/vocab.txt \
 --bert_config_file=$BERT_BASE_DIR/bert_config.json \
 --init_checkpoint=$BERT_BASE_DIR/bert_model.ckpt \
 --do_train=True \
 --train_file=$SQUAD_DIR/train-v1.1.json \
 --do_predict=True \
 --predict_file=$SQUAD_DIR/dev-v1.1.json \
 --train_batch_size=12 \
 --learning_rate=5e-5 \
 --num_train_epochs=2.0 \
 --max_seq_length=384 \
 --doc_stride=128 \
 --output_dir=/tmp/squad_base/
複製代碼

dev 集預測結果將保存到 output_dir 目錄的一個名爲 predictions.json 的文件中:

python SQUAD_DIR/evaluate-v1.1.pySQUAD_DIR/dev-v1.1.json ./squad/predictions.json 應該產生這樣的輸出:

{"f1": 88.41249612335034, "exact_match": 81.2488174077578}
複製代碼

你應該看到論文中提到的 88.5%的 F1。

若是你能夠訪問 Cloud TPU,那麼就能夠訓練 BERT-Large 模型。下面的超參數(與論文中稍有不一樣)能夠得到大約 90.5%-91.0%的 F1(僅在 SQuAD 上訓練):

python run_squad.py \
 --vocab_file=$BERT_LARGE_DIR/vocab.txt \
 --bert_config_file=$BERT_LARGE_DIR/bert_config.json \
 --init_checkpoint=$BERT_LARGE_DIR/bert_model.ckpt \
 --do_train=True \
 --train_file=$SQUAD_DIR/train-v1.1.json \
 --do_predict=True \
 --predict_file=$SQUAD_DIR/dev-v1.1.json \
 --train_batch_size=48 \
 --learning_rate=5e-5 \
 --num_train_epochs=2.0 \
 --max_seq_length=384 \
 --doc_stride=128 \
 --output_dir=gs://some_bucket/squad_large/ \
 --use_tpu=True \
 --tpu_name=$TPU_NAME
複製代碼

例如,使用這些參數隨機進行一次會產生如下 dev 得分:

{"f1": 90.87081895814865, "exact_match": 84.38978240302744}
複製代碼

使用 BERT 提取固定的特徵向量

在某些狀況下,相比對整個預訓練模型進行端到端的微調,得到預訓練的上下文嵌入可能會更好,這些嵌入是預訓練模型隱藏層生成的每一個輸入標記的固定上下文表示。

例如,咱們可能會這樣使用 extract_features.py 腳本:

# Sentence A and Sentence B are separated by the ||| delimiter.
# For single sentence inputs, don't use the delimiter.
echo 'Who was Jim Henson ? ||| Jim Henson was a puppeteer' > /tmp/input.txt

python extract_features.py \
 --input_file=/tmp/input.txt \
 --output_file=/tmp/output.jsonl \
 --vocab_file=$BERT_BASE_DIR/vocab.txt \
 --bert_config_file=$BERT_BASE_DIR/bert_config.json \
 --init_checkpoint=$BERT_BASE_DIR/bert_model.ckpt \
 --layers=-1,-2,-3,-4 \
 --max_seq_length=128 \
 --batch_size=8
複製代碼

這將建立一個 JSON 文件,其中包含由 layers 指定的每一個 Transformer 層的 BERT 激活(-1 是 Transformer 的最後隱藏層,並以此類推)。

請注意,這個腳本將生成很是大的輸出文件(默認狀況下,每一個輸入標記大約 15kb)。

若是你須要對齊原始單詞和標記化單詞,請參閱下面的標記化部分。

標記化(tokenization)

對於句子(或句子對)任務,標記化是很是簡單的。只須要遵循 run_classifier.py 和 extract_features.py 中的示例代碼便可。句子級任務的基本流程:

實例化 tokenizer = tokenization.FullTokenizer;

使用 tokens = tokenizer.tokenize(raw_text)對原始文本進行標記;

截斷到最大序列長度(最多可使用 512,但處於內存和速度方面的考慮,最好使用短一點的);

在正確的位置添加 [CLS] 和 [SEP] 標記。

單詞級和 span 級的任務(例如 SQuAD 和 NER)會複雜一些,由於你須要對齊輸入文本和輸出文本。SQuAD 是一個特別複雜的例子,由於輸入標籤是基於字符的,而 SQuAD 段落一般比咱們的最大序列長度要長。請參閱 run_squad.py 中的代碼,瞭解咱們如何處理這個問題。

在咱們描述處理單詞級任務的通常方法以前,須要先了解咱們的標記器都作了哪些事情。它有三個主要步驟:

文本規範化:將全部空白字符轉換爲空格,(對於 Uncased 模型)將輸入轉換爲小寫並刪除重音標記。例如,「John Johanson’s」變成「john johanson’s」。

標點符號拆分:拆分兩側的全部標點符號(即在全部標點符號周圍添加空格)。標點符號是指具備 P* Unicode 內容或任何非字母 / 數字 / 空格 ASCII 字符。例如,「johanson’s,」變成「john johanson ' s ,」。

WordPiece 標記化:對上一步驟的輸出進行空格標記化,並對每一個標記進行 WordPiece 標記化。例如,「john johanson ' s , 」變成「john johan ##son ' s ,」。

這個方案的優勢是它與大多數現有的英語標記符「兼容」。例如,假設你有一個詞性標記任務,以下所示:

Input:  John Johanson 's house Labels: NNP NNP POS NN 複製代碼

標記化輸出以下所示:

Tokens: john johan ##son ' s house 若是你有一個帶有單詞級註解的預標記表示,你能夠單獨標記每一個輸入單詞,並對齊原始單詞和標記化單詞:

### Input
orig_tokens = ["John", "Johanson", "'s",  "house"]
labels      = ["NNP",  "NNP",      "POS", "NN"]

### Output
bert_tokens = []

# Token map will be an int -> int mapping between the `orig_tokens` index and
# the `bert_tokens` index.
orig_to_tok_map = []

tokenizer = tokenization.FullTokenizer(
   vocab_file=vocab_file, do_lower_case=True)

bert_tokens.append("[CLS]")
for orig_token in orig_tokens:
 orig_to_tok_map.append(len(bert_tokens))
 bert_tokens.extend(tokenizer.tokenize(orig_token))
bert_tokens.append("[SEP]")

# bert_tokens == ["[CLS]", "john", "johan", "##son", "'", "s", "house", "[SEP]"]
# orig_to_tok_map == [1, 2, 4, 6]
複製代碼

如今 orig_to_tok_map 可用於將 labels 投影到標記化表示。

有一些常見的英語標記化方案會致使 BERT 預訓練之間的輕微不匹配。例如,若是輸入標記化分離了縮略形式,如「do n’t」,就會出現不匹配。若是有可能,你應該預處理數據,將這些數據轉換回原始文本,若是不行,這種不匹配可能也不是什麼大問題。

使用 BERT 進行預訓練

咱們正在嘗試在任意文本語料庫上進行「masked LM」和「下一個句子預測」。請注意,這些代碼不一樣於論文中所述的代碼(原始代碼是用 C++ 編寫的,有一些額外的複雜性),但能夠生成論文中所述的預訓練數據。

輸入是純文本文件,一行一個句子。文檔使用空行進行分隔。輸出是一組序列化爲 TFRecord 文件格式的 tf.train.Example。

腳本將整個輸入文件的樣本保存在內存中,對於大型數據文件,須要將其分片並屢次調用腳本。

max_predictions_per_seq 是每一個序列的 masked LM 預測的最大數量。你應該將其設置爲 max_seq_length * masked_lm_prob。

python create_pretraining_data.py \
 --input_file=./sample_text.txt \
 --output_file=/tmp/tf_examples.tfrecord \
 --vocab_file=$BERT_BASE_DIR/vocab.txt \
 --do_lower_case=True \
 --max_seq_length=128 \
 --max_predictions_per_seq=20 \
 --masked_lm_prob=0.15 \
 --random_seed=12345 \
 --dupe_factor=5
複製代碼

若是你是從頭開始進行預訓練,請不要包含 init_checkpoint。模型配置(包括詞彙大小)在 bert_config_file 中指定。演示代碼僅預訓練少許步驟(20 個),但在實際當中你可能須要將 num_train_steps 設置爲 10000 步或更多。傳給 run_pretraining.py 的 max_seq_length 和 max_predictions_per_seq 參數必須與 create_pretraining_data.py 相同。

python run_pretraining.py \
 --input_file=/tmp/tf_examples.tfrecord \
 --output_dir=/tmp/pretraining_output \
 --do_train=True \
 --do_eval=True \
 --bert_config_file=$BERT_BASE_DIR/bert_config.json \
 --init_checkpoint=$BERT_BASE_DIR/bert_model.ckpt \
 --train_batch_size=32 \
 --max_seq_length=128 \
 --max_predictions_per_seq=20 \
 --num_train_steps=20 \
 --num_warmup_steps=10 \
 --learning_rate=2e-5
複製代碼

這將產生以下輸出:

***** Eval results *****
 global_step = 20
 loss = 0.0979674
 masked_lm_accuracy = 0.985479
 masked_lm_loss = 0.0979328
 next_sentence_accuracy = 1.0
 next_sentence_loss = 3.45724e-05
複製代碼

請注意,因爲 sample_text.txt 文件很是小,這個示例將在幾個步驟以內出現過擬合,併產生不切實際的高準確率。

預訓練數據

咱們將沒法發佈論文中使用的預處理數據集。 對於 Wikipedia,建議下載最新的轉儲(dumps.wikimedia.org/enwiki/late… WikiExtractor.py 提取文本,而後進行必要的清理將其轉換爲純文本。

惋惜的是,收集 BookCorpus 的研究人員再也不提供公開下載。 Guttenberg 數據集(web.eecs.umich.edu/~lahiri/gut… 億個單詞)的舊書集合。

Common Crawl(commoncrawl.org/)是另外一個很是大的文本… BERT 預訓練。

在 Colab 中使用 BERT 若是你想將 BERT 與 Colab 一塊兒使用,能夠從「BERT FineTuning with Cloud TPU」(colab.sandbox.google.com/github/tens… 年 10 月 31 日),Colab 用戶能夠徹底免費訪問一個 Cloud TPU。每一個用戶可使用一個,可用性有限,須要一個帶有存儲空間的 Google Cloud Platform 賬戶,而且在將來可能沒法再使用。

英文原文:

github.com/google-rese…

相關文章
相關標籤/搜索