結論
deeplearning4jのKmeansを使うときにdistanceFunction
にcosinesimilarity
を使ってはいけない。
理由
※deeplearning4jは2017/7/2時点で最新版の0.8.0
です。
deeplearning4jにはKmeansの関数があります。以下のような形で使います。
KMeansClustering kmc = KMeansClustering.setup(num, iter, distanceFunction);
ClusterSet cs = kmc.applyTo(pointsLst);
ここで、num
はクラスタ数、iter
は繰り返し回数(10
がよく使われる)、destanceFunction
は距離関数(euclidean
かmanhattan
かcosinesimilarity
を指定できる)です。
さて、ここに地雷があります。destanceFunction
にcosinesimilarity
を指定すると、思ったような結果は出てきません。
※「普通euclidean
を指定するだろ」って方は問題なしです。
鋭い方はお気づきでしょうが、cosinesimilarity
だけ類似度です。残りの2つは距離です。すなわち、cosinesimilarity
は値が大きいほど(MAXは1)類似度が高く、残りの2つは値が小さいほど類似度が高いということです。
deeplearning4jのプログラム的にはこんな感じになっています。ClusterSet.class
にて、
public Pair<Cluster, Double> nearestCluster(Point point) {
Cluster nearestCluster = null;
double minDistance = Float.MAX_VALUE;
double currentDistance;
for (Cluster cluster : getClusters()) {
currentDistance = cluster.getDistanceToCenter(point);
if (currentDistance < minDistance) {
minDistance = currentDistance;
nearestCluster = cluster;
}
}
return new Pair<>(nearestCluster, minDistance);
}
この関数で今あるクラスタと自分の距離を計算し、一番距離が短いクラスタに分類するわけです。すごく真当な処理ですが、cosinesimilarity
だけ値の解釈が逆なので、ここでとんでもないことになります。
おわりに
どうしてもcosinesimilarity
を使いたい場合は、現状だとextendsクラス作るしかないです。そこだけ例外処理加えてプルリク送ろうかと思いましたが、なんかコード汚いので辞めました。ほっとくのもどうかと思うのですが、どうしましょ。
追記
issuesに登録されていました。
https://github.com/deeplearning4j/deeplearning4j/issues/2361
どうやら「正常な動作」ということで片付いてるようです。たしかに・・・正常な動作ですけど「正しい処理」ではないですよね。使えなくするか、無理やり使うならcosinesimilarity
をcosinedistance
として、s[cosinedistance]=1-s[cosinesimilarity]
のみ受け付けるようにした方が良いと思いますね。