spark TF-IDF特征提取生成文章关键词

本文详细介绍了TF-IDF算法的基本原理及其在文本特征提取中的应用,通过具体实例展示了如何利用Spark ML库实现TF-IDF算法,包括数据预处理、特征向量化等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

TF-IDF

TF-IDF(term frequency–inverse document frequency)中文"词频-逆向文件频率",通过它将文本特征向量化,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。

词频(TF)表示某关键词在文本中出现次数:

T F = 某单词在文档中出现次数 文档中所有单词出现次数总和 TF = \dfrac{某单词在文档中出现次数}{文档中所有单词出现次数总和} TF=文档中所有单词出现次数总和某单词在文档中出现次数

逆向文件频率(IDF)是一个词语普遍重要性的度量:

I D F = l o g 文档总数 n 包含单词 w 的文档数目 + 1 IDF = log\dfrac{文档总数n}{包含单词w的文档数目+1} IDF=log包含单词w的文档数目+1文档总数n

其中加1是为了避免分母为0

T F − I D F = T F ∗ I D F TF-IDF=TF*IDF TFIDF=TFIDF

在n个文档中,如果一个单词在文档 D i D_i Di中出现次数比较多,而很少出现在其它的文档中,那么TF值越大,IDF值越大,单词就更能作为文档 D i D_i Di的关键词。

数据准备

可以到http://qwone.com/~jason/20Newsgroups/获取一些外国的英文新闻内容。为了比较好观察运行结果,随便从百度找了几篇新闻,以新闻标题为文件名称:
这里写图片描述

测试代码

    val conf = new SparkConf().setMaster("local[2]").setAppName("TF_IDF_NEWS")
    var sc = new SparkContext(conf);
    val sqlContext = new SQLContext(sc)

    val path = "G:\\news\\*"
    val rdd = sc.wholeTextFiles(path) //读取目录下所有文件:[(filename1:content1),(filename2:context2)]
    val filename = rdd.map(_._1); //保存所有文件名称:[filename1,filename2],为输出结果做准备

    import scala.collection.JavaConverters._
    val stopWords = sc.textFile("zh-stopWords.txt").collect().toSeq.asJava //构建停词

    val filter = new StopRecognition().insertStopWords(stopWords) //过滤停词
    filter.insertStopNatures("w", null, "null") //根据词性过滤
    val splitWordRdd = rdd.map(file => { //使用中文分词器将内容分词:[(filename1:w1 w3 w3...),(filename2:w1 w2 w3...)]
      val str = ToAnalysis.parse(file._2).recognition(filter).toStringWithOutNature(" ")
      (file._1, str.split(" "))
    })
    val df = sqlContext.createDataFrame(splitWordRdd).toDF("fileName", "words");

    //val tokenizer = new org.apache.spark.ml.feature.RegexTokenizer().setInputCol("context").setOutputCol("words").setMinTokenLength(3)
    //val words = tokenizer.transform(df)
    //val stopWordsRemover = new org.apache.spark.ml.feature.StopWordsRemover().setInputCol("words").setOutputCol("stopWords");
    //val stopWords = stopWordsRemover.transform(df);
    //stopWords.select("stopWords").show(10, 200)
    val hashingTF = new org.apache.spark.ml.feature.HashingTF()
      .setInputCol("words").setOutputCol("rawFeatures").setNumFeatures(200000)
    val documents = hashingTF.transform(df)
    val idf = new org.apache.spark.ml.feature.IDF().setInputCol("rawFeatures").setOutputCol("features")
    val idfModel = idf.fit(documents)
    val idfData = idfModel.transform(documents)
    var wordMap = df.select("words").rdd.flatMap { row => //保存所有单词以及经过hasingTf后的索引值:{单词1:索引,单词2:索引...}
      {
        row.getAs[Seq[String]](0).map { w => (hashingTF.indexOf(w), w) }
      }
    }.collect().toMap
    val keyWords = idfData.select("features").rdd.map { x =>
      {
        val v = x.getAs[org.apache.spark.ml.linalg.SparseVector](0)//idf结果以稀疏矩阵保存
        v.indices.zip(v.values).sortWith((a, b) => { a._2 > b._2 }).take(10).map(x => (wordMap.get(x._1).get, x._2))//根据idf值从大到小排序,取前10个,并通过索引反查到词
      }
    } //[(文章1的关键词索引1:tf-idf值,文章1的关键词索引2:tf-idf值),(文章n的关键词索引1:tf-idf值,文章n的关键词索引2:tf-idf值)...],每组()表示一个新闻的关键词

    filename.zip(keyWords).collect().foreach(x => {
      println(x._1)
      x._2.foreach(x => println(x._1 + ":" + x._2 + " "))
    })
 

代码基于spark2.1.0中的ml库api进行编写,使用ansj进行对中文内容进行分词

  1. 通过wholeTextFiles读取目录下所有的文件,本次的试验文件点击,或者随便从网上复制几个新闻
  2. 分别对每个文件的内容使用ansj进行分词,并把一些常用的中文停词过滤,还根据词性过滤了标记符号和空值,最终得到每个文档对应的独立字词
  3. 将rdd转换成DataFrame后,通过HashingTF计算词频,同时会得用哈稀转化成固定长度的特征向量
  4. 构建IDFModel 并接收由HashingTF产生的特征向量,得到每个词(索引值表示)的TF-IDF值
  5. 由于在结果中,词已经被数值化,用索引值来表示,所以需要根据索引值反射获得原始的词,需要把索引值与词的对应关系保存到wordMap中,hashingTF.indexOf即是计算词的索引值,这个方法在ml.HashingTF中是不存在的,这里稍微修改了原始类,参考https://github.com/apache/spark/pull/18736
  6. 最后根据计算得到的tf-idf值进行排序,获取最大的前10个词并输出

运行结果

file:/G:/news/世界杯后的中超怎么看?恒大有了新玩法
球员:10.995488782489861 
队:10.079198050615705 
预备队:8.246616586867397 
奖金:7.330325854993241 
球队:7.330325854993241 
一线:6.414035123119086 
恒:5.497744391244931 
处罚:5.497744391244931 
表现:5.497744391244931 
下放:4.5814536593707755 
file:/G:/news/人工智能为什么要从本科生抓起?
ai:83.38245660054811 
人工智能:17.409523905608946 
本科:15.576942441860638 
人才:15.576942441860638 
研究:12.828070246238171 
发展:10.995488782489861 
本科生:10.995488782489861 
专业:10.216512475319814 
科学:10.079198050615705 
国内:9.162907318741551 
file:/G:/news/农业农村部猪价将处下降通道 养殖户需合理安排
新闻:4.5814536593707755 
农村:4.5814536593707755 
农业:4.5814536593707755 
市场:4.5814536593707755 
部:4.5814536593707755 
生猪:3.6651629274966204 
减:3.6651629274966204 
同比:3.6651629274966204 
价:3.6651629274966204 
唐:3.6651629274966204 
file:/G:/news/欧盟《通用数据保护条例》影响医疗卫生领域
医疗:11.911779514364017 
患者:9.162907318741551 
gdpr:8.246616586867397 
收集:5.497744391244931 
保健:4.5814536593707755 
信息:4.086604990127926 
安全:3.6651629274966204 
欧盟:3.6651629274966204 
了解:2.7488721956224653 
使:2.7488721956224653 

由于选取的几个新闻都是类型区别比较大,所以最终得到的关键词还是有比较明显的区别。分词不是十分准确,一些过滤规则应该根据业务做相应的调整。后面可以结合余弦相似性来判断不同文档的相似性来做自动分类。

参考

https://www.cntofu.com/book/85/ml/clean-feature/spark-fextract.md
https://blog.youkuaiyun.com/jiangpeng59/article/details/52786344
http://dblab.xmu.edu.cn/blog/1261-2/

<think>首先,用户的问题是:“我想使用TF-IDF算法提取大规模中文文本中的关键词 请问如何使用TF-IDF算法提取大规模中文文本中的关键词”。我需要基于提供的引用内容来回答。 关键点: - 用户想用TF-IDF提取大规模中文文本的关键词- 引用内容涉及TF-IDF的定义、优缺点、以及在Python中的实现(如sklearn和jieba)。 系统级指令: - 所有行内数学表达式用$...$格式。例如,TF-IDF公式:$TF(t,d) = \frac{f_{t,d}}{\sum_{t' \in d} f_{t',d}}$ 和 $IDF(t,D) = \log \frac{|D|}{|\{d \in D: t \in d\}|}$。 - 独立公式用$$...$$格式并单独成段。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾添加引用标识,如[^1]。 - 回答结构清晰,逐步解决问题。 - 参考站内引用,但用户说“上述引用仅供参考”,所以我应该基于一般知识,但尽量融入引用内容。 结构化回答: 1. **介绍TF-IDF算法**:简要解释TF-IDF是什么,基于引用[^1]和[^3]。 2. **步骤使用TF-IDF提取关键词**: - 预处理中文文本:分词(如使用jieba),参考引用[^4]。 - 计算TF-IDF值:使用库如sklearn或jieba的TF-IDF实现。 - 处理大规模文本:提到如何应对矩阵太大问题,参考引用[^2]和[^3]。 - 提取关键词:基于TF-IDF分数选择关键词。 3. **实现示例**:提供Python代码片段,使用jieba或sklearn。 4. **优缺点**:基于引用[^3]和[^5],讨论TF-IDF的局限性。 5. **生成相关问题**:在末尾添加相关问题。 数学表达式: - TF公式:$TF(t,d) = \frac{\text{词t在文档d中的出现次数}}{\text{文档d的总词数}}$ - IDF公式:$IDF(t,D) = \log \frac{\text{语料库中文档总数}}{\text{包含词t的文档数}}$ - TF-IDF公式:$TFIDF(t,d,D) = TF(t,d) \times IDF(t,D)$ 大规模文本处理: - 引用[^2]提到文本太大时矩阵放不下,建议使用增量计算或分布式处理。 - 在实现中,使用sklearn的TfidfVectorizer,它可以处理稀疏矩阵,适合大规模数据。 中文特定处理: - 使用jieba进行分词,参考引用[^4]。 代码示例: - 使用jieba和sklearn实现一个简单的TF-IDF关键词提取。 相关问题生成- 基于TF-IDF的扩展、优化、应用等。 最终回答结构: - 引言:解释TF-IDF- 步骤分解。 - 代码示例。 - 优缺点。 - 相关问题。</think>### 使用TF-IDF算法提取大规模中文文本关键词的指南 TF-IDF(Term Frequency–Inverse Document Frequency)是一种统计方法,用于评估词语在文档集中的重要性:词频(TF)衡量词语在单个文档中的出现频率,而逆文档频率(IDF)衡量词语在整个语料库中的稀有程度。TF-IDF值越高,表示该词语越能代表文档的关键内容[^1]。对于大规模中文文本,处理时需要特别注意分词、矩阵稀疏性和计算效率。下面我将逐步解释如何使用TF-IDF算法提取关键词,并提供实现示例。 #### 1. **理解TF-IDF算法公式** TF-IDF的计算基于两个核心组件: - **词频(TF)**:表示词语 $t$ 在文档 $d$ 中的出现频率,公式为: $$TF(t,d) = \frac{f_{t,d}}{\sum_{t' \in d} f_{t',d}}$$ 其中 $f_{t,d}$ 是词语 $t$ 在文档 $d$ 中的出现次数,分母是文档 $d$ 的总词数(归一化处理,避免长文档偏差)[^3]。 - **逆文档频率(IDF)**:表示词语 $t$ 在整个语料库 $D$ 中的稀有程度,公式为: $$IDF(t,D) = \log \frac{|D|}{|\{d \in D: t \in d\}|}$$ 其中 $|D|$ 是语料库中文档总数,分母是包含词语 $t$ 的文档数。IDF值越大,词语区分能力越强[^3]。 - **TF-IDF值**:最终得分为 $TFIDF(t,d,D) = TF(t,d) \times IDF(t,D)$。高分词语被视为关键词。 在大规模中文文本中,TF-IDF能有效识别重要词语(如高频但跨文档稀有的词),但需处理中文分词和计算效率问题[^4][^5]。 #### 2. **步骤分解:如何提取关键词** 针对大规模中文文本(如新闻语料库),遵循以下步骤: - **步骤1: 数据预处理(中文分词)** - 中文文本必须先分词,因为词语是基本单位。使用工具如 **jieba分词**(高效且支持新词识别),将文本切分为词语序列。 - 示例:文档“清华大学在北京” → 分词为 `["清华大学", "在", "北京"]`(精确模式)[^4]。 - **大规模处理技巧**:分批处理文本,避免内存溢出。例如,将语料库划分为小批量(如每批10万文档),并使用分布式框架(如Spark)加速。 - **步骤2: 构建语料库和计算TF-IDF** - 使用Python库(如scikit-learn或jieba内置TF-IDF)计算TF-IDF值。关键工具是 `TfidfVectorizer`(scikit-learn),它自动处理TF-IDF计算,并支持稀疏矩阵存储(节省内存)[^2][^4]。 - **IDF计算优化**:对于大规模语料,IDF需要全局文档频率统计。采用增量学习:先扫描所有文档统计IDF,再分批计算TF-IDF矩阵。引用[^2]提到,这可以避免矩阵过大问题。 - 注意:IDF计算依赖整个语料库,确保文档频率信息准确。 - **步骤3: 提取关键词** - 对每个文档,计算所有词语的TF-IDF分数,并排序选择最高分的词语作为关键词(通常取前5-10个)。 - 优化:结合停用词过滤(移除常见无意义词如“的”、“是”)和词性标注(优先选择名词),以提高关键词质量[^4]。 - **步骤4: 处理大规模挑战** - **矩阵稀疏性**:大规模文本下,TF-IDF矩阵往往稀疏(大多数词语出现频率低)。使用稀疏矩阵格式(如CSR格式)减少内存占用[^2]。 - **分布式计算**:如果语料库超大(如TB级),使用分布式系统(如Spark MLlib的TF-IDF实现)或云计算平台。 - **性能考虑**:TF-IDF计算复杂度为 $O(n \times m)$($n$为文档数,$m$为词语数)。引用[^5]建议,通过降维(如PCA)或特征选择加速。 #### 3. **Python实现示例** 以下是一个使用jieba和scikit-learn的代码示例,适合大规模中文文本(分批处理)。假设您有中文文本文件(如`corpus.txt`),每行一个文档。 ```python import jieba import jieba.analyse from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.externals.joblib import Parallel, delayed import numpy as np # 步骤1: 中文分词函数(分批处理) def tokenize_chinese(text): return " ".join(jieba.cut(text)) # 分词并用空格连接,便于TF-IDF处理 # 步骤2: 加载语料库(示例:分批读取) corpus = [] with open("corpus.txt", "r", encoding="utf-8") as f: for line in f: corpus.append(tokenize_chinese(line.strip())) # 分词处理 # 步骤3: 计算TF-IDF矩阵(使用TfidfVectorizer,支持稀疏矩阵) vectorizer = TfidfVectorizer(tokenizer=lambda x: x.split(), max_features=10000) # max_features限制词语数,避免内存溢出 tfidf_matrix = vectorizer.fit_transform(corpus) # 自动计算TF-IDF,返回稀疏矩阵 # 步骤4: 提取每个文档的关键词 def extract_keywords(tfidf_row, feature_names, top_n=5): # 获取文档的TF-IDF分数并排序 indices = tfidf_row.indices data = tfidf_row.data sorted_indices = indices[np.argsort(data)[::-1]] # 降序排序 return [feature_names[i] for i in sorted_indices[:top_n]] # 返回top_n关键词 feature_names = vectorizer.get_feature_names_out() keywords_list = [] for i in range(tfidf_matrix.shape[0]): keywords = extract_keywords(tfidf_matrix[i], feature_names, top_n=5) keywords_list.append(keywords) # 输出示例:第一个文档的关键词 print("文档1的关键词:", keywords_list[0]) ``` **说明**: - 此代码使用`TfidfVectorizer`自动计算TF-IDF,参数`max_features=10000`限制词语数量,防止内存不足(适合大规模数据)[^2]。 - 分词部分:jieba处理中文,`tokenize_chinese`函数将文本转换为空格分隔的词语串。 - 扩展性:对于超大规模数据,使用`joblib.Parallel`并行处理或集成Spark- 关键词提取:基于TF-IDF分数排序,选择高分词语。 #### 4. **优缺点分析** - **优点**: - 实现简单,结果直观:高频且稀有的词语被优先选为关键词,适合中文文本分类和信息检索[^1][^3]。 - 可扩展:通过稀疏矩阵和分批处理,能应对大规模数据集(引用[^2]验证了此方法)[^2][^5]。 - **缺点**: - 忽略位置信息:文档开头的关键词可能更重要,但TF-IDF视为同等[^5]。 - 对稀有词敏感:如果词语在语料库中分布不均,IDF可能不准确(如某些领域专有词)[^3]。 - 中文特有挑战:分词错误会影响TF-IDF准确性(建议结合新词识别,如jieba的搜索引擎模式)[^4]。 - **改进建议**:对于大规模中文文本,结合TextRank算法(基于图模型)或深度学习模型(如BERT)提升关键词质量[^4][^5]。 #### 5. **相关问题** 基于您的查询,以下是TF-IDF算法在中文关键词提取中的延伸问题: 1. 如何优化TF-IDF算法以处理超大规模中文语料库(如10亿级文档)? 2. TF-IDF在中文文本中与英文有哪些主要区别?如何处理中文停用词和分词歧义? 3. 除了TF-IDF,还有哪些算法(如TextRank或Word2Vec)更适合大规模中文关键词提取?如何结合使用? 4. TF-IDF在搜索引擎中如何应用?它与PageRank算法的区别是什么[^1][^3]? 5. 如何评估TF-IDF提取的关键词质量(如使用准确率或召回率)? 通过以上步骤,您可以高效地使用TF-IDF提取大规模中文文本关键词。如有具体数据集或性能问题,欢迎提供更多细节!
评论 35
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值