如何仅针对单词而非句子使用BERT获取词向量嵌入?
用原生BERT生成名词向量的解决方案
你提到的SentenceTransformer确实是封装好的工具,但原生BERT也能直接生成可用的单词向量,核心是要正确处理分词后的输入,并从模型输出中提取合适的向量表示。下面是具体步骤和代码:
核心思路
BERT的Tokenizer负责将文本转换为模型可接受的输入格式(token ID、attention mask等),但真正的语义向量是模型处理这些输入后输出的隐藏层状态。对于单个名词,通常有两种常用的向量提取方式:
- 取
[CLS]token的向量:BERT输入的第一个token,是整个序列的聚合表示 - 取所有非特殊token的平均向量:处理名词被拆分为subword的情况(比如"happiness"可能被拆为"happy"和"##ness"),平均后更贴近单词本身的语义
完整代码示例
from transformers import BertTokenizer, BertModel import torch # 加载预训练BERT模型和分词器 tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') model = BertModel.from_pretrained('bert-base-uncased') model.eval() # 切换到评估模式,避免梯度更新 # 你的名词列表 words = ["apple", "banana", "computer", "unhappiness"] word_embeddings = [] # 关闭梯度计算,节省内存和计算资源 with torch.no_grad(): for word in words: # 分词:生成模型所需的输入张量,自动添加[CLS]和[SEP] inputs = tokenizer( word, return_tensors="pt", padding=True, truncation=True ) # 传入模型,获取输出 outputs = model(**inputs) # last_hidden_state的形状是 (batch_size, sequence_length, hidden_size) # 方案1:提取[CLS] token的向量(第一个位置) cls_emb = outputs.last_hidden_state[:, 0, :].squeeze().numpy().tolist() # 方案2:提取所有非特殊token的平均向量(推荐用于subword拆分的情况) token_ids = inputs["input_ids"][0] # 过滤掉[CLS]和[SEP]的token ID valid_token_indices = [ idx for idx, tid in enumerate(token_ids) if tid not in [tokenizer.cls_token_id, tokenizer.sep_token_id] ] valid_embeddings = outputs.last_hidden_state[0, valid_token_indices, :] avg_emb = torch.mean(valid_embeddings, dim=0).numpy().tolist() # 选择一种向量添加到结果列表,这里用平均向量 word_embeddings.append(avg_emb) # 现在word_embeddings就是可直接用于后续任务的向量列表
关键问题解释
- Tokenizer的作用:Tokenizer只是文本到模型输入的转换器,输出的
input_ids、attention_mask等是模型的输入参数,不是语义向量,必须传入BERT模型才能得到向量。 - 为什么SentenceTransformer可以直接用:它是在预训练语言模型(比如BERT、MPNet)基础上做了微调,专门优化了句子/短语的向量生成任务,所以
encode方法直接封装了输入处理和向量提取的逻辑。 - 批量处理优化:如果你的名词列表很大,可以将所有单词一次性传入分词器,批量处理能大幅提高效率,示例如下:
# 批量处理示例 inputs = tokenizer(words, return_tensors="pt", padding=True, truncation=True) with torch.no_grad(): outputs = model(**inputs) # 批量提取平均向量 batch_avg_embs = [] for i in range(len(words)): token_ids = inputs["input_ids"][i] valid_indices = [idx for idx, tid in enumerate(token_ids) if tid not in [tokenizer.cls_token_id, tokenizer.sep_token_id]] valid_embs = outputs.last_hidden_state[i, valid_indices, :] avg_emb = torch.mean(valid_embs, dim=0).numpy().tolist() batch_avg_embs.append(avg_emb)
额外建议
- 根据任务选择预训练模型:比如需要大小写敏感的场景用
bert-base-cased,追求更好效果用bert-large-uncased(但计算量更大)。 - 如果需要更贴合特定领域的向量,可以用领域预训练的BERT模型(比如医疗领域的
biobert-base-cased-v1.1)。
内容的提问来源于stack exchange,提问作者RoAGn
相关产品推荐
相关产品推荐

