Cosine Similarity article on Wikipedia
Can you show the vectors here (in a list or something) and then do the math, and let us see how it works?
Cosine Similarity article on Wikipedia
Can you show the vectors here (in a list or something) and then do the math, and let us see how it works?
Simple JAVA code to calculate cosine similarity
/**
* Method to calculate cosine similarity of vectors
* 1 - exactly similar (angle between them is 0)
* 0 - orthogonal vectors (angle between them is 90)
* @param vector1 - vector in the form [a1, a2, a3, ..... an]
* @param vector2 - vector in the form [b1, b2, b3, ..... bn]
* @return - the cosine similarity of vectors (ranges from 0 to 1)
*/
private double cosineSimilarity(List<Double> vector1, List<Double> vector2) {
double dotProduct = 0.0;
double normA = 0.0;
double normB = 0.0;
for (int i = 0; i < vector1.size(); i++) {
dotProduct += vector1.get(i) * vector2.get(i);
normA += Math.pow(vector1.get(i), 2);
normB += Math.pow(vector2.get(i), 2);
}
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
Let me try explaining this in terms of Python code and some graphical Mathematics formulas.
Suppose we have two very short texts in our code:
texts = ["I am a boy", "I am a girl"]
And we want to compare the following query text to see how close the query is to the texts above, using fast cosine similarity scores:
query = ["I am a boy scout"]
How should we compute the cosine similarity scores? First, let's build a tfidf matrix in Python for these texts:
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(texts)
Next, let's check the values of our tfidf matrix and its vocabulary:
print(tfidf_matrix.toarray())
# output
array([[0.57973867, 0.81480247, 0. ],
[0.57973867, 0. , 0.81480247]])
Here, we get a tfidf matrix with tfidf values of 2 x 3, or 2 documents/text x 3 terms. This is our tfidf document-term matrix. Let's see what are the 3 terms by calling vectorizer.vocabulary_
print(vectorizer.vocabulary_)
# output
{'am': 0, 'boy': 1, 'girl': 2}
This tells us that our 3 terms in our tfidf matrix are 'am', 'boy' and 'girl'. 'am' is at column 0, 'boy' is at column 1, and 'girl' is at column 2. The terms 'I' and 'a' has been removed by the vectorizer because they are stopwords.
Now we have our tfidf matrix, we want to compare our query text with our texts and see how close our query is to our texts. To do that, we can compute the cosine similarity scores of the query vs the tfidf matrix of the texts. But first, we need to compute the tfidf of our query:
query = ["I am a boy scout"]
query_tfidf = vectorizer.transform([query])
print(query_tfidf.toarray())
#output
array([[0.57973867, 0.81480247, 0. ]])
Here, we computed the tfidf of our query. Our query_tfidf has a vector of tfidf values [0.57973867, 0.81480247, 0. ], which we will use to compute our cosine similarity multiplication scores. If I am not mistaken, the query_tfidf values or vectorizer.transform([query]) values are derived by just selecting the row or document from tfidf_matrix that has the most word matching with the query. For example, row 1 or document/text 1 of the tfidf_matrix has the most word matching with the query text which contains "am" (0.57973867) and "boy" (0.81480247), hence row 1 of the tfidf_matrix of [0.57973867, 0.81480247, 0. ] values are selected to be the values for query_tfidf. (Note: If someone could help further explain this that would be good)
After computing our query_tfidf, we can now matrix multiply or dot product our query_tfidf vector with our text tfidf_matrix to obtain the cosine similarity scores.
Recall that cosine similarity score or formula is equal to the following:
cosine similarity score = (A . B) / ||A|| ||B||
Here, A = our query_tfidf vector, and B = each row of our tfidf_matrix
Note that: A . B = A * B^T, or A dot product B = A multiply by B Transpose.
Knowing the formula, let's manually compute our cosine similarity scores for query_tfidf, then compare our answer with the values provided by the sklearn.metrics cosine_similarity function. Let's manually compute:
query_tfidf_arr = query_tfidf.toarray()
tfidf_matrix_arr = tfidf_matrix.toarray()
cosine_similarity_1 = np.dot(query_tfidf_arr, tfidf_matrix_arr[0].T) /
(np.linalg.norm(query_tfidf_arr) * np.linalg.norm(tfidf_matrix_arr[0]))
cosine_similarity_2 = np.dot(query_tfidf_arr, tfidf_matrix_arr[1].T) /
(np.linalg.norm(query_tfidf_arr) * np.linalg.norm(tfidf_matrix_arr[1]))
manual_cosine_similarities = [cosine_similarity_1[0], cosine_similarity_2[0]]
print(manual_cosine_similarities)
#output
[1.0, 0.33609692727625745]
Our manually computed cosine similarity scores give values of [1.0, 0.33609692727625745]. Let's check our manually computed cosine similarity score with the answer value provided by the sklearn.metrics cosine_similarity function:
from sklearn.metrics.pairwise import cosine_similarity
function_cosine_similarities = cosine_similarity(query_tfidf, tfidf_matrix)
print(function_cosine_similarities)
#output
array([[1.0 , 0.33609693]])
The output values are both the same! The manually computed cosine similarity values are the the same as the function computed cosine similarity values!
Hence, this simple explanation shows how the cosine similarity values are computed. Hope you found this explanation helpful.
Two Vectors A and B exists in a 2D space or 3D space, the angle between those vectors is cos similarity.
If the angle is more (can reach max 180 degree) which is Cos 180=-1 and the minimum angle is 0 degree. cos 0 =1 implies the vectors are aligned to each other and hence the vectors are similar.
cos 90=0 (which is sufficient to conclude that the vectors A and B are not similar at all and since distance cant be negative, the cosine values will lie from 0 to 1. Hence, more angle implies implies reducing similarity (visualising also it makes sense)
Here's a simple Python code to calculate cosine similarity:
import math
def dot_prod(v1, v2):
ret = 0
for i in range(len(v1)):
ret += v1[i] * v2[i]
return ret
def magnitude(v):
ret = 0
for i in v:
ret += i**2
return math.sqrt(ret)
def cos_sim(v1, v2):
return (dot_prod(v1, v2)) / (magnitude(v1) * magnitude(v2))