Tworzenie osadzeń [embeddings] dla wyszukiwania wektorowego Neo4j

Jeśli temat 'wyszukiwania wektorowego' jest dla Ciebie nowy, przejdź na wcześniejszą stronę tego bloga by zapoznać się z tym przedmiotem. W tym artykule dowiesz się jak tworzyć osadzenia - wektory w wielowymiarowej przestrzeni. Czyli zamiana słów, zdań i zapytań na wektory.

Odczytanie danych z pliku Excela

Tworzymy embeddings dla opisów produktów. Nasza baza produktów znajduje się w pliku Excela. Użyjemy modułu pandas który wykorzystuje openpyxl by odczytać plik Excela. Utworzone wektory zapiszemy w pliku emb_result.csv [kolumny sku;description;embedding

from sentence_transformers import SentenceTransformer
import numpy as np
import pandas as pd
import re

#Model 50 jezykow w tym Polski
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')

#Sprawdzmy ile tokenów słów obsługuje model:
print("Max Sequence Length:", model.max_seq_length)

#Ustawienie maksymalnej długości kodowanych sekwencji
model.max_seq_length = 128

#Otwarcie pliku Excela, skoroszyt 0
file_name =  "produkty.xlsx"
sheet =  0

#Otwarcie pliku, kolumny 0 i 19
df = pd.read_excel(io=file_name, sheet_name=sheet, usecols=[0, 19])
#Listuj 5 pierwszych linii pliku
#print(df.head(5)) 


#Testowy zapis do pliku:
#kolumna_do_zapisu = df[['sku', 'product description']]
#kolumna_do_zapisu.to_csv('test.csv', index=False)

file = open("emb_result.csv", "w", encoding='utf-8')

for index, row in df.iterrows():
    emb = model.encode(row['product description'])
    emb_str = np.array2string(emb, separator=',')
    emb_str = emb_str.replace("\n","")
    emb_str =emb_str.replace(" ","")
    emb_str = re.sub(r'[\[\]]', '', emb_str)
    file.write(str(row['sku']) + ';' + row['product description'] + ';' + emb_str)
    file.write("\n")

file.close()

Import osadzeń do grafowej bazy danych Neo4j

Mając plik z osadzeniami [wektorami], możemy je wgrać do Neo4j. To tylko test więc utworzymy węzły z kilkoma właściwościami [id, name, description, embedding] a same węzły nie będą mieć żadnych relacji z innymi.

Importując wektory musimy wiedzieć iż muszą one być tablicą z danymi typu float. Jeśli wgramy je inaczej, będą po prostu tekstem który nie będzie nadawać się do przeszukiwania. Mamy zatem dwa sposoby:

Użycie funkcji toFloatList(), aby przekonwertować listę ciągów na listę zmiennoprzecinkową:

LOAD CSV FROM 'file:///emb_result.csv' AS line FIELDTERMINATOR ';'
CREATE (:Product {id: line[0], name: line[0], description: line[1], embedding: toFloatList(split(line[2], ","))})

Lub wykorzystać 'list comprehension' by utworzyć nową listę na podstawie istniejącej (ciągów znaków z CSV):

LOAD CSV FROM 'file:///emb_result.csv' AS line FIELDTERMINATOR ';'
CREATE (:Product {id: line[0], name: line[0], description: line[1], embedding: [x in split(line[2], ",") | toFloat(x)]})

Porównywanie opisów - testy

Mamy nasze dane w bazie. Zróbmy test - wyszukajmy podobne opisy do referencyjnego:

from neo4j import GraphDatabase
from sentence_transformers import SentenceTransformer
import numpy as np

#Jezyk polski:
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')

#Łączymy się z Neo4j
URI = "bolt://localhost:7687"
AUTH = ("neo4j","haslo")

driver = GraphDatabase.driver(URI, auth=AUTH)
session = driver.session() 

emb = model.encode('Searching a large corpus with millions of embeddings can be time-consuming if exact nearest neighbor search is used')
emb_str = np.array2string(emb, separator=',')
emb_str = emb_str.replace("\n","")
emb_str =emb_str.replace(" ","")

query = "CALL db.index.vector.queryNodes('product-embeddings', 15, "+str(emb_str)+") " \
       + "YIELD node AS similarAbstract, score MATCH(similarAbstract) " \
       + "RETURN similarAbstract.name As product, similarAbstract.description AS description, score ORDER BY score DESC"

result = session.run(query)

for i in result:
    print(str(i['score'])+" "+i['product']+" "+i['description'])

Będzie Ci dużo wygodniej jeśli wykonasz wszystkie te skrypty w notatniku Jupyter [jupyterlab]. Jeśli nie wiesz jak to zrobić, zapoznaj się z instrukcją dla wersji desktopowej lub online - colaboratory.