Tokens and Embeddings

#deepLearning/llm/2

基于单词(Word-based)的分词

将文本字符串分割成单词作为最小单位。这种方法的基本原则是:每个空格、标点符号、换行符等自然语言的分隔符都作为分词的边界。 ![[word-based spilt.png]]

  1. 词汇表大小和ID分配:基于单词的分词方法会将每个独立的单词分配一个唯一的 ID,ID 从 0 开始到词汇表的大小。为了完全覆盖一种语言,必须为每个单词分配一个 ID,这样会生成一个非常大的词汇表。例如,英语中有超过 500,000 个单词,需要为每个单词创建一个标识符。

  2. 相似词问题:基于单词的分词方法将每个词当作独立的 token,像“dog”和“dogs”或“run”和“running”这样的词会被视为不相关的 token,而忽略它们的相似性。

  3. 未知词(Unknown token):如果词汇表中没有某个单词,tokenizer 会使用一个特殊的“未知”标记(通常为 “[UNK]” 或 “”)表示该单词。这通常是个不好的信号,表示分词器无法识别该词的合理表示,可能会导致信息丢失。

  4. 减少未知词:为了减少“未知”标记的出现,可以使用更细粒度的分词方法,如基于字符的分词器(character-based tokenizer),从而降低词汇表的需求,并帮助模型更好地处理词形变化。

基于字符(Character-based)的 tokenization

将文本拆分为字符,而不是单词,每个字符作为一个独立的 token。 ![[char-based spilt.png]] 基于字符的分词方法虽然解决了OOV问题,但字符本身缺乏语义信息,且会产生大量tokens,增加计算复杂度。

基于子词(subword)的 tokenization

subword 算法把 tokenization 分为两部分,保持空间效率的同时具有语义意义(只需要两个 tokens 就能表示一个长词)

![[Subword token.png]]

prompt = "Write an email apologizing to Sarah for the tragic gardening mishap. Explain how it happened.<|assistant|>"
# Tokenize the input prompt

input_ids = tokenizer(prompt, return_tensors="pt").input_ids.to("cuda")

for id in input_ids[0]:
print(tokenizer.decode(id))

# outputs:
<s>
Write
an
email
apolog
izing
to
Sarah
for
the
trag
ic
garden
ing
m
ish
ap
.
Exp
lain
how
it
happened
.
<|assistant|>

Pipeline 内部

pipeline 集成了三个步骤:预处理、模型计算和后处理: ![[Pipeline 内部.png]] ## 使用 tokenizer( tokenizer )进行预处理

与其他神经网络一样,Transformer 模型无法直接处理原始文本,因此管道的第一步是将文本输入转换为模型能够理解的数字。为此,我们使用 tokenizer( tokenizer ),它将负责:

  • 将输入拆分为单词、子单词或符号(如标点符号),称为 token
  • 将每个标记(token)映射到一个数字,称为 input ID
  • 添加模型需要的其他输入,例如特殊标记(如 [CLS] 和 [SEP] )
    • 位置编码:指示每个标记在句子中的位置。
    • 段落标记:区分不同段落的文本。
    • 特殊标记:例如 [CLS] 和 [SEP] 标记,用于标识句子的开头和结尾。
tokenizer = AutoTokenizer.from_pretrained(model_name)

当有了 tokenizer,把 prompt 传给它,会得到 Input ID

prompt = "Write an email apologizing to Sarah for the tragic gardening mishap. Explain how it happened.<|assistant|>"

inputs = tokenizer(prompt, padding=True, truncation=True, return_tensors="pt")

print(inputs)

# outputs:
{
'input_ids': tensor([
[14350, 385, 4876, 27746, 5281, 304, 19235, 363, 278, 25305,
293, 16423, 292, 286, 728, 481, 29889, 12027, 7420, 920,
372, 9559, 29889, 32001]
]),
'attention_mask': tensor([
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
])
}

Transformers 通常有三个维度:

  • Batch size(批次大小):一次处理的序列数。
  • Sequence length(序列长度):表示序列(句子)的长度。
  • Hidden size(隐藏层大小):每个模型输入的向量维度。

将预处理的之后的值输入到模型中,会得到以下输入:

outputs = model(**inputs)
print(outputs.last_hidden_state.shape)

# outputs:
torch.Size([2, 16, 768])

Embedding

Word Embedding (与 LLM 无关)

Word2vec会显示意思相近的词源,但对于上下文意思不同但词源相同的文本 outputs 还是一样的。

import gensim.downloader as api

# Download embeddings (66MB, glove, trained on wikipedia, vector size: 50)
# Other options include "word2vec-google-news-300"
# More options at https://github.com/RaRe-Technologies/gensim-data
model = api.load("glove-wiki-gigaword-50")

model.most_similar([model['king']], topn=11)

# outputs:
[('king', 1.0000001192092896),
('prince', 0.8236179351806641),
('queen', 0.7839043140411377),
('ii', 0.7746230363845825),
('emperor', 0.7736247777938843),
('son', 0.766719400882721),
('uncle', 0.7627150416374207),
('kingdom', 0.7542161345481873),
('throne', 0.7539914846420288),
('brother', 0.7492411136627197),
('ruler', 0.7434253692626953)]

上下文相关的 Embedding

Contextualized Word Embeddings From a Language Model (Like BERT)

from transformers import AutoModel, AutoTokenizer

# Load a tokenizer
tokenizer = AutoTokenizer.from_pretrained("microsoft/deberta-base")

# Load a language model
model = AutoModel.from_pretrained("microsoft/deberta-v3-xsmall")

# Tokenize the sentence
tokens = tokenizer('Hello world', return_tensors='pt')

# Process the tokens
output = model(**tokens)[0]
output.shape
# outputs:
torch.Size([1, 4, 384])
for token in tokens['input_ids'][0]:
print(tokenizer.decode(token))

# outputs:
[CLS]
Hello
world
[SEP]
output

# outputs:
tensor([[[-3.4816, 0.0861, -0.1819, ..., -0.0612, -0.3911, 0.3017],
[ 0.1898, 0.3208, -0.2315, ..., 0.3714, 0.2478, 0.8048],
[ 0.2071, 0.5036, -0.0485, ..., 1.2175, -0.2292, 0.8582],
[-3.4278, 0.0645, -0.1427, ..., 0.0658, -0.4367, 0.3834]]],
grad_fn=<NativeLayerNormBackward0>)

文本向量(句向量)

Text Embeddings (For Sentences and Whole Documents)

from sentence_transformers import SentenceTransformer

# Load model
model = SentenceTransformer('sentence-transformers/all-mpnet-base-v2')

# Convert text to text embeddings
vector = model.encode("Best movie ever!")
vector.shape

# outputs:
(768,)