尝试在 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 对文档进行聚类。这段代码有啥问题?的主要内容,如果未能解决你的问题,请参考以下文章
使用 Python 的 Apache Spark TFIDF