PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Postgres :index sur la similarité cosinus des tableaux flottants pour la recherche un-à-plusieurs

Je suppose qu'aucune extension ne fait cela, j'ai donc trouvé une solution de contournement limitée :

Si A et B sont tous les deux normalisés (longueur 1), cos(A, B) = 1 - 0.5 * ||A - B||^2 . ||A - B|| est la distance euclidienne, et cos(A, B) est la similitude cosinus. Donc, une plus grande distance euclidienne <=> une moindre similitude cosinus (a un sens intuitivement si vous imaginez un cercle unitaire), et si vous avez des vecteurs non normaux, changer leurs amplitudes sans changer leurs directions n'affecte pas leurs similitudes cosinus. Super, je peux donc normaliser mes vecteurs et comparer leurs distances euclidiennes...

Il y a une belle réponse ici à propos de Cube , qui prend en charge les points n-dimensionnels et les index GiST sur Euclidean distance, mais il ne prend en charge que 100 dimensions ou moins (peut être piraté plus haut, mais j'ai eu des problèmes autour de 135 et plus, alors maintenant j'ai peur). Nécessite également Postgres 9.6 ou version ultérieure.

Donc :

  1. Assurez-vous que je ne me soucie pas d'avoir au maximum 100 dimensions. Mettez à niveau vers Postgres 9.6 ou version ultérieure.
  2. Remplir mon tableau avec des tableaux pour représenter les vecteurs.
  3. Normaliser les vecteurs pour créer une colonne supplémentaire de cube points. Créez un index GiST sur cette colonne.
  4. Ordonner par distance euclidienne croissante pour obtenir la similarité cosinus décroissante :EXPLAIN SELECT * FROM mytable ORDER BY normalized <-> cube(array[1,2,3,4,5,6,7,8,9,0]) LIMIT 10;

Si j'ai besoin de plus de 100 dimensions, je pourrais peut-être y parvenir en utilisant plusieurs colonnes indexées. Mettra à jour la réponse dans ce cas.

Mise à jour : Je suis presque sûr que je ne peux rien faire en divisant le vecteur> 100 dimensions en plusieurs colonnes. Je finis par devoir scanner toute la table.