TSVM

TSVMはTransductive SVMなのだけど、結構前からあるものだけど、今まで関わることなく、生きてきました (^^; というわけで、ここにきて勉強してみる。そもそもTransductiveっていうのが日本語で何なのかわかりにくいのだけど、Transductiveと比較されるのがInductiveで、Inductiveは帰納的という感じ。帰納的っていうのもいまいちわかりにくい気もするけど、ある(限られた)事象から規則を見出す方法という感じかな。Transductiveは変換的な感じなのかと思うけど、日本語はよくわからない…。機械学習的にはInductiveは訓練例があってそこからモデルを学習して、テスト例に適用して評価する感じだけど、Transductiveは事前にテスト例もわかっていて、訓練例とテスト例からモデルを学習して、テスト例を評価する感じ。つまり、ラベルがないものも情報として利用してモデルの精度を上げるのを目指す感じ。そんな感じで、TSVMもSVM lightで対応済みらしいので、ちょっと試してみなければならない。

人工データを作る

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として取得できる.

混合多項分布

私自身の混合多項分布の理解度はまだいまいちな気もするけど、とりあえず、ここにあった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のやつと同じ感じの結果だったからとりあえず、よしとする。