3. 语言指令编码与嵌入
任务目标
实现一个轻量级语言指令编码器,将自然语言指令(如“拿起红色方块”)转换为固定长度的嵌入向量,并通过余弦相似度验证语义相近指令的向量距离更近。
核心实现
import re
import numpy as np
from collections import Counter
class SimpleInstructionEncoder:
"""基于词袋+TF-IDF加权+哈希降维的轻量指令编码器(无外部依赖)"""
def __init__(self, dim=64):
self.dim = dim
self.vocab = {}
self.idf = {}
self._fitted = False
def _tokenize(self, text):
# 中文按字切分,英文按词切分,统一小写
text = text.lower().strip()
tokens = re.findall(r'[\u4e00-\u9fff]|[a-z]+', text)
return tokens
def _hash_(self, , salt=0):
# 简单字符串哈希到[0, dim)
h = 0
for ch in + str(salt):
h = (h * 31 + ord(ch)) & 0xffffffff
return h % self.dim
def fit(self, sentences):
"""构建词表与IDF权重"""
doc_freq = Counter()
tokenized_docs = []
for sent in sentences:
tokens = set(self._tokenize(sent))
tokenized_docs.append(tokens)
for tok in tokens:
doc_freq[tok] += 1
n_docs = len(sentences)
self.vocab = {tok: idx for idx, tok in enumerate(doc_freq.keys())}
self.idf = {tok: np.log((1 + n_docs) / (1 + freq)) + 1.0
for tok, freq in doc_freq.items()}
self._fitted = True
return self
def encode(self, sentence):
"""将单条指令编码为固定维度向量"""
if not self._fitted:
raise RuntimeError("必须先调用fit()")
tokens = self._tokenize(sentence)
vec = np.zeros(self.dim)
for tok in tokens:
if tok in self.idf:
w = self.idf[tok]
# 正负哈希各一次,减少碰撞偏差
vec[self._hash_(tok, 0)] += w
vec[self._hash_(tok, 1)] -= w
# L2归一化
norm = np.linalg.norm(vec)
return vec / norm if norm > 0 else vec
def encode_batch(self, sentences):
return np.array([self.encode(s) for s in sentences])
def similarity(self, s1, s2):
"""计算两条指令的余弦相似度"""
v1, v2 = self.encode(s1), self.encode(s2)
return float(np.dot(v1, v2))
def demo():
# 训练语料(模拟机器人指令场景)
corpus = [
"拿起红色方块",
"放下蓝色圆柱",
"抓取红色方块",
"放置蓝色圆柱到桌面",
"移动到门口",
"走到窗边",
"打开抽屉",
"关闭抽屉",
"拿起蓝色方块",
"放下红色圆柱"
]
encoder = SimpleInstructionEncoder(dim=64)
encoder.fit(corpus)
# 测试语义相似性
test_pairs = [
("拿起红色方块", "抓取红色方块"), # 近义
("拿起红色方块", "拿起蓝色方块"), # 部分相似
("拿起红色方块", "打开抽屉"), # 不相关
("移动到门口", "走到窗边"), # 近义
]
print("=== 指令语义相似度测试 ===")
for s1, s2 in test_pairs:
sim = encoder.similarity(s1, s2)
print(f"{s1} <-> {s2}: {sim:.4f}")
# 验证嵌入维度固定
vec = encoder.encode("拿起红色方块")
print(f"\n嵌入维度: {vec.shape[0]} (固定长度)")
print(f"向量范数: {np.linalg.norm(vec):.4f} (L2归一化)")
if __name__ == "__main__":
demo()
运行与验证
- 环境要求:Python 3.6+,仅需NumPy(
pip install numpy) - 运行方式:将代码保存为
instruction_encoder.py,执行python instruction_encoder.py - 通过条件:观察输出,应满足:
- “拿起红色方块”与“抓取红色方块”的相似度明显高于与“打开抽屉”的相似度(通常差距 > 0.3)
- 所有输出向量的维度均为64,且L2范数为1.0(归一化)
- “移动到门口”与“走到窗边”的相似度高于与“拿起红色方块”的相似度
关键说明
- 编码器采用词袋+TF-IDF加权+哈希降维,无需外部预训练模型即可运行,适合教学演示
- 中文按单字切分、英文按单词切分,通过正负哈希降低碰撞影响
- 实际工程中可替换为BERT等预训练模型,但本案例聚焦于“语言→固定长度向量”的核心映射逻辑
- 相似度计算采用余弦相似度,值域[-1,1],越接近1表示语义越相近