藏语自然语言处理入门 - 4 找相似的句子
句向量与相似度:给一句话找“近义例句”
本节要做的三件小事
- 把
sentences.txt
里的每句话变成一个“向量”(一串数字)。 - 用“余弦相似度”比较两句话有多像。
- 做一个Top-K 例句检索小工具:输入一句,返回最接近的几句。
做完你会得到:
embeddings.npy
:每句的句向量(后续可复用)。similar_examples.py
:给定句子,找 Top-K 相似例句的脚本。sim_results.txt
(示例输出):你查过的结果记录。
小贴士:本课需要下载一个多语句向量模型(体积较大)。如果当前环境网络不方便,可先看懂流程与代码,改天在网络好时跑一下。
0. 准备
pip install sentence-transformers numpy pandas
模型选择(二选一):
- 更准:
sentence-transformers/LaBSE
(覆盖语种多,含藏语;体积较大) - 更轻(可临时用):
paraphrase-multilingual-MiniLM-L12-v2
(更小更快,但对藏语可能略弱)
我会在代码里给出
MODEL_NAME
,默认用 LaBSE;你也可以换成 MiniLM 版本试试。
1) 一次性向量化所有句子
把下面保存为 build_embeddings.py
,放在和 sentences.txt
同目录下运行。
# build_embeddings.py
# 功能:读取 sentences.txt -> 计算句向量 -> 保存 embeddings.npy 和 sentences_index.csvfrom pathlib import Path
import numpy as np
from sentence_transformers import SentenceTransformerSENTS_FILE = "sentences.txt"
MODEL_NAME = "sentence-transformers/LaBSE" # 或 "paraphrase-multilingual-MiniLM-L12-v2"def normalize(vectors: np.ndarray) -> np.ndarray:# L2 归一化,便于用点积近似余弦相似度norms = np.linalg.norm(vectors, axis=1, keepdims=True) + 1e-12return vectors / normsdef main():sents = [s.strip() for s in Path(SENTS_FILE).read_text(encoding="utf-8").splitlines() if s.strip()]if not sents:raise SystemExit("❌ sentences.txt 为空,请先完成前几课。")print(f"📦 加载模型:{MODEL_NAME}")model = SentenceTransformer(MODEL_NAME)print(f"🔢 计算向量(共 {len(sents)} 句)...")emb = model.encode(sents, batch_size=32, convert_to_numpy=True, normalize_embeddings=True)# 若你的模型没有自带 normalize_embeddings,可以改为: emb = normalize(emb)np.save("embeddings.npy", emb)Path("sentences_index.csv").write_text("\n".join(sents), encoding="utf-8")print("✅ 已保存:embeddings.npy / sentences_index.csv")if __name__ == "__main__":main()
运行:
python build_embeddings.py
输出文件:
embeddings.npy
:形状约为[句子数, 向量维度]
;sentences_index.csv
:和向量同序的原始句子。
2) 近义例句检索
把下面保存为 similar_examples.py
。它会读取上一步的 embeddings.npy
和 sentences_index.csv
,然后对你输入的句子做检索。
# similar_examples.py
# 用法示例:
# python similar_examples.py --q "ཁྱེད་ལ་བཀྲ་ཤིས།" --k 5
# 输出:Top-K 相似句及相似度import argparse
import numpy as np
from pathlib import Path
from sentence_transformers import SentenceTransformer, utilMODEL_NAME = "sentence-transformers/LaBSE" # 或 "paraphrase-multilingual-MiniLM-L12-v2"def load_corpus():sents = [s.strip() for s in Path("sentences_index.csv").read_text(encoding="utf-8").splitlines() if s.strip()]emb = np.load("embeddings.npy")if len(sents) != emb.shape[0]:raise SystemExit("❌ 句子数量与向量行数不一致,请重新构建。")return sents, embdef main():parser = argparse.ArgumentParser()parser.add_argument("--q", type=str, required=True, help="查询句子(藏文)")parser.add_argument("--k", type=int, default=5, help="返回前K条")args = parser.parse_args()sents, emb = load_corpus()print(f"📦 加载模型:{MODEL_NAME}")model = SentenceTransformer(MODEL_NAME)# 编码查询句(已做归一化,余弦相似度=点积)q_emb = model.encode([args.q], convert_to_numpy=True, normalize_embeddings=True)[0]# 相似度(向量点积)并排序scores = emb @ q_emb # shape: [N]idx = np.argsort(-scores)[:args.k]lines = []for rank, i in enumerate(idx, 1):lines.append(f"[Top{rank}] score={scores[i]:.4f}\n{sents[i]}\n")out = "\n".join(lines)Path("sim_results.txt").write_text(out, encoding="utf-8")print(out)print("✅ 结果已保存:sim_results.txt")if __name__ == "__main__":main()
试运行:
python similar_examples.py --q "ཁྱེད་ལ་བཀྲ་ཤིས།" --k 5
你会看到最接近的 5 句(通常是各种问候、祝福类句子)。
3) 代码“人话讲解”
- 句向量:把一句话翻译成一个“坐标点”(比如 768 维),相似的句子会靠得更近。
- 归一化:把向量长度调成 1,这样点积≈余弦相似度,计算更快。
- 相似度排序:对所有句子算分数,最高的前 K 条就是“近义例句”。
- 缓存:向量只算一次保存,下次直接用,速度飞起。
4) 自检清单
-
embeddings.npy
和sentences_index.csv
一一对应(行数要相等)。 - 查询的句子是藏文 Unicode(不是图片/不是转写)。
- 返回的 Top 结果读起来确实接近(同一话题、同一语气)。
5) 小任务
任务A(必做):把 similar_examples.py
包一层简单函数,做个**“课堂查询”**:
- 输入:一个词或短句;
- 输出:Top 5 相似句;
- 额外:把相似度 < 0.5 的结果过滤掉(避免“牵强相似”)。
任务B(可选):做一个**“去重”**小规则:如果返回的句子同一行在原文里重复多次,只保留一次(可以按句子文本去重)。
任务C(可选):把 --q
改成从文件读取(比如 queries.txt
每行一个查询),一次性批量生成结果。
6) 常见小坑(快速排雷)
-
模型下载太大/太慢?
先跑小模型paraphrase-multilingual-MiniLM-L12-v2
验证流程;等网络好再换回 LaBSE 做最终检索。 -
结果不稳定?
语料太短时很正常。多加几段同主题的句子,或先回第1–3课把清洗/停用词做好。 -
中文/英文混在里面?
句向量是多语的,一般也能“感觉”相似,但这门课我们以藏文主语料为主,更稳。 -
返回了一些“太像的句子”(几乎重复)?
检索前先对sentences.txt
去重;或者在返回阶段做“文本去重”。
7) 本课小结
- 我们把“句子 → 向量 → 相似度”这一条链路跑通了;
- 以后你可以把这套向量直接复用到:聚类、主题分组、跨语检索;
- 这一步是“让机器感受语义”的关键基础。