藏语自然语言处理入门 - 3 找关键词
主题:用 TF-IDF 找“关键词”,顺手挑“关键句”
这一课要做什么?
- 把
sentences.txt
(一行一句)继续用 Botok 分词; - 用 TF-IDF 给词打“信息量分”,捞出关键词Top;
- 给每句加总一个分数,挑出关键句Top(可当小摘要参考)。
做完你会得到:
tfidf_keywords_top50.csv
:全局关键词Top50;key_sentences.txt
:关键句Top10(句子+分数);tfidf_terms_full.csv
:所有词的平均TF-IDF分(备用分析)。
一、什么是“TF-IDF”
- TF(Term Frequency):这个词在“当前这句/段”里出现几次?出现越多,越重要。
- IDF(Inverse Document Frequency):这个词在“整份语料”里稀不稀有?越稀有越重要(到处都有就不重要)。
- TF × IDF = TF-IDF 分数:分数越高,越像“关键词”。
小例子(藏语氛围):
ནས་
这类功能词几乎句句都有 → IDF≈0 → 再高的 TF 也乘成 0 → 不是关键词。བོད་ཡིག
、སློབ་ཚན
这种“内容词”并非处处都有 → IDF>0 → 有机会成关键词。
关键点:先分词靠谱(用 Botok),再配停用词表(把“粘合词”剔除),TF-IDF 才更准。
二、开箱即用脚本
确保已安装依赖:
pip install botok pandas regex scikit-learn
把下面保存为 tfidf_keywords_and_summary.py
,跟 sentences.txt
放一起:
# tfidf_keywords_and_summary.py
# 输入:
# sentences.txt —— 一行一句
# stopwords.txt(可选) —— 你修订后的停用词
# stopwords_suggest.txt(兜底) —— 第二课生成的候选停用词
# 输出:
# tfidf_keywords_top50.csv —— 关键词Top50(term, score)
# key_sentences.txt —— 关键句Top10(含分数)
# tfidf_terms_full.csv —— 全量词平均TF-IDF(备用)from pathlib import Path
import pandas as pd
import regex as re
from botok import WordTokenizer
from sklearn.feature_extraction.text import TfidfVectorizer
import numpy as npSENTS_FILE = "sentences.txt"
STOP_FINAL = Path("stopwords.txt")
STOP_SUGG = Path("stopwords_suggest.txt")# 1) 读句子
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 为空,请先完成第1/2课。")# 2) 停用词(优先用你修订后的)
if STOP_FINAL.exists():stopwords = {w.strip() for w in STOP_FINAL.read_text(encoding="utf-8").splitlines() if w.strip()}
elif STOP_SUGG.exists():stopwords = {w.strip() for w in STOP_SUGG.read_text(encoding="utf-8").splitlines() if w.strip()}
else:stopwords = set()
print(f"🧹 停用词载入:{len(stopwords)} 个")# 3) 分词:句子 → token 列表(过滤句末符与停用词)→ 用空格连接
wt = WordTokenizer()
def tokenize(sent: str):toks = [t.text for t in wt.tokenize(sent) if t.text and t.text.strip()]toks = [t for t in toks if not re.fullmatch(r"[།༎]+", t) and t not in stopwords]return toksseg_sents = [" ".join(tokenize(s)) for s in sents]# 4) TF-IDF:我们已经自己用空格切好词,所以 token_pattern 设“非空格”
vectorizer = TfidfVectorizer(token_pattern=r"[^ ]+",lowercase=False, # 藏文无大小写use_idf=True,smooth_idf=True, # 防止分母为0sublinear_tf=True # TF取对数,弱化重复词
)
X = vectorizer.fit_transform(seg_sents) # [句子数 × 词表大小]
terms = vectorizer.get_feature_names_out()# 5) 全局关键词Top:按“每个词在所有句子的平均TF-IDF”排序
tfidf_mean = np.asarray(X.mean(axis=0)).ravel()
df_terms = pd.DataFrame({"term": terms, "tfidf_mean": tfidf_mean}).sort_values("tfidf_mean", ascending=False)topN = min(50, len(df_terms))
df_terms.head(topN).to_csv("tfidf_keywords_top50.csv", index=False, encoding="utf-8")
df_terms.to_csv("tfidf_terms_full.csv", index=False, encoding="utf-8")
print(f"✅ tfidf_keywords_top50.csv(Top {topN})已生成")# 6) 关键句:把每句的 TF-IDF 向量沿词维度求和 = 句子“信息量分”
sent_scores = X.sum(axis=1).A.ravel()
rank = np.argsort(-sent_scores)
topK = min(10, len(sents))
lines = [f"[Top{i+1}] score={sent_scores[idx]:.4f}\n{sents[idx]}\n" for i, idx in enumerate(rank[:topK])]
Path("key_sentences.txt").write_text("\n".join(lines), encoding="utf-8")
print(f"✅ key_sentences.txt(Top {topK})已生成")
三、跑一下!
python tfidf_keywords_and_summary.py
如何读结果?
- 打开
tfidf_keywords_top50.csv
:前10个词大多就是“这组句子的主题词”。 - 打开
key_sentences.txt
:Top 关键句念一遍,像不像摘要?不满意就扩充/清洗语料或完善停用词再跑。
四、分析
-
为什么TF-IDF能找关键词?
因为它同时看这句里多不多(TF)和全库里稀不稀有(IDF);“处处都有”的词(功能词)IDF≈0,自然会被压下去。 -
为什么先做停用词?
把“粘合词”先过滤,关键词就更干净(ན་/ནས་/ལ་/དང་/ཀྱི་/…
这类)。 -
为什么要先分词?
不分词,机器只能按字符或固定长度切片,不符合藏语习惯,TF-IDF分会偏。 -
关键句怎么来的?
“把这一句里的词分数加起来”,谁的总分高,谁更代表主题(简单好用的基线)。
五、30秒自检清单
- 关键词Top里还有明显的功能词?→ 补到
stopwords.txt
再跑一次。 - 关键句Top读起来不像摘要?→ 文本太短/主题太杂/停用词不全,逐步改进。
- 分词怪怪的?→ 回第1课检查清洗是否到位(括注/页码/装饰符未清掉会干扰)。
六、10分钟小任务
任务A(必做):把 tfidf_keywords_top50.csv
里你不认可的词加入 stopwords.txt
;再跑脚本,观察Top是否更“像话”。
任务B(可选):把 key_sentences.txt
Top3 贴到讲义封面,当“本段要点”。
任务C(进阶):把语料按章节切两份,分别跑一遍,比较两个章节的 Top 关键词差异。