mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
96 lines
4.9 KiB
Markdown
96 lines
4.9 KiB
Markdown
# 1. 分词
|
||
|
||
## 分词
|
||
|
||
**分词**是将数据(如文本)分解为更小、可管理的部分,称为_标记_的过程。每个标记都被分配一个唯一的数字标识符(ID)。这是为机器学习模型处理文本做准备的基本步骤,特别是在自然语言处理(NLP)中。
|
||
|
||
> [!TIP]
|
||
> 这个初始阶段的目标非常简单:**以某种有意义的方式将输入分成标记(ID)**。
|
||
|
||
### **分词的工作原理**
|
||
|
||
1. **拆分文本:**
|
||
- **基本分词器:** 一个简单的分词器可能会将文本拆分为单个单词和标点符号,去除空格。
|
||
- _示例:_\
|
||
文本:`"Hello, world!"`\
|
||
标记:`["Hello", ",", "world", "!"]`
|
||
2. **创建词汇表:**
|
||
- 为了将标记转换为数字ID,创建一个**词汇表**。这个词汇表列出所有唯一的标记(单词和符号),并为每个标记分配一个特定的ID。
|
||
- **特殊标记:** 这些是添加到词汇表中的特殊符号,以处理各种场景:
|
||
- `[BOS]`(序列开始):表示文本的开始。
|
||
- `[EOS]`(序列结束):表示文本的结束。
|
||
- `[PAD]`(填充):用于使批次中的所有序列具有相同的长度。
|
||
- `[UNK]`(未知):表示不在词汇表中的标记。
|
||
- _示例:_\
|
||
如果`"Hello"`被分配ID `64`,`","`是`455`,`"world"`是`78`,`"!"`是`467`,那么:\
|
||
`"Hello, world!"` → `[64, 455, 78, 467]`
|
||
- **处理未知单词:**\
|
||
如果像`"Bye"`这样的单词不在词汇表中,它将被替换为`[UNK]`。\
|
||
`"Bye, world!"` → `["[UNK]", ",", "world", "!"]` → `[987, 455, 78, 467]`\
|
||
&#xNAN;_(假设`[UNK]`的ID是`987`)_
|
||
|
||
### **高级分词方法**
|
||
|
||
虽然基本分词器适用于简单文本,但在处理大型词汇表和新或稀有单词时存在局限性。高级分词方法通过将文本拆分为更小的子单元或优化分词过程来解决这些问题。
|
||
|
||
1. **字节对编码(BPE):**
|
||
- **目的:** 通过将稀有或未知单词拆分为频繁出现的字节对,减少词汇表的大小并处理稀有或未知单词。
|
||
- **工作原理:**
|
||
- 从单个字符作为标记开始。
|
||
- 迭代地将最频繁的标记对合并为一个标记。
|
||
- 继续直到没有更多频繁的标记对可以合并。
|
||
- **好处:**
|
||
- 消除了对`[UNK]`标记的需求,因为所有单词都可以通过组合现有的子词标记来表示。
|
||
- 更高效和灵活的词汇表。
|
||
- _示例:_\
|
||
`"playing"`可能被标记为`["play", "ing"]`,如果`"play"`和`"ing"`是频繁的子词。
|
||
2. **WordPiece:**
|
||
- **使用者:** 像BERT这样的模型。
|
||
- **目的:** 类似于BPE,它将单词拆分为子词单元,以处理未知单词并减少词汇表大小。
|
||
- **工作原理:**
|
||
- 从单个字符的基本词汇表开始。
|
||
- 迭代地添加最大化训练数据可能性的最频繁子词。
|
||
- 使用概率模型决定合并哪些子词。
|
||
- **好处:**
|
||
- 在拥有可管理的词汇表大小和有效表示单词之间取得平衡。
|
||
- 高效处理稀有和复合单词。
|
||
- _示例:_\
|
||
`"unhappiness"`可能被标记为`["un", "happiness"]`或`["un", "happy", "ness"]`,具体取决于词汇表。
|
||
3. **单元语言模型:**
|
||
- **使用者:** 像SentencePiece这样的模型。
|
||
- **目的:** 使用概率模型确定最可能的子词标记集。
|
||
- **工作原理:**
|
||
- 从一组潜在标记开始。
|
||
- 迭代地删除对模型的训练数据概率改善最小的标记。
|
||
- 最终确定一个词汇表,其中每个单词由最可能的子词单元表示。
|
||
- **好处:**
|
||
- 灵活,可以更自然地建模语言。
|
||
- 通常导致更高效和紧凑的标记化。
|
||
- _示例:_\
|
||
`"internationalization"`可能被标记为更小的、有意义的子词,如`["international", "ization"]`。
|
||
|
||
## 代码示例
|
||
|
||
让我们通过来自[https://github.com/rasbt/LLMs-from-scratch/blob/main/ch02/01_main-chapter-code/ch02.ipynb](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch02/01_main-chapter-code/ch02.ipynb)的代码示例更好地理解这一点:
|
||
```python
|
||
# Download a text to pre-train the model
|
||
import urllib.request
|
||
url = ("https://raw.githubusercontent.com/rasbt/LLMs-from-scratch/main/ch02/01_main-chapter-code/the-verdict.txt")
|
||
file_path = "the-verdict.txt"
|
||
urllib.request.urlretrieve(url, file_path)
|
||
|
||
with open("the-verdict.txt", "r", encoding="utf-8") as f:
|
||
raw_text = f.read()
|
||
|
||
# Tokenize the code using GPT2 tokenizer version
|
||
import tiktoken
|
||
token_ids = tiktoken.get_encoding("gpt2").encode(txt, allowed_special={"[EOS]"}) # Allow the user of the tag "[EOS]"
|
||
|
||
# Print first 50 tokens
|
||
print(token_ids[:50])
|
||
#[40, 367, 2885, 1464, 1807, 3619, 402, 271, 10899, 2138, 257, 7026, 15632, 438, 2016, 257, 922, 5891, 1576, 438, 568, 340, 373, 645, 1049, 5975, 284, 502, 284, 3285, 326, 11, 287, 262, 6001, 286, 465, 13476, 11, 339, 550, 5710, 465, 12036, 11, 6405, 257, 5527, 27075, 11]
|
||
```
|
||
## 参考文献
|
||
|
||
- [https://www.manning.com/books/build-a-large-language-model-from-scratch](https://www.manning.com/books/build-a-large-language-model-from-scratch)
|