我有一个 wiki 页面的语料库(棒球、曲棍球、音乐、足球),我通过 tfidf
运行,然后通过 kmeans
运行。在开始几个问题之后(您可以看到我之前的问题),我终于得到了 KMeansModel
...但是当我尝试预测
时,我不断得到同一个中心。这是因为数据集较小,还是因为我正在将多单词文档与少量单词(1-20)查询进行比较?还是我做错了什么?请参阅下面的代码:
//Preprocessing of data includes splitting into words
//and removing words with only 1 or 2 characters
val corpus: RDD[Seq[String]]
val hashingTF = new HashingTF(100000)
val tf = hashingTF.transform(corpus)
val idf = new IDF().fit(tf)
val tfidf = idf.transform(tf).cache
val kMeansModel = KMeans.train(tfidf, 3, 10)
val queryTf = hashingTF.transform(List("music"))
val queryTfidf = idf.transform(queryTf)
kMeansModel.predict(queryTfidf) //Always the same, no matter the term supplied
这个问题似乎与this one有些关系。
最佳答案
更多的是 list 而不是答案:
单个单词查询或非常短的句子可能不是一个好的选择,尤其是与大型特征向量结合使用时。我将从语料库中的重要文档片段开始
手动检查每个集群的查询之间的相似性。它与每个集群有远程相似吗?
import breeze.linalg.{DenseVector => BDV, SparseVector => BSV, Vector => BV} import breeze.linalg.functions.cosineDistance import org.apache.spark.mllib.linalg.{Vector, SparseVector, DenseVector} def toBreeze(v: Vector): BV[Double] = v match { case DenseVector(values) => new BDV[Double](values) case SparseVector(size, indices, values) => { new BSV[Double](indices, values, size) } } val centers = kMeansModel.clusterCenters.map(toBreeze(_)) val query = toBreeze(queryTfidf) centers.map(c => cosineDistance(query, c))
K 均值收敛吗?根据数据集和初始质心,十次或二十次迭代可能是不够的。尝试将此数字增加到一千左右,看看问题是否仍然存在。
您的语料库是否足够多样化以形成有意义的集群?尝试找到语料库中每个文档的质心。您是否获得相对均匀的分布或几乎所有文档都分配到单个集群。
进行目视检查。将您的 tfidf RDD 转换为矩阵,应用 PCA、绘图、按簇着色,看看是否获得有意义的结果。
同时绘制质心并检查它们是否覆盖了可能的簇。如果没有再次检查收敛性。
您还可以检查质心之间的相似性:
(0 until centers.size) .toList .flatMap(i => ((i + 1) until centers.size) .map(j => (i, j, 1 - cosineDistance(centers(i), centers(j)))))
你的预处理足够彻底吗?简单地删除简短的单词很可能是不够的。我至少会使用删除停用词来扩展它。一些词干也不会造成伤害。
K-Means 结果取决于初始质心。尝试多次运行算法,看看问题是否仍然存在。
尝试更复杂的算法,例如 LDA
关于apache-spark - K-Means 聚类偏向于一个中心,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32936380/