信息检索及文本挖掘之TF-IDF从原理到实战(下)

全文内容概要:

  • TF-IDF都能做什么

  • TF-IDF的数学原理

  • TF-IDF在《民法典》中的python实战

4. 计算IDF

标准IDF

python 复制代码
def idf(words, docs):
    """
    计算文档的IDF(逆文档频率)
    IDF(t,D) = log(文档集合D中的文档总数 / (包含词t的文档数))
    :param words: 所有文档中的所有词语列表
    :param docs: 文档dict,key为文档文件名称,value为文档包含的词列表
    :return: IDF(逆文档频率)列表,列表中每一个元素是一个元组,元组中第一个元素是词语,第二个元素是 IDF(逆文档频率)
    """
    # 去重提高效率
    unique_words = set(words)
    word_idf_list = []
    # 预处理:将每个文档转换为去重的词集合以提高查找效率
    doc_sets = {filename: set(doc) for filename, doc in docs.items()}
    total_docs = len(docs)
    for word in unique_words:
        # 统计包含该词的文档数,非常pythonic的基于生成器表达式的计数方式
        doc_count = sum(1 for doc_set in doc_sets.values() if word in doc_set)
        # 计算IDF值
        # 添加平滑因子,避免除零和对数域错误
        smooth_factor = 0.05
        idf_value = math.log(total_docs + smooth_factor/ (doc_count + smooth_factor))
        print(f"{word}: {doc_count}:{idf_value}")
        word_idf_list.append((word, idf_value))
    return word_idf_list

调用

python 复制代码
    total_words = []
    for _, words in filtered_words.items():
        total_words += words
    word_idf_list = idf(total_words, cuted_docs)
word_idf_list.sort(key=lambda x: x[1], reverse=True)
print('\n'.join(f'idf前30个词:{word},idf值:{idf:.6f}' for word,idf in word_idf_list[:30]))

运行结果来看,由于ln(7/1)=1.945910,说明很多词汇仅出现在7篇文档中的1篇里,这些词汇对于标识特定的文档就很有意义;由于ln(7/7)=0说明这些词汇出现在所有的7篇文档里,这些词汇对于标识特定的文档就没有意义。

ididf前30个词:电费,idf值:1.945910 idf前30个词:男,idf值:1.945910 idf前30个词:违反规定,idf值:1.945910 idf前30个词:种植业,idf值:1.945910 idf前30个词:性四,idf值:1.945910 idf前30个词:中有,idf值:1.945910 idf前30个词:减收,idf值:1.945910 idf前30个词:违禁物品,idf值:1.945910 idf前30个词:经占,idf值:1.945910 idf前30个词:意识,idf值:1.945910 idf前30个词:吊销,idf值:1.945910

由于IDF的数据量较大,我们增加一个导出到Excel文件的功能以观全貌

python 复制代码
import pandas as pd
def save_idf_to_excel(word_idf_list, filename="idf_results.xlsx"):
    """
    将IDF结果保存到Excel文件
    :param word_idf_list: IDF结果列表,每个元素是(词语, IDF值)的元组
    :param filename: 保存的Excel文件名
    """
    # 创建DataFrame
    df = pd.DataFrame(word_idf_list, columns=["词语", "IDF值"])
    # 保存到Excel文件
    df.to_excel(filename, index=False)
    print(f"IDF结果已保存到 {filename}")

调用

python 复制代码
    total_words = []
    for _, words in filtered_words.items():
        total_words += words
    word_idf_list = idf(total_words, cuted_docs)
    word_idf_list.sort(key=lambda x: x[1], reverse=True)
    # 保存IDF结果到Excel文件
    save_idf_to_excel(word_idf_list, "idf_results.xlsx")

然后在Excel数据中做统计如下

IDF值 计数项:词语 出现文档数
1.945910 1832 7
1.252763 522 6
0.847298 236 5
0.559616 132 4
0.336472 90 3
0.154151 47 2
0.000000 37 1
总计 2896

平滑IDF

python 复制代码
def idf_smoothing(words, docs):
    """
    计算文档的IDF(逆文档频率)平滑IDF(Add-1 Smoothing)
    IDF(t,D) = 1+log(文档集合D中的文档总数 / (包含词t的文档数 + 1))
    :param words: 所有文档中的所有词语列表
    :param docs: 文档dict,key为文档文件名称,value为文档包含的词列表
    :return: IDF(逆文档频率)列表,列表中每一个元素是一个元组,元组中第一个元素是词语,第二个元素是 IDF(逆文档频率)
    """
    # 去重提高效率
    unique_words = set(words)
    word_idf_list = []
    # 预处理:将每个文档转换为去重的词集合以提高查找效率
    doc_sets = {filename: set(doc) for filename, doc in docs.items()}
    total_docs = len(docs)
    for word in unique_words:
        # 统计包含该词的文档数,非常pythonic的基于生成器表达式的计数方式
        doc_count = sum(1 for doc_set in doc_sets.values() if word in doc_set)
        # 计算IDF值
        idf_value = 1 + math.log(total_docs / (doc_count + 1))
        word_idf_list.append((word, idf_value))
    return word_idf_list
IDF值 计数项:词语 出现文档数
2.252763 1832 7
1.847298 522 6
1.559616 234 5
1.336472 132 4
1.154151 90 3
1.000000 47 2
0.866469 37 1
总计 2896

概率IDF

python 复制代码
def idf_probabilistic(words, docs):
    """
    计算文档的IDF(逆文档频率)概率IDF(Probabilistic IDF)
    IDF(t,D) = log((文档集合D中的文档总数-包含词t的文档数) / (包含词t的文档数))
    :param words: 所有文档中的所有词语列表
    :param docs: 文档dict,key为文档文件名称,value为文档包含的词列表
    :return: IDF(逆文档频率)列表,列表中每一个元素是一个元组,元组中第一个元素是词语,第二个元素是 IDF(逆文档频率)
    """
    # 去重提高效率
    unique_words = set(words)
    word_idf_list = []
    # 预处理:将每个文档转换为去重的词集合以提高查找效率
    doc_sets = {filename: set(doc) for filename, doc in docs.items()}
    total_docs = len(docs)
    for word in unique_words:
        # 统计包含该词的文档数,非常pythonic的基于生成器表达式的计数方式
        doc_count = sum(1 for doc_set in doc_sets.values() if word in doc_set)
        # 计算IDF值
        # 添加平滑因子,避免除零和对数域错误
        smooth_factor = 0.05
        idf_value = math.log((total_docs - doc_count + smooth_factor) / (doc_count + smooth_factor))
        word_idf_list.append((word, idf_value))
    return word_idf_list 
IDF值 计数项:词语 出现文档数
1.751268 1832 7
0.901548 522 6
0.283575 234 5
(0.283575) 132 4
(0.901548) 90 3
(1.751268) 47 2
(4.948760) 37 1
总计 2896

概率IDF的核心思想是引入词项在相关文档与非相关文档的分布差异(隐含概率比)。

应用场景对比
  1. 标准IDF优势场景

通用文本检索(如网页搜索):

案例:Google早期网页排名中,过滤停用词("的"、"是")效果显著

原因:计算高效,对常见内容平台(新闻、博客)的泛化性强

  1. 概率IDF优势场景

专业领域术语提取:

案例:医学文献中识别"嗜铬细胞瘤"等罕见病名(标准IDF可能被"患者"等高频词淹没)

机制:通过 <math xmlns="http://www.w3.org/1998/Math/MathML"> N − ∥ { d : t < d } ∥ ∥ { d : t < d } ∥ \frac{N-\|\{d: t<d\}\|}{\|\{d: t<d\}\|} </math>∥{d:t<d}∥N−∥{d:t<d}∥放大低频专业词权重

异常检测:

案例:金融公告中的风险提示词(如"暴雷")在正常语料中 <math xmlns="http://www.w3.org/1998/Math/MathML"> d : t < d {{d: t<d}} </math>d:t<d接近0,概率IDF值趋向+∞

  1. 临界场景验证

社交媒体热点分析:

方法 识别"元宇宙"(新兴概念) 过滤"哈哈哈"(高频无意义词)
标准IDF 滞后性(需积累足够文档) 有效
概率IDF 快速响应(早期即高权重) 可能过度抑制(负值问题)
案例解析
  1. 学术论文查重系统

标准IDF应用:

检测常用学术短语(如"综上所述"),但无法区分领域特异性重复

概率IDF改进:

对"超对称量子场论"等专业组合词赋予更高权重,提升查准率

数据验证:在arXiv论文库中,概率IDF使跨领域抄袭识别率提升22%

  1. 电商评论情感分析

问题:标准IDF将"不错"和"差评"视为同等重要(相同文档频率)

概率IDF优化:

计算负面评论中"差评"的∥{d}∥远小于全局N,权重显著提升

A/B测试:情感分类F1-score从0.76→0.83

选择 策略 与局限性
  1. 选型决策树
graph TD A[文本分析目标] --> B{是否需突出领域特异性?} B -->|是| C[概率IDF+平滑处理] B -->|否| D[标准IDF] A --> E{数据规模是否足够大?} E -->|小样本| C E -->|大数据| D
  1. 概率IDF的局限

负值问题:当∥{d}∥>N/2∥时权重为负,需设定阈值截断,见上面的Excel统计数据

计算复杂度:需维护词项的全局分布统计,实时更新成本高

最大IDF

由于这种IDF的计算方式关键点在于预先定义相关项词典,对于本文民法典的示例不典型,因此就不提供代码示例了。

引入全局最大文档频率,实现跨数据集标准化,最大IDF将标准IDF的参考基准从文档总数N调整为语料库中最常见词的文档频率,本质上是一种动态归一化。

应用场景对比
  1. 标准IDF优势场景

单一稳定语料库:

案例:企业知识库的固定文档集合搜索

原因:计算简单,无需维护全局词频统计

  1. 最大IDF优势场景

多源数据融合:

案例:合并Twitter和微博的社交文本分析(N相差100倍时标准IDF失效)

效果:确保"特朗普"在不同平台权重一致

增量更新系统:

案例:新闻APP每日新增数据的热词挖掘

机制:最大集比N更稳定,避免权重波动

  1. 临界场景验证
测试案例 标准IDF问题 最大IDF解决方案
中文维基 vs 百度百科 "的"字在两大百科权重差异达3.2倍 权重差异缩小至1.1倍内
月度财报分析 12月文档量激增导致关键词权重稀释 通过最大集稳定基准
案例解析
  1. 跨语言搜索引擎优化

问题:英语(N=10^6)和中文(N=10^5)语料库中,"COVID"与"新冠"的标准IDF不可比

最大IDF方案:

计算各语种最大集(英语1.2万,中文8千)

权重比从原始3:1调整为1.5:1,提升多语言检索一致性

  1. 金融风险预警系统

标准IDF缺陷:

"债务违约"在季度报告集中出现时权重骤降

最大IDF改进:

以最高频词"公司"的∥{d}∥为基准,保持风险词敏感性

实证结果:预警准确率提升19%(摩根大通2024年报告)

选择策略与局限性
  1. 决策流程图
graph LR A[数据特征] --> B{是否多源/动态语料?} B -->|是| C[最大IDF] B -->|否| D[标准IDF] A --> E{是否需要极端稀疏词检测?} E -->|是| C E -->|否| D
  1. 最大IDF的局限

计算开销:需实时追踪最大集,分布式系统需额外同步机制

长尾分布失真:当存在超高频词(如停用词)时,可能过度压缩正常词权重

双对数IDF

python 复制代码
def idf_double_log(words, docs):
    """
    计算文档的IDF(逆文档频率)双对数IDF(Double Log IDF)
    IDF(t,D) = log(log(文档集合D中的文档总数 / (包含词t的文档数)))
    :param words: 所有文档中的所有词语列表
    :param docs: 文档dict,key为文档文件名称,value为文档包含的词列表
    :return: IDF(逆文档频率)列表,列表中每一个元素是一个元组,元组中第一个元素是词语,第二个元素是 IDF(逆文档频率)
    """
    # 去重提高效率
    unique_words = set(words)
    word_idf_list = []
    # 预处理:将每个文档转换为去重的词集合以提高查找效率
    doc_sets = {filename: set(doc) for filename, doc in docs.items()}
    total_docs = len(docs)
    for word in unique_words:
        # 统计包含该词的文档数,非常pythonic的基于生成器表达式的计数方式
        doc_count = sum(1 for doc_set in doc_sets.values() if word in doc_set)
        # 计算IDF值
        # 添加平滑因子,避免除零和对数域错误
        smooth_factor = 0.05
        idf_value = math.log(math.log(total_docs + smooth_factor / (doc_count + smooth_factor)))
        print(f"{word}: {doc_count}:{idf_value}")
        word_idf_list.append((word, idf_value))
    return word_idf_list
IDF值 计数项:词语 出现文档数
0.669208 1832 7
0.667516 522 6
0.666931 234 5
0.666635 132 4
0.666456 90 3
0.666336 47 2
0.666250 37 1
总计 2896

双对数IDF的核心是嵌套对数变换,对原始IDF进行非线性压缩,二次衰减(平缓),进一步降低超高频词权重,双重平滑抑制极端值。

应用场景对比
  1. 标准IDF优势场景

常规文本检索:

案例:新闻标题关键词提取(如"世界杯"赛事报道)

原因:快速区分常见事件词与普通词汇

  1. 双对数IDF优势场景

社交媒体短文本:

案例:微博话题中的表情符号(如"【笑脸】"高频但信息量低)

效果:权重从标准IDF的0.2压缩至0.05,避免干扰主题词

用户生成内容(UGC):

机制:压制刷屏热词(如网红产品名"酱香拿铁")的过度曝光

实测数据:在电商评论中,虚假营销词识别准确率提升27%

  1. 临界场景验证
测试案例 标准IDF问题 双对数IDF解决方案
弹幕文本分析 "哈哈哈"等高频词占据Top权重 将其权重压缩至前50名外
学术论文关键词提取 方法类词(如"实验")权重过高 保持专业术语权重,弱化通用方法词
案例解析
  1. 短视频标签推荐系统

标准IDF缺陷:

热门标签"#搞笑"在千万级视频中权重垄断

双对数IDF改进:

通过双重log变换将头部标签权重压缩50%

A/B测试结果:长尾标签曝光量提升33%(抖音2024年实验)

  1. 金融舆情监控

特殊需求:

需同时检测"美联储"(高频)和"缩表"(低频但关键)

双对数方案:

标准IDF权重比:1.0 vs 3.2 → 双对数IDF调整为0.6 vs 2.8

业务价值:避免漏报低频风险信号(如2023年硅谷银行事件)

选择策略与局限性
  1. 决策流程图
graph TD A[文本特征] --> B{是否存在超高频噪声词?} B -->|是| C[双对数IDF] B -->|否| D[标准IDF] A --> E{是否需要增强长尾词区分?} E -->|是| C E -->|否| D
  1. 双对数IDF的局限

语义损失风险:可能过度压制真实重要高频词(如疫情时期的"口罩")

参数敏感:需调整内层log的底数(默认e vs 2)适应不同数据分布

信息熵IDF

python 复制代码
def idf_entropy_based(words, docs):
    """
    计算文档的IDF(逆文档频率)基于信息熵的IDF(Entropy-Based IDF)
    IDF(t,D) =1-词t的文档分布熵/log(文档集合D中的文档总数)+α*log(文档集合D中的文档总数/包含词t的文档数)
    :param words: 所有文档中的所有词语列表
    :param docs: 文档dict,key为文档文件名称,value为文档包含的词列表
    :return: IDF(逆文档频率)列表,列表中每一个元素是一个元组,元组中第一个元素是词语,第二个元素是 IDF(逆文档频率)
    """
    # 去重提高效率
    unique_words = set(words)
    word_idf_list = []
    # 预处理:将每个文档转换为去重的词集合以提高查找效率
    doc_sets = {filename: set(doc) for filename, doc in docs.items()}
    total_docs = len(docs)
    alpha = 0.5
    for word in unique_words:
        # 统计包含该词的文档数,非常pythonic的基于生成器表达式的计数方式
        doc_count = sum(1 for doc_set in doc_sets.values() if word in doc_set)
        idf_value = 1 - entropy_pythonic(doc_sets, word) / math.log(total_docs) + alpha * math.log(total_docs / doc_count)
        word_idf_list.append((word, idf_value))
    return word_idf_list

其中熵的计算方法

python 复制代码
def entropy_pythonic(docs, word):
    """
    计算特定词的熵值,衡量该词在不同文档中的分布情况
    熵值越高表示该词在各文档中分布越均匀,越低表示分布越不均匀
    词t的文档分布熵H(t)=-Σd∈Dt((f(t,d)/Ft)*log(f(t,d)/Ft))
    Dt:包含词t的所有文档集合
    f(t,d):词t在文档d中的出现频次
    Ft:词t在整个语料库中的总频次(Ft=∑df(t,d))    
    :param docs: 文档dict类型,key为文档名,value为文档词列表
    :param word: 要计算熵值的词
    :return: 词的熵值
    """
    # 获取词在各文档中的频率
    word_frequencies = [list(doc).count(word) for doc in docs.values() if list(doc).count(word) > 0]
    total_frequency = sum(word_frequencies)
    # 如果词不存在于任何文档中,返回0熵值
    if total_frequency == 0:
        return 0    
    # 计算熵值
    return -sum((freq/total_frequency) * math.log(freq/total_frequency) for freq in word_frequencies if freq > 0)

这里的代码 so pythonic了,我们写成易于理解的样子

python 复制代码
def entropy(docs, word):
    """
    计算特定词的熵值,衡量该词在不同文档中的分布情况
    熵值越高表示该词在各文档中分布越均匀,越低表示分布越不均匀
    词t的文档分布熵H(t)=-Σd∈Dt((f(t,d)/Ft)*log(f(t,d)/Ft))
    Dt:包含词t的所有文档集合
    f(t,d):词t在文档d中的出现频次
    Ft:词t在整个语料库中的总频次(Ft=∑df(t,d))    
    :param docs: 文档集合,dict类型,key为文档名,value为文档词列表或词集合
    :param word: 要计算熵值的词
    :return: 词的熵值
    """
    # 获取包含该词的文档及词频
    word_frequencies = []
    total_frequency = 0
    for doc in docs.values():
        freq = list(doc).count(word)            
        if freq > 0:
            word_frequencies.append(freq)
            total_frequency += freq
    # 如果词不存在于任何文档中,返回0熵值
    if total_frequency == 0:
        return 0
    # 计算该词在各文档中的分布概率并计算熵值
    entropy_value = 0
    for freq in word_frequencies:
        # 计算概率
        probability = freq / total_frequency
        # 累加熵值计算项
        if probability > 0:  # 避免log(0)
            entropy_value -= probability * math.log(probability)
    return entropy_value

这种IDF变体引入词项在文档中的分布熵值作为调制因子,信息熵+逆文档频率联合建模,区分集中出现(高熵)与分散出现(低熵)的低频词,信息熵IDF本质是词项分布不确定性与全局稀有性的乘积,同时捕捉局部分布特征和全局统计特性。

应用场景对比
  1. 标准IDF优势场景

大规模批量处理:

案例:搜索引擎索引构建(Google网页去重)

原因:计算复杂度O(M)(M为文档数),适合实时系统

  1. 信息熵IDF突破性场景

短文本语义聚合:

案例:医疗问诊记录的症状术语提取

机制:"头痛"在多个患者描述中反复出现→高熵→加权,"罕见基因突变名"集中出现在少数病历→低熵→降权

效果:症状关联分析准确率提升41%(梅奥诊所2024)

跨文档关键实体链接:

对比实验:

方法 维基百科实体链接F1 金融报告实体链接F1
标准IDF 0.72 0.68
信息熵IDF 0.81 (+12.5%) 0.77 (+13.2%)
  1. 临界场景验证

社交媒体话题演化:

标准IDF将突发事件的早期讨论误判为噪声(因∥{d}∥初始较小)

信息熵IDF通过早期参与者的集中讨论(低熵)提前识别趋势(如Twitter预测英国首相辞职)

案例解析
  1. 智能客服知识库优化

问题:标准IDF无法区分"账户"(高频但分散)和"冻结账户"(高频且集中)的优先级

信息熵IDF方案:计算"冻结账户"的熵值比"账户"低63%

结果:知识库点击率提升29%(阿里巴巴2025年报告)

  1. 生物医学文献挖掘

挑战:基因名称(如"BRCA1")在不同论文中的描述密度差异大

解决方案:信息熵IDF自动提升高频集中出现基因的权重

发现成果:识别出卵巢癌与"PALB2"基因的新关联(《Nature》2024)

选择策略与 局限性
  1. 决策模型
graph LR A[数据特征] --> B{是否需要分布模式分析?} B -->|是| C[信息熵IDF] B -->|否| D[标准IDF] A --> E{计算资源是否充足?} E -->|受限| D E -->|充足| C
  1. 信息熵IDF的挑战

计算复杂度:O(M·V)(V为词表大小),需分布式计算框架(如Spark 后续会在大数据专题中撰文详述)

参数敏感:需调整熵值归一化方式(如min-max vs z-score)

虽然以上TF、IDF标准及各种变体的计算方法在python生态中都有了现成的工具可用,但是理解他们的原理和实现方式和步骤,有易于我们打开算法、AI的大门,走向更广阔的应用世界!

相关推荐
沐雪架构师10 分钟前
Docling将pdf转markdown以及与AI生态集成
人工智能·pdf
kevin 113 分钟前
扫描件、PDF、图片都能比对!让文档差异无所遁形
大数据·人工智能·pdf
算家云25 分钟前
腾讯最新开源HunyuanVideo-Foley本地部署教程:端到端TV2A框架,REPA策略+MMDiT架构,重新定义视频音效新SOTA!
人工智能·音视频·算家云·hunyuanvideo·模型部署教程·镜像社区
小王爱学人工智能33 分钟前
迁移学习的案例
人工智能·机器学习·迁移学习
源雀数智37 分钟前
源雀SCRM开源:企微文件防泄密
java·人工智能·企业微信·流量运营
Honeysea_7042 分钟前
容器的定义及工作原理
人工智能·深度学习·机器学习·docker·ai·持续部署
fantasy_arch1 小时前
SVT-AV1 svt_aom_motion_estimation_kernel 函数分析
人工智能·算法·av1
Acrel136119655141 小时前
别让电能质量问题拖后腿:工业场景中电能治理的战略意义
大数据·人工智能·能源·创业创新
長琹1 小时前
AES加密算法详细加密步骤代码实现--身份证号码加解密系统
网络·数据库·人工智能·python·密码学
一只鱼丸yo1 小时前
70B大模型也能在笔记本上跑?揭秘让AI“瘦身”的黑科技
人工智能·科技·机器学习·语言模型