|
|
|
@ -4,7 +4,7 @@
|
|
|
|
|
|
|
|
|
|
在传给Transformer前,文本会先通过tokenizer(分词器),将原始文本分割成词汇单元(tokens),这些词汇单元对应于模型词汇表中的索引。然后,这些索引会被转换成模型能够处理的输入序列。
|
|
|
|
|
|
|
|
|
|
### 实际案例
|
|
|
|
|
### 文字转索引
|
|
|
|
|
|
|
|
|
|
前面的"LLM with me"会转成4个索引,代码如下(下面用GPT2做示例,因为GPT2是开源的):
|
|
|
|
|
|
|
|
|
@ -46,7 +46,9 @@ print(tokenizer.convert_ids_to_tokens(inputs['input_ids'][0]))
|
|
|
|
|
|
|
|
|
|
通过上面的代码,我们发现"LLM"文本被分成了"LL"和"M"两个词。
|
|
|
|
|
|
|
|
|
|
为什么会被分成两个,原因是即使英文也有数十万甚至更多的词,大部分词都是通过subword(子单词)组成,也就是input可以由in和put组成。如果每个独立的词都单独做词汇会造成极大浪费,最后每次词可能要与几十万的向量去做点积。为了提高资源利用率,以及不造成数据维度爆炸,我们会现在词汇量的大小,如GPT2Tokenizer的词汇量是50257。代码如下:
|
|
|
|
|
为什么会被分成两个,因为许多现代NLP模型,使用了一种称为子词分割(subword tokenization)的技术。这种技术允许模型有效地处理词汇量大和词汇外(out-of-vocabulary, OOV)单词的问题。子词分割的常见算法包括Byte-Pair Encoding (BPE)、WordPiece和SentencePiece。
|
|
|
|
|
|
|
|
|
|
分成多个索引的原因:**词汇量限制**、**处理未知单词**、**保留语义信息**、**提高模型效率**,如GPT2Tokenizer的词汇量是50257。代码如下:
|
|
|
|
|
|
|
|
|
|
~~~python
|
|
|
|
|
from transformers import GPT2Tokenizer
|
|
|
|
@ -61,4 +63,41 @@ The vocabulary size of GPT2Tokenizer is: 50257
|
|
|
|
|
"""
|
|
|
|
|
~~~
|
|
|
|
|
|
|
|
|
|
<img src="../assets/image-20240426165150933.png" alt="image-20240426165150933" style="zoom:50%;" />
|
|
|
|
|
<img src="../assets/image-20240426165150933.png" alt="image-20240426165150933" style="zoom:50%;" />
|
|
|
|
|
|
|
|
|
|
### 索引向量化
|
|
|
|
|
|
|
|
|
|
`tokenizer`将文本转换为索引(`input_ids`),然后我们通过`model.get_input_embeddings()`获取模型的嵌入层。这个嵌入层是一个预训练的层,它将索引映射到嵌入向量。我们通过调用`embeddings(input_ids)`将索引转换为嵌入向量(`input_embeddings`)。
|
|
|
|
|
|
|
|
|
|
`input_embeddings`现在包含了文本中每个token的嵌入向量,这些向量可以被送入模型的其余部分进行进一步处理。。例子如下:
|
|
|
|
|
|
|
|
|
|
~~~python
|
|
|
|
|
from transformers import GPT2Tokenizer, GPT2Model
|
|
|
|
|
|
|
|
|
|
# 初始化分词器和模型
|
|
|
|
|
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
|
|
|
|
|
model = GPT2Model.from_pretrained('gpt2')
|
|
|
|
|
# 待处理的文本
|
|
|
|
|
text = "LLM with me"
|
|
|
|
|
# 分词并转换为索引
|
|
|
|
|
inputs = tokenizer(text, return_tensors="pt")
|
|
|
|
|
input_ids = inputs["input_ids"]
|
|
|
|
|
# 获取模型的嵌入层
|
|
|
|
|
embeddings = model.get_input_embeddings()
|
|
|
|
|
# 将索引转换为嵌入向量
|
|
|
|
|
input_embeddings = embeddings(input_ids)
|
|
|
|
|
print(input_embeddings)
|
|
|
|
|
print(input_embeddings.shape)
|
|
|
|
|
"""out:
|
|
|
|
|
tensor([[[ 0.2509, -0.1875, 0.1510, ..., 0.1094, 0.1639, 0.3363],
|
|
|
|
|
[-0.0159, -0.1385, 0.2203, ..., -0.0501, 0.0990, -0.0755],
|
|
|
|
|
[ 0.0644, 0.0104, 0.0293, ..., 0.0400, 0.1087, 0.0350],
|
|
|
|
|
[ 0.1515, -0.0247, 0.0936, ..., -0.1684, 0.1065, 0.0572]]],
|
|
|
|
|
grad_fn=<EmbeddingBackward0>)
|
|
|
|
|
torch.Size([1, 4, 768])
|
|
|
|
|
"""
|
|
|
|
|
~~~
|
|
|
|
|
|
|
|
|
|
<img src="../assets/image-20240426174122893.png" alt="image-20240426174122893" style="zoom:50%;" />
|
|
|
|
|
|
|
|
|
|
可以看到最终维度是转成了768列,4行。也就对应着4个索引,和GPT2的嵌入向量维度768
|