数据挖掘-基于Kmeans算法、MBSAS算法及DBSCAN算法的newsgroup18828文本聚类器的JAVA实现(下)...

本文接数据挖掘-基于Kmeans算法、MBSAS算法及DBSCAN算法的newsgroup18828文本聚类器的JAVA实现(上).

(update 2012.12.28 关于本项目下载及运行的常见问题 FAQ见newsgroup18828文本分类器、文本聚类器、关联分析频繁模式挖掘算法的Java实现工程下载及运行FAQ)

本文要点如下:

介绍基于LSI(隐性语义索引)中SVD分解做特征降维的方法

介绍两外两种文本聚类算法MBSAS算法及DBSCAN算法

对比三种算法对newsgroup18828文档集的聚类效果

1、SVD分解降维

以词项(terms)为行, 文档(documents)为列做一个大矩阵(matrix). 设一共有t行d列, 矩阵名为A. 矩阵的元素为词项的tf-idf值。然后对该矩阵做SVD分解 A=T*S*D‘,把S的m个对角元素的前k个保留(最大的k个保留), 后m-k个置0, 我们可以得到一个新的近似的分解:Xhat=T*S*D’ 。Xhat在最小二乘意义下是X的最佳近似

给定矩阵A, 基于A可以问三类同文件检索密切有关的问题
术语i和j有多相似?
即术语的类比和聚类问题
文件i和j有多相似?
即文件的类比和聚类问题
术语i和文件j有多相关?
即术语和文件的关联问题

利用SVD分解得到的矩阵可以计算这三个问题,方法如下(DT代表D的转置,以此类推)

比较两个术语
做"正向"乘法:
Xhat*XhatT=T*S*DT*D*S*TT=T*S2*TT=(TS)*(TS)T
DT*D=I, 因为D已经是正交归一的 ,s=sT
它的第i行第j列表明了术语i和j的相似程度

比较两个文件做"逆向"乘法:
XhatT*Xhat=D*S*TT*T*S*DT=D*S2*DT=(DS)(DS)T
TT*T=I, 因为T已经是正交归一的, s=sT
它的第i行第j列表明了文件i和j的相似程度
此法给出了求文件之间相似度的一个途径,于是可以基于此相似度矩阵实现K-means算法

比较一个文件和一个术语恰巧就是Xhat本身.
它的第i行第j列表明了术语i和文件j的相关联程度.

SVD分解主要基于JAMA矩阵运算包实现,JAMA矩阵运算包下载见http://math.nist.gov/javanumerics/jama/

DimensionReduction.java

  1. packagecom.pku.yangliu;
  2. importjava.io.IOException;
  3. importjava.util.Iterator;
  4. importjava.util.Map;
  5. importjava.util.Set;
  6. importJama.Matrix;
  7. importJama.SingularValueDecomposition;
  8. /**基于LSI对文档的特征向量做降维,SVD运算基于JAMA矩阵运算包实现
  9. *
  10. */
  11. publicclassDimensionReduction{
  12. /**把测试样例的map转化成文档相似性矩阵
  13. *@paramMap<String,Map<String,Double>>allTestSampleMap所有测试样例的<文件名,向量>构成的map
  14. *@paramString[]terms特征词集合
  15. *@returndouble[][]doc-doc相似性矩阵
  16. *@throwsIOException
  17. */
  18. publicdouble[][]getSimilarityMatrix(
  19. Map<String,Map<String,Double>>allTestSampleMap,String[]terms){
  20. //TODOAuto-generatedmethodstub
  21. System.out.println("BegincomputedocTermMatrix!");
  22. inti=0;
  23. double[][]docTermMatrix=newdouble[allTestSampleMap.size()][terms.length];
  24. Set<Map.Entry<String,Map<String,Double>>>allTestSampleMapSet=allTestSampleMap.entrySet();
  25. for(Iterator<Map.Entry<String,Map<String,Double>>>it=allTestSampleMapSet.iterator();it.hasNext();){
  26. Map.Entry<String,Map<String,Double>>me=it.next();
  27. for(intj=0;j<terms.length;j++){
  28. if(me.getValue().containsKey(terms[j])){
  29. docTermMatrix[i][j]=me.getValue().get(terms[j]);
  30. }
  31. else{
  32. docTermMatrix[i][j]=0;
  33. }
  34. }
  35. i++;
  36. }
  37. double[][]similarityMatrix=couputeSimilarityMatrix(docTermMatrix);
  38. returnsimilarityMatrix;
  39. }
  40. /**基于docTermMatrix生成相似性矩阵
  41. *@paramdouble[][]docTermMatrixdoc-term矩阵
  42. *@returndouble[][]doc-doc相似性矩阵
  43. *@throwsIOException
  44. */
  45. privatedouble[][]couputeSimilarityMatrix(double[][]docTermMatrix){
  46. //TODOAuto-generatedmethodstub
  47. System.out.println("ComputedocTermMatrixdone!begincomputeSVD");
  48. MatrixdocTermM=newMatrix(docTermMatrix);
  49. SingularValueDecompositions=docTermM.transpose().svd();
  50. System.out.println("ComputeSVDdone!");
  51. //A*A'=D*S*S'*D'如果是doc-term矩阵
  52. //A'*A=D*S'*S*D'如果是term-doc矩阵
  53. //注意svd函数只适合行数大于列数的矩阵,如果行数小于列数,可对其转置矩阵做SVD分解
  54. MatrixD=s.getU();
  55. MatrixS=s.getS();
  56. for(inti=100;i<S.getRowDimension();i++){//降到100维
  57. S.set(i,i,0);
  58. }
  59. System.out.println("ComputeSimilarityMatrixdone!");
  60. returnD.times(S.transpose().times(S.times(D.transpose()))).getArray();
  61. }
  62. }
2、基于SVD分解降维的K-means算法

有了上面得到的文档与文档之间的相似性矩阵后,我们就可以实现另一个版本的K-means算法了。注意中心点的计算是直接对该聚类中的所有文档的距离向量求平均,作为该中心点与其他所有文档的距离。具体实现如下,主函数在数据挖掘-基于Kmeans算法、MBSAS算法及DBSCAN算法的newsgroup18828文本聚类器的JAVA实现(上)中已经给出。

[java] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. packagecom.pku.yangliu;
  2. importjava.io.FileWriter;
  3. importjava.io.IOException;
  4. importjava.util.Iterator;
  5. importjava.util.Map;
  6. importjava.util.Set;
  7. importjava.util.TreeMap;
  8. importjava.util.Vector;
  9. importjava.lang.Integer;
  10. /**Kmeans聚类算法的实现类,将newsgroups文档集聚成10类、20类、30类,采用SVD分解
  11. *算法结束条件:当每个点最近的聚类中心点就是它所属的聚类中心点时,算法结束
  12. *
  13. */
  14. publicclassKmeansSVDCluster{
  15. /**Kmeans算法主过程
  16. *@paramMap<String,Map<String,Double>>allTestSampleMap所有测试样例的<文件名,向量>构成的map
  17. *@paramdouble[][]docSimilarityMatrix文档与文档的相似性矩阵[i,j]为文档i与文档j的相似性度量
  18. *@paramintK聚类的数量
  19. *@returnMap<String,Integer>聚类的结果即<文件名,聚类完成后所属的类别标号>
  20. *@throwsIOException
  21. */
  22. privateMap<String,Integer>doProcess(
  23. Map<String,Map<String,Double>>allTestSampleMap,double[][]docSimilarityMatrix,intK){
  24. //TODOAuto-generatedmethodstub
  25. //0、首先获取allTestSampleMap所有文件名顺序组成的数组
  26. String[]testSampleNames=newString[allTestSampleMap.size()];
  27. intcount=0,tsLength=allTestSampleMap.size();
  28. Set<Map.Entry<String,Map<String,Double>>>allTestSampeleMapSet=allTestSampleMap.entrySet();
  29. for(Iterator<Map.Entry<String,Map<String,Double>>>it=allTestSampeleMapSet.iterator();it.hasNext();){
  30. Map.Entry<String,Map<String,Double>>me=it.next();
  31. testSampleNames[count++]=me.getKey();
  32. }
  33. //1、初始点的选择算法是随机选择或者是均匀分开选择,这里采用后者
  34. Map<Integer,double[]>meansMap=getInitPoint(testSampleNames,docSimilarityMatrix,K);//保存K个中心点
  35. //2、初始化K个聚类
  36. int[]assignMeans=newint[tsLength];//记录所有点属于的聚类序号,初始化全部为0
  37. Map<Integer,Vector<Integer>>clusterMember=newTreeMap<Integer,Vector<Integer>>();//记录每个聚类的成员点序号
  38. Vector<Integer>mem=newVector<Integer>();
  39. intiterNum=0;//迭代次数
  40. while(true){
  41. System.out.println("IterationNo."+(iterNum++)+"----------------------");
  42. //3、找出每个点最近的聚类中心
  43. int[]nearestMeans=newint[tsLength];
  44. for(inti=0;i<tsLength;i++){
  45. nearestMeans[i]=findNearestMeans(meansMap,i);
  46. }
  47. //4、判断当前所有点属于的聚类序号是否已经全部是其离得最近的聚类,如果是或者达到最大的迭代次数,那么结束算法
  48. intokCount=0;
  49. for(inti=0;i<tsLength;i++){
  50. if(nearestMeans[i]==assignMeans[i])okCount++;
  51. }
  52. System.out.println("okCount="+okCount);
  53. if(okCount==tsLength||iterNum>=25)break;//最大迭代次数1000次
  54. //5、如果前面条件不满足,那么需要重新聚类再进行一次迭代,需要修改每个聚类的成员和每个点属于的聚类信息
  55. clusterMember.clear();
  56. for(inti=0;i<tsLength;i++){
  57. assignMeans[i]=nearestMeans[i];
  58. if(clusterMember.containsKey(nearestMeans[i])){
  59. clusterMember.get(nearestMeans[i]).add(i);
  60. }
  61. else{
  62. mem.clear();
  63. mem.add(i);
  64. Vector<Integer>tempMem=newVector<Integer>();
  65. tempMem.addAll(mem);
  66. clusterMember.put(nearestMeans[i],tempMem);
  67. }
  68. }
  69. //6、重新计算每个聚类的中心点
  70. for(inti=0;i<K;i++){
  71. if(!clusterMember.containsKey(i)){//注意kmeans可能产生空聚类
  72. continue;
  73. }
  74. double[]newMean=computeNewMean(clusterMember.get(i),docSimilarityMatrix);
  75. meansMap.put(i,newMean);
  76. }
  77. }
  78. //7、形成聚类结果并且返回
  79. Map<String,Integer>resMap=newTreeMap<String,Integer>();
  80. for(inti=0;i<tsLength;i++){
  81. resMap.put(testSampleNames[i],assignMeans[i]);
  82. }
  83. returnresMap;
  84. }
  85. /**计算新的聚类中心与每个文档的相似度
  86. *@paramclusterM该聚类包含的所有文档的序号
  87. *@paramdouble[][]docSimilarityMatrix文档之间的相似度矩阵
  88. *@returndouble[]新的聚类中心与每个文档的相似度
  89. *@throwsIOException
  90. */
  91. privatedouble[]computeNewMean(Vector<Integer>clusterM,
  92. double[][]docSimilarityMatrix){
  93. //TODOAuto-generatedmethodstub
  94. doublesim;
  95. double[]newMean=newdouble[docSimilarityMatrix.length];
  96. doublememberNum=(double)clusterM.size();
  97. for(inti=0;i<docSimilarityMatrix.length;i++){
  98. sim=0;
  99. for(Iterator<Integer>it=clusterM.iterator();it.hasNext();){
  100. sim+=docSimilarityMatrix[it.next()][i];
  101. }
  102. newMean[i]=sim/memberNum;
  103. }
  104. returnnewMean;
  105. }
  106. /**找出距离当前点最近的聚类中心
  107. *@paramMap<Integer,double[]>meansMap中心点Mapvalue为中心点和每个文档的相似度
  108. *@paramintm
  109. *@returni最近的聚类中心的序号
  110. *@throwsIOException
  111. */
  112. privateintfindNearestMeans(Map<Integer,double[]>meansMap,intm){
  113. //TODOAuto-generatedmethodstub
  114. doublemaxSim=0;
  115. intj=-1;
  116. double[]simArray;
  117. Set<Map.Entry<Integer,double[]>>meansMapSet=meansMap.entrySet();
  118. for(Iterator<Map.Entry<Integer,double[]>>it=meansMapSet.iterator();it.hasNext();){
  119. Map.Entry<Integer,double[]>me=it.next();
  120. simArray=me.getValue();
  121. if(maxSim<simArray[m]){
  122. maxSim=simArray[m];
  123. j=me.getKey();
  124. }
  125. }
  126. returnj;
  127. }
  128. /**获取kmeans算法迭代的初始点
  129. *@paramk聚类的数量
  130. *@paramString[]testSampleNames测试样例文件名数组
  131. *@paramdouble[][]docSimilarityMatrix文档相似性矩阵
  132. *@returnMap<Integer,double[]>初始中心点容器key是类标号,value为该类与其他文档的相似度数组
  133. *@throwsIOException
  134. */
  135. privateMap<Integer,double[]>getInitPoint(String[]testSampleNames,double[][]docSimilarityMatrix,intK){
  136. //TODOAuto-generatedmethodstub
  137. inti=0;
  138. Map<Integer,double[]>meansMap=newTreeMap<Integer,double[]>();//保存K个聚类中心点向量
  139. System.out.println("本次聚类的初始点对应的文件为:");
  140. for(intcount=0;count<testSampleNames.length;count++){
  141. if(count==i*testSampleNames.length/K){
  142. meansMap.put(i,docSimilarityMatrix[count]);
  143. System.out.println(testSampleNames[count]);
  144. i++;
  145. }
  146. }
  147. returnmeansMap;
  148. }
  149. /**输出聚类结果到文件中
  150. *@paramkmeansClusterResultFile输出文件目录
  151. *@paramkmeansClusterResult聚类结果
  152. *@throwsIOException
  153. */
  154. privatevoidprintClusterResult(Map<String,Integer>kmeansClusterResult,StringkmeansClusterResultFile)throwsIOException{
  155. //TODOAuto-generatedmethodstub
  156. FileWriterresWriter=newFileWriter(kmeansClusterResultFile);
  157. Set<Map.Entry<String,Integer>>kmeansClusterResultSet=kmeansClusterResult.entrySet();
  158. for(Iterator<Map.Entry<String,Integer>>it=kmeansClusterResultSet.iterator();it.hasNext();){
  159. Map.Entry<String,Integer>me=it.next();
  160. resWriter.append(me.getKey()+""+me.getValue()+"\n");
  161. }
  162. resWriter.flush();
  163. resWriter.close();
  164. }
  165. /**Kmeans算法
  166. *@paramStringtestSampleDir测试样例目录
  167. *@paramString[]term特征词数组
  168. *@throwsIOException
  169. */
  170. publicvoidKmeansClusterMain(StringtestSampleDir,String[]terms)throwsIOException{
  171. //首先计算文档TF-IDF向量,保存为Map<String,Map<String,Double>>即为Map<文件名,Map<特征词,TF-IDF值>>
  172. ComputeWordsVectorcomputeV=newComputeWordsVector();
  173. DimensionReductiondimReduce=newDimensionReduction();
  174. int[]K={10,20,30};
  175. Map<String,Map<String,Double>>allTestSampleMap=computeV.computeTFMultiIDF(testSampleDir);
  176. //基于allTestSampleMap生成一个doc*term矩阵,然后做SVD分解
  177. double[][]docSimilarityMatrix=dimReduce.getSimilarityMatrix(allTestSampleMap,terms);
  178. for(inti=0;i<K.length;i++){
  179. System.out.println("开始聚类,聚成"+K[i]+"类");
  180. StringKmeansClusterResultFile="F:/DataMiningSample/KmeansClusterResult/";
  181. Map<String,Integer>KmeansClusterResult=newTreeMap<String,Integer>();
  182. KmeansClusterResult=doProcess(allTestSampleMap,docSimilarityMatrix,K[i]);
  183. KmeansClusterResultFile+=K[i];
  184. printClusterResult(KmeansClusterResult,KmeansClusterResultFile);
  185. System.out.println("TheEntropyforthisClusteris"+computeV.evaluateClusterRes(KmeansClusterResultFile,K[i]));
  186. }
  187. }
  188. }
3、K-means算法、MBSAS算法、DBSCAN算法三种算法的聚类结果对比

另外两种聚类算法MBSAS算法和DBSCAN算法由我们组另外两位同学实现,其实也很简单,源码这里就不贴出来了。感兴趣的朋友可以到点击打开链接下载eclipse工程运行。这三种算法的聚类结果采用熵值大小来评价,熵值越小聚类效果越好,具体如下






可见对newsgroup文档集聚类采用K-means算法,用余弦相似度或者内积度量相似度可以达到良好的效果。而SVD分解还是很耗时间,事实上对20000X3000的矩阵做SVD分解的时间慢得难以忍受,我还尝试对小规模数据聚类,但是发现降维后聚类结果熵值超过了2,不及DF法降维的聚类效果。因此对于文本聚类的SVD降维未必是好方法,。除了这三种聚类算法,还有层次聚类算法等其他很多算法,以后会尝试给出其他算法的实现和聚类效果对比。敬请关注:)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值