Keras 教程:BERT 文本摘要

2020-11-22 雷鋒網

字幕組雙語原文:Keras 教程:BERT 文本摘要

英語原文:BERT (from HuggingFace Transformers) for Text Extraction

翻譯:雷鋒字幕組(yhfwww、wiige)


介紹

這個演示使用了SQuAD (Stanford question - answer Dataset)。在SQuAD 數據集中,輸入由一個問題和一個上下文段落組成。目標是找到回答問題的段落的跨度。我們使用「精確匹配(Exact Match)」指標來評估我們在這些數據上的表現,它度量了精確匹配任何一個真實答案的預測的百分比。

我們對一個BERT模型進行微調,如下所示:

  1. 將上下文和問題作為輸入,輸入給BERT。

  2. 取兩個向量S和T它們的維數等於BERT中隱藏狀態的維數。

  3. 計算每個token作為答案範圍的開始和結束的概率。一個token作為答案開始的概率是由S和在最後一層BERT中表示的token之間的點積給出的,然後是所有token的softmax。token作為最終答案的概率的計算方法與向量T類似。

  4. 微調BERT,學習S和T。

參考:

BERT

SQuAD

設置:

import os
import re
import json
import string
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from tokenizers import BertWordPieceTokenizer
from transformers import BertTokenizer,TFBertModel,Bert
Configmax_len = 384
configuration = BertConfig()  
# default paramters and configuration for BERT

設置BERT分詞器

# Save the slow pretrained tokenizerslow_tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")save_path = "bert_base_uncased/"if not os.path.exists(save_path):
    os.makedirs(save_path)slow_tokenizer.save_pretrained(save_path)# Load the fast tokenizer from saved filetokenizer = BertWordPieceTokenizer("bert_base_uncased/vocab.txt", lowercase=True)

載入數據

train_data_url = "https://rajpurkar.github.io/SQuAD-explorer/dataset/train-v1.1.json"train_path = keras.utils.get_file("train.json", train_data_url)eval_data_url = "https://rajpurkar.github.io/SQuAD-explorer/dataset/dev-v1.1.json"eval_path = keras.utils.get_file("eval.json", eval_data_url)

數據預處理

  1. 遍歷JSON文件,把每行記錄都保存為SquadExample對象.

  2. 遍歷每個SquadExample對象來創建x_train, y_train, x_eval, y_eval.

class SquadExample:    def __init__(self, question, context, start_char_idx, answer_text, all_answers):
        self.question = question
        self.context = context
        self.start_char_idx = start_char_idx
        self.answer_text = answer_text
        self.all_answers = all_answers
        self.skip = False
    def preprocess(self):
        context = self.context
        question = self.question
        answer_text = self.answer_text
        start_char_idx = self.start_char_idx
        # Clean context, answer and question        context = " ".join(str(context).split())
        question = " ".join(str(question).split())
        answer = " ".join(str(answer_text).split())
        # Find end character index of answer in context        end_char_idx = start_char_idx + len(answer)
        if end_char_idx >= len(context):
            self.skip = True
            return        # Mark the character indexes in context that are in answer        is_char_in_ans = [0] * len(context)
        for idx in range(start_char_idx, end_char_idx):
            is_char_in_ans[idx] = 1        # Tokenize context        tokenized_context = tokenizer.encode(context)
        # Find tokens that were created from answer characters        ans_token_idx = []
        for idx, (start, end) in enumerate(tokenized_context.offsets):
            if sum(is_char_in_ans[start:end]) > 0:
                ans_token_idx.append(idx)
        if len(ans_token_idx) == 0:
            self.skip = True
            return        # Find start and end token index for tokens from answer        start_token_idx = ans_token_idx[0]
        end_token_idx = ans_token_idx[-1]
        # Tokenize question        tokenized_question = tokenizer.encode(question)
        # Create inputs        input_ids = tokenized_context.ids + tokenized_question.ids[1:]
        token_type_ids = [0] * len(tokenized_context.ids) + [1] * len(
            tokenized_question.ids[1:]
        )
        attention_mask = [1] * len(input_ids)
        # Pad and create attention masks.        # Skip if truncation is needed        padding_length = max_len - len(input_ids)
        if padding_length > 0:  # pad            input_ids = input_ids + ([0] * padding_length)
            attention_mask = attention_mask + ([0] * padding_length)
            token_type_ids = token_type_ids + ([0] * padding_length)
        elif padding_length < 0:  # skip            self.skip = True
            return        self.input_ids = input_ids
        self.token_type_ids = token_type_ids
        self.attention_mask = attention_mask
        self.start_token_idx = start_token_idx
        self.end_token_idx = end_token_idx
        self.context_token_to_char = tokenized_context.offsetswith open(train_path) as f:    raw_train_data = json.load(f)with open(eval_path) as f:    raw_eval_data = json.load(f)def create_squad_examples(raw_data):
    squad_examples = []
    for item in raw_data["data"]:
        for para in item["paragraphs"]:
            context = para["context"]
            for qa in para["qas"]:
                question = qa["question"]
                answer_text = qa["answers"][0]["text"]
                all_answers = [_["text"] for _ in qa["answers"]]
                start_char_idx = qa["answers"][0]["answer_start"]
                squad_eg = SquadExample(
                    question, context, start_char_idx, answer_text, all_answers
                )
                squad_eg.preprocess()
                squad_examples.append(squad_eg)
    return squad_examplesdef create_inputs_targets(squad_examples):
    dataset_dict = {
        "input_ids": [],
        "token_type_ids": [],
        "attention_mask": [],
        "start_token_idx": [],
        "end_token_idx": [],
    }
    for item in squad_examples:        if item.skip == False:            for key in dataset_dict:                dataset_dict[key].append(getattr(item, key))
    for key in dataset_dict:        dataset_dict[key] = np.array(dataset_dict[key])
    x = [
        dataset_dict["input_ids"],
        dataset_dict["token_type_ids"],
        dataset_dict["attention_mask"],
    ]
    y = [dataset_dict["start_token_idx"], dataset_dict["end_token_idx"]]
    return x, ytrain_squad_examples = create_squad_examples(raw_train_data)x_train, y_train = create_inputs_targets(train_squad_examples)print(f"{len(train_squad_examples)} training points created.")eval_squad_examples = create_squad_examples(raw_eval_data)x_eval, y_eval = create_inputs_targets(eval_squad_examples)print(f"{len(eval_squad_examples)} evaluation points created.")
87599 training points created.10570 evaluation points created.

用BERT和函數式API來構建問答模塊

def create_model():    ## BERT encoder    encoder = TFBertModel.from_pretrained("bert-base-uncased")
    ## QA Model    input_ids = layers.Input(shape=(max_len,), dtype=tf.int32)
    token_type_ids = layers.Input(shape=(max_len,), dtype=tf.int32)
    attention_mask = layers.Input(shape=(max_len,), dtype=tf.int32)
    embedding = encoder(
        input_ids, token_type_ids=token_type_ids, attention_mask=attention_mask
    )[0]
    start_logits = layers.Dense(1, name="start_logit", use_bias=False)(embedding)
    start_logits = layers.Flatten()(start_logits)
    end_logits = layers.Dense(1, name="end_logit", use_bias=False)(embedding)
    end_logits = layers.Flatten()(end_logits)
    start_probs = layers.Activation(keras.activations.softmax)(start_logits)
    end_probs = layers.Activation(keras.activations.softmax)(end_logits)
    model = keras.Model(
        inputs=[input_ids, token_type_ids, attention_mask],
        outputs=[start_probs, end_probs],
    )
    loss = keras.losses.SparseCategoricalCrossentropy(from_logits=False)
    optimizer = keras.optimizers.Adam(lr=5e-5)
    model.compile(optimizer=optimizer, loss=[loss, loss])
    return model

這段代碼很適合用Google Colab TPU來跑. 用Colab TPUs, 每個epoch大概花5-6分鐘即可.

use_tpu = Trueif use_tpu:
    # Create distribution strategy    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()
    tf.config.experimental_connect_to_cluster(tpu)
    tf.tpu.experimental.initialize_tpu_system(tpu)
    strategy = tf.distribute.experimental.TPUStrategy(tpu)
    # Create model    with strategy.scope():
        model = create_model()else:
    model = create_model()model.summary()
INFO:absl:Entering into master device scope: /job:worker/replica:0/task:0/device:CPU:0INFO:tensorflow:Initializing the TPU system: grpc://10.48.159.170:8470INFO:tensorflow:Clearing out eager cachesINFO:tensorflow:Finished initializing TPU system.INFO:tensorflow:Found TPU system:INFO:tensorflow:*** Num TPU Cores: 8INFO:tensorflow:*** Num TPU Workers: 1INFO:tensorflow:*** Num TPU Cores Per Worker: 8Model: "model"__________________________________________________________________________________________________Layer (type)                    Output Shape         Param #     Connected to                     ==================================================================================================input_1 (InputLayer)            [(None, 384)]        0                                            __________________________________________________________________________________________________input_3 (InputLayer)            [(None, 384)]        0                                            __________________________________________________________________________________________________input_2 (InputLayer)            [(None, 384)]        0                                            __________________________________________________________________________________________________tf_bert_model (TFBertModel)     ((None, 384, 768), ( 109482240   input_1[0][0]                    __________________________________________________________________________________________________start_logit (Dense)             (None, 384, 1)       768         tf_bert_model[0][0]              __________________________________________________________________________________________________end_logit (Dense)               (None, 384, 1)       768         tf_bert_model[0][0]              __________________________________________________________________________________________________flatten (Flatten)               (None, 384)          0           start_logit[0][0]                __________________________________________________________________________________________________flatten_1 (Flatten)             (None, 384)          0           end_logit[0][0]                  __________________________________________________________________________________________________activation_7 (Activation)       (None, 384)          0           flatten[0][0]                    __________________________________________________________________________________________________activation_8 (Activation)       (None, 384)          0           flatten_1[0][0]                  ==================================================================================================Total params: 109,483,776Trainable params: 109,483,776Non-trainable params: 0__________________________________________________________________________________________________

構建評價回調函數

這個回調函數會在每個epoch後用驗證集數據計算匹配值.

def normalize_text(text):
    text = text.lower()
    # Remove punctuations    exclude = set(string.punctuation)
    text = "".join(ch for ch in text if ch not in exclude)
    # Remove articles    regex = re.compile(r"\b(a|an|the)\b", re.UNICODE)
    text = re.sub(regex, " ", text)
    # Remove extra white space    text = " ".join(text.split())
    return textclass ExactMatch(keras.callbacks.Callback):
    """    Each `SquadExample` object contains the character level offsets for each token    in its input paragraph. We use them to get back the span of text corresponding    to the tokens between our predicted start and end tokens.    All the ground-truth answers are also present in each `SquadExample` object.    We calculate the percentage of data points where the span of text obtained    from model predictions matches one of the ground-truth answers.    """    def __init__(self, x_eval, y_eval):
        self.x_eval = x_eval
        self.y_eval = y_eval
    def on_epoch_end(self, epoch, logs=None):
        pred_start, pred_end = self.model.predict(self.x_eval)
        count = 0        eval_examples_no_skip = [_ for _ in eval_squad_examples if _.skip == False]
        for idx, (start, end) in enumerate(zip(pred_start, pred_end)):
            squad_eg = eval_examples_no_skip[idx]
            offsets = squad_eg.context_token_to_char
            start = np.argmax(start)
            end = np.argmax(end)
            if start >= len(offsets):
                continue
            pred_char_start = offsets[start][0]
            if end < len(offsets):
                pred_char_end = offsets[end][1]
                pred_ans = squad_eg.context[pred_char_start:pred_char_end]
            else:
                pred_ans = squad_eg.context[pred_char_start:]
            normalized_pred_ans = normalize_text(pred_ans)
            normalized_true_ans = [normalize_text(_) for _ in squad_eg.all_answers]
            if normalized_pred_ans in normalized_true_ans:
                count += 1        acc = count / len(self.y_eval[0])
        print(f"\nepoch={epoch+1}, exact match score={acc:.2f}")

訓練和評估

exact_match_callback = ExactMatch(x_eval, y_eval)model.fit(
    x_train,
    y_train,
    epochs=1,  # For demonstration, 3 epochs are recommended    verbose=2,
    batch_size=64,
    callbacks=[exact_match_callback],)

epoch=1, exact match score=0.781346/1346 - 350s - activation_7_loss: 1.3488 - loss: 2.5905 - activation_8_loss: 1.2417<tensorflow.python.keras.callbacks.History at 0x7fc78b4458d0>


雷鋒字幕組是一個由 AI 愛好者組成的翻譯團隊,匯聚五百多位志願者的力量,分享最新的海外AI資訊,交流關於人工智慧技術領域的行業變革與技術創新的見解。

團隊成員有大數據專家、算法工程師、圖像處理工程師、產品經理、產品運營、IT諮詢人、在校師生;志願者們來自IBM、AVL、Adobe、阿里、百度等知名企業,北大、清華、港大、中科院、南卡羅萊納大學、早稻田大學等海內外高校研究所。

如果,你也是位熱愛分享的AI愛好者。歡迎與雷鋒字幕組一起,學習新知,分享成長。

雷鋒網(公眾號:雷鋒網)雷鋒網

雷鋒網版權文章,未經授權禁止轉載。詳情見轉載須知。

相關焦點

  • NLP之文本分類:「Tf-Idf、Word2Vec和BERT」三種模型比較
    NLP常用於文本數據的分類。文本分類是指根據文本數據內容對其進行分類的問題。我們有多種技術從原始文本數據中提取信息,並用它來訓練分類模型。本教程比較了傳統的詞袋法(與簡單的機器學習算法一起使用)、流行的詞嵌入模型(與深度學習神經網絡一起使用)和最先進的語言模型(和基於attention的transformers模型中的遷移學習一起使用),語言模型徹底改變了NLP的格局。
  • 大神教程乾貨:使用BERT的多類別情感分析!(附代碼)
    使用BERT輕鬆快速地實現針對Yelp評論的多類別文本情感分析。在本文中,我們將使用BERT在Yelp評論上開發多類文本分類。BERT概述BERT是用於通用「語言理解」 的深度雙向表示模型,可從左至右和從右至左學習信息。
  • 使用BERT和TensorFlow構建搜尋引擎
    為了證明這一點,將使用BERT特徵提取為文本構建最近鄰搜尋引擎。這包括執行整個文本預處理管道和為BERT 準備feed_dict。 首先,將每個文本樣本轉換為包含INPUT_NAMES 中列出的必要功能的tf.Example實例。
  • 乾貨| BERT fine-tune 終極實踐教程
    以下是奇點機智技術團隊對BERT在中文數據集上的fine tune終極實踐教程。在自己的數據集上運行 BERTBERT的代碼同論文裡描述的一致,主要分為兩個部分。一個是訓練語言模型(language model)的預訓練(pretrain)部分。
  • 教你用BERT進行多標籤文本分類
    我們將使用Kaggle的「惡意評論分類挑戰」來衡量BERT在多標籤文本分類中的表現。在本次競賽中,我們將嘗試構建一個能夠將給文本片段分配給同惡評類別的模型。我們設定了惡意評論類別作為模型的目標標籤,它們包括普通惡評、嚴重惡評、汙言穢語、威脅、侮辱和身份仇視。
  • Keras入門系列教程:兩分鐘構建你的第一個神經網絡模型
    本教程由深度學習中文社區(Studydl.com)持續發布與更新, 本系列其餘教程地址見文章末尾.tf.keras使TensorFlow更易於使用而不犧牲靈活性和性能。, 歡迎大家關注我的百家號,第一時間獲得最新教程推送.
  • 基於Bert和通用句子編碼的Spark-NLP文本分類
    自然語言處理(NLP)是許多數據科學系統中必須理解或推理文本的關鍵組成部分。常見的用例包括文本分類、問答、釋義或總結、情感分析、自然語言BI、語言建模和消歧。NLP在越來越多的人工智慧應用中是越來越重要。如果你正在構建聊天機器人、搜索專利資料庫、將患者與臨床試驗相匹配、對客戶服務或銷售電話進行分級、從財務報告中提取摘要,你必須從文本中提取準確的信息。
  • 谷歌最強NLP模型BERT,為何炫酷又強大?騰訊程式設計師給你從頭講起
    query-title 分檔任務要求針對 query 和 title 按文本相關性進行 5 個檔位的分類(1~5 檔),各檔位從需求滿足及語義匹配這兩方面對 query-doc 的相關度進行衡量,檔位越大表示相關性越高,如 1 檔表示文本和語義完全不相關,而 5 檔表示文本和語義高度相關,完全符合 query 的需求。
  • 這是一份覆蓋全面的基於 PyTorch 和 keras 的 NLP...
    最近,小編在 github 上發現了一份基於 PyTorch 和 keras 的 NLP 學習教程。這份教程內容相當豐富,內容涵蓋神經網絡機器翻譯、問答匹配、電影評價分類、新聞分類等多個領域。項目的 GitHub 地址為:https://github.com/lyeoni/nlp-tutorial。
  • 英文教程太難啃?這裡有一份TensorFlow2.0中文教程
    近兩個月,網上已經出現了大量 TensorFlow 2.0 英文教程。在此文章中,機器之心為大家推薦一個持續更新的中文教程,以便大家學習。過去一段時間,機器之心為大家編譯介紹了部分英文教程,例如: 如何在 TensorFlow 2.0 中構建強化學習智能體 TensorFlow 2.0 到底怎麼樣?
  • 了解1D和3D卷積神經網絡|Keras
    以下是在keras中添加Conv2D圖層的代碼。import kerasfrom keras.layers import Conv2Dmodel = keras.models.Sequential()model.add(Conv2D(1, kernel_size
  • Keras和TensorFlow究竟哪個會更好?
    文本中,Rosebrock 展示了如何訓練使用 Keras 的神經網絡和使用直接構建在 TensorFlow 庫中的 Keras+TensorFlow 集成(具有自定義功能)的模型。 我會使用基於 TensorFlow 的標準 keras 模塊和 tf.keras 模塊,來實現一個卷積神經網絡(CNN)。然後,基於一個示例數據集,來訓練這些 CNN,然後檢查所得結果,你會發現,Keras 和 TensorFlow 是可以和諧共處的。
  • 谷歌搜索:幾乎所有的英文搜索都用上BERT了
    作為自然語言處理領域裡程碑式的進展,BERT 為該領域帶來了以下創新:利用無標籤文本進行預訓練;雙向上下文模型;transformer 架構的應用;masked 語言建模;注意力機制;文本蘊涵(下一句預測)
  • 超越BERT 和 GPT,微軟亞洲研究院開源新模型 MASS!
    然而, NLP 領域除了自然語言理解任務之外,還存在很多序列到序列的語言生成任務,例如機器翻譯、文本摘要生成、對話生成、問答、文本風格轉換等。對於這些任務,使用編碼器-注意力-解碼器框架是主流方法。值下 MASS 的概率公式研究人員通過實驗來分析了在不同 k 值下的 MASS 性能,如圖 5 所示:圖 5 在訓練前和微調階段的各種掩蔽長度 k 下 MASS 的表現,其中包括 a) 英語句子預訓練模型的PPL b) WMT13 英語-法語翻譯的法語句子 c) WMT13 無監督英語-法語翻譯的 BLEU 值 d) 文本摘要生成的
  • 通過Keras 構建基於 LSTM 模型的故事生成器
    你不會從一開始或者從中間部分閱讀就能夠直接理解文本意義,而是隨著你閱讀的深入,你的大腦才最終形成上下文聯繫,能夠理解文本意義。傳統神經網絡的一個主要不足在於不能夠真正地像人類大腦的神經元一樣工作運行,往往只能夠利用短期記憶或者信息。 一旦數據序列較長,就難以將早期階段信息傳遞至後面階段考慮下面兩個句子。
  • 文本匹配模型TextMatching
    寫在前面本次分享一篇NLP文本匹配的經典文章,主要思路是借鑑圖像識別的思路來解決NLP文本匹配問題,畢竟計算機視覺技術的發展在當時異常迅猛。個人心得:文本匹配問題可轉化為二分類問題,輸出概率則為相似度將圖像識別的思路應用於文本匹配任務中,利用卷積抽取文本特徵論文地址:https://arxiv.org/pdf/1602.06359.pdf1.
  • 圖像分類入門,輕鬆拿下90%準確率|教你用Keras搞Fashion-MNIST
    在TensorFlow中,可以使用tf.keras函數來編寫Keras程序,這樣就能充分利用動態圖機制eager execution和tf.data函數。下面可能還會遇到其他深度學習名詞,我們就不提前介紹啦。
  • 為文本摘要模型添加注意力機制:採用LSTM的編解碼器模型實現
    摘要是自然語言處理領域中最具挑戰性和最有趣的問題之一。它是一個從多種文本資源(如書籍、新聞文章、博客文章、研究論文、電子郵件和tweet)中生成簡潔而有意義的文本摘要的過程。現在,隨著大量的文本文檔的可用性,摘要是一個更加重要的任務。那麼有哪些不同的方法呢?
  • Keras 2發布:實現與TensorFlow的直接整合
    事實上,繼續發展將會出現 Keras 技術規範的兩個不同實現:(a)TensorFlow 的內部實現(如 tf.keras),純由 TensorFlow 寫成,與 TensorFlow 的所有功能深度兼容;(b)外部的多後臺實現,同時支持 Theano 和 TensorFlow(並可能在未來有更多的後臺)。