ベクトル間の余弦の類似性を見つけるにはどうすればよいですか?
2行のテキスト間の関連性を測定するために類似性を見つける必要があります。
たとえば、次のような2つの文があります。
ユーザーインターフェースのためのシステム
ユーザーインターフェースマシン
…およびtF-idfの後のそれぞれのベクトル、続いてLSIを使用した正規
[1,0.5]
化[0.5,1]
。
これらのベクトル間の類似性を測定するにはどうすればよいですか?
ベクトル間の余弦の類似性を見つけるにはどうすればよいですか?
2行のテキスト間の関連性を測定するために類似性を見つける必要があります。
たとえば、次のような2つの文があります。
ユーザーインターフェースのためのシステム
ユーザーインターフェースマシン
…およびtF-idfの後のそれぞれのベクトル、続いてLSIを使用した正規
[1,0.5]
化[0.5,1]
。
これらのベクトル間の類似性を測定するにはどうすればよいですか?
このような単純なタスクでサードパーティのライブラリに依存することを避けたい場合は、プレーンな Java 実装を次に示します。
public static double cosineSimilarity(double[] vectorA, double[] vectorB) {
double dotProduct = 0.0;
double normA = 0.0;
double normB = 0.0;
for (int i = 0; i < vectorA.length; i++) {
dotProduct += vectorA[i] * vectorB[i];
normA += Math.pow(vectorA[i], 2);
normB += Math.pow(vectorB[i], 2);
}
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
この関数は、2 つのベクトルの長さが同じであると想定していることに注意してください。安全のために明示的にチェックすることをお勧めします。
http://en.wikipedia.org/wiki/Cosine_similarityをご覧ください。
ベクトル A と B があるとします。
類似度は次のように定義されます。
cosine(theta) = A . B / ||A|| ||B||
For a vector A = (a1, a2), ||A|| is defined as sqrt(a1^2 + a2^2)
For vector A = (a1, a2) and B = (b1, b2), A . B is defined as a1 b1 + a2 b2;
So for vector A = (a1, a2) and B = (b1, b2), the cosine similarity is given as:
(a1 b1 + a2 b2) / sqrt(a1^2 + a2^2) sqrt(b1^2 + b2^2)
例:
A = (1, 0.5), B = (0.5, 1)
cosine(theta) = (0.5 + 0.5) / sqrt(5/4) sqrt(5/4) = 4/5
public class CosineSimilarity extends AbstractSimilarity {
@Override
protected double computeSimilarity(Matrix sourceDoc, Matrix targetDoc) {
double dotProduct = sourceDoc.arrayTimes(targetDoc).norm1();
double eucledianDist = sourceDoc.normF() * targetDoc.normF();
return dotProduct / eucledianDist;
}
}
最近、大学の情報検索ユニットで tf-idf の作業を行いました。Jama: Java Matrix Packageを使用するこの Cosine Similarity メソッドを使用しました。
完全なソース コードについては、IR Math with Java : Similarity Measures を参照してください。これは、いくつかの異なる類似度測定をカバーする非常に優れたリソースです。
Java のマトリックス コードについては、Coltライブラリを使用することをお勧めします。これがある場合、コードは次のようになります (テストもコンパイルもされていません)。
DoubleMatrix1D a = new DenseDoubleMatrix1D(new double[]{1,0.5}});
DoubleMatrix1D b = new DenseDoubleMatrix1D(new double[]{0.5,1}});
double cosineDistance = a.zDotProduct(b)/Math.sqrt(a.zDotProduct(a)*b.zDotProduct(b))
上記のコードを変更して、いずれかのBlas.dnrm2()
方法を使用するかAlgebra.DEFAULT.norm2()
、ノルム計算を行うこともできます。好みによっては、まったく同じ結果が読みやすくなります。
少し前にテキスト マイニングを行っていたとき、Java でさまざまなメトリックを幅広く提供するSimMetricsライブラリを使用していました。さらに必要な場合は、常にR と CRANを参照してください。
しかし、ウィキペディアの説明からコーディングするのはかなり簡単な作業であり、良い練習になる可能性があります。
これを使用したベクトルのスパース表現についてMap(dimension -> magnitude)
は、scala バージョンです (Java 8 でも同様のことができます)。
def cosineSim(vec1:Map[Int,Int],
vec2:Map[Int,Int]): Double ={
val dotProduct:Double = vec1.keySet.intersect(vec2.keySet).toList
.map(dim => vec1(dim) * vec2(dim)).sum
val norm1:Double = vec1.values.map(mag => mag * mag).sum
val norm2:Double = vec2.values.map(mag => mag * mag).sum
return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2))
}