2011年12月23日
■[FESS]Fess 5.0 リリース

最近、書く時間がなくて、このブログも放置気味な感じですけど…。そんな感じですが、先週、Fess 5 をリリースしました。Fess 5では
という感じで新機能的な感じは少ないですけど、まぁ、機能的には(たぶん)十分に揃っているかと。以下で入手可能です。
http://sourceforge.jp/projects/fess/releases/
次の Fess 5.1 はUIの変更やlucene-gosenを統合したりとか、細々としたことを入れてリリースしたいと考えています。まぁ、もしかしたら、バージョンは 5.5 とかにするかもしれないけど。
あとは、今週、第7回 Solr&検索エンジン勉強会で話した資料は以下に置いておきました。
http://www.slideshare.net/shinsuke/solr-fess
という感じで、どんどん使ってくださいー。
2011年08月06日
■[Mahout][機械学習]人工データを作る

Mahoutというか,その中にはそれっぽいライブラリが見つからなかったのだけど,科学技術計算の実験系で人工データを作りたいときに困ったので作ってみた.
import java.io.Serializable;
import java.util.Random;
import org.apache.mahout.math.DenseVector;
import org.apache.mahout.math.Vector;
public class SyntheticDataGenerator implements Serializable {
private static final long serialVersionUID = 1L;
private Random[] randoms;
private double[] means;
private double[] stdevs;
public SyntheticDataGenerator(long seed) {
means = new double[1];
means[0] = 0;
stdevs = new double[1];
stdevs[0] = 1;
init(seed, 1, means, stdevs);
}
public SyntheticDataGenerator(long seed, int cardinality, double[] means,
double[] stdevs) {
init(seed, cardinality, means, stdevs);
}
private void init(long seed, int cardinality, double[] means,
double[] stdevs) {
if (cardinality != means.length || cardinality != stdevs.length) {
throw new IllegalArgumentException("Invalid cardinality.");
}
randoms = new Random[cardinality];
for (int i = 0; i < cardinality; i++) {
randoms[i] = new Random(seed + i);
}
this.means = means;
this.stdevs = stdevs;
}
public double nextDouble() {
return nextDouble(0);
}
protected double nextDouble(int i) {
return randoms[i].nextGaussian() * stdevs[i] + means[i];
}
public double[] nextDoubles() {
double[] values = new double[randoms.length];
for (int i = 0; i < randoms.length; i++) {
values[i] = nextDouble(i);
}
return values;
}
public Vector nextVector() {
return new DenseVector(nextDoubles());
}
}
1次元の正規分布に基づく人工データを作りたいときには以下な感じ.
double[] means = new double[1]; double[] stdevs = new double[1]; means[0] = 10; // 平均 10 stdevs[0] = 5; // 標準偏差 5 SyntheticDataGenerator generator = new SyntheticDataGenerator(0, 1, means, stdevs);
あとは,generator.nextDouble() で値を取得していくと指定した分布の乱数が取得できる.そんで,多次元のデータが欲しい場合は,各次元ごとのmeansとstdevsを配列に格納して,generator.nextDoubles() としてやれば配列がとれるし,nextVector()でMahoutのVectorとして取得できる.
2011年07月24日
■[FESS]Fess 4.1.0リリース

4.0.0から半年以上経過してしまいましたが,ようやく4.1.0をリリースしました.新機能の追加というよりバグなどの対応が主になるかと思います.まぁ,バグと言ってもFess自体のはあまりなくて,Tikaとか,PDFBoxとか,HttpClientとか利用しているものたちに起因するものがほとんどかと思います.そういうこともあって,ここまで来るのに時間がかかった気もします.一応,新機能的なところとしては,Solrの持つファセットも利用できるようになりました.という感じで,4.x系はバグ対応的なリリースしか今後はせずに,基本は5.x系に注力していく予定です.っで,次のFess 5.0では,Tomcat 7とSolr 3.2の採用を考えています.これらがさくっとできれば,すぐに5.0がリリースされると思いますし,そうでなければちょっと時間がかかるかも.そんな感じですが,引き続きよろしくお願いしますー.
2011年07月09日
■[Mahout]Vectorは良いのか?

気になるので,Vectorを使う場合とプリミティブな配列で処理したときのパフォーマンス影響を確認してみる.たぶん,Vectorで利用される実装はDenseVectorとRandomAccessSparseVectorあたりな気がするので,これらと比較する.DenseVectorは内部的にはプリミティブ配列と同様に全次元数分の容量を確保する(内部的にはdouble配列だし).RandomAccessSparseVectorは必要な要素だけを確保する(簡単に言うと内部的にはMapみみたいなイメージだと思う).単純に以下のコードで比較してみた.
public void test_performance() {
int count = 100;
int dim = .../*次元数*/;
int testNum = .../*利用した要素数*/;
testDenseVector(count, dim, testNum);
sleep(5000);
testSparseVector(count, dim, testNum);
sleep(5000);
testArrayVector(count, dim, testNum);
sleep(5000);
testDenseVector(count, dim, testNum);
sleep(5000);
testSparseVector(count, dim, testNum);
sleep(5000);
testArrayVector(count, dim, testNum);
}
private void testArrayVector(int count, int dim, int testNum) {
long time = System.currentTimeMillis();
long oldHeapSize = getHeapSize();
double[][] data = new double[count][];
for (int i = 0; i < count; i++) {
data[i] = new double[dim];
for (int j = 0; j < testNum; j++) {
data[i][j] = j;
}
}
long heapSize = getHeapSize();
System.out.println("array vector: "
+ (System.currentTimeMillis() - time) + "ms, " + heapSize
+ "MB(" + (heapSize - oldHeapSize) + "MB)");
for (int i = 0; i < count; i++) {
for (int j = 0; j < testNum; j++) {
data[i][j] = j;
}
}
}
private void testDenseVector(int count, int dim, int testNum) {
long time = System.currentTimeMillis();
long oldHeapSize = getHeapSize();
DenseVector[] vectors = new DenseVector[count];
for (int i = 0; i < count; i++) {
vectors[i] = new DenseVector(dim);
for (int j = 0; j < testNum; j++) {
vectors[i].setQuick(j, j);
}
}
long heapSize = getHeapSize();
System.out.println("dense vector: "
+ (System.currentTimeMillis() - time) + "ms, " + heapSize
+ "MB(" + (heapSize - oldHeapSize) + "MB)");
for (int i = 0; i < count; i++) {
for (int j = 0; j < testNum; j++) {
vectors[i].setQuick(j, j);
}
}
}
private void testSparseVector(int count, int dim, int testNum) {
long time = System.currentTimeMillis();
long oldHeapSize = getHeapSize();
RandomAccessSparseVector[] vectors = new RandomAccessSparseVector[count];
for (int i = 0; i < count; i++) {
vectors[i] = new RandomAccessSparseVector(dim);
for (int j = 0; j < testNum; j++) {
vectors[i].setQuick(j, j);
}
}
long heapSize = getHeapSize();
System.out.println("sparse vector: "
+ (System.currentTimeMillis() - time) + "ms, " + heapSize
+ "MB(" + (heapSize - oldHeapSize) + "MB)");
for (int i = 0; i < count; i++) {
for (int j = 0; j < testNum; j++) {
vectors[i].setQuick(j, j);
}
}
}
private long getHeapSize() {
final Runtime runtime = Runtime.getRuntime();
return (runtime.totalMemory() - runtime.freeMemory()) / 1000000;
}
private void sleep(long time) {
System.gc();
try {
Thread.sleep(time);
} catch (InterruptedException e) {
}
}
dense vector: 103ms, 80MB(80MB) sparse vector: 133ms, 37MB(37MB) array vector: 92ms, 80MB(80MB)
dense vector: 113ms, 80MB(80MB) sparse vector: 237ms, 67MB(67MB) array vector: 99ms, 80MB(80MB)
さらに,要素数を25,000にすると
dense vector: 114ms, 80MB(80MB) sparse vector: 388ms, 110MB(110MB) array vector: 108ms, 80MB(80MB)
で最後に100,000要素を利用すると
dense vector: 130ms, 80MB(80MB) sparse vector: 1469ms, 376MB(376MB) array vector: 110ms, 80MB(80MB)
という感じだった.
というわけで,メモリ的には,次元数の20%以下の利用で済むのであれば,RandomAccessSparseVectorで,それ以上ならDenseVectorが良いかな(時間的なことを考えると,10%くらいでもよいのかも).プリミティブ配列とDenseVectorに大きな差はないけど,若干早いような感じかね.プリミティブ配列で処理するかは,10%の速度向上をとるか,利便性をとるかのどちらが必要かを考えて判断するべきかね.
2011年07月08日
■[Mahout]Tasteについて思うこと

ここ数カ月くらい、研究の実験でMahoutのTasteを結構使ってみました。個人的な感想ですが、フレームワークとしては使えるけど、富豪(?)でない人が利用するのは厳しい気がしました。ここでいう富豪とは最新のPCを数十や数百台とか使って問題解決できる人のことを指してます。最新のPCを数台とかで計算するようなときにはTasteの中身の実装は非効率と感じています(富豪でもコストパフォーマンスを気にする人は微妙かもな)。機械学習みたいな、科学技術計算的な問題を解こうとすると、基本は激しいループ処理なのでその中でnew 〜とかでインスタンスを作ったり、インスタンスの配列やListやMapなどで何かしだすと終わりません…。MahoutのVectorもベクトルの計算するには便利なのですけど、富豪でないとちょっと厳しい気がしてきています。Javaだと、インスタンスの破棄をGCに期待することになり、メモリは消費されるし、GCスレッドも負荷が高くなるし、とかなりやられました(パラレルGC、CMS、G1とかも試したけど、CMSが一番良かった気がする)。そんなわけで、始めはTasteに乗っかって作っていましたが、最終的にはインターフェースは同じ感じだけど、中身の実装は作りこんでいった感じ(Mahoutが実装したKDDCupDataModelだと10G以上のメモリが必要っぽいけど,実装しなおすと6Gくらいでさくっと扱えるようになったりします)。改善して行った点は、インスタンスの生成やListとかMapの利用とかもできる限りやめて、基本はプリミティブな値やその配列とかでやる感じにしました(Javaっぽくないけど)。そんな感じの改善をしていくことで、数十〜数百倍くらいのパフォーマンスが改善できた気がしてます。というわけで、富豪でない私みたいな人用に、プリミティブな配列とかでベクトルや行列の計算ができるようなものを作る必要があるのかな、と考え始めてます…。
2011年05月27日
■[機械学習]混合多項分布

私自身の混合多項分布の理解度はまだいまいちな気もするけど、とりあえず、ここにあったPythonのコードをJavaで書き直してみた。
public class MultinomialMixture {
double[][] dataset;
int num;
int dim;
int mNum;
double[][] c;
double[][] q;
public MultinomialMixture(double[][] dataset, int k) {
this.dataset = dataset;
num = dataset.length;
dim = dataset[0].length;
mNum = k;
q = new double[mNum][dim];
c = new double[num][mNum];
Random random = new Random(1);
for (int i = 0; i < mNum; i++) {
for (int j = 0; j < dim; j++) {
q[i][j] = random.nextDouble();
}
q[i] = normalize(q[i]);
}
}
public void execute(int loop) {
for (int i = 0; i < loop; i++) {
stepE();
stepM();
}
}
double[] normalize(double[] value) {
double[] ret = new double[value.length];
double sum = 0;
for (int i = 0; i < value.length; i++) {
sum += value[i];
}
for (int i = 0; i < value.length; i++) {
ret[i] = value[i] / sum;
}
return ret;
}
double multi(double[] u, double[] x) {
// return prod([q[w] ** d[w] for w in range(W)])
double value = 1;
for (int i = 0; i < u.length; i++) {
value *= Math.pow(u[i], x[i]);
}
return value;
}
void stepE() {
// for n in range(N):
// C[n] = normalize([multi(D[n], Q[k]) for k in range(K)])
for (int i = 0; i < num; i++) {
double[] dn = dataset[i];
for (int j = 0; j < mNum; j++) {
c[i][j] = multi(q[j], dn);
}
c[i] = normalize(c[i]);
}
}
void stepM() {
// for k in range(K):
// Q[k] = normalize([sum([C[n][k] * D[n][w] for n in range(N)]) for w in range(W)])
for (int i = 0; i < mNum; i++) {
double[] value = new double[dim];
for (int j = 0; j < dim; j++) {
value[j] = 0;
for (int k = 0; k < num; k++) {
value[j] += c[k][i] * dataset[k][j];
}
}
value = normalize(value);
q[i] = value;
}
}
double logLikelihood() {
// L = 0
// for n in range(N):
// p = [C[n][k] * multinomial(D[n], Q[k]) for k in range(K)]
// L += log(sum(p))
double l = 0;
for (int i = 0; i < num; i++) {
double sum = 0;
for (int j = 0; j < mNum; j++) {
sum += c[i][j] * multinomial(dataset[i], q[j]);
}
l += Math.log(sum);
}
return l;
}
double multinomial(double[] d, double[] q) {
// return factorial(sum(d)) / prod([factorial(d[w]) for w in range(W)]) * multi(d,q)
double prod = 1;
for (int i = 0; i < dim; i++) {
prod *= factorial(d[i]);
}
return factorial(sum(d)) / prod * multi(q, d);
}
double factorial(double x) {
// if x == 0: return 1
// return reduce(mul, xrange(1, x+1))
double prod = 1;
for (int i = 1; i < x + 1; i++) {
prod *= i;
}
return prod;
}
double sum(double[] v) {
double sum = 0;
for (int i = 0; i < v.length; i++) {
sum += v[i];
}
return sum;
}
public void print() {
System.out.println("L=" + logLikelihood());
// c = new double[num][mNum];
StringBuilder buf = new StringBuilder();
buf.append("C=[");
for (int i = 0; i < num; i++) {
buf.append('[');
for (int j = 0; j < mNum; j++) {
buf.append(c[i][j]);
if (j != mNum - 1) {
buf.append(',');
}
}
buf.append(']');
if (i != num - 1) {
buf.append(',');
}
}
buf.append(']');
System.out.println(buf.toString());
// q = new double[mNum][dim];
buf = new StringBuilder();
buf.append("Q=[");
for (int i = 0; i < mNum; i++) {
buf.append('[');
for (int j = 0; j < dim; j++) {
buf.append(q[i][j]);
if (j != dim - 1) {
buf.append(',');
}
}
buf.append(']');
if (i != mNum - 1) {
buf.append(',');
}
}
buf.append(']');
System.out.println(buf.toString());
}
}
使うときは以下の感じ。
int k = 2; MultinomialMixture mm = new MultinomialMixture(dataset, k); mm.execute(10); mm.print();
かなり勢いで書いたから、細かいことは後で直すとして、Pythonのやつと同じ感じの結果だったからとりあえず、よしとする。
2011年04月10日
■[Others]学生になりました

最近,このブログの更新も放置気味ですが,今月から九州大学大学院システム情報科学研究院情報学専攻の社会人博士課程に入学しました.そんなわけで,10年ぶりくらいに学生です(学割が使えるのかしら…).大学に戻って何がしたいかというと,機械学習やデータマイニングを再度勉強しようかと思っています.そんでもって,その知識を使って利益を上げていく仕組みを作るっていうあたりが目指すところかと(なので,何か仕事があればください).福岡にもこの前,初めて行ったくらいな感じで,近頃の大学事情とかもよくわかっていなかったりするので,学生の方々にはいろいろと教えてもらえると嬉しいなとか思っています.そんな感じで,がんばっていきたいと思いますのでよろしくおねがいしますー.
2011年04月09日
■[Mahout]Random Forestsを試す

ここに書いてあるけど、Random Forestsを試してみる。Hadoop環境が構築済みで、Mahoutをチェックアウトしてビルド(mvn install -DskipTestsをやっておく)してあることが前提で進めると、まず、テストデータを取得してHDFSに入れる。
$ wget http://archive.ics.uci.edu/ml/machine-learning-databases/glass/glass.data $ hadoop fs -mkdir mahout $ hadoop fs -mkdir mahout/glass $ hadoop fs -put glass.data mahout/glass
そんで、説明ファイルを作る。
$ hadoop jar $MAHOUT_HOME/core/target/mahout-core-0.5-SNAPSHOT-job.jar org.apache.mahout.df.tools.Describe -p mahout/glass/glass.data -f mahout/glass/glass.info -d I 9 N L
以下のようにやれば中身を確認できる。
$ hadoop fs -cat mahout/glass/glass.info
Iが無視、Nが数値、Cがカテゴリ文字列、Lがラベル(目的変数)な感じ。N N N N みたいに書くのは面倒だから、4 N みたいな省略も可能。そんで、実行。
$ hadoop jar $MAHOUT_HOME/examples/target/mahout-examples-0.5-SNAPSHOT-job.jar org.apache.mahout.df.BreimanExample -d mahout/glass/glass.data -ds mahout/glass/glass.info -i 10 -t 100 ... 11/04/09 07:29:09 INFO df.BreimanExample: ******************************************** 11/04/09 07:29:09 INFO df.BreimanExample: Selection error : 0.2857142857142857 11/04/09 07:29:09 INFO df.BreimanExample: Single Input error : 0.3 11/04/09 07:29:09 INFO df.BreimanExample: One Tree error : 0.41356654135338333 11/04/09 07:29:09 INFO df.BreimanExample: Mean Random Input Time : 0h 0m 0s 280 11/04/09 07:29:09 INFO df.BreimanExample: Mean Single Input Time : 0h 0m 0s 95 11/04/09 07:29:09 INFO df.BreimanExample: Mean Random Input Num Nodes : 6722 11/04/09 07:29:09 INFO df.BreimanExample: Mean Single Input Num Nodes : 11326
という感じで実行結果が表示される。
■[Mahout]Random Forests(Partial Implementation)を試す

$ wget http://nsl.cs.unb.ca/NSL-KDD/KDDTrain+.arff $ wget http://nsl.cs.unb.ca/NSL-KDD/KDDTest+.arff
ダウンロードしたら,それらのファイルで@で始まる行を削除する.そしたら,それらのファイルをhdfsに上げる.
$ hadoop fs -mkdir mahout $ hadoop fs -mkdir mahout/nslkdd $ hadoop fs -put KDDTrain+.arff mahout/nslkdd $ hadoop fs -put KDDTest+.arff mahout/nslkdd
次に説明ファイルを作る.
$ hadoop jar $MAHOUT_HOME/core/target/mahout-core-0.5-SNAPSHOT-job.jar org.apache.mahout.df.tools.Describe -p mahout/nslkdd/KDDTrain+.arff -f mahout/nslkdd/KDDTrain+.info -d N 3 C 2 N C 4 N C 8 N 2 C 19 N L
できたら,モデルを作るために以下を実行.
$ hadoop jar $MAHOUT_HOME/examples/target/mahout-examples-0.5-SNAPSHOT-job.jar org.apache.mahout.df.mapreduce.BuildForest -Dmapred.max.split.size=1874231 -oob -d mahout/nslkdd/KDDTrain+.arff -ds mahout/nslkdd/KDDTrain+.info -sl 5 -p -t 100 -o nsl-forest
$ hadoop jar $MAHOUT_HOME/examples/target/mahout-examples-0.5-SNAPSHOT-job.jar org.apache.mahout.df.mapreduce.TestForest -i mahout/nslkdd/KDDTest+.arff -ds mahout/nslkdd/KDDTrain+.info -m nsl-forest -a -mr -o predictions
という感じで,一通り完了.
がしかし,今日時点のtrunkはバグっていました….数日待てば修正されそうな気もするけど,とりあえず,試したかったので,以下の修正で回避しました.
Index: core/src/main/java/org/apache/mahout/df/mapreduce/Classifier.java
===================================================================
--- core/src/main/java/org/apache/mahout/df/mapreduce/Classifier.java (リビジョン 1090534)
+++ core/src/main/java/org/apache/mahout/df/mapreduce/Classifier.java (作業コピー)
@@ -158,6 +158,8 @@
// read all the output
for (Path path : outfiles) {
+log.info("path = {}", path.toString());
+if(path.getName().startsWith("part-m-")){
FSDataOutputStream ofile = null;
try {
for (Pair<LongWritable,Text> record : new SequenceFileIterable<LongWritable,Text>(path, true, conf)) {
@@ -179,7 +181,7 @@
}
} finally {
ofile.close();
- }
+ }}
}
}
Index: core/src/main/java/org/apache/mahout/df/mapreduce/partial/PartialBuilder.java
===================================================================
--- core/src/main/java/org/apache/mahout/df/mapreduce/partial/PartialBuilder.java (リビジョン 1090534)
+++ core/src/main/java/org/apache/mahout/df/mapreduce/partial/PartialBuilder.java (作業コピー)
@@ -170,6 +170,7 @@
// read all the outputs
int index = 0;
for (Path path : outfiles) {
+if(path.getName().startsWith("part-m-")){
for (Pair<TreeID,MapredOutput> record : new SequenceFileIterable<TreeID, MapredOutput>(path, conf)) {
TreeID key = record.getFirst();
MapredOutput value = record.getSecond();
@@ -181,7 +182,7 @@
}
processOutput(firstIds, key, value, callback);
index++;
- }
+ }}
}
// make sure we got all the keys/values
Index: core/src/main/java/org/apache/mahout/df/mapreduce/partial/Step0Job.java
===================================================================
--- core/src/main/java/org/apache/mahout/df/mapreduce/partial/Step0Job.java (リビジョン 1090534)
+++ core/src/main/java/org/apache/mahout/df/mapreduce/partial/Step0Job.java (作業コピー)
@@ -139,10 +139,12 @@
// read all the outputs
for (Path path : outfiles) {
+log.info("path = {}", path.toString());
+if(path.getName().startsWith("part-m-")){
for (Pair<IntWritable,Step0Output> record : new SequenceFileIterable<IntWritable,Step0Output>(path, conf)) {
keys.add(record.getFirst().get());
values.add(record.getSecond());
- }
+ }}
}
return processOutput(keys, values);
という感じで,Random Forestsを試してみたものの,今やりたいことがこのままでは適用できない感じだから,このRandom Forestsを手直しするか,自前でCARTを実装するか,迷い中….どうしようかな.
2011年03月18日
■[Others][Android]Smartia(LifeTouch)を購入

携帯も今年は買い換えなさそうな感じで,Android機が手元に一台もないのでどうしようかなっと思っていたところに,19800円でBIGLOBEでSmartiaを購入できるということで購入してみた.Androidのバージョンは2.1です.ざっと,いろいろ使ってみた感じの感想としては,iPadやiPod Touchのようなレベルをイメージするとかなり残念な気持ちになる.タッチパネルの反応がいまいちな感じもあるし,液晶部分がフィルムっぽいのもなんだかな,という感じ.開発・検証機的なモチベーションで買ったから別に問題ないけど,日常に使う目的で買ったら,これに19800円は痛い感じだな….それにAndroidのバージョンも上がるのかもわからないし.そんな感じで,結局はiPadとかはよく出来ているなという気持ちになった.