《從零構建大語言模型》學習筆記2,文本數據處理1
文章目錄
- 《從零構建大語言模型》學習筆記2,文本數據處理1
- 前言
- 1、分詞
- 2.將把提取出來的詞元轉換為數字ID
- 3.添加特殊上下文標記
- 4. 字節對編碼(以及tiktoken庫無法下載gpt2參數,調用get_encoding時SSL超時的解決方法)
前言
本書原項目地址:https://github.com/rasbt/LLMs-from-scratch
接下來就開始學習代碼了,在訓練我們的LLM模型之前,我們先要準備好學習的文本數據,所以第一步是把文本轉成計算機能運算的向量,這個過程也叫做embedding,如果有自己部署過開源大模型的小伙伴,就會知道除了要下載大預言模型,在這之前還有個embedding模型,我個人理解應該就是這個過程。其實就是建立一個把所有的文字或者詞句映射成方便計算機運算的一些向量庫。
這里就引用原作者的圖片來介紹了
當然不止是文本數據有這個過程,音視頻和圖片文件都有這個過程。
當然embedding 需要收集龐大的數據來構造,我們自己收集是比較麻煩的,所以我們后面會用開源的gpt2來進行映射。
有了這個向量庫之后,我們需要對我們自己的語料進行轉換,把普通的文字根據向量庫轉成數字。所有第一步要把我們的語料進行分詞,把一整句完整的句子拆分為單個的單詞和符號。
1、分詞
這一步前面的代碼還是比較好理解的,原作者用來一本小說的所有文字舉例來說明如何簡單的對語料進行分詞。關于代碼我就貼一些關鍵代碼進行解讀。
下面我就貼代碼了
with open("the-verdict.txt", "r", encoding="utf-8") as f:raw_text = f.read() ##讀入文件按照utf-8
print("Total number of character:", len(raw_text))##先輸出總長度
print(raw_text[:99])##輸出前一百個內容
preprocessed = re.split(r'([,.:;?_!"()\']|--|\s)', raw_text) ##按照符號繼續把原文件給分割了
preprocessed = [item.strip() for item in preprocessed if item.strip()]##去掉兩端的空白字符 也是去掉了空字符串和僅包含空白字符的項
print(preprocessed[:30])
以上代碼是為了把小說中的所有詞語和標點符號拆分為一個列表,后續好進行處理和索引。主要用的正則表達式來進行拆分。
2.將把提取出來的詞元轉換為數字ID
上一步提取出來的詞元列表,我們去重后編排一個數字ID列表,過程如下圖:

代碼如下:
all_words = sorted(set(preprocessed))#從去掉重復的字符
vocab_size = len(all_words)#計總的單詞書print(vocab_size)
print(all_words[:50])vocab = {token:integer for integer,token in enumerate(all_words)}##先把word進行編號,再按照單詞或者標點為索引(有HashList那味道了)class SimpleTokenizerV1:#一個實例的名字創立def __init__(self, vocab): ## 初始化一個字符串self.str_to_int = vocab #單詞到整數的映射self.int_to_str = {i:s for s,i in vocab.items()} #方便解碼,進行整數到詞匯的反向映射def encode(self, text):preprocessed = re.split(r'([,.:;?_!"()\']|--|\s)', text)##正則化分詞標點符號preprocessed = [item.strip() for item in preprocessed if item.strip()## 去掉兩端空格與全部的空句]ids = [self.str_to_int[s] for s in preprocessed]##整理完的額字符串列表對應到id,從字典出來return idsdef decode(self, ids):text = " ".join([self.int_to_str[i] for i in ids]) #映射整數id到字符串。join是用前面那個(“ ”)聯結成一個完整的字符串# Replace spaces before the specified punctuationstext = re.sub(r'\s+([,.?!"()\'])', r'\1', text) #使用正則表達式,去除標點符號前的多余空格# \s+匹配一個或者多個空白 \1 替換到匹配return texttokenizer = SimpleTokenizerV1(vocab) #用vocab創造一個實例
text = """"It's the last he painted, you know," Mrs. Gisburn said with pardonable pride."""
ids = tokenizer.encode(text) #按照這個例子里的encode函數處理text
print(ids)
all_words 是詞元去重排序后的一個字典。然后定義了一個SimpleTokenizerV1類,這個類里面定義了兩個函數,一個encode函數能把任意段話轉為數字ID列表,decode函數能把數字ID列表解碼為對應的文字段落。
3.添加特殊上下文標記
上面的SimpleTokenizerV1類還有些缺陷,就是all_words這個字典太小了,可能會有很多沒有收錄的詞語,這時候就要對SimpleTokenizerV1類進行改進了,通過判斷來添加"<|endoftext|>", “<|unk|>” 這兩個標簽,一個是段落結束標志,一個是沒有對應詞元時的標志。
代碼如下:
class SimpleTokenizerV2:##版本2.0,啟動!def __init__(self, vocab):self.str_to_int = vocabself.int_to_str = { i:s for s,i in vocab.items()}#s為單詞,i是keydef encode(self, text):preprocessed = re.split(r'([,.:;?_!"()\']|--|\s)', text)#正則化按照標點分類preprocessed = [item.strip() for item in preprocessed if item.strip()]#去掉兩頭與所有空余句preprocessed = [item if item in self.str_to_int else "<|unk|>" for item in preprocessed#遍歷 preprocessed 中的每個 item,如果 item 存在于 self.str_to_int(即詞匯表)中,就保留 item#如果不存在(即該單詞或符號未定義在詞匯表中),就替換為特殊標記 <|unk|>。#拓展:推導式(如列表推導式)是一種緊湊的語法,專門用于生成新列表(或其他容器)#與普通 for 循環相比,它更加簡潔和高效,但邏輯復雜時可能會降低可讀性。]ids = [self.str_to_int[s] for s in preprocessed]#單詞或標點映射為整數列表return idsdef decode(self, ids):text = " ".join([self.int_to_str[i] for i in ids])# Replace spaces before the specified punctuationstext = re.sub(r'\s+([,.:;?!"()\'])', r'\1', text)return texttokenizer = SimpleTokenizerV2(vocab)text1 = "Hello, do you like tea?"
text2 = "In the sunlit terraces of the palace."text = " <|endoftext|> ".join((text1, text2))#用句子分隔符鏈接兩個句子print(text) #跟第一個一樣,但不會報錯了
tokenizer.decode(tokenizer.encode(text))
主要是編碼的時候做了一次未知詞元的判斷
4. 字節對編碼(以及tiktoken庫無法下載gpt2參數,調用get_encoding時SSL超時的解決方法)
以上提到的分詞方式是比較簡單的,主要是方便大家理解。接下來使用一種基于字節對編碼(BPE)概念的更復雜的分詞方案。BPE分詞器被用于訓練諸如GPT-2、GPT-3以及ChatGPT最初使用的模型等大型語言模型。那具體怎么實現的我這里也不深究了,我們直接拿過來用就好,下面需要使用到tiktoken這個庫。
import importlib
import tiktoken
print("tiktoken version:", importlib.metadata.version("tiktoken"))#驗證下載并輸出版本信息
tokenizer = tiktoken.get_encoding("gpt2")#初始化GPT2!
到這里需要注意一下,因為這里需要下載gpt2的參數,但是大部分人的網絡應該下載不下來,根據錯誤提示可以手動下載vocab.bpe和encoder.json這兩個文件,下載后在當前路徑創建一個文件夾
.tiktoken
,把文件放在這里面。然后還需要把緩存路徑設置為該路徑,同時手動修改vocab.bpe文件名為6d1cbeee0f20b3d9449abfede4726ed8212e3aee,修改encoder.json文件名為6c7ea1a7e38e3a7f062df639a5b80947f075ffe6。
vocab.bpe下載地址:https://openaipublic.blob.core.windows.net/gpt-2/encodings/main/vocab.bpe
encoder.json下載地址:https://openaipublic.blob.core.windows.net/gpt-2/encodings/main/encoder.json
修改后的代碼如下:
import importlib
import tiktoken
print("tiktoken version:", importlib.metadata.version("tiktoken"))#驗證下載并輸出版本信息import hashlib
blobpath = "https://openaipublic.blob.core.windows.net/gpt-2/encodings/main/vocab.bpe"
vocab_key = hashlib.sha1(blobpath.encode()).hexdigest()
print(vocab_key)
blobpath = "https://openaipublic.blob.core.windows.net/gpt-2/encodings/main/encoder.json"
encoder_key = hashlib.sha1(blobpath.encode()).hexdigest()
print(encoder_key)import os
tiktoken_cache_dir = ".tiktoken"
os.environ["TIKTOKEN_CACHE_DIR"] = tiktoken_cache_dir
# validate
assert os.path.exists(os.path.join(tiktoken_cache_dir, vocab_key))
assert os.path.exists(os.path.join(tiktoken_cache_dir, encoder_key))tokenizer = tiktoken.get_encoding("gpt2")#初始化GPT2!
完整的解決思路可以參考下面兩個鏈接:
how to use tiktoken in offline mode computer
SSLError: HTTPSConnectionPool(host=‘openaipublic.blob.core.windows.net’, port=443)
后面的代碼就是使用gpt2的參數對本地語料進行加解碼了。