目录
让我们来看一个具体的例子。假设您有这样一个中文句子:
"我爱学习自然语言处理"
将它输入到embedding层之前,需要经过以下步骤:
1.分词(Tokenization):
将整个句子分割成一个个单词(或字符)的序列,比如['我', '爱', '学习', '自然语言', '处理']
(在中文的分词中经常会使用jieba库)
2.索引映射(Index Mapping):
将每个单词映射成词表中的索引值,假设词表为:
vocab = {'我':0, '爱':1, '学习':2, '自然语言':3, '处理':4, '...'}
那么上面的单词序列就变成了索引序列: [0, 1, 2, 3, 4]
3.添加特殊标记(Add Special Tokens):
有时需要在序列首尾添加特殊标记,如<start>
和 <end>
4.padding(填充):
由于每个句子长度不同,我们需要将它们填充到同样长度。假设最大长度为10,那么上面序列就变成[0, 1, 2, 3, 4, 0, 0, 0, 0, 0]
(后面添加了5个填充索引0)
通过以上步骤,一个长度为T的句子变成了一个形状为[1, T]
的索引序列(批次大小为1)
5.批量(Batching):
对多句话重复上述步骤并堆叠,得到[N, T]
的索引矩阵,其中N是批次大小。
最后,将这个[N, T]
的索引矩阵输入到embedding层,就可以得到[N, T, embedding_dim]
的词嵌入表示了。
所以[N, T]
中的N表示批次大小(有多少个句子),T表示每个句子的最大长度(经过padding)。这种表示方式可最大化利用批量计算的并行加速能力。
接下来用代码表示:
import torch
import jieba
# 原始句子
raw_text = "我爱学习自然语言处理"
# 1. 分词
tokens = jieba.lcut(raw_text)
# tokens = ['我', '爱', '学习', '自然语言', '处理']
# 2. 构建词表
vocab = {}
for token in tokens:
if token not in vocab:
vocab[token] = len(vocab)
# vocab = {'我': 0, '爱': 1, '学习': 2, '自然语言': 3, '处理': 4}
# 3. 索引映射
ids = [vocab[token] for token in tokens]
# ids = [0, 1, 2, 3, 4]
# 4. 添加特殊标记
pad_id = len(vocab) # pad_id = 5, 代表[PAD]标记
ids = [pad_id] + ids + [pad_id] # 加入[PAD]作为开始和结束标记
# ids = [5, 0, 1, 2, 3, 4, 5]
# 5. padding
max_len = 10
padded_ids = ids + [pad_id] * (max_len - len(ids))
# padded_ids = [5, 0, 1, 2, 3, 4, 5, 5, 5, 5]
# 6. 转为张量
input_ids = torch.tensor([padded_ids])
# input_ids.shape = [1, 10] 形状为[N, T]
# 可以重复上述步骤,并在最后一步将多个张量堆叠起来得到批量表示
# batch_input_ids = torch.cat((input_ids, other_input_ids), dim=0)
# 最终输入到embedding层
embeddings = embedding_layer(input_ids)
# embeddings.shape = [1, 10, embedding_dim]
在这个例子中:
- 使用jieba分词器将原始文本分词,得到词元(token)序列。
- 构建词表,将每个词元映射为一个索引值。
- 将词元序列转换为对应的索引序列。
- 在序列首尾添加
[PAD]
标记,其索引值为词表大小。 - 将序列padding到固定长度
max_len
。 - 将padding后的索引序列转换为PyTorch张量,形状为
[N, T]
。 - 最后可以输入到
embedding_layer
得到[N, T, embedding_dim]
的词嵌入表示。
bert分词器
import torch
# 原始句子
raw_text = "我爱学习自然语言处理"
# 1. 分词
from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
tokens = tokenizer.tokenize(raw_text)
# tokens = ['我', '爱', '学习', '自然', '语言', '处理']
# 2. 索引映射
ids = tokenizer.convert_tokens_to_ids(tokens)
# ids = [1416, 4610, 1103, 6210, 7273, 3346]
# 3. 添加特殊标记
ids = [101] + ids + [102] # 加入[CLS]和[SEP]标记
# ids = [101, 1416, 4610, 1103, 6210, 7273, 3346, 102]
# 4. padding
max_len = 10
pad_id = tokenizer.pad_token_id # 0 对应的是[PAD]标记
padded_ids = ids + [pad_id] * (max_len - len(ids))
# padded_ids = [101, 1416, 4610, 1103, 6210, 7273, 3346, 102, 0, 0]
# 5. 转为张量
input_ids = torch.tensor([padded_ids])
# input_ids.shape = [1, 10] 形状为[N, T]
# 可以重复上述步骤,并在最后一步将多个张量堆叠起来得到批量表示
# batch_input_ids = torch.cat((input_ids, other_input_ids), dim=0)
# 最终输入到embedding层
embeddings = embedding_layer(input_ids)
# embeddings.shape = [1, 10, embedding_dim]
在这个例子中:
- 我使用了BERT的分词器来将原始文本分词,获得词元(token)序列。
- 将词元映射为词表中对应的索引值。
- 添加了BERT模型需要的特殊标记
[CLS]
和[SEP]
。 - 将序列padding到固定长度
max_len
。 - 将padding后的索引序列转换为PyTorch张量,形状为
[N, T]
。 - 最后可以输入到
embedding_layer
得到[N, T, embedding_dim]
的词嵌入表示。