トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS

Mahout:サンプル2:初めてのクラスタリング

Last-modified: 2013-08-24 (土) 02:14:21 (3891d)
Top / Mahout:サンプル2:初めてのクラスタリング

Mahout:環境構築

Mahout:サンプル2:初めてのクラスタリング

Mahout本を読みながら、本のとおり+アルファでクラスタリングのサンプルを作ってみた。

kミーンズクラスタリングです。

本ではk平均方クラスタリングっても書いてありましたが、一番ベーシックなクラスタリング手法らしいです。

Mahoutでのクラスタリング手法

他にもMahoutには以下のようなクラスタリングの手法が使えるみたいです。

  • kミーンズクラスタリング
  • キャノピークラスタリング
  • ファジィkミーンズクラスタリング
  • ディリクレクラスタリング
  • 等々

コード

コードは長いけど、大きく以下のブロックに分かれる。→つまり、クラスタリングの際に必要となること

  • kミーンズクラスタリングのインプットとなるベクトルをSequenceFile?形式で出力
  • kミーンズクラスタリングのインプットとなるクラスタの初期中心点の生成
  • kミーンズクラスタリングの実行
    ここでは、距離測定方法をいろいろ変えて実行するようにしてます。

HelloWorldClustering?.java

package clustering;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hadoop.io.Text;
import org.apache.mahout.clustering.classify.WeightedVectorWritable;
import org.apache.mahout.clustering.kmeans.KMeansDriver;
import org.apache.mahout.clustering.kmeans.Kluster;
import org.apache.mahout.common.HadoopUtil;
import org.apache.mahout.common.distance.CosineDistanceMeasure;
import org.apache.mahout.common.distance.EuclideanDistanceMeasure;
import org.apache.mahout.common.distance.ManhattanDistanceMeasure;
import org.apache.mahout.common.distance.SquaredEuclideanDistanceMeasure;
import org.apache.mahout.common.distance.TanimotoDistanceMeasure;
import org.apache.mahout.math.RandomAccessSparseVector;
import org.apache.mahout.math.Vector;
import org.apache.mahout.math.VectorWritable;

public class HelloWorldClustring {

	private static final double[][] points =
		{ {1,1},{2,1},{1,2},{2,2},{3,3},{8,8},{9,8},{8,9},{9,9} };
	
	private static Configuration conf;
	
	private static FileSystem fs;
	
	private static StringBuffer sb = new StringBuffer(); 
	
	
	/**
	 * pointから、KミーンズのインプットとなるSequenceFileを生成
	 * @param points
	 * @param filename
	 * @param fs
	 * @param conf
	 * @throws IOException
	 */
	public static void writePointsToFile(List<Vector> points,
			String filename,
			FileSystem fs,
			Configuration conf) throws IOException{
		Path path = new Path(filename);
		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();
	}
	

	/**
	 * 配列をRandomAccessSparseVectorのListに変換し、返却する。
	 * @param raw
	 * @return
	 */
	public static List<Vector> getPoints(double[][] raw){
		List<Vector> points = new ArrayList<Vector>();
		for(int i = 0 ; i < raw.length; i++){
			double[] fr = raw[i];
			Vector vec = new RandomAccessSparseVector(fr.length);
			vec.assign(fr);
			points.add(vec);
		}
		return points;
	}
	
	/**
	 * mainメソッド
	 * @param args
	 * @throws IOException 
	 * @throws ClassNotFoundException 
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws IOException, 
                     InterruptedException, ClassNotFoundException {
		// kミーンズのk値 : 2クラスタにクラスタリング
		int k = 2;
		
		List<Vector> vectors = getPoints(points);
		
		// ディレクトリの作成
		File testData = new File("testdata");
		if(!testData.exists()){
			testData.mkdir();
		}
		testData = new File("testData/points");
		if(!testData.exists()){
			testData.mkdir();
		}
		
		// 特になにも設定しないため、スタンドアロン環境での実行となる。
		conf = new Configuration();
		fs = FileSystem.get(conf);
		
		// Kミーンズのインプットとなるkey、vectorのSequenceFileを生成
		writePointsToFile(vectors, "testdata/points/file1", fs, conf);
		
		// kミーンズのクラスタリングのインプットとなる初期クラスタの中心点の生成
		Path path = new Path("testdata/cluster/part-00000");
		SequenceFile.Writer writer = 
				new SequenceFile.Writer(fs, conf, path, 
                               Text.class, Kluster.class);
		// kミーンズのインプットとなる、クラスタの中心点を出力
		for (int i = 0; i < k; i++){
			Vector vec = vectors.get(i);
			// Klusterクラスのコンストラクタには「中心となるベクトル、
                       // クラスタID、距離測定法」を引き渡す。
			Kluster cluster = new Kluster(vec,i,new EuclideanDistanceMeasure());
			writer.append(new Text(cluster.getIdentifier()), cluster);
		}
		writer.close();
		
		Path output = new Path("output");
		
		// ユークリッド距離を利用したKミーンズによるクラスタリングの実行
		HadoopUtil.delete(conf, output); //前回の実行結果を削除する
		KMeansDriver.run(conf, new Path("testdata/points"), 
				new Path("testdata/cluster"),
				new Path("output"),
				new EuclideanDistanceMeasure(), 0.001, 10, true, 0.0, false);
		printResult(EuclideanDistanceMeasure.class.getName());
		
		// ユークリッド平方距離を利用したKミーンズによるクラスタリング
		HadoopUtil.delete(conf, output); //前回の実行結果を削除する
		KMeansDriver.run(conf, new Path("testdata/points"), 
				new Path("testdata/cluster"),
				new Path("output"),
				new SquaredEuclideanDistanceMeasure(), 0.001, 10, true, 0.0, false);
		printResult(SquaredEuclideanDistanceMeasure.class.getName());
		
		// マンハッタン距離を利用したKミーンズ
		HadoopUtil.delete(conf, output); //前回の実行結果を削除する
		KMeansDriver.run(conf, new Path("testdata/points"), 
				new Path("testdata/cluster"),
				new Path("output"),
				new ManhattanDistanceMeasure(), 0.001, 10, true, 0.0, false);
		printResult(ManhattanDistanceMeasure.class.getName());
		
		// コサイン距離を利用したKミーンズ
		HadoopUtil.delete(conf, output); //前回の実行結果を削除する
		KMeansDriver.run(conf, new Path("testdata/points"), 
				new Path("testdata/cluster"),
				new Path("output"),
				new CosineDistanceMeasure(), 0.001, 10, true, 0.0, false);
		printResult(CosineDistanceMeasure.class.getName());
		
		// Tanimoto距離を利用したKミーンズ
		HadoopUtil.delete(conf, output); //前回の実行結果を削除する
		KMeansDriver.run(conf, new Path("testdata/points"), 
				new Path("testdata/cluster"),
				new Path("output"),
				new TanimotoDistanceMeasure(), 0.001, 10, true, 0.0, false);
		printResult(TanimotoDistanceMeasure.class.getName());
		
		// 最後に、StringBufferに貯めた出力結果をまとめて出力する。
		System.out.println();
		System.out.println();
		System.out.println(sb.toString());
	}
	

	/**
	 * クラスタリングの結果を出力する
	 * @throws IOException
	 */
	public static void printResult(String distanceMeasureName) throws IOException {
		SequenceFile.Reader reader = 
				new SequenceFile.Reader(fs, 
						new Path("output/" + Kluster.CLUSTERED_POINTS_DIR 
								+ "/part-m-00000"), conf);

		IntWritable key = new IntWritable();
		WeightedVectorWritable value = new WeightedVectorWritable();
		sb.append("DistanceMeasureName : " + distanceMeasureName + "\n");
		System.out.println("DistanceMeasureName : " + distanceMeasureName);
		while(reader.next(key,value)){
			sb.append(value.toString()+" belongs to cluster " + key.toString()+ "\n");
			System.out.println(value.toString()+" belongs to cluster " + key.toString());
		}
		reader.close();
		sb.append("\n");
	}
}

実行結果

いろいろ出力されますが、プログラムから出力している、最後のsysアウトだけ貼り付けます。

DistanceMeasureName : org.apache.mahout.common.distance.EuclideanDistanceMeasure
1.0: [1.000, 1.000] belongs to cluster 0
1.0: [2.000, 1.000] belongs to cluster 0
1.0: [1.000, 2.000] belongs to cluster 0
1.0: [2.000, 2.000] belongs to cluster 0
1.0: [3.000, 3.000] belongs to cluster 0
1.0: [8.000, 8.000] belongs to cluster 1
1.0: [9.000, 8.000] belongs to cluster 1
1.0: [8.000, 9.000] belongs to cluster 1
1.0: [9.000, 9.000] belongs to cluster 1

DistanceMeasureName : org.apache.mahout.common.distance.SquaredEuclideanDistanceMeasure
1.0: [1.000, 1.000] belongs to cluster 0
1.0: [2.000, 1.000] belongs to cluster 0
1.0: [1.000, 2.000] belongs to cluster 0
1.0: [2.000, 2.000] belongs to cluster 0
1.0: [3.000, 3.000] belongs to cluster 0
1.0: [8.000, 8.000] belongs to cluster 1
1.0: [9.000, 8.000] belongs to cluster 1
1.0: [8.000, 9.000] belongs to cluster 1
1.0: [9.000, 9.000] belongs to cluster 1

DistanceMeasureName : org.apache.mahout.common.distance.ManhattanDistanceMeasure
1.0: [1.000, 1.000] belongs to cluster 0
1.0: [2.000, 1.000] belongs to cluster 0
1.0: [1.000, 2.000] belongs to cluster 0
1.0: [2.000, 2.000] belongs to cluster 0
1.0: [3.000, 3.000] belongs to cluster 0
1.0: [8.000, 8.000] belongs to cluster 1
1.0: [9.000, 8.000] belongs to cluster 1
1.0: [8.000, 9.000] belongs to cluster 1
1.0: [9.000, 9.000] belongs to cluster 1

DistanceMeasureName : org.apache.mahout.common.distance.CosineDistanceMeasure
1.0: [1.000, 1.000] belongs to cluster 0
1.0: [2.000, 1.000] belongs to cluster 0
1.0: [1.000, 2.000] belongs to cluster 0
1.0: [2.000, 2.000] belongs to cluster 0
1.0: [3.000, 3.000] belongs to cluster 0
1.0: [8.000, 8.000] belongs to cluster 1
1.0: [9.000, 8.000] belongs to cluster 1
1.0: [8.000, 9.000] belongs to cluster 1
1.0: [9.000, 9.000] belongs to cluster 1

DistanceMeasureName : org.apache.mahout.common.distance.TanimotoDistanceMeasure
1.0: [1.000, 1.000] belongs to cluster 0
1.0: [2.000, 1.000] belongs to cluster 0
1.0: [1.000, 2.000] belongs to cluster 0
1.0: [2.000, 2.000] belongs to cluster 0
1.0: [3.000, 3.000] belongs to cluster 0
1.0: [8.000, 8.000] belongs to cluster 1
1.0: [9.000, 8.000] belongs to cluster 1
1.0: [8.000, 9.000] belongs to cluster 1
1.0: [9.000, 9.000] belongs to cluster 1

なんか、Mahout本には「CosineDistanceMeasure?のときだけ、クラスタリングの結果が他と違う」ってかいてあったんだけど、全く同じ結果になったんだよな、、、どこかまちがってるのかな、、、w