Как вычислить сходство косинусов двух векторов?

Как найти сходство косинусов между векторами?

Мне нужно найти сходство, чтобы измерить взаимосвязь между двумя строками текста.

Например, у меня есть два предложения типа:

для пользовательского интерфейса

машина пользовательского интерфейса

... и их соответствующие векторы после tF-idf, за которыми следует нормализация с использованием LSI, например [1,0.5] и [0.5,1].

Как измерить смехотворность между этими векторами?

Ответ 1

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: пакет Matrix Java.

Для полного исходного кода см. IR Math с Java: меры подобия, действительно хороший ресурс, который охватывает несколько разных измерений сходства.

Ответ 2

Если вы хотите избежать использования сторонних библиотек для такой простой задачи, вот простая реализация 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));
}

Обратите внимание, что функция предполагает, что два вектора имеют одинаковую длину. Вы можете явно проверить его на предмет безопасности.

Ответ 3

Посмотрите на: 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

Ответ 4

Для кода матрицы в 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() для вычисления нормы. Точно такой же результат, который является более читаемым, зависит от вкуса.

Ответ 5

Когда я некоторое время назад работал с текстовым методом, я использовал библиотеку SimMetrics, которая предоставляет широкий диапазон различных показателей в Java. Если это случилось, вам нужно больше, тогда всегда есть R и CRAN, чтобы посмотреть.

Но кодирование его из описания в Википедии является довольно тривиальной задачей и может быть хорошим упражнением.

Ответ 6

Для разреженного представления векторов с использованием 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))
}