Posterior a investigar en diferentes repositorios y sitios de internet al final nos encontramos con la respuesta que te compartimos más adelante.
Solución:
Versión corta calculando la similitud con pdist
:
S2 = squareform(1-pdist(S1,'cosine')) + eye(size(S1,1));
Explicación:
pdist(S1,'cosine')
calcula la distancia del coseno entre todas las combinaciones de filas en S1
. Por lo tanto, la similitud entre todas las combinaciones es 1 - pdist(S1,'cosine')
.
Podemos convertir eso en una matriz cuadrada donde el elemento (i,j)
corresponde a la similitud entre filas i
y j
con squareform(1-pdist(S1,'cosine'))
.
Finalmente, tenemos que establecer la diagonal principal en 1 porque la similitud de una fila consigo misma es obviamente 1, pero eso no se calcula explícitamente por pdist
.
Su código recorre todas las filas, y para cada fila recorre (aproximadamente) la mitad de las filas, calculando el producto escalar para cada combinación única de filas:
n_row = size(S1,1);
norm_r = sqrt(sum(abs(S1).^2,2)); % same as norm(S1,2,'rows')
S2 = zeros(n_row,n_row);
for i = 1:n_row
for j = i:n_row
S2(i,j) = dot(S1(i,:), S1(j,:)) / (norm_r(i) * norm_r(j));
S2(j,i) = S2(i,j);
end
end
(Me he tomado la libertad de completar su código para que realmente se ejecute. Tenga en cuenta la inicialización de S2
antes del bucle, ¡esto ahorra mucho tiempo!)
Si observa que el producto escalar es un producto matricial de un vector fila con un vector columna, puede ver que lo anterior, sin el paso de normalización, es idéntico a
S2 = S1 * S1.';
Esto se ejecuta mucho más rápido que el ciclo explícito, incluso si (¿quizás?) No puede usar la simetría. La normalización es simplemente dividir cada fila por norm_r
y cada columna por norm_r
. Aquí multiplico los dos vectores para producir una matriz cuadrada para normalizar con:
S2 = (S1 * S1.') ./ (norm_r * norm_r.');