mahout聚类的基本流程

  1. 基本概念,格式

    • SequenceFile文件是一种二制制存储的key-value键值对,是mahout(hadoop)接受的文件输入格式
    • mahout进行聚类需要的数据是sequenceFile,以文章聚类来看,seqFile的key,value分别是<docId, docVector>,docId是文章的id,docVector是文章的词频统计向量
    • docVector生成过程,首先构造doc的词频向量(数组),之后放到mahout的Vector中,根据需要可以选择DenseVector, RandomAccessSparseVector以及。。。
    • 使用SequenceFile.Writer writer = new SequenceFile.Writer(fs, conf, path, LongWritable.class, VectorWritable.class);将<docId, docVector>写到sequenceFile中
  2. 处理原始文件

    • 如果输入是目录下的N个文件,每个文件是一片txt的文章,可以使用如下命令转化成mahout需要的输入
      1. mahout seqdirectory:将文本文件转成SequenceFile文件,对应的源文件是org.apache.mahout.text.SequenceFilesFromDirectory.java
      2. mahout seq2sparse:将SequenceFile转成向量文件,对应的源文件是org.apache.mahout.vectorizer.SparseVectorsFromSequenceFiles.java
    • 如果输入是用户打分数据,文件中每行包括<userId, itemId, rate>,这种格式mahout没办法直接处理,需要自己写一个转换程序
      • 由于userId,itemId可能是hash后的值,建议做个映射,做成用户打分矩阵,方便处理
      • 之后分别使用下面程序读入打分矩阵,存储Seqfile

        public static List<Vector> getMatrix(String dfname, int row, int col) throws IOException{
          List<Vector> matrix = new ArrayList<Vector>();
          BufferedReader br = new BufferedReader(new FileReader(dfname));
          String line = null;
          String[] svec = null;
          double[] dvec = new double[col];
          int cline = 0;
          while((line=br.readLine())!=null) {
            svec = line.split("\t");
            if(svec.length!=col)
              System.exit(COLUMN_ERROR);
            for(int i=0; i<col; i++){
              dvec[i] = Double.valueOf(svec[i]);
              if(dvec[i]>0.1)
                System.out.print(dvec[i] + "\t");
            }
            Vector vector = new RandomAccessSparseVector(col);
            vector.assign(dvec);
            matrix.add(vector);
                    
            if(cline>=row)
              break;
            else
              System.out.format("\nline count:%d\t\n", ++cline);
          }
          return matrix;
        }
        
        public static void writePointsToFile(List<Vector> points,
            Configuration conf, Path path) throws IOException {
          FileSystem fs = FileSystem.get(path.toUri(), conf);
          SequenceFile.Writer writer = new SequenceFile.Writer(fs, conf, path,
              LongWritable.class, VectorWritable.class);
          long recNum = 0;
          VectorWritable vec = new VectorWritable();
          for (Vector point : points) {
            vec.set(point);
            writer.append(new LongWritable(recNum++), vec);
          }
          writer.close();
        }
        
  3. 进行聚类

    mahout kmeans -i seqFile(dir) -o output-result -k 3 -dm org.apache.mahout.common.distance.CosineDistanceMeasure -cd 0.001 -x 2 -ow -cl -c kmeans-clusters // mahout Kmeans聚类有两个重要参数:收敛Delta和最大迭代次数.所以有时候改敛时,并还没有达到最大迭代次数

  4. 显示聚类结果

    mahout clusterdump -o clusteranalyze.txt -p kmeans-result/clusteredPoints -dm org.apache.mahout.common.distance.CosineDistanceMeasure -i kmeans-result/clusters-0 -d dictOut

  5. 输出文件

    • 运行mahout kmeas,结果文件夹有clusteredPoints,clusters-N,data,用命令mahout seqdumper仔细看了一下结果文件
    • clusteredPoints:存放的是最后聚类的结果,将cluster-id和documents-id都展示出来了,用mahout seqdumper读clusteredPoints结果的key-value类型是(IntWritable,WeightedVectorWritable)
    • clusters-N:是第N次聚类的结果,其中n为某类的样本数目,c为各类各属性的中心,r为各类属性的半径。 clusters-N结果类型是(Text,Cluster)
    • data:存放的是原始数据,这个文件夹下的文件可以用mahout vectordump来读取,原始数据是向量形式的,其它的都只能用mahout seqdumper来读取,向量文件也可以用mahout seqdumper来读取,只是用vectordump读取出来的是数字结果,没有对应的key,用seqdumper读出来的可以看到key,即对应的url,而value读出来的是一个类描述,而不是数组向量

从数据库上读取数据

mahout没有提供直接生成seqfile的方法,可以使用lucene建立索引,mahout直接读lucene索引生成的vector 1. 使用lucene建立索引 2. 得到向量

mahout lucene.vector --dir index --output vectors --field doctitle --dictOut dictOut --idField doctitle --norm 2 --maxDFPercent 20 --minDF 2 -w TFIDF

其他Utils

TOP