尝试在 Spark 中使用 TF-IDF 和 KMeans 对文档进行聚类。这段代码有啥问题?

Posted

技术标签:

【中文标题】尝试在 Spark 中使用 TF-IDF 和 KMeans 对文档进行聚类。这段代码有啥问题?【英文标题】:Attempting to cluster documents with TF-IDF and KMeans in Spark. What's wrong with this piece of code?尝试在 Spark 中使用 TF-IDF 和 KMeans 对文档进行聚类。这段代码有什么问题? 【发布时间】:2017-08-17 13:39:43 【问题描述】:

我有一个带有文本字段的 CSV 文件,有 2 种语言(法语和英语)。我正在尝试进行聚类分析,并且由于语言差异,我有点期望将文本分为 2 个聚类。

我想出了以下代码,但它没有按预期工作:

import org.apache.spark.sql.SQLContext
import org.apache.spark.sql.types.StructType, StructField, StringType
import org.apache.spark.ml.feature.HashingTF, IDF, Tokenizer
import org.apache.spark.ml.clustering.KMeans

val sqlContext = new SQLContext(sc)

val customSchema = StructType(Array(
    StructField("id_suivi", StringType, true),
    StructField("id_ticket", StringType, true),
    StructField("id_affectation", StringType, true),
    StructField("id_contact", StringType, true),
    StructField("d_date", StringType, true),
    StructField("n_duree_passe", StringType, true),
    StructField("isPublic", StringType, true),
    StructField("Ticket_Request_Id", StringType, true),
    StructField("IsDoneInHNO", StringType, true),
    StructField("commments", StringType, true),
    StructField("reponse", StringType, true)))

val tokenizer = new Tokenizer().setInputCol("reponse").setOutputCol("words")
val hashingTF = new HashingTF().setInputCol("words").setOutputCol("rawFeatures").setNumFeatures(32768)
val idf = new IDF().setInputCol("rawFeatures").setOutputCol("features")

val df = sqlContext.read.format("com.databricks.spark.csv").
    option("header", "true").
    option("delimiter", ";").
    schema(customSchema).
    load("C:/noSave/tmp/22/tickets1.csv").
    select("id_suivi", "reponse")

val tokenizedDF = tokenizer.transform(df)
val hashedDF = hashingTF.transform(tokenizedDF).cache()

val idfModel = idf.fit(hashedDF)

val rescaledDF = idfModel.transform(hashedDF).cache()

val kmeans = new KMeans().setK(2).setSeed(1L).setFeaturesCol("features")
val model = kmeans.fit(rescaledDF)

val clusteredDF = model.transform(rescaledDF)

我相信这段代码是正确的,或者至少我看不出错误在哪里。但是,确实有些问题,因为当我计算错误时,它真的很大:

scala> model.computeCost(rescaledDF)
res0: Double = 3.1555983509935196E7

我还为K 尝试了不同的值(我认为 2 是一个很好的值,因为我的文本使用 2 种语言(法语、英语)),例如 10、100 甚至更大,寻找“肘部”价值,但没有运气。

谁能指出我正确的方向?

提前非常感谢!

【问题讨论】:

您是否尝试插入一对StopWordsRemovers(每种语言一个)来清理您的输入? @BenFradet 我没有,但主要是因为我认为 TF-IDF 会通过降低整个集合中常见特征的权重来解决这个问题。我错了吗? 不,你是对的,但是StopWordsRemover 将使 TF-IDF 不必处理许多无用的功能。 好的,有道理,谢谢! @BenFradet 【参考方案1】:

我会回答我自己的问题(希望 SO 的礼仪可以接受),以防万一这对其他人有用。

区分这两种语言的一种更简单的方法是考虑它们使用停用词(即:每种语言中常见的词)。

一开始就使用 TF-IDF 是个坏主意,因为它抵消了停用词的贡献(其目的是将重点放在文档中“非常常见”的术语上)

我设法通过使用 CountVectorizer 来更接近按语言进行聚类的目标,它创建了最常用术语的字典并计算每个文档的术语。

最常见的术语是停用词,我们最终通过使用停用词对文档进行聚类,在两种语言中它们是不同的集合,因此按语言进行聚类。

【讨论】:

以上是关于尝试在 Spark 中使用 TF-IDF 和 KMeans 对文档进行聚类。这段代码有啥问题?的主要内容,如果未能解决你的问题,请参考以下文章

Spark实现tf-idf

特征抽取 — TF-IDF

使用 Python 的 Apache Spark TFIDF

如何在 PMID 列中使用 TF-IDF

Spark MllibTF-IDF&Word2Vec——文本相似度

使用analyzer ='char'如何计算Tf-Idf值?