tf-idf计算需确保idf分母为含该词的文档数而非总文档数,tf用词频除以文档总词数,idf用log(总文档数/含词文档数),未登录词idf设为log(总文档数+1);中文需先分词再过滤停用词,向量用float但防下溢,领域适配需构建专用停用词表并加权长度。

TF-IDF 的核心计算逻辑怎么写才不翻车
直接用 std::map 统计词频没问题,但 IDF 部分最容易错在分母——不是文档总数,而是包含该词的文档数。漏掉这一步,所有权重都会偏高,关键词排序失真。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 先用
std::set<:string></:string>记录每个文档中出现过的词(去重),再用std::map<:string int></:string>累加“含该词的文档数” - TF 用词频 / 文档总词数(非唯一词数),避免长文档天然占优
- IDF 公式必须是
log(总文档数 / 含该词文档数),别用自然对数或底数 10 混用,保持一致性 - 遇到未登录词(训练时没出现过的词),IDF 不能硬设为 0,应设为
log(总文档数 + 1)防止除零和权重坍缩
中文文本预处理绕不开的三个坎
C++ 没有现成分词库,硬上 std::string::find 拆空格只会提取出“的”“了”“和”这种停用词,关键词全跑偏。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 至少做基础清洗:用
std::regex_replace去除标点、数字、空白符,保留中文字符和英文字母([^\u4e00-\u9fa5a-zA-Z]) - 停用词表必须外置为文件(如
"stopwords.txt"),逐行读入std::unordered_set<:string></:string>,别硬编码 - 单字过滤要谨慎——“云”“端”“智”单独看是停用词,合起来“云计算”就是关键,所以分词必须前置;实在没分词库,先用开源小模型(如 TinySeg)导出词表,C++ 只做匹配
vector 存 TF-IDF 向量时内存和精度怎么平衡
用 float 能省一半内存,但累加几十个文档的 TF-IDF 值后,小权重项可能下溢成 0,尤其 IDF 接近 0 的常见词。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 词向量维度建议限制在 5000 以内,按 IDF 降序截断,别把所有词都塞进去
- 初始化向量用
std::vector<float>(vocab_size, 0.0f)</float>,别用double后强转,C++ 模板推导容易隐式转换出问题 - 如果后续要算余弦相似度,提前把向量单位化(除以 L2 范数),避免每次重复开方;范数计算用
std::sqrt(std::accumulate(...)),别手写循环漏平方
为什么你的 top-k 关键词总是“用户”“系统”“功能”
不是算法错了,是没做词性过滤或领域适配。通用停用词表对技术文档完全不够用,“接口”“调用”“实例”在开发日志里是高频关键,但在新闻语料里就是噪音。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 构建领域停用词表:扫描一批目标文档,统计
TF * IDF值最低的 200 个词,人工筛一遍加入"domain_stopwords.txt" - 关键词长度加权:对长度 ≥ 3 的词,TF-IDF 值乘 1.2;单字词强制 × 0.5,抑制“的”“是”干扰
- 输出前做局部归一化:取每个文档内 top-20 词的 TF-IDF 最大值,所有值除以它,再排序,能缓解文档长度差异导致的绝对值偏差
真正卡住效果的,往往不是公式实现,而是中文切分粒度和领域停用词的边界感——前者决定你能看到什么,后者决定你信什么。









