本文分享自華為雲社群《淺談如何處理大語言模型訓練資料之一常見的資料處理方法》,作者: 碼上開花_Lancer。
大語言模型訓練需要數萬億的各型別資料。如何構造海量“高質量”資料對於大語言模型的訓練具有至關重要的作用。雖然,截止到2023 年9 月為止,還沒有非常好的大模型的理論分析和解釋,也缺乏對語言模型訓練資料的嚴格說明和定義。但是,大多數研究人員都普遍認為訓練資料是影響大語言模型效果以及樣本泛化能力的關鍵因素之一。從此前的研究來看,預訓練資料需要涵蓋各種型別,包括網路資料、圖書、論文、百科和社交媒體等,還需要覆蓋儘可能多的領域、語言、文化和視角,從而提高大語言模型的泛化能力和適應性。本文將介紹當前常見的大語言模型訓練資料的來源、處理方法、預訓練資料對大語言模型影響的分析以及常見開源資料集合等。
一、資料來源
有論文專門 介紹了OpenAI 訓練GPT-3 所使用的主要資料來源,包含經過過濾的CommonCrawl資料集、WebText2、Books1、Books2 以及英文Wikipedia 等資料集合。其中CommonCrawl 的原始資料有45TB,進行過濾後僅保留了570GB 的資料。透過詞元方式對上述語料進行切分,大約一共包含5000 億詞元。
為了保證模型使用更多高質量資料進行訓練,在GPT-3 訓練時,根據語料來源的不同,設定不同的取樣權重。在完成3000 億詞元訓練時,英文Wikipedia 的語料平均訓練輪數為3.4 次,而CommonCrawl 和Books 2 僅有0.44 次和0.43 次。
由於CommonCrawl 資料集合的過濾過程繁瑣複雜,Meta 公司的研究人員在訓練OP 模型時則採用了混合RoBERTa、Pile[68] 和PushShift.io Reddit 資料的方法。由於這些資料集合中包含的絕大部分都是英文資料,因此OPT 也從CommonCrawl 資料集中抽取了部分非英文資料加入訓練語料。
大語言模型訓練所需的資料來源大體上可以分為通用資料和專業資料兩大類。通用資料(GeneralData)包括網頁、圖書、新聞、對話文字等內容。通用資料具有規模大、多樣性和易獲取等特點,因此可以支援大語言模型的構建語言建模和泛化能力。
專業資料(Specialized Data)包括多語言資料、科學資料、程式碼以及領域特有資料等資料。透過在預訓練階段引入專業資料可以有效提供大語言模型的任務解決能力。圖3.1給出了一些典型大語言模型所使用數量型別的分佈情況。可以看到不同的大語言模型在訓練型別分佈上的差距很大,截止2023 年9 月為止,還沒有得到廣泛認可資料型別分佈比例。
典型大語言模型所使用數量型別的分佈
1.1 通用資料
通用資料在大模型訓練資料中佔比通常非常高,主要包括網頁、書籍、對話文字等型別,為大模型提供了大規模且多樣的訓練資料。網頁(Webpages)是通用資料中數量最大的一類。隨著網際網路的大規模普及,人們透過網站、論壇、部落格、APP 等各種型別網站和應用,創造了海量的資料。
根據2016 年Google 公開的資料,其搜尋引擎索處理了超過130 萬億網頁。網頁資料所包含的海量內容,使得語言模型能夠獲得多樣化的語言知識並增強其泛化能力。爬取和處理這些海量網頁內容並不是一件容易的事情,因此一些研究人員構建了包括ClueWeb09[70]、ClueWeb12、SogouT-16、CommonCrawl 等在內的開源網頁資料集。但是,這些爬取的網路資料雖然包含大量高質量的文字,如維基百科,但也包含非常多的低質量的文字,如垃圾郵件等。因此,如何過濾和處理網頁以提高質量資料對與大語言模型訓練來說非常重要。
對話資料(Conversation Text)是指包含兩個或更多參與者之間交流的文字內容。對話資料包含書面形式的對話、聊天記錄、論壇帖子、社交媒體評論等。當前的一些研究也表明,對話資料可以有效增強語言模型的對話能力,並潛在地提高其在多種問答任務上的表現。對話資料可以透過收集、清洗、歸併等過程從社會媒體、論壇、郵件組等構建。
相較於網頁資料,對話資料收集和處理更加困難,資料數量也相對少非常多。常見的對話資料集包括PushShift.io Reddit、Ubuntu Dialogue Corpus、Douban Conversation Corpus、Chromium Conversations Corpus 等。此外,文獻[75] 也提出瞭如何使用大語言模型自動生成對話資料的UltraChat 方法。
書籍(Book)是人類知識的主要積累方式之一,從古代經典著作到現代學術著述,書籍承載了豐富多樣的人類思想。書籍通常包含廣泛的詞彙,包括專業術語、文學表達以及各種主題詞彙。
利用書籍資料進行訓練,語言模型可以接觸到多樣化的詞彙,從而提高其對不同領域和主題的理解能力。相較於其他語料庫,書籍也是最重要的,甚至是唯一的長文字書面語的資料來源。書籍提供了完整的句子和段落,使得語言模型可以學習到上下文之間的聯絡。這對於模型理解句子中的複雜結構、邏輯關係和語義連貫性非常重要。書籍涵蓋了各種文體和風格,包括小說、科學著作、歷史記錄等等。透過使用書籍資料訓練語言模型,可以使模型學習到不同的寫作風格和表達方式,提高大語言模型在各種文字型別上的能力。由於版權因素,開源書籍資料集合很少,現有的開源大語言模型研究通常採用Pile 資料集[68] 中提供的Books3 和Bookcorpus2 資料集。
2.1 專業資料
專業資料在通用大語言模型中所佔比例通常較低,但是專業資料對於改進大語言模型在下游任務上的特定能力有著非常重要的作用。專業資料有非常多的種類,我總結了當前大語言模型使用的三類專業資料,包括多語言資料、科學文字以及程式碼。
多語言資料(Multilingual Text)對於增強大語言模型語言理解和生成多語言能力具有至關重要的作用。當前的大語言模型訓練除了需要目標語言中的文字之外,通常還要整合多語言語料庫。
例如,BLOO的預訓練語料中包含46 種語言,而PaLM[的訓練語料中甚至高達122 種語言的資料。此前的研究發現,透過多語言混合訓練,預訓練模型中可以在一定程度上自動構建多語言之間的語義關聯。因此,多語言資料混合訓練,可以有效提升翻譯、多語言摘要和多語言問答等任務能力。此外,由於不同語言中不同型別的知識獲取難度不同,多語言資料還可以有效增加資料的多樣性和知識的豐富性。
科學文字(Scientific Text)包括教材、論文、百科以及其他相關資源。這些資料對於提升大型語言模型在理解科學知識方面具有重要作用。科學文字資料的來源主要包括arXiv 論文、PubMed 論文、教材、課件和教學網頁等。由於科學領域涉及眾多專業領域且資料形式複雜,通常還需要對公式、化學式、蛋白質序列等採用特定的符號標記進行預處理。
例如,公式可以使用LaTeX 語法進行表示,化學結構可以使用SMILES(Simplified Molecular Input Line Entry System)表示,蛋白質序列可以使用單字母程式碼或三字母程式碼。這樣可以將不同格式的資料轉換為統一的形式,使得語言模型更好地處理和分析科學文字資料。
程式碼(Code)資料是進行程式生成任務所必須的訓練資料。最近的研究和ChatGPT 的結果表明,透過在大量程式碼上進行預訓練,大語言模型可以有效提升程式碼生成的效果。程式碼資料不僅包含程式程式碼本身,還包含大量的註釋資訊。與自然語言文字相比,程式碼資料具有一些顯著的區別。程式碼是一種格式化語言,它對應著長程依賴和準確的執行邏輯。程式碼的語法結構、關鍵字和特定的程式設計正規化都對其含義和功能起著重要的作用。程式碼資料的主要來源是程式設計問答社群(如Stack Exchange)和公共軟體倉庫(如GitHub)。程式設計問答社群中的資料包含了開發者提出的問題、其他開發者的回答以及相關程式碼示例。這些資料提供了豐富的語境和真實世界中的程式碼使用場景。公共軟體倉庫中的資料則包含了大量的開原始碼,涵蓋了各種程式語言和領域。這些程式碼庫中的很多程式碼經過了嚴格的程式碼評審和實際的使用測試,因此具有一定的質量和可靠性。
二、資料處理
大語言模型的相關研究表明,資料質量對於模型的影響非常大。因此在收集到各型別資料之後,需要對資料進行處理,去除低質量資料、重複資料、有害資訊、個人隱私等內容[14, 85]。典型的資料處理過程如圖3.1所示,主要包含質量過濾、冗餘去除、隱私消除、詞元切分等幾個步驟。本節將依次介紹上述內容。
圖2.1 典型大語言模型資料處理流程圖
1. 低質過濾
網際網路上的資料質量參差不齊,無論是OpenAI 聯合創始人Andrej Karpathy 在微軟Build 2023的報告,還是當前的一些研究都表明,訓練資料的質量對於大語言模型效果具有非常重要的影響。因此,如何從收集到的資料中刪除低質量資料成為大語言模型訓練中的重要步驟。大語言模型訓練中所使用的低質量資料過濾方法可以大致分為兩類:基於分類器的方法和基於啟發式的方法。基於分類器的方法目標是訓練文字質量判斷模型,並利用該模型識別並過濾低質量資料。
GPT-3、PALM 以及GLam模型在訓練資料構造時都使用了基於分類器的方法。 採用基於特徵雜湊的線性分類器(Feature Hash Based Linear Classifier),可以非常高效地完成文字質量判斷。該分類器使用一組精選文字(維基百科、書籍和一些選定的網站)進行訓練,目標是將與訓練資料類似的網頁給定較高分數。利用這個分類器可以評估網頁的內容質量。在實際應用中,還可以透過使用Pareto 分佈對網頁進行取樣,根據其得分選擇合適的閾值,從而選定合適的資料集合。
但是,一些研究也發現,基於分類器的方法可能會刪除包含方言或者口語的高質量文字,從而損失一定的多樣性。基於啟發式的方法則透過一組精心設計的規則來消除低質量文字,BLOOM 和Gopher採用了基於啟發式的方法。這些啟發式規則主要包括:
• 語言過濾:如果一個大語言模型僅關注一種或者幾種語言,那麼就可以大幅度的過濾掉資料中其他語言的文字。
• 指標過濾:利用評測指標也可以過濾低質量文字。例如,可以使用語言模型對於給定文字的困惑度(Perplexity)進行計算,利用該值可以過濾掉非自然的句子。
• 統計特徵過濾:針對文字內容可以計算包括標點符號分佈、符號字比(Symbol-to-Word Ratio)、句子長度等等在內的統計特徵,利用這些特徵過濾低質量資料。
• 關鍵詞過濾:根據特定的關鍵詞集,可以識別和刪除文字中的噪聲或無用元素,例如,HTML標籤、超連結以及冒犯性詞語等。
在大語言模型出現之前,在自然語言處理領域已經開展了很多文章質量判斷(Text Quality Evaluation)相關研究,主要應用於搜尋引擎、社會媒體、推薦系統、廣告排序以及作文評分等任務中。
在搜尋和推薦系統中,結果的內容質量是影響使用者體驗的的重要因素之一,因此,此前很多工作都是針對使用者生成內容(User-Generated Content,UGC)質量進行判斷。自動作文評分也是文章質量判斷領域的一個重要子任務,自1998 年文獻[87] 提出了使用貝葉斯分類器進行作文評分預測以來,基於SVM[88]、CNN-RNN[89]、BERT[90, 91] 等方法的作文評分演算法也相繼提出,並取得了較大的進展。
這些方法也都可以應用於大語言模型預訓練資料過濾中。但是由於預訓練資料量非常大,並且對於質量判斷的準確率並不要求非常高,因此一些基於深度學習以及基於預訓練的方法還沒有應用於低質過濾過濾中。
2.冗餘去除
文獻指出大語言模型訓練語料庫中的重複資料,會降低語言模型的多樣性,並可能導致訓練過程不穩定,從而影響模型效能。因此,需要對預訓練語料庫中的重複進行處理,去除其中的冗餘部分。文字冗餘發現(Text Duplicate Detection)也稱為文字重複檢測,是自然語言處理和資訊檢索中的基礎任務之一,其目標是發現不同粒度上的文字重複,包括句子、段落以及文件等不同級別。
冗餘去除就是在不同的粒度上進行去除重複內容,包括句子、文件和資料集等粒度的重複。在句子級別上,文獻 指出,包含重複單詞或短語的句子很可能造成語言建模中引入重複的模式。這對語言模型來說會產生非常嚴重的影響,使得模型在預測時容易陷入重複迴圈(RepetitionLoops)。
例如,使用GPT-2 模型,對於給定的上下文:“In a shocking finding, scientist discovereda herd of unicorns living in a remote, previously unexplored valley, in the Andes Mountains. Evenmore surprising to the researchers was the fact that the unicorns spoke perfect English.”。如果使用束搜尋(Beam Search),在設定b = 32 時,模型就會產生如下輸出,進入了重複迴圈模式。“Thestudy, published in the Proceedings of the National Academy of Sciences of the United States of America(PNAS), was conducted by researchers from the Universidad Nacional Autónoma de México (UNAM)and the Universidad Nacional Autónoma de México (UNAM/Universidad Nacional Autónoma de México/ Universidad Nacional Autónoma de México/Universidad Nacional Autónoma de México/Universidad Nacional Autónoma de ...”。由於重複迴圈對於語言模型生成的文字質量有非常大的影響,因此在預訓練語料中需要刪除這些包含大量重複單詞或者短語的句子。
RefinedWeb 構造過程中也進行了句子級別的過濾。使用了文獻所提出的過濾方法,提取並過濾文件間超過一定長度的相同字串。給定兩個文件xi 和xj,其中存在長度為k 的公共子串xa...a+ki = xb...b+kj 。當k ⩾ 50 時,就將其中一個子串過濾。公共子串匹配的關鍵是如何高效完成字串匹配,文獻[64] 將整個文件D 轉換為一個超長的字串序列S,之後構造序列S 的字尾陣列(Suffix Array)A。
該陣列包含在該序列中的所有字尾的按字典順序排列的列表。具體而言,字尾陣列A是一個整數陣列,其中每個元素表示S 中的一個字尾的起始位置。按照字典順序,A中的元素按照字尾的字典順序排列。例如,序列“banana”的字尾包括“banana”,“anana”,“nana”,“ana”,“na”和“a”,對應的字尾陣列A 為[6, 4, 2, 1, 5, 3]。根據陣列A,可以很容易的找出相同的子串。如果S i..i+|s| = S j..j+|s|,那麼i 和j 在陣列A 中一定在緊鄰的位置上。
文獻 中設計了並行的字尾陣列構造方法,針對Wiki-40B 訓練語料(約包含4GB 文字內容),使用擁有96 核CPU 以及768GB 記憶體的伺服器,可以在2.3 分鐘內完成計算。對於包含350GB 文字的C4 資料集合,僅需要12 小時可以完成字尾陣列構造。在文件級別上,大部分大語言模型都是依靠文件之間的表面特徵相似度(例如n-gram 重疊比例)進行檢測並刪除重複文件[33, 37, 64, 94]。
LLaMA 採用CCNet的處理模式,首先將文件拆分為段落,並把所有字元轉換為小寫字元、將數字替換為佔位符,以及刪除所有Unicode 標點符號和重音符號來對每個段落進行規範化處理。然後,使用為SHA-1 方法為每個段落計算一個雜湊碼(Hash Code),並使用前64 位數字作為鍵。最後,利用每個段落的鍵進行重複判斷。RefinedWeb[64]首先去除掉頁面中選單、標題、頁尾、廣告等內容,僅抽取頁面中的主要內容。在此基礎上,在文件級別進行過濾,採用與文獻提到類似的方法,使用n-gram 重疊程度來衡量句子、段落以及文件的相似度。如果重複程度超過預先設定的閾值,則會過濾掉重複段落或文件。
此外,資料集層面也可能存在一定數量的重複情況,比如很多大語言模型預訓練集合都會包含GitHub、Wikipedia、C4 等資料集。還需要特別注意的是,預訓練語料中混入測試語料,從而造成資料集汙染的情況。在實際產生預訓練資料時,需要從資料集、文件以及句子三個級別去除重複,這對於改善語言模型的訓練具有重要的作用[14, 96]
3.隱私消除
由於絕大多數預訓練資料來源於網際網路,因此不可避免地會包含涉及敏感或個人資訊(PersonallyIdentifiable Information,PII)的使用者生成內容,這可能會增加隱私洩露的風險。如圖2.2所示,輸入字首詞“East Stroudsburg Stroudsburg”,語言模型在此基礎上補全了姓名、電子郵件地址、電話號碼、傳真號碼以及實際地址。這些資訊都是模型從預訓練語料中學習得到的。因此,有非常必要從預訓練語料庫中刪除包含個人身份資訊的內容。
刪除隱私資料最直接的方法是採用基於規則的演算法,BigScience ROOTS Corpus 構建過程從大語言模型中獲得隱私資料的例子中就是採用了基於命名實體識別的方法,利用命名實體識別演算法檢測姓名、地址和電話號碼等個人資訊內容並進行刪除或者替換。該方法使用了基於Transformer 的模型,並結合機器翻譯技術,可以處理超過100 種語言的文字,消除其中的隱私資訊。該演算法被整合在muliwai 類庫中。
圖2.2 從大語言模型中獲得隱私資料的例子
4 詞元切分
傳統的自然語言處理通常以單詞為基本處理單元,模型都依賴預先確定的詞表V,在編碼輸入詞序列時,這些詞表示模型只能處理詞表中存在的詞。因此,在使用中,如果遇到不在詞表中的未登入詞,模型無法為其生成對應的表示,只能給予這些未登入詞(Out-of-vocabulary,OOV)一個預設的通用表示。
在深度學習模型中,詞表示模型會預先在詞表中加入一個預設的“[UNK]”(unknown)標識,表示未知詞,並在訓練的過程中將[UNK] 的向量作為詞表示矩陣的一部分一起訓練,透過引入某些相應機制來更新[UNK] 向量的引數。在使用時,對於全部的未登入詞,都使用[UNK] 的向量作為這些詞的表示向量。
此外,基於固定詞表的詞表示模型對詞表大小的選擇比較敏感。當詞表大小過小時,未登入詞的比例較高,影響模型效能。而當詞表大小過大時,大量低頻詞出現在詞表中,而這些詞的詞向量很難得到充分學習。理想模式下,詞表示模型應能覆蓋絕大部分的輸入詞,並避免詞表過大所造成的資料稀疏問題。
為了緩解未登入詞問題,一些工作透過利用亞詞級別的資訊構造詞表示向量。一種直接的解決思路是為輸入建立字元級別表示,並透過字元向量的組合來獲得每個單詞的表示,以解決資料稀疏問題。然而,單詞中的詞根、詞綴等構詞模式往往跨越多個字元,基於字元表示的方法很難學習跨度較大的模式。
為了充分學習這些構詞模式,研究人員們提出了子詞詞元化(Subword Tokenization)方法,試圖緩解上文介紹的未登入詞問題。詞元表示模型會維護一個詞元詞表,其中既存在完整的單詞,也存在形如“c”, “re”, “ing”等單詞部分資訊,稱為子詞。詞元表示模型對詞表中的每個詞元計算一個定長向量表示,供下游模型使用。對於輸入的詞序列,詞元表示模型將每個詞拆分為詞表內的詞元。
例如,將單詞“reborn”拆分為“re”和“born”。模型隨後查詢每個詞元的表示,將輸入重新組成為詞元表示序列。當下遊模型需要計算一個單詞或片語的表示時,可以將對應範圍內的詞元表示合成為需要的表示。因此,詞元表示模型能夠較好地解決自然語言處理系統中未登入詞的問題。詞元分析(Tokenization)目標是將原始文字分割成由詞元(Token)序列的過程。詞元切分也是資料預處理中至關重要的一步。
位元組對編碼(Byte Pair Encoding,BPE)模型[99] 是一種常見的子詞詞元模型。該模型所採用的詞表包含最常見的單詞以及高頻出現的子詞。在使用中,常見詞通常本身位於BPE 詞表中,而罕見詞通常能被分解為若干個包含在BPE 詞表中的詞元,從而大幅度降低未登入詞的比例。BPE演算法包括兩個部分:(1)詞元詞表的確定;(2)全詞切分為詞元以及詞元合併為全詞的方法。計算過程如圖4所示。
首先,確定語料庫中全詞的詞表和詞頻,然後將每個單詞切分為單個字元的序列,並在序列最後新增符號“</w>”作為單詞結尾的標識。比如單詞“low”被切分為序列“l␣o␣w␣</w>”。所切分出的序列元素稱為位元組,即每個單詞都切分為位元組的序列。之後,按照每個位元組序列的相鄰位元組對和單詞的詞頻,統計每個相鄰位元組對的出現頻率,合併出現頻率最高的位元組對,將其作為新的詞元加入詞表,並將全部單詞中的該位元組對合併為新的單一位元組。如圖4所示,在第一次迭代時,出現頻率最高的位元組對是(e,s),故將“es”作為詞元加入詞表,並將全部序列中相鄰的(e,s)位元組對合併為es 位元組。重複這一步驟,直至BPE 詞元詞表的大小達到指定的預設值,或沒有可合併的位元組對為止。
在詞元詞表確定之後,對於輸入詞序列中未在詞表中的全詞進行切分,BPE 演算法對詞表中的詞元按從長到短的順序進行遍歷,用每一個詞元和當前序列中的全詞或未完全切分為詞元的部分進行匹配,將其切分為該詞元和剩餘部分的序列。例如,對於單詞“lowest</w>”,首先透過匹配詞元“est</w>”將其切分為“low”, “est</w>”的序列,再透過匹配詞元“low”,確定其最終切分結果為“low”, “est</w>”的序列。透過這樣的過程,BPE 儘量將序列中的詞切分成已知的詞元。在遍歷詞元詞表後,對於切分得到的詞元序列,為每個詞元查詢詞元表示,構成詞元表示序列。若出現未登入詞元,即未出現在BPE 詞表中的詞元,則採取和未登入詞類似的方式,為其賦予相同的表示,最終獲得輸入的詞元表示序列。
此外,位元組級(Byte-level)BPE 透過將位元組視為合併的基本符號,用來改善多語言語料庫(例如包含非ASCII 字元的文字)的分詞質量。GPT-2、BART 和LLaMA 等大語言模型都採用了這種分詞方法。原始LLaMA 的詞表大小是32K,並且主要根據英文進行訓練,因此,很多漢字都沒有直接出現在詞表中,需要位元組來支援所有的中文字元,由2 個或者3 個Byte Token 才能拼成一個完整的漢字。
對於使用了位元組對編碼的大語言模型,其輸出序列也是詞元序列。對於原始輸出,根據終結語符</w> 的位置確定每個單詞的範圍,合併範圍內的詞元,將輸出重新組合為詞序列,作為最終的結果。WordPiece也是一種常見的詞元分析演算法,最初應用於語音搜尋系統。此後,該演算法做為BERT 的分詞器。
WordPiece 與BPE 有非常相似的思想,都是透過迭代地合併連續的詞元,但在合併的選擇標準上略有不同。為了進行合併,WordPiece 需要首先訓練一個語言模型,並用該語言模型對所有可能的詞元對進行評分。在每次合併時,選擇使得訓練資料似然機率增加最多的詞元對。
圖4 BPE 模型中詞元詞表的計算過程
由於Google 並沒有釋出其WordPiece 演算法的官方實現,HuggingFace 在其線上NLP 課程中提供了一種更直觀的選擇度量方法:一個詞元對的評分是根據訓練語料庫中兩個詞元的共現計數除以它們各自的出現計數的乘積。計算公式如下所示:
Unigram 詞元分析 是另外一種應用於大語言模型的詞元分析方法,T5 和mBART 採用該方法構建詞元分析器。不同於BPE 和WordPiece,Unigram 詞元分析從一個足夠大的可能詞元集合開始,然後迭代地從當前列表中刪除詞元,直到達到預期的詞彙表大小為止。基於訓練好的Unigram語言模型,使用從當前詞彙表中刪除某個字詞後,訓練語料庫似然性的增加量作為選擇標準。
為了估計一元語言(Unigram)模型,採用了期望最大化(Expectation–Maximization,EM)演算法:每次迭代中,首先根據舊的語言模型找到當前最佳的單詞切分方式,然後重新估計一元語言單元機率以更新語言模型。在這個過程中,使用動態規劃演算法(如維特比演算法)來高效地找到給定語言模型時單詞的最佳分解方式。以HuggingFace NLP 課程中介紹的Byte Pair Encoding 程式碼為例,介紹BPE 方法的構建和使用,程式碼實現如下所示:
from transformers import AutoTokenizer from collections import defaultdict corpus = [ "This is the Hugging Face Course.", "This chapter is about tokenization.", "This section shows several tokenizer algorithms.", "Hopefully, you will be able to understand how they are trained and generate tokens.", ] # 使用GPT-2 tokenizer 將輸入分解為單詞: tokenizer = AutoTokenizer.from_pretrained("gpt2") word_freqs = defaultdict(int) for text in corpus: words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text) new_words = [word for word, offset in words_with_offsets] for word in new_words: word_freqs[word] += 1 # 計算基礎詞典, 這裡使用語料庫中的所有字元: alphabet = [] for word in word_freqs.keys(): for letter in word: if letter not in alphabet: alphabet.append(letter) alphabet.sort() # 增加特殊Token 在字典的開頭,GPT-2 中僅有一個特殊Token``<|endoftext|>''表示文字結束 vocab = ["<|endoftext|>"] + alphabet.copy() # 將單詞切分為字元 splits = {word: [c for c in word] for word in word_freqs.keys()} #compute_pair_freqs 函式用於計算字典中所有詞元對的頻率 def compute_pair_freqs(splits): pair_freqs = defaultdict(int) for word, freq in word_freqs.items(): split = splits[word] if len(split) == 1: continue for i in range(len(split) - 1): pair = (split[i], split[i + 1]) pair_freqs[pair] += freq return pair_freqs #merge_pair 函式用於合併詞元對 def merge_pair(a, b, splits): for word in word_freqs: split = splits[word] if len(split) == 1: continue i = 0 while i < len(split) - 1: if split[i] == a and split[i + 1] == b: split = split[:i] + [a + b] + split[i + 2 :] else: i += 1 splits[word] = split return splits # 迭代訓練,每次選取得分最高詞元對進行合併,直到字典大小達到設定目標為止: vocab_size = 50 while len(vocab) < vocab_size: pair_freqs = compute_pair_freqs(splits) best_pair = "" max_freq = None for pair, freq in pair_freqs.items(): if max_freq is None or max_freq < freq: best_pair = pair max_freq = freq splits = merge_pair(*best_pair, splits) merges[best_pair] = best_pair[0] + best_pair[1] vocab.append(best_pair[0] + best_pair[1]) # 訓練完成後,tokenize 函式用於給定文字進行詞元切分 def tokenize(text): pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text) pre_tokenized_text = [word for word, offset in pre_tokenize_result] splits = [[l for l in word] for word in pre_tokenized_text] for pair, merge in merges.items(): for idx, split in enumerate(splits): i = 0 while i < len(split) - 1: if split[i] == pair[0] and split[i + 1] == pair[1]: split = split[:i] + [merge] + split[i + 2 :] else: i += 1 splits[idx] = split return sum(splits, []) tokenize("This is not a token.")
Huggingface 的transformer 類中已經整合了很多分詞器,可以直接使用。例如,利用BERT 分詞器獲得輸入“I have a new GPU!”的詞元程式碼如下所示:
>>> from transformers import BertTokenizer >>> tokenizer = BertTokenizer.from_pretrained("bert-base-uncased") >>> tokenizer.tokenize("I have a new GPU!") ["i", "have", "a", "new", "gp", "##u", "!"]
好了,上面就是關於大語言模型訓練資料常見的處理方法,大家如果還想了解更多關於大語言模型的乾貨,可以持續關注我!