AGI 之 【Hugging Face】 的【文本摘要】的 [評估PEGASUS ] / [ 微調PEGASUS ] / [生成對話摘要]? 的簡單整理
目錄
AGI 之 【Hugging Face】 的【文本摘要】的 [評估PEGASUS ] / [ 微調PEGASUS ] / [生成對話摘要]? 的簡單整理
一、簡單介紹
二、文本摘要
三、在CNN/DailyMail數據集上評估PEGASUS
四、訓練摘要模型
1、評估PEGASUS在SAMSum上的性能
2、微調PEGASUS
3、生成對話摘要
一、簡單介紹
AGI,即通用人工智能(Artificial General Intelligence),是一種具備人類智能水平的人工智能系統。它不僅能夠執行特定的任務,而且能夠理解、學習和應用知識于廣泛的問題解決中,具有較高的自主性和適應性。AGI的能力包括但不限于自我學習、自我改進、自我調整,并能在沒有人為干預的情況下解決各種復雜問題。
- AGI能做的事情非常廣泛:
??? 跨領域任務執行:AGI能夠處理多領域的任務,不受限于特定應用場景。
??? 自主學習與適應:AGI能夠從經驗中學習,并適應新環境和新情境。
??? 創造性思考:AGI能夠進行創新思維,提出新的解決方案。
??? 社會交互:AGI能夠與人類進行復雜的社會交互,理解情感和社會信號。
- 關于AGI的未來發展前景,它被認為是人工智能研究的最終目標之一,具有巨大的變革潛力:
??? 技術創新:隨著機器學習、神經網絡等技術的進步,AGI的實現可能會越來越接近。
??? 跨學科整合:實現AGI需要整合計算機科學、神經科學、心理學等多個學科的知識。
??? 倫理和社會考量:AGI的發展需要考慮隱私、安全和就業等倫理和社會問題。
??? 增強學習和自適應能力:未來的AGI系統可能利用先進的算法,從環境中學習并優化行為。
??? 多模態交互:AGI將具備多種感知和交互方式,與人類和其他系統交互。
Hugging Face作為當前全球最受歡迎的開源機器學習社區和平臺之一,在AGI時代扮演著重要角色。它提供了豐富的預訓練模型和數據集資源,推動了機器學習領域的發展。Hugging Face的特點在于易用性和開放性,通過其Transformers庫,為用戶提供了方便的模型處理文本的方式。隨著AI技術的發展,Hugging Face社區將繼續發揮重要作用,推動AI技術的發展和應用,尤其是在多模態AI技術發展方面,Hugging Face社區將擴展其模型和數據集的多樣性,包括圖像、音頻和視頻等多模態數據。
- 在AGI時代,Hugging Face可能會通過以下方式發揮作用:
??????? 模型共享:作為模型共享的平臺,Hugging Face將繼續促進先進的AGI模型的共享和協作。
??????? 開源生態:Hugging Face的開源生態將有助于加速AGI技術的發展和創新。
??????? 工具和服務:提供豐富的工具和服務,支持開發者和研究者在AGI領域的研究和應用。
??????? 倫理和社會責任:Hugging Face注重AI倫理,將推動負責任的AGI模型開發和應用,確保技術進步同時符合倫理標準。
AGI作為未來人工智能的高級形態,具有廣泛的應用前景,而Hugging Face作為開源社區,將在推動AGI的發展和應用中扮演關鍵角色。
(注意:以下代碼運行,可能需要科學上網)
二、文本摘要
你可能曾經需要總結一份文件,包括研究文章、財務收益報告、一系列電子郵件。如果你仔細思考,這需要一系列的能力,包括理解長篇內容、推理內容、然后產生一段流暢的、包括原始文檔主要主題的文本。此外,準確地總結新聞文章與總結法律合同非常不同,因此需要復雜的領域泛化能力。出于這些原因,總結文本(專業術語為文本摘要)對于神經語言模型,包括Transformer模型來說是一項困難的任務。盡管面臨這些挑戰,文本摘要因為能夠顯著加速領域專家的工作流程,企業可以通過文本摘要壓縮內部知識、總結合同、自動生成社交媒體發布內容等。因此文本摘要NLP任務很有價值。
為了幫助你理解相關的挑戰,本節將探討如何利用Transformer預訓練模型來進行文本摘要。摘要是一種經典的序列到序列(seq2seq)任務,需要輸入文本和目標文本。
文本摘要是一種自然語言處理任務,其目標是從一個長文本中提取出簡潔、重要的信息,生成一個簡短的版本。文本摘要可以分為兩種主要類型:抽取式摘要和生成式摘要。
- 抽取式摘要
抽取式摘要通過選擇原始文本中的重要句子或段落,直接提取這些內容作為摘要。這種方法不改變原始文本中的詞語和句子結構。
實現原理:
- ??? 特征提取:首先,需要提取文本的各種特征,例如詞頻、句子位置、關鍵詞、命名實體等。
- ??? 重要性評分:基于提取的特征,計算每個句子的得分,以確定其重要性。
- ??? 句子選擇:根據重要性得分,選擇最重要的句子來構建摘要。
難點:
- ??? 重要性衡量:如何準確衡量句子的相對重要性。
- ??? 冗余消除:避免選擇內容重復的句子。
實現方式:
- ??? 基于規則的方法:使用預定義的規則和統計方法來選擇句子。
- ??? 機器學習方法:使用有監督的學習算法,根據訓練數據學習如何選擇重要句子。
- 生成式摘要
生成式摘要通過理解原始文本并生成新的句子來概括其內容。這種方法可以創建更為自然和連貫的摘要,但也更加復雜。
實現原理:
- ??? 編碼器-解碼器架構:使用序列到序列(Seq2Seq)模型,其中編碼器將輸入文本編碼成上下文向量,解碼器根據上下文向量生成摘要。
- ??? 注意力機制:在解碼過程中,模型可以關注輸入文本的不同部分,從而生成更相關的內容。
- ??? 預訓練模型:使用預訓練的語言模型(如BERT、GPT等)來提高生成摘要的質量。
難點:
- ??? 內容連貫性:生成的摘要需要保持邏輯連貫,避免內容斷裂。
- ??? 信息完整性:確保生成的摘要包含原始文本中的關鍵信息。
- ??? 模型復雜度:生成式摘要模型通常比抽取式摘要模型更復雜,需要更多的計算資源和訓練數據。
實現方式:
- ??? 經典的 Seq2Seq 模型:如基于 LSTM 的編碼器-解碼器模型。
- ??? 預訓練的 Transformer 模型:如 BERTSUM、T5、BART 等。
- Hugging Face 中的文本摘要
Hugging Face 提供了多種預訓練模型和工具,可以方便地實現文本摘要任務。以下是一些常用的文本摘要模型和使用方法:
- ?使用預訓練模型進行摘要
以下是使用 Hugging Face 提供的 BART 模型進行文本摘要的示例代碼:
from transformers import BartForConditionalGeneration, BartTokenizer# 加載預訓練的BART模型和對應的tokenizer model_name = "facebook/bart-large-cnn" model = BartForConditionalGeneration.from_pretrained(model_name) tokenizer = BartTokenizer.from_pretrained(model_name)# 輸入文本 input_text = """Your text to summarize goes here."""# 對輸入文本進行tokenize,并添加必要的模型輸入 inputs = tokenizer([input_text], max_length=1024, return_tensors='pt')# 使用模型生成摘要 summary_ids = model.generate(inputs['input_ids'], num_beams=4, max_length=150, early_stopping=True)# 將生成的token序列轉換回文本 summary = tokenizer.decode(summary_ids[0], skip_special_tokens=True)print(summary)
- 支持的摘要模型
Hugging Face 提供了多種用于文本摘要的預訓練模型,包括但不限于:
- ??? BART (facebook/bart-large-cnn)
- ??? T5 (t5-small, t5-base, t5-large, t5-3b, t5-11b)
- ??? PEGASUS (google/pegasus-xsum, google/pegasus-cnn_dailymail)
- ??? 訓練自己的摘要模型
如果需要更好地適應特定領域的文本摘要任務,可以使用自己的數據集對預訓練模型進行微調。以下是一個簡單的微調示例:
from transformers import Trainer, TrainingArguments, BartForConditionalGeneration, BartTokenizer from datasets import load_dataset# 加載數據集 dataset = load_dataset("cnn_dailymail", "3.0.0")# 加載預訓練的BART模型和tokenizer model_name = "facebook/bart-large-cnn" model = BartForConditionalGeneration.from_pretrained(model_name) tokenizer = BartTokenizer.from_pretrained(model_name)# 數據預處理 def preprocess_function(examples):inputs = [doc for doc in examples['article']]model_inputs = tokenizer(inputs, max_length=1024, truncation=True)# 設定摘要作為目標with tokenizer.as_target_tokenizer():labels = tokenizer(examples['highlights'], max_length=150, truncation=True)model_inputs['labels'] = labels['input_ids']return model_inputstokenized_dataset = dataset.map(preprocess_function, batched=True)# 定義訓練參數 training_args = TrainingArguments(output_dir="./results",evaluation_strategy="epoch",learning_rate=2e-5,per_device_train_batch_size=4,per_device_eval_batch_size=4,num_train_epochs=3,weight_decay=0.01, )# 使用Trainer進行訓練 trainer = Trainer(model=model,args=training_args,train_dataset=tokenized_dataset["train"],eval_dataset=tokenized_dataset["validation"], )trainer.train()
文本摘要是一個復雜且具有挑戰性的自然語言處理任務。通過使用 Hugging Face 提供的預訓練模型和工具,可以大大簡化文本摘要的實現過程。用戶可以根據具體需求選擇合適的模型,進行微調,以獲得最佳的摘要效果。
在本節中,我們將建立自己的編碼器-解碼器模型,將多人對話壓縮成簡明的摘要。但在此之前,我們先來看看摘要領域中一個經典數據集:CNN/DailyMail語料庫。
三、在CNN/DailyMail數據集上評估PEGASUS
現在充分評估模型的條件都齊全了:我們擁有CNN/DailyMail測試集數據集、評估用的ROUGE指標,以及一個摘要模型。
# 導入所需的庫
import matplotlib.pyplot as plt # 導入 matplotlib.pyplot,用于繪制圖形
import pandas as pd # 導入 pandas,用于數據處理
from datasets import load_dataset, load_metric # 從 datasets 庫中導入 load_dataset 和 load_metric 函數
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer # 從 transformers 庫中導入 AutoModelForSeq2SeqLM 和 AutoTokenizer# 加載 CNN/DailyMail 數據集,版本為 3.0.0
dataset = load_dataset("cnn_dailymail", "3.0.0")# 加載 ROUGE 評價指標,用于計算文本摘要的質量
rouge_metric = load_metric("rouge", cache_dir=None)# 定義要計算的 ROUGE 分數的名稱列表
rouge_names = ["rouge1", "rouge2", "rougeL", "rougeLsum"]
我們只需要把這些部分組合起來。首先,我們評估三句話基準模型的性能:
# 定義一個函數,用于評估基線模型生成的摘要
def evaluate_summaries_baseline(dataset, metric, column_text="article", column_summary="highlights"):# 使用 three_sentence_summary 函數對數據集中的每篇文章生成摘要summaries = [three_sentence_summary(text) for text in dataset[column_text]]# 將生成的摘要和參考摘要添加到評價指標中metric.add_batch(predictions=summaries, references=dataset[column_summary])# 計算評價指標的分數score = metric.compute()# 返回評價指標的分數return score
然后我們把該函數應用于數據的一個子集。由于CNN/DailyMail數據集的測試部分包含大約10?000個樣本,生成所有這些文章的摘要需要很多時間。回顧第5章,每個生成的詞元都需要通過模型進行前向傳遞。為每個樣本生成100個詞元將需要100萬次前向傳遞,如果我們使用束搜索,則此數字還需要乘以束的數量。為了讓計算更快一些,我們將對測試集進行子采樣,最終使用1000個樣本進行評估。這樣我們使用單個GPU上不到一小時就能完成PEGASUS模型的評估,而且得到穩定的分數估計:
# 從測試集中隨機抽取1000條樣本,用于評估
test_sampled = dataset["test"].shuffle(seed=42).select(range(1000))# 使用基線模型生成摘要并評估其質量
score = evaluate_summaries_baseline(test_sampled, rouge_metric)# 將評價指標的分數存儲在字典中
rouge_dict = dict((rn, score[rn].mid.fmeasure) for rn in rouge_names)# 將評價指標的分數轉換為DataFrame格式,并轉置以便顯示
pd.DataFrame.from_dict(rouge_dict, orient="index", columns=["baseline"]).T
運行結果:
rouge1 | rouge2 | rougeL | rougeLsum | |
baseline | 0.38928 | 0.171296 | 0.245061 | 0.354239 |
分數大多數比上一個示例差,但仍然比GPT-2實現的分數要好!現在我們依樣畫葫蘆來評估PEGASUS模型:
# 導入 tqdm 模塊,用于顯示進度條
from tqdm import tqdm
# 導入 torch 模塊,用于使用 GPU 或 CPU 進行計算
import torch# 設置設備為 GPU(如果可用)或 CPU
device = "cuda" if torch.cuda.is_available() else "cpu"def chunks(list_of_elements, batch_size):"""將 list_of_elements 按 batch_size 切分成多個小塊"""for i in range(0, len(list_of_elements), batch_size):yield list_of_elements[i : i + batch_size]def evaluate_summaries_pegasus(dataset, metric, model, tokenizer, batch_size=16, device=device, column_text="article", column_summary="highlights"):"""評估使用 Pegasus 模型生成的摘要"""# 將文章和摘要分別按 batch_size 切分成多個小塊article_batches = list(chunks(dataset[column_text], batch_size))target_batches = list(chunks(dataset[column_summary], batch_size))# 使用 tqdm 顯示進度條,遍歷每個文章批次和相應的摘要批次for article_batch, target_batch in tqdm(zip(article_batches, target_batches), total=len(article_batches)):# 對文章批次進行標記,將其轉換為模型輸入的張量inputs = tokenizer(article_batch, max_length=1024, truncation=True, padding="max_length", return_tensors="pt")# 使用 Pegasus 模型生成摘要summaries = model.generate(input_ids=inputs["input_ids"].to(device),attention_mask=inputs["attention_mask"].to(device), length_penalty=0.8, num_beams=8, max_length=128)# 解碼生成的摘要,將其從張量轉換為字符串decoded_summaries = [tokenizer.decode(s, skip_special_tokens=True, clean_up_tokenization_spaces=True) for s in summaries]decoded_summaries = [d.replace("", " ") for d in decoded_summaries]# 將生成的摘要和目標摘要添加到評價指標中metric.add_batch(predictions=decoded_summaries, references=target_batch)# 計算評價指標分數score = metric.compute()return score
我們來詳細解釋一下這段評估代碼。首先,我們將數據集分成較小的批量,以便可以同時處理。然后對于每個批量,我們對輸入文章進行詞元化,然后將它們提供給generate()函數,使用束搜索生成摘要。我們使用論文中的相同生成參數。懲罰參數新的長度確保模型不會生成過長的序列。最后,我們解碼生成文本,替換<n>詞元,并將解碼的文本與參考文本一起添加到度量中。最后,我們計算并返回ROUGE分數。現在,我們再次使用用于seq2seq生成任務的AutoModelForSeq2SeqLM類來加載模型,并對其進行評估:
# 從 transformers 庫中導入用于序列到序列任務的模型和標記器
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer# 設置模型檢查點名稱,使用 Google 的 PEGASUS 模型,預訓練于 CNN/DailyMail 數據集
model_ckpt = "google/pegasus-cnn_dailymail"# 從預訓練的模型檢查點中加載標記器和模型,并將模型移動到指定的設備(CPU 或 GPU)
tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
model = AutoModelForSeq2SeqLM.from_pretrained(model_ckpt).to(device)# 使用評估函數 evaluate_summaries_pegasus 評估 PEGASUS 模型生成的摘要
# 輸入參數包括測試數據、ROUGE 評價指標、模型、標記器和批處理大小
score = evaluate_summaries_pegasus(test_sampled, rouge_metric, model, tokenizer, batch_size=8)# 從評估結果中提取 ROUGE 分數,將其轉換為字典格式,其中鍵為 ROUGE 指標名稱,值為 F-measure 分數
rouge_dict = dict((rn, score[rn].mid.fmeasure) for rn in rouge_names)# 將 ROUGE 分數字典轉換為 pandas 數據框,并以 "pegasus" 作為索引
pd.DataFrame(rouge_dict, index=["pegasus"])
運行結果:
(這里暫時報錯:
TypeError: Couldn't build proto file into descriptor pool: duplicate file name sentencepiece_model.proto
,使用例子參考的運行結果如下)
rouge1 | rouge2 | rougeL | rougeLsum | |
pegasus | 0.43438 | 0.210883 | 0.307195 | 0.373231 |
這些數字非常接近論文中的結果。這里需要注意的是,損失和每個詞元的準確率在某種程度上與ROUGE分數解耦。損失與解碼策略無關,而ROUGE分數則強耦合。
由于ROUGE和BLEU比人工評估的損失或準確率更好,因此在構建文本生成模型時應重點關注它們,并仔細探索和選擇解碼策略。然而,這些指標遠非完美,因此應始終考慮人工評估。
現在我們已經有了評估函數,可以訓練我們自己的摘要模型了。
四、訓練摘要模型
至此,我們已經仔細研究了文本摘要和評估的許多細節,現在我們使用這些知識來訓練一個自定義的文本摘要模型!對于我們這個自定義應用,我們將使用三星開發的SAMSum數據集(https://oreil.ly/n1ggq),該數據集包含了一系列對話以及簡短的摘要。這些對話可以代表客戶與客服中心之間的互動,并以此生成準確的摘要以幫助改善客戶服務,并檢測客戶請求中的常見模式。我們先加載數據集并查看一個樣本:
# 從 datasets 庫中導入用于加載數據集的函數
from datasets import load_dataset# 加載 SamSum 數據集,該數據集包含對話和相應的摘要
dataset_samsum = load_dataset("samsum",trust_remote_code=True)# 獲取數據集的每個劃分(訓練集、驗證集、測試集)的長度,并存儲在列表 split_lengths 中
split_lengths = [len(dataset_samsum[split]) for split in dataset_samsum]# 打印每個數據集劃分的長度
print(f"Split lengths: {split_lengths}")# 打印訓練集中列的名稱(特征)
print(f"Features: {dataset_samsum['train'].column_names}")# 打印測試集中第一個對話樣本
print("\nDialogue:")
print(dataset_samsum["test"][0]["dialogue"])# 打印測試集中第一個對話樣本的摘要
print("\nSummary:")
print(dataset_samsum["test"][0]["summary"])
(注意:可能需要安裝 py7zr,pip install py7zr)
運行結果:
Split lengths: [14732, 819, 818] Features: ['id', 'dialogue', 'summary']Dialogue: Hannah: Hey, do you have Betty's number? Amanda: Lemme check Hannah: <file_gif> Amanda: Sorry, can't find it. Amanda: Ask Larry Amanda: He called her last time we were at the park together Hannah: I don't know him well Hannah: <file_gif> Amanda: Don't be shy, he's very nice Hannah: If you say so.. Hannah: I'd rather you texted him Amanda: Just text him 🙂 Hannah: Urgh.. Alright Hannah: Bye Amanda: Bye byeSummary: Hannah needs Betty's number but Amanda doesn't have it. She needs to contact Larry.
對話看起來就像你通過短信或WhatsApp聊天一樣,包括了表情符號和為GIF準備的占位符。dialogue字段包含了完整的文本,而summary字段則是對話的摘要。在CNN/DailyMail數據集上進行微調的模型能夠處理這個數據集嗎?我們來看看!
1、評估PEGASUS在SAMSum上的性能
首先,我們將使用PEGASUS運行相同的摘要生成流程,以查看輸出結果。我們可以重用CNN/DailyMail摘要生成的代碼:
# 使用已加載的summarization管道對測試集中的第一個對話樣本進行摘要
pipe_out = pipe(dataset_samsum["test"][0]["dialogue"])# 打印生成的摘要標題
print("Summary:")# 打印生成的摘要文本,并將每個句子的句號后面的空格替換為換行符
# 這行代碼會輸出生成的摘要,其中 ". " 替換為 ".\n" 使其更易讀
print(pipe_out[0]["summary_text"].replace(" .", ".\n"))
運行結果:
Summary: Hannah asks Amanda for Betty's number. Amanda can't find it. Hannah asks Larry. Amanda asks Larry to text him. Hannah says she'll text him back. Hannah calls it a day and says she's going to go home. Hannah: "Bye bye"
我們可以看到,該模型主要嘗試通過提取對話中的關鍵句子來進行摘要。這在CNN/DailyMail數據集上可能效果相對較好,但是在SAMSum中,摘要更加抽象,效果不一定好。我們可以通過在測試集上運行完整的ROUGE評估來確認這一點:
# 使用評估函數 evaluate_summaries_pegasus 對 SamSum 數據集的測試集進行摘要生成評估
# 傳入的參數包括數據集、評價指標、模型、tokenizer、文本列名、摘要列名和批量大小
score = evaluate_summaries_pegasus(dataset_samsum["test"], rouge_metric, model,tokenizer, column_text="dialogue",column_summary="summary", batch_size=8)# 創建一個字典 rouge_dict,用于存儲 ROUGE 評分的中值 F-measure 值
rouge_dict = dict((rn, score[rn].mid.fmeasure) for rn in rouge_names)# 將 ROUGE 評分字典轉換為 Pandas 數據框,并以 "pegasus" 為索引
pd.DataFrame(rouge_dict, index=["pegasus"])
運行結果:
(這里暫時報錯:
TypeError: Couldn't build proto file into descriptor pool: duplicate file name sentencepiece_model.proto
,使用例子參考的運行結果如下)
rouge1 | rouge2 | rougeL | rougeLsum | |
pegasus | 0.29617 | 0.087803 | 0.229604 | 0.229514 |
雖然結果并不太好,但這并不出乎意料,因為遠離了CNN/DailyMail數據分布。盡管如此,在訓練之前設置評估流程有兩個優點:我們可以直接使用度量指標來度量訓練的成功,而且我們有一個很好的基準。在我們的數據集上對模型進行微調應該會立即改善ROUGE度量指標,如果沒有改善,那么我們就知道我們的訓練循環出了問題。
2、微調PEGASUS
在我們對數據進行訓練之前,我們快速查看輸入和輸出的長度分布:
# 編碼訓練集中的對話文本和摘要,并計算其長度
d_len = [len(tokenizer.encode(s)) for s in dataset_samsum["train"]["dialogue"]]
s_len = [len(tokenizer.encode(s)) for s in dataset_samsum["train"]["summary"]]# 創建一個包含兩個子圖的圖形對象
fig, axes = plt.subplots(1, 2, figsize=(10, 3.5), sharey=True)# 繪制對話文本的長度分布直方圖
axes[0].hist(d_len, bins=20, color="C0", edgecolor="C0")
axes[0].set_title("Dialogue Token Length")
axes[0].set_xlabel("Length")
axes[0].set_ylabel("Count")# 繪制摘要的長度分布直方圖
axes[1].hist(s_len, bins=20, color="C0", edgecolor="C0")
axes[1].set_title("Summary Token Length")
axes[1].set_xlabel("Length")# 調整子圖布局,使其更加緊湊
plt.tight_layout()# 顯示繪制的圖形
plt.show()
運行結果:
(這里暫時報錯:
TypeError: Couldn't build proto file into descriptor pool: duplicate file name sentencepiece_model.proto
,使用例子參考的運行結果如下)
我們可以看到,大多數對話比CNN/DailyMail的文章要短得多,每個對話有大約100~200個詞元。同樣地,摘要也要短得多,大約有20~40個詞元(與推文的平均長度相同)。
我們先記住這些結果,后面會用到。首先,我們需要對數據集進行詞元化。我們將對話和摘要的最大長度分別設置為1024和128:
def convert_examples_to_features(example_batch):"""將示例批處理轉換為模型輸入特征。Args:- example_batch (dict): 包含對話和摘要的示例批處理字典。Returns:- dict: 包含轉換后特征的字典,包括輸入編碼和目標編碼。"""# 對對話文本進行編碼處理,生成輸入編碼input_encodings = tokenizer(example_batch["dialogue"], max_length=1024,truncation=True)# 使用目標編碼器處理摘要文本,生成目標編碼with tokenizer.as_target_tokenizer():target_encodings = tokenizer(example_batch["summary"], max_length=128,truncation=True)# 返回包含輸入編碼、目標標簽和注意力掩碼的字典return {"input_ids": input_encodings["input_ids"],"attention_mask": input_encodings["attention_mask"],"labels": target_encodings["input_ids"]}# 使用 map 方法將 SamSum 數據集轉換為 PyTorch 格式
dataset_samsum_pt = dataset_samsum.map(convert_examples_to_features, batched=True)# 設置數據集格式為 Torch 張量類型,并指定列名
columns = ["input_ids", "labels", "attention_mask"]
dataset_samsum_pt.set_format(type="torch", columns=columns)
運行結果:
(這里暫時報錯:
TypeError: Couldn't build proto file into descriptor pool: duplicate file name sentencepiece_model.proto)
在詞元化步驟中有一個新事物:tokenizer.as_target_tokenizer()上下文。某些模型需要在解碼器輸入中使用特殊詞元,因此將編碼器和解碼器輸入的詞元化步驟分開非常重要。在with語句中(稱為上下文管理器),詞元分析器知道它正在為解碼器進行詞元化處理。
現在,我們需要創建數據整理器。在大多數情況下,我們可以使用默認的整理器,它收集批量中的所有張量并將它們簡單地堆疊起來。對于摘要任務,我們不僅需要堆疊輸入,還需要在解碼器側準備目標。PEGASUS是一種編碼器-解碼器Transformer,因此具有經典的seq2seq架構。在seq2seq設置中,一種常見的方法是在解碼器中應用teacher?forcing。使用此策略時,解碼器接收輸入詞元(與僅包含解碼器的模型相同,如GPT-2),這些詞元由標注向右移動一個位置,除此之外還有編碼器輸出。因此,在預測下一個詞元時,解碼器將獲得向右移動一個位置的真實值作為輸入,如下表所示:
# 示例文本序列和標簽生成過程
text = ['PAD', 'Transformers', 'are', 'awesome', 'for', 'text', 'summarization']# 初始化存儲每步結果的列表
rows = []# 循環生成每步的數據行
for i in range(len(text)-1):rows.append({'step': i+1, # 步驟號,從1開始'decoder_input': text[:i+1], # 解碼器輸入序列,從文本開始到當前位置'label': text[i+1] # 標簽,當前位置的下一個詞})# 創建數據幀,并以步驟號作為索引
pd.DataFrame(rows).set_index('step')
運行結果:
step | decoder_input | label |
1 | [PAD] | Transformers |
2 | [PAD, Transformers] | are |
3 | [PAD, Transformers, are] | awesome |
4 | [PAD, Transformers, are, awesome] | for |
5 | [PAD, Transformers, are, awesome, for] | text |
6 | [PAD, Transformers, are, awesome, for, text] | summarization |
我們將它向右移動一個位置,這樣解碼器只會看到前一個正確的標注,而不是當前或未來的標注。僅進行移位就足夠了,因為解碼器有一個掩碼自注意力機制,它會掩碼當前和未來的所有輸入。
因此,在準備批量時,我們通過將標注向右移動一個位置來設置解碼器的輸入。之后,我們通過將標注中的填充詞元設置為-100來確保忽略損失函數中的填充詞元。實際上,我們不必手動執行這些步驟,因為DataCollatorForSeq2Seq會幫我們完成所有這些步驟:
# 導入 Seq2Seq 數據集整理器模塊
from transformers import DataCollatorForSeq2Seq# 創建 Seq2Seq 數據集整理器實例
seq2seq_data_collator = DataCollatorForSeq2Seq(tokenizer, model=model)
然后,和往常一樣,我們為訓練設置了一個TrainingArguments:
# 導入訓練參數和訓練器模塊
from transformers import TrainingArguments, Trainer# 定義訓練參數
training_args = TrainingArguments(output_dir='pegasus-samsum', # 模型輸出目錄num_train_epochs=1, # 訓練的輪數warmup_steps=500, # 學習率預熱步數per_device_train_batch_size=1, # 每個設備的訓練批次大小per_device_eval_batch_size=1, # 每個設備的評估批次大小weight_decay=0.01, # 權重衰減率logging_steps=10, # 訓練日志記錄步數push_to_hub=True, # 是否推送到模型中心evaluation_strategy='steps', # 評估策略eval_steps=500, # 評估步數間隔save_steps=1e6, # 模型保存步數間隔gradient_accumulation_steps=16 # 梯度累積步數
)
與以往設置不同的是,這次有了一個新的參數gradient_accumulation_steps。由于模型非常大,因此我們不得不將批量大小設置為1。然而,批量大小太小會影響收斂。為了解決這個問題,我們可以使用一種稱為梯度累積的巧妙技巧。顧名思義,我們不是一次性計算整個批量的梯度,而是分批計算并聚合梯度。當我們聚合足夠的梯度時,我們運行優化步驟。這比一次性完成自然會慢一些,但它可以節省我們大量的GPU內存。
現在,我們登錄到Hugging?Face,這樣我們就可以在訓練后將模型推送到Hub上:
from huggingface_hub import notebook_loginnotebook_login()
運行結果:
現在我們已經擁有初始化訓練器所需的一切,包括模型、詞元分析器、訓練參數、數據整理器,以及訓練和評估數據集:
from transformers import TrainingArguments, Trainer# 創建一個 Trainer 實例用于訓練序列到序列模型。
trainer = Trainer(model=model, # 要訓練的序列到序列模型args=training_args, # 定義的訓練參數tokenizer=tokenizer, # 用于預處理輸入數據的分詞器data_collator=seq2seq_data_collator, # 用于批處理數據的數據整理器train_dataset=dataset_samsum_pt["train"], # 訓練數據集eval_dataset=dataset_samsum_pt["validation"] # 評估數據集
)
運行結果:
(這里暫時報錯:
TypeError: Couldn't build proto file into descriptor pool: duplicate file name sentencepiece_model.proto)
我們已經準備好進行訓練了。訓練完成后,我們可以直接在測試集上運行評估函數,以查看模型的表現如何:
from transformers import TrainingArguments, Trainer# 開始訓練模型
trainer.train()# 使用評估函數評估 Pegasus 模型的摘要質量
score = evaluate_summaries_pegasus(dataset_samsum["test"], rouge_metric, trainer.model, tokenizer,batch_size=2, column_text="dialogue", column_summary="summary")# 提取 ROUGE 指標結果
rouge_dict = dict((rn, score[rn].mid.fmeasure) for rn in rouge_names)# 創建 DataFrame 顯示 ROUGE 指標
pd.DataFrame(rouge_dict, index=[f"pegasus"])
運行結果:
(這里暫時報錯:
TypeError: Couldn't build proto file into descriptor pool: duplicate file name sentencepiece_model.proto
,使用例子參考的運行結果如下)
rouge1 | rouge2 | rougeL | rougeLsum | |
pegasus | 0.42761 | 0.200571 | 0.340648 | 0.340738 |
我們可以看到,ROUGE分數比沒有進行微調的模型有了顯著的提高,因此盡管之前的模型也是用于摘要生成訓練的,但它并沒有很好地適應新的領域。我們把我們的模型推送到Hub上吧:
# 將訓練完成的模型推送到 Hub 上
trainer.push_to_hub("Training complete!")
接下來我們將使用這個模型為我們生成一些摘要。
你也可以將生成的結果作為訓練循環的一部分進行評估:使用名為Seq2SeqTrainingArguments的TrainingArguments擴展,并指定predict_with_generate=True。將其傳給名為Seq2SeqTrainer的專用Trainer,該Trainer使用generate()函數而不是模型的前向傳遞來創建用于評估的預測。你動手試一試吧!
3、生成對話摘要
從損失和ROUGE分數來看,該模型似乎比僅在CNN/DailyMail上訓練的原始模型表現出顯著的改進。從測試集中的一個樣本生成的摘要如下所示:
import transformers# 設置transformers的日志級別為錯誤,以減少輸出日志
transformers.logging.set_verbosity_error()# 定義生成摘要時的參數
gen_kwargs = {"length_penalty": 0.8, "num_beams": 8, "max_length": 128}# 從測試集中選擇一個示例
sample_text = dataset_samsum["test"][0]["dialogue"]
reference = dataset_samsum["test"][0]["summary"]# 使用預訓練的pegasus-samsum模型創建摘要管道
pipe = pipeline("summarization", model="transformersbook/pegasus-samsum")# 輸出對話和參考摘要
print("Dialogue:")
print(sample_text)
print("\nReference Summary:")
print(reference)# 使用模型生成摘要并輸出
print("\nModel Summary:")
print(pipe(sample_text, **gen_kwargs)[0]["summary_text"])
運行結果:
Dialogue: Hannah: Hey, do you have Betty's number? Amanda: Lemme check Hannah: <file_gif> Amanda: Sorry, can't find it. Amanda: Ask Larry Amanda: He called her last time we were at the park together Hannah: I don't know him well Hannah: <file_gif> Amanda: Don't be shy, he's very nice Hannah: If you say so.. Hannah: I'd rather you texted him Amanda: Just text him 🙂 Hannah: Urgh.. Alright Hannah: Bye Amanda: Bye byeReference Summary: Hannah needs Betty's number but Amanda doesn't have it. She needs to contact Larry.Model Summary: Amanda can't find Betty's number. Larry called Betty last time they were at the park together. Hannah wants Amanda to text Larry instead of calling Betty.
這與參考摘要十分相似。看起來模型已經學會了將對話綜合成摘要而不僅僅是提取段落。現在進行最終測試:模型在自定義輸入上的表現如何?
# 自定義對話示例
custom_dialogue = """\
Thom: Hi guys, have you heard of transformers?
Lewis: Yes, I used them recently!
Leandro: Indeed, there is a great library by Hugging Face.
Thom: I know, I helped build it ;)
Lewis: Cool, maybe we should write a book about it. What do you think?
Leandro: Great idea, how hard can it be?!
Thom: I am in!
Lewis: Awesome, let's do it together!
"""# 使用預訓練的pegasus-samsum模型生成摘要,并輸出摘要結果
print(pipe(custom_dialogue, **gen_kwargs)[0]["summary_text"])
運行結果:
Thom and Lewis wanted to write a book about transformers. They came up with the idea with the help of Hugging Face's Leandro. The book will be called "Transformers: The Power of Transformers" and will be published in 2015. The project is currently in the planning stages.
生成的自定義對話摘要很有意義。它很好地總結了討論中所有人都想一起寫書的內容,而不僅僅是提取單個句子。例如,它將第3行和第4行合成為一個邏輯組合。