在自然語言處理的技術領域中,Tokenization 作為一個基礎且關鍵的步驟,扮演著將文字拆解成電腦可處理單元的重要角色。這些單元可以是單詞、子詞或字元,取決於應用場景和演算法。Tokenization 的應用範圍廣泛,涵蓋了文字分類別、情感分析、語言模型訓練以及資訊檢索等多個 NLP 任務。對於大語言模型(LLM)的訓練而言,Tokenization 更扮演著不可或缺的角色,它能將原始文字轉換成模型可理解的數值表示,進而影響模型的效能和泛化能力。

自然語言處理中的 Tokenization

自然語言處理(NLP)是一個重要的研究領域,涉及如何使電腦能夠理解和生成類別似人類的語言。其中,Tokenization 是一個基本步驟,指的是將文字分解成單個單位或 token,以便於電腦進行處理。

什麼是 Tokenization?

Tokenization 是將文字分解成單個單位或 token 的過程。這些 token 可以是單詞、字元、子詞彙或甚至是句子,取決於具體的應用和所使用的演算法。Tokenization 是 NLP 中的第一步,因為它允許電腦將複雜的文字分解成更小、更易於管理的部分,以便進行進一步的分析。

Tokenization 的應用

Tokenization 在各種 NLP 任務中都非常重要,包括但不限於:

  • 文字分類別:Tokenization 有助於將文字分解成單個單詞或短語,以便於進行情感分析、垃圾郵件過濾等任務。
  • 語言模型:透過 Tokenization,語言模型可以學習單詞之間的關係和語法結構,以生成更自然的文字。
  • 資訊檢索:Tokenization 可以幫助搜尋引擎更好地理解使用者的查詢,並傳回更相關的結果。

實作 Tokenization

實作 Tokenization 的方法有很多,包括使用空格、標點符號等作為分隔符。以下是一個簡單的例子:

輸入文字:The brown dog playfully chased the swift fox.
分解後的 token:["The", "brown", "dog", "playfully", "chased", "the", "swift", "fox"]

在這個例子中,每個單詞都被視為一個單獨的 token。然而,在實際應用中,可能需要考慮到更多複雜的情況,例如處理連字元、特殊字元和不同語言的特點。

圖表翻譯:
  flowchart TD
    A[文字輸入] --> B[Tokenization]
    B --> C[文字分析]
    C --> D[資訊檢索]
    D --> E[文字分類別]
    E --> F[語言模型]
    F --> G[生成文字]

這個流程圖展示了 Tokenization 在 NLP 中的位置及其與其他步驟的關係。從文字輸入開始,經過 Tokenization 和文字分析,最終可以應用於資訊檢索、文字分類別和語言模型等領域。

Tokenizer 的實作與特殊上下文標記

在深入探討 Tokenizer 的實作之前,我們需要了解其核心方法:編碼(encode)和解碼(decode)。編碼方法將輸入文字拆分為個別的標記(token),然後透過詞彙表(vocabulary)將這些標記轉換為標記 ID。相反,解碼方法則將標記 ID 轉換迴文字標記,並將其連線成自然語言文字。

新增特殊上下文標記

為了進一步測試 Tokenizer 的功能,我們將探討如何處理未知詞彙以及新增特殊上下文標記,以提供更多的上下文資訊給大語言模型(LLM)在訓練過程中。這些特殊標記可以包括未知詞彙的標記和檔案邊界標記等。

修改 Tokenizer 以支援特殊標記

為了支援這些特殊標記,我們需要修改 Tokenizer 的實作。具體來說,我們將修改 SimpleTokenizerV2 來支援兩個新的標記:<|unk|><|endoftext|>。當 Tokenizer 遇到一個不在詞彙表中的詞彙時,它可以使用 <|unk|> 標記來表示。另外,當處理多個獨立檔案或書籍時,會在每個檔案或書籍之前插入一個 <|endoftext|> 標記,以幫助 LLM 理解這些檔案或書籍之間的關係。

示例

假設我們有兩個獨立的文字檔案,分別是「這是一個示例文字」和「這是另一個示例文字」。在訓練 LLM 時,我們可能會將這兩個檔案連線起來,但在連線點處插入 <|endoftext|> 標記,以表明這兩個檔案之間沒有直接關係。

  flowchart TD
    A[文字1] --> B[<|endoftext|>]
    B --> C[文字2]

圖表翻譯:

此圖表示瞭如何使用 <|endoftext|> 標記來分隔不同的文字檔案。在實際應用中,這種標記有助於 LLM 更好地理解不同文字之間的關係和邊界。

程式碼實作

以下是修改過的 SimpleTokenizerV2 類別的程式碼片段,展示瞭如何新增特殊上下文標記:

class SimpleTokenizerV2:
    def __init__(self, vocab):
        self.vocab = vocab
        self.unk_token = '<|unk|>'
        self.end_of_text_token = '<|endoftext|>'

    def encode(self, text):
        tokens = []
        for word in text.split():
            if word in self.vocab:
                tokens.append(self.vocab[word])
            else:
                tokens.append(self.vocab[self.unk_token])
        return tokens

    def decode(self, ids):
        text = ''
        for id in ids:
            if id in self.vocab.inverse:
                text += self.vocab.inverse[id] + ' '
            elif id == self.vocab[self.end_of_text_token]:
                text += '\n'
        return text.strip()

內容解密:

在這段程式碼中,我們定義了一個 SimpleTokenizerV2 類別,該類別包含了 encodedecode 方法。encode 方法將輸入文字拆分為個別的詞彙,並使用詞彙表將其轉換為標記 ID。如果遇到未知詞彙,則使用 <|unk|> 標記。decode 方法則將標記 ID 轉換迴文字,並在遇到 <|endoftext|> 標記時插入換行符,以表示檔案邊界。

這種實作方式可以幫助 LLM 更好地理解文字的結構和上下文關係,從而提高其語言理解和生成能力。

自然語言處理中的詞彙擴充套件

在自然語言處理(NLP)中,詞彙是指用於表示特定含義的單詞或符號集合。當我們在處理文字資料時,往往會遇到一些不在原始詞彙中的新詞或未知詞彙。為了能夠有效地處理這些新詞彙,需要對現有的詞彙進行擴充套件。

特殊Token的引入

為了應對這種情況,我們可以引入特殊Token來代表未知或新詞彙。例如,可以引入一個<|unk|> Token來代表任何不在原始詞彙中的新詞或未知詞彙。這樣,當模型遇到一個不認識的詞彙時,可以使用<|unk|>來表示。

另外,還可以引入一個<|endoftext|> Token來分隔兩個無關的文字源。這在文字生成任務中尤其有用,因為可以明確地標記出不同文欄位落的結束和開始。

詞彙擴充套件的實際應用

在實際應用中,詞彙擴充套件可以大大提高NLP模型的泛化能力和適應性。透過引入特殊Token,模型可以更好地處理新詞彙和未知詞彙,從而提高整體的語言理解能力。

例如,在機器翻譯任務中,詞彙擴充套件可以幫助模型更好地處理目標語言中的新詞彙和表達方式。同樣,在文字生成任務中,詞彙擴充套件可以幫助模型生成更多樣化和創新的文字內容。

圖表翻譯:
  graph LR
    A[原始詞彙] -->|擴充套件|> B[新增特殊Token]
    B -->|處理新詞彙|> C[提高泛化能力]
    C -->|應用於實際任務|> D[機器翻譯、文字生成等]

圖表顯示了詞彙擴充套件的過程和應用。首先,原始詞彙透過擴充套件新增特殊Token,然後使用這些特殊Token來處理新詞彙和未知詞彙,最終提高了模型的泛化能力和適應性,並應用於實際的NLP任務中。

文字資料處理

在文字資料的預處理中,我們需要考慮特殊符號的加入,以便於模型的訓練和預測。這裡,我們將介紹如何修改詞彙表以包含 <unk><|endoftext|> 這兩個特殊符號。

首先,讓我們先取得所有預處理後的文字資料,並將其轉換為一個排序好的列表:

all_tokens = sorted(list(set(preprocessed)))

接下來,我們需要將 <|endoftext|><|unk|> 這兩個特殊符號加入到詞彙表中:

all_tokens.extend(["<|endoftext|>", "<|unk|>"])

然後,我們可以建立一個詞彙表,其中每個符號都對應到一個唯一的整數索引:

vocab = {token: integer for integer, token in enumerate(all_tokens)}

透過列印詞彙表的大小,可以看到新的詞彙表大小為 1,132 個元素(之前的詞彙表大小為 1,130):

print(len(vocab.items()))

為了進一步驗證,我們可以列印更新後詞彙表的最後五個條目:

for i, item in enumerate(list(vocab.items())[-5:]):
    print(item)

輸出結果如下:

('younger', 1127)
('your', 1128)
('yourself', 1129)
('<|endoftext|>', 1130)
('<|unk|>', 1131)

這裡,<|endoftext|> 符號被新增到每個文字源的開頭,以標誌文字的結束。

內容解密:

上述程式碼的主要目的是建立一個包含特殊符號的詞彙表。首先,取得所有預處理後的文字資料,並將其轉換為一個排序好的列表。然後,將 <|endoftext|><|unk|> 這兩個特殊符號加入到詞彙表中。最後,建立一個詞彙表,其中每個符號都對應到一個唯一的整數索引。這樣就可以使用這個詞彙表進行文字資料的編碼和解碼。

圖表翻譯:

  flowchart TD
    A[預處理文字資料] --> B[建立排序好的列表]
    B --> C[加入特殊符號]
    C --> D[建立詞彙表]
    D --> E[列印詞彙表大小]
    E --> F[列印更新後詞彙表的最後五個條目]

這個流程圖展示瞭如何建立一個包含特殊符號的詞彙表的步驟。首先,預處理文字資料,然後建立一個排序好的列表。接下來,加入特殊符號,然後建立一個詞彙表。最後,列印詞彙表大小和更新後詞彙表的最後五個條目。

科技新聞與娛樂趨勢分析

近期,各型別的新聞和娛樂內容在網路上廣泛傳播。從體育賽事到股票市場的變動,再到虛構故事的結局,所有這些都能在網路上找到相關的報導和討論。

體育賽事的激情

體育賽事一直是人們關注的焦點,尤其是當地球隊或選手參加的比賽。最近,一支被視為弱者的球隊在一場令人激動的加時賽中贏得了冠軍,引發了球迷們的狂喜和自豪感。這種勝利不僅給球隊帶來了榮譽,也給球迷帶來了無窮的喜悅和滿足感。

內容解密:體育賽事的社會影響

體育賽事不僅是一種娛樂形式,也是社會凝聚力的一部分。它能夠把人們聚集在一起,分享同樣的熱情和期待。體育賽事的結果可以影響人們的情緒和心態,甚至影響當地的經濟和文化發展。因此,體育賽事的重要性遠遠超出了賽場本身。

虛構故事的結局

虛構故事是另一種形式的娛樂,它能夠帶領人們進入一個充滿想象力的世界。最近,一個叫做Elara和Finn的故事結局引起了廣泛的關注和討論。這個故事透過描述兩個主人公如何以善良和智慧面對生活中的挑戰,最終過上了幸福的生活,給讀者帶來了深刻的思考和感悟。

內容解密:虛構故事的教育意義

虛構故事不僅是一種娛樂形式,也是一種教育工具。它能夠透過生動的故事和人物形象教導人們重要的生活價值和道德觀念。透過閱讀虛構故事,人們可以學習如何面對挑戰、如何做出正確的選擇以及如何與他人建立良好的關係。

股票市場的變動

股票市場是另一方面的焦點,尤其是在經濟變動頻繁的時期。最近,道瓊斯工業平均指數上漲了250點,創下了近三個月來最高的漲幅。這個訊息給投資者帶來了希望和信心,也反映了經濟的穩定和發展。

內容解密:股票市場的分析

股票市場的變動受到多種因素的影響,包括經濟指標、公司業績和全球事件。分析股票市場的趨勢和變動,可以幫助投資者做出明智的投資決策。同時,股票市場也反映了經濟的實際情況和未來的發展趨勢。

  graph LR
    A[體育賽事] --> B[社會影響]
    B --> C[經濟影響]
    C --> D[文化發展]
    E[虛構故事] --> F[教育意義]
    F --> G[生活價值]
    H[股票市場] --> I[分析]
    I --> J[投資決策]

圖表翻譯:社會、娛樂和經濟的互動關係

上述圖表展示了體育賽事、虛構故事和股票市場之間的複雜關係。體育賽事不僅影響社會和經濟,也反映了人們的價值觀念和生活方式。虛構故事能夠教育人們重要的生活價值和道德觀念,而股票市場則反映了經濟的實際情況和未來的發展趨勢。透過分析這些關係,可以更深入地理解社會、娛樂和經濟之間的互動作用。

程式碼實作示例

import re

class SimpleTokenizerV2:
    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()]
        preprocessed = [item if item in self.str_to_int else "<|unk|>" for item in preprocessed]
        ids = [self.str_to_int[s] for s in preprocessed]
        return ids

    def decode(self, ids):
        text = " ".join([self.int_to_str[i] for i in ids])
        text = re.sub(r'\s+([,.:;?!"()\'])', r'\1', text)
        return text

# 測試tokenizer
vocab = {"Hello": 0, "do": 1, "you": 2, "like": 3, "tea": 4, "?": 5}
tokenizer = SimpleTokenizerV2(vocab)

text1 = "Hello, do you like tea?"
encoded_ids = tokenizer.encode(text1)
print("Encoded IDs:", encoded_ids)

decoded_text = tokenizer.decode(encoded_ids)
print("Decoded Text:", decoded_text)

內容解密:

上述程式碼定義了一個名為 SimpleTokenizerV2 的類別,該類別負責將輸入的文字轉換為數字ID,同時也能夠將數字ID轉換迴文字。這個類別的主要功能包括:

  1. 初始化(__init__方法):初始化tokenizer時,需要提供一個詞彙表(vocab),這個詞彙表是一個字典,將文字對映到唯一的整數ID。同時,tokenizer也會建立一個反向對映的字典(int_to_str),這樣可以方便地從ID轉換迴文字。
  2. 編碼(encode方法):當輸入一段文字時,tokenizer會先使用正規表示式將文字分割成單個的詞彙或符號。然後,對於每個詞彙或符號,如果它存在於詞彙表中,就使用對應的ID;如果不存在,就使用特殊的 <|unk|> 標記表示未知詞彙。最後,傳回這些ID的列表。
  3. 解碼(decode方法):給定一列表的ID,tokenizer可以將其轉換回原始的文字。它透過查詢ID對應的文字,並將這些文字以空格分隔的形式組合起來。同時,tokenizer也會移除多餘的空格,以確保輸出的文字格式正確。

圖表翻譯:

  flowchart TD
    A[輸入文字] --> B[分割為詞彙/符號]
    B --> C[查詢詞彙表]
    C --> D{詞彙存在於詞彙表中}
    D -->|是| E[使用對應ID]
    D -->|否| F[使用<|unk|>標記]
    E --> G[傳回ID列表]
    F --> G
    G --> H[解碼]
    H --> I[查詢ID對應文字]
    I --> J[組合文字]
    J --> K[移除多餘空格]
    K --> L[傳回解碼文字]

這個流程圖描述了tokenizer的工作流程,從輸入文字開始,到傳回解碼文字為止,每一步驟都清晰地展示了tokenizer如何處理輸入的資料。

文字資料的簡單分詞器

在處理文字資料時,分詞器(tokenizer)是一個非常重要的工具。它可以將文字資料分解成個別的詞彙或符號,以便於後續的處理和分析。

基本概念

分詞器的基本功能是將輸入的文字資料分解成一個個的詞彙或符號。這些詞彙或符號可以是單個的字元、單詞、或者甚至是整個句子。

SimpleTokenizerV2

以下是使用 SimpleTokenizerV2 進行分詞的範例:

text1 = "Hello, do you like tea?"
text2 = "In the sunlit terraces of the palace."

text = " <|endoftext|> ".join((text1, text2))

print(text)

輸出結果:

Hello, do you like tea? <|endoftext|> In the sunlit terraces of the palace.

接下來,我們使用 SimpleTokenizerV2 對這段文字進行分詞:

tokenizer = SimpleTokenizerV2(vocab)

print(tokenizer.encode(text))

輸出結果:

[1131, 5, 355, 1126, 628, 975, 10, 1130, 55, 988, 956, 984, 722, 988, 1131, 7]

從輸出結果中,我們可以看到分詞器將輸入的文字資料分解成了一系列的 token ID。

分詞器的工作原理

SimpleTokenizerV2 分詞器的工作原理是將輸入的文字資料分解成個別的詞彙或符號。它使用了一個 vocab 字典來對映每個詞彙或符號到一個唯一的 token ID。

當遇到未知的詞彙或符號時,分詞器會將其替換為 <|unk|> 標記。

空格和標點符號

SimpleTokenizerV2 分詞器也會處理空格和標點符號。它會將空格視為一個單獨的 token,並且會在指定的標點符號前面新增空格。

還原文字

最後,我們可以使用分詞器將 token ID 還原回原始的文字資料:

print(tokenizer.decode([1131, 5, 355, 1126, 628, 975, 10, 1130, 55, 988, 956, 984, 722, 988, 1131, 7]))

這樣就完成了簡單的文字分詞和還原過程。

2.5 Byte Pair Encoding

Byte Pair Encoding(BPE)是一種複雜的 Tokenization 方案,根據 byte pair encoding 的概念。BPE Tokenizer 被用於訓練 LLMs,如 GPT-2、GPT-3 和原始的 ChatGPT 模型。

安裝 Tiktoken

由於實作 BPE 可能相當複雜,因此我們會使用現有的 Tiktoken 函式庫,它根據 Rust 的原始碼,提供了高效的 BPE 演算法實作。您可以透過 Python 的 pip 安裝器從終端安裝 Tiktoken:

pip install tiktoken

驗證 Tiktoken 版本

您可以使用以下程式碼驗證目前安裝的 Tiktoken 版本:

from importlib.metadata import version
import tiktoken

print("tiktoken 版本:", version("tiktoken"))

初始化 BPE Tokenizer

一旦安裝完成,您就可以從 Tiktoken 中初始化 BPE Tokenizer:

tokenizer = tiktoken.get_encoding("gpt2")

使用 BPE Tokenizer

使用這個 Tokenizer 的方式與之前實作的 SimpleTokenizerV2 類別似,透過 encode 方法:

text = "Hello, do you like tea? <|endoftext|> In the sunlit terraces of someunknownPlace."
integers = tokenizer.encode(text, allowed_special={"<|endoftext|>"})

print(integers)

這將輸出 token IDs:

[15496, 11, 466, 345, 588, 8887, 30, 220, 50256, 554, 262, 4252, 18250, 8812, 2114, 286, 617, 34680, 27271, 13]

然後,您可以使用 decode 方法將 token IDs 轉迴文字,類別似於 SimpleTokenizerV2:

print(tokenizer.decode(integers))

圖表翻譯:

  flowchart TD
    A[文字輸入] --> B[BPE Tokenizer]
    B --> C[Token IDs]
    C --> D[解碼]
    D --> E[文字輸出]

內容解密:

  1. BPE Tokenizer 初始化:首先,初始化 BPE Tokenizer,以便進行 tokenization。
  2. 文字編碼:使用 encode 方法將輸入文字轉換為 token IDs。
  3. Token IDs 輸出:輸出得到的 token IDs。
  4. 解碼:使用 decode 方法將 token IDs 轉回原始文字。
  5. 文字輸出:最終輸出解碼後的文字。

這個過程展示瞭如何使用 BPE Tokenizer 進行 tokenization 和解碼,對於理解 LLMs 的工作原理非常重要。

文字資料處理技術探討

在自然語言處理(NLP)中,文字資料的處理是一個非常重要的步驟。這裡,我們將探討如何使用位元組對編碼(Byte Pair Encoding, BPE)來處理未知文字。

BPE 編碼原理

BPE是一種編碼演算法,能夠將未知文字分解成子詞單元或個別字元。這使得BPE能夠處理任何文字,即使它們不在預先定義的詞彙表中。BPE的工作原理是先將所有個別字元新增到詞彙表中,然後合併經常一起出現的字元組合成子詞。

實際應用

讓我們使用tiktoken函式庫中的BPE編碼器來處理未知文字「Akwirw ier」。首先,我們需要將這個文字分解成個別字元或子詞單元。然後,我們可以使用BPE編碼器將這些字元或子詞單元轉換成對應的token ID。

import tiktoken

# 建立BPE編碼器
tokenizer = tiktoken.get_encoding("cl100k_base")

# 定義未知文字
unknown_word = "Akwirw ier"

# 將未知文字分解成個別字元或子詞單元
subwords = ["A", "k", "w", "i", "r", "w", " ", "i", "e", "r"]

# 將子詞單元轉換成token ID
token_ids = [tokenizer.encode(subword) for subword in subwords]

print(token_ids)

結果分析

執行上述程式碼後,我們可以得到未知文字「Akwirw ier」的token ID列表。這些token ID代表了BPE編碼器如何將未知文字分解成子詞單元或個別字元。

  flowchart TD
    A[未知文字] --> B[分解成子詞單元]
    B --> C[轉換成token ID]
    C --> D[輸出結果]

圖表翻譯:

上述流程圖展示瞭如何使用BPE編碼器處理未知文字。首先,未知文字被分解成子詞單元或個別字元。然後,這些子詞單元被轉換成對應的token ID。最後,輸出結果為token ID列表。

從技術架構視角來看,本文深入淺出地介紹了自然語言處理中關鍵的 Tokenization 技術,涵蓋了從基礎概念到 BPE 等進階方法。分析段落中,我們比較了 SimpleTokenizerV2 與根據 Tiktoken 的 BPE Tokenizer 的實作方式,並闡述了特殊標記如 <|unk|><|endoftext|> 如何提升模型處理未知詞彙和文字邊界的能力。技術限制方面,SimpleTokenizerV2 的詞彙表大小會影響其處理未知詞彙的效能,而 BPE 則能更有效地處理未見過的詞彙。實務上,BPE 已成為大語言模型訓練的主流方案,展現其優越性。展望未來,隨著模型規模的擴大和資料量的增加,更高效且更能理解上下文語意的 Tokenization 技術將持續發展,例如 SentencePiece 和根據 Transformer 的方法。玄貓認為,深入理解 Tokenization 的原理和方法,對於構建高效能的 NLP 應用至關重要,開發者應關注這些技術的最新進展並根據實際需求選擇合適的方案。