一、概念
今天我们来聊一聊一个比较抽象的概念——语义空间。语义空间(Semantic Space)是自然语言处理(NLP)领域中一个重要的概念,在语义空间中,文本中的语义信息通过数学的方式来表示和处理。语义空间是一个高维的向量空间,其中每个维度代表一个特定的语义特征或属性(前提是该语义空间中的向量都是由相同的文本表示模型所生成,否则这个语义空间几乎没有任何意义)。在语义空间中,词语、短语或句子被表示为向量,这些向量的各个维度值反映了它们在不同语义特征上的强度或权重(实际上我们不关注也无法得知每一个维度的具体含义)。得益语义空间的这些特性,我们能够通过数学运算来分析和比较不同词语或句子的语义相似性或差异性。
语义空间的构建通常依赖于机器学习/深度学习算法,如Word2Vec、GloVe、LSTM或BERT等,这些模型能够从大规模语料库中学习词语的向量表示,从而将自然语言转化为语义空间中的嵌入,即文本表示。这些文本表示捕捉了词语的语义信息,使得语义相关的词语在向量空间中彼此接近。
二、在语义空间中能干什么?
显然,我们通过各种相似度比较方法,即可获得语义空间中两个或者多个文本表示之间的距离,而其距离的远近则反映了文本表示之间的关系远近。由此,我们可以在语义空间中进行一系列的自然语言处理任务,下面举几个典型例子:
1、文本分类/聚类
文本分类和聚类是最常见的自然语言处理任务了,通过把待处理的文本数据转变为同一语义空间中的文本表示,即可比较文本之间的相似程度,从而完成我们的分类或者聚类任务。当然,想要取得更好的任务性能,我们就必须使用更为强大的文本表示模型,例如预训练的Bert文本表示能力自然是要比我们自己训练一个Word2Vec模型要更强的。
这里,我们简单给出5条文本示例,使用Bert进行文本向量化(这里用的是Hugging Face上微调过的用于语义检索的Bert)。显然,语义相近的两句话在向量空间中的距离也更为接近,这就是深度学习中的文本分类和聚类模型的知识基础。
import torch
from transformers import BertTokenizer, BertModel
import matplotlib.pyplot as plt
from sklearn.metrics.pairwise import cosine_similarity
# 检查是否有可用的GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 初始化BERT tokenizer和模型
tokenizer = BertTokenizer.from_pretrained('sentence-transformers/bert-base-nli-mean-tokens')
model = BertModel.from_pretrained('sentence-transformers/bert-base-nli-mean-tokens').to(device)
# 三句话
sentences = [
"The cat is on the mat.",
"A cat is on a mat.",
"Python is popular in the world!",
"That is a happy person",
"Today I brought a car!"
]
# 编码句子
encoded_inputs = tokenizer(sentences, return_tensors='pt', padding=True, truncation=True, max_length=30)
encoded_inputs = encoded_inputs.to(device)
# 获取BERT模型的输出
with torch.no_grad():
outputs = model(**encoded_inputs)
# 获取最后一层的隐藏状态
hidden_states = outputs.last_hidden_state
# 取每个句子的第一个token的表示作为句子的表示
sentence_embeddings = hidden_states[:, 0, :].cpu()
# 计算余弦相似度
cosine_sim = cosine_similarity(sentence_embeddings)
# 打印句子两两之间的相似度
for i, sentence1 in enumerate(sentences):
for j, sentence2 in enumerate(sentences):
if i < j: # 只打印上三角矩阵,避免重复
print(f"Similarity between '{sentence1}' and '{sentence2}': {cosine_sim[i, j]:.4f}")
2、信息推荐
信息推荐的关键也是文本表示的相似度计算,例如我们在百度搜索内容时,算法应当计算我们的检索Query与数据库中的资源相似程度,并按照相似度从高到低返回检索结果。那么这时候如果我们数据库中的资源量非常大,使用for循环逐条比较Query与资源的相似度必然是很不现实的。这时候,我们可以通过python第三方库提供的一些工具来实现快速的相似度计算,例如torch.nn提供了一个相似度计算方法。
import torch
from torch.nn.functional import cosine_similarity
# 假设我们有一个查询向量query和多个目标向量target_vectors
query = torch.tensor([1.0, 2.0, 3.0], dtype=torch.float32)
target_vectors = torch.tensor([[2.0, 1.0, 0.0], [1.0, 2.0, 3.0], [7.0, 3.0, -1.0]], dtype=torch.float32)
# 使用torch计算余弦相似度
cosine_sim = torch.nn.CosineSimilarity(dim=1, eps=1e-6)
similarities = cosine_sim(query.unsqueeze(0), target_vectors).squeeze()
此外,对于两个数组之间的每对向量的相似度计算,我们可以使用scipy。需要注意的是,cdist输出的结果是XA中的每个元素与XB中的每个元素之间的距离,而1-cdist()才是余弦相似度。
import numpy as np
from scipy.spatial.distance import cdist
# 假设我们有两个向量集合
XA = np.array([[1, 2, 3], [4, 5, 6]]) # 查询向量集合
XB = np.array([[7, 3, 1], [1, 2, 3]]) # 目标向量集合
# 使用 cdist 计算余弦相似度
cosine_distances = cdist(XA, XB, metric='cosine')
# 打印距离
print(cosine_distances) # [[0.4432888 0. ], [0.27301624 0.02536815]]
# 打印余弦相似度
print(1-cosine_distances) # [[0.5567112 1. ], [0.72698376 0.97463185]]
3、机器翻译
语义空间中的文本向量相似度计算也是机器翻译的核心,例如我们对中文文本进行向量化,同时对它的英文形式进行向量化,那么我们必然是期望这两个向量在同一语义空间中的距离应当是无限接近于重合的。由此,我们基于文本相似度来设计机器翻译模型的损失函数,最小化这个损失函数从而提升机器翻译模型的性能。例如同样使用1中的代码,我们将文本改为如下内容,并使用HuggingFace网友微调过的预训练模型“EZlee/e-commerce-bert-base-multilingual-cased”,打印结果。
sentences = [
"The cat is on the mat.",
"垫子上有一只猫",
"Python is popular in the world!",
"Python语言在世界上很受欢迎。"
]
从结果上看,显然这个模型还有较大的提升空间。我们可以通过增加训练语料并基于余弦相似度设置损失函数来进一步微调该模型,例如对比学习就是机器翻译中的一个重要的建模策略。
三、总结
我们在阅读论文的过程中,时常会看到语义空间这个概念。听起来它很神秘,但实际上它就是一个向量集合,而且这其中的向量必须要由相同的模型或者方法生成,这样向量之间的比较才具有实际意义。