<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>wektory &#8211; Solr.pl</title>
	<atom:link href="https://solr.pl/tag/wektory/feed/" rel="self" type="application/rss+xml" />
	<link>https://solr.pl</link>
	<description>All things to be found - Blog related to Apache Solr &#38; Lucene projects - https://solr.apache.org</description>
	<lastBuildDate>Mon, 18 Nov 2024 07:43:15 +0000</lastBuildDate>
	<language>pl-PL</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>
	<item>
		<title>Apache Solr i wektory &#8211; jak się do tego zabrać?</title>
		<link>https://solr.pl/2024/11/18/apache-solr-i-wektory-jak-sie-do-tego-zabrac/</link>
					<comments>https://solr.pl/2024/11/18/apache-solr-i-wektory-jak-sie-do-tego-zabrac/#respond</comments>
		
		<dc:creator><![CDATA[Rafał Kuć]]></dc:creator>
		<pubDate>Mon, 18 Nov 2024 07:43:12 +0000</pubDate>
				<category><![CDATA[Solr]]></category>
		<category><![CDATA[solr]]></category>
		<category><![CDATA[wektory]]></category>
		<guid isPermaLink="false">https://solr.pl/?p=1361</guid>

					<description><![CDATA[Wyszukiwanie semantyczne i wszystko co związane z uczeniem maszynowym stało się bardzo popularnym tematem. Prawdę powiedziawszy, nie tylko samo wyszukiwanie semantyczne, ale ze względu na ogromną popularność tzw. Large Language Models i nie tylko, wiele organizacji korzystających z Solr próbuje]]></description>
										<content:encoded><![CDATA[
<p>Wyszukiwanie semantyczne i wszystko co związane z uczeniem maszynowym stało się bardzo popularnym tematem. Prawdę powiedziawszy, nie tylko samo wyszukiwanie semantyczne, ale ze względu na ogromną popularność tzw. Large Language Models i nie tylko, wiele organizacji korzystających z Solr próbuje wdrożyć różnego rodzaju logikę zapytań opartą o modele maszynowe, techniki RAG, czy rescoring. Ze względu na to, że przez pewien czas było tutaj dość cicho czas pochylić się nad tematem. W tym wpisie skupimy się na wyszukiwaniu i zobaczymy co oferuje Solr jeżeli chodzi o wyszukiwanie danych w oparciu wektory. </p>



<span id="more-1361"></span>



<h2 class="wp-block-heading">Krótki opis problemu</h2>



<p>Załóżmy, że nasze dokumenty składają się z szeregu pól, które wykorzystywane są do wyszukiwania pełnotekstowego oraz pola, które chcemy wykorzystać do wyszukiwania semantycznego. Te pole zawiera tagi opisujące dokument. </p>



<p>Ważne, aby podkreślić, iż nie jest to idealny przykład danych &#8211; np. nie skorzystamy z kontekstu, same tagi są pojedynczymi słowami, a my dodatkowo chcemy budować jeden wektor dla dokumentu, co nie sprzyja wykorzystaniu tagów. Ale tak, czy inaczej skorzystamy z tego podejścia i zindeksujemy tak przygotowane dane w Solr. </p>



<h2 class="wp-block-heading">Przygotowanie Solr</h2>



<p>Aby zrealizować wyszukiwanie semantyczne skorzystamy z tzw. Dense Vector Search, czyli wyszukiwania przy pomocy wektorów, które zostaną wygenerowane przez model uczenia maszynowego zarówno dla dokumentów, jak i zapytania. Podejście charakteryzuje się tym, iż korzystamy z wektorów o z góry zdefiniowanej, stałej liczbie wymiarów. Zarówno dokumenty podczas indeksowania, jak i zapytania  tłumaczone są na wektory, ale o tym trochę później. Na początek przygotujmy strukturę kolekcji w Solr.</p>



<p>W tym celu skorzystamy z Solr 9.7.0 oraz pola opartego o klasę <strong>solr.DenseVectorField</strong>. W tym celu stworzymy sobie nowy typ wyglądający następująco:</p>



<pre class="wp-block-code"><code class="">{
  "name":"knn_vector_384",
  "class":"solr.DenseVectorField",
  "vectorDimension":384,
  "similarityFunction":"cosine",
  "knnAlgorithm":"hnsw"
}</code></pre>



<p>Mamy tutaj nazwę <strong>knn_vector_384</strong>, klasę <strong>solr.DenseVectorField</strong>, wymiar wektora <strong>384</strong>, metodę podobieństwa wektorów <strong>cosine</strong> oraz algorytm wyliczania bliskości &#8211; <strong>hnsw</strong> &#8211; na razie jedyny dostępny. Pełną dokumentację klasy  <strong>solr.DenseVectorField</strong> znajdziemy w <a href="https://solr.apache.org/guide/solr/latest/query-guide/dense-vector-search.html">dokumentacji</a>. Warto powiedzieć, że będziemy korzystać z modelu, który generuje wektory o 384 wymiarach, a co za tym idzie tak definiujemy nasz typ. </p>



<p>Nasze dokumenty będą składać się z 5 pól:</p>



<ul class="wp-block-list">
<li>identyfikatora (pole <em>id</em>),</li>



<li>nazwy (pole <em>name</em>), </li>



<li>wektora (pole <em>vector</em>), </li>



<li>kategorii (pole <em>category</em>), </li>



<li>tagów (pole <em>tags</em>).</li>
</ul>



<p>Aby stworzyć kolekcję i przygotować ją do testów możemy skorzystać z następujących poleceń:</p>



<pre class="wp-block-code"><code class="">$ bin/solr create -c test -s 1 -rf 1

$ curl -XPOST -H 'Content-type:application/json' 'http://localhost:8983/solr/test/schema' --data-binary '{
  "add-field-type" : {
    "name":"knn_vector_384",
    "class":"solr.DenseVectorField",
    "vectorDimension":384,
    "similarityFunction":"cosine",
    "knnAlgorithm":"hnsw"
  },
  "add-field" : [
      {
        "name":"vector",
        "type":"knn_vector_384",
        "indexed":true,
        "stored":false
      },
      {
        "name":"name",
        "type":"text_general",
        "multiValued":false,
        "indexed":true,
        "stored":true
      },
      {
        "name":"category",
        "type":"string",
        "multiValued":false,
        "indexed":true,
        "stored":true
      },
      {
        "name":"tags",
        "type":"string",
        "multiValued":true,
        "indexed":true,
        "stored":true
      }
    ]
}'</code></pre>



<h2 class="wp-block-heading">Przygotowanie danych</h2>



<p>Przygotowanie danych jest trochę bardziej skomplikowane. Nie będziemy tworzyć własnego modelu, wykorzystamy jeden z dostępnych na Hugging Face, a dokładniej model <em>https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2</em>. Jest to jeden z wielu dostępnych modeli umożliwiających pracę ze zdaniami i paragrafami. Warto pamiętać, że wyliczenie wektora to nie tylko spojrzenie na słowa, bądź same zdanie, ale kontekst w którym występują. Tak stworzony wektor koduje wszystkie informacje pozwalając algorytmom znaleźć podobne dokumenty. Oczywiście my pójdziemy na skróty i wykorzystamy ten model do przetworzenia pola z tagami, ale jak już wspominałem &#8211; model nie jest dzisiaj najważniejszy &#8211; interesuje nas Solr.</p>



<p>Aby zindeksować dokumenty skorzystamy z następującego kodu w Pythonie zapisanego w pliku <strong>index.py</strong>:</p>



<pre class="wp-block-code"><code class="">import pysolr
import uuid
from typing import List, Dict
import torch
from transformers import AutoTokenizer, AutoModel
import numpy as np

class DocumentEmbedder:
    def __init__(self):
        self.model_name = "sentence-transformers/all-MiniLM-L6-v2"
        self.tokenizer = AutoTokenizer.from_pretrained(self.model_name)
        self.model = AutoModel.from_pretrained(self.model_name)
        
        self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
        self.model.to(self.device)

    def mean_pooling(self, model_output, attention_mask):
        token_embeddings = model_output[0]
        input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
        return torch.sum(token_embeddings * input_mask_expanded, 1) / torch.clamp(input_mask_expanded.sum(1), min=1e-9)

    def get_embedding(self, text: str) -&gt; np.ndarray:
        encoded_input = self.tokenizer(
            text,
            padding=True,
            truncation=True,
            max_length=512,
            return_tensors='pt'
        )
        
        encoded_input = {k: v.to(self.device) for k, v in encoded_input.items()}

        with torch.no_grad():
            model_output = self.model(**encoded_input)

        sentence_embeddings = self.mean_pooling(model_output, encoded_input['attention_mask'])
        sentence_embeddings = torch.nn.functional.normalize(sentence_embeddings, p=2, dim=1)
        
        return sentence_embeddings.cpu().numpy()

class SolrIndexer:
    def __init__(self, solr_url: str = 'http://localhost:8983/solr/test'):
        self.solr = pysolr.Solr(solr_url, always_commit=True)
        self.embedder = DocumentEmbedder()

    def create_document(self, name: str, tags: str, category: str) -&gt; Dict:
        """Create a document with embeddings."""
        embedding = self.embedder.get_embedding(tags)
        
        doc = {
            'id': str(uuid.uuid4()),
            'name': name,
            'tags': tags.split(', '),  
            'category': category,
            'vector': embedding.flatten().tolist()
        }
        return doc

    def index_documents(self, documents: List[Dict]):
        """Index multiple documents to Solr."""
        try:
            self.solr.add(documents)
            print(f"Successfully indexed {len(documents)} documents")
        except Exception as e:
            print(f"Error indexing documents: {str(e)}")

def main():
    documents = [
        {"name": "Apple iPhone 13", "tags": "phone, smartphone, screen, iOS", "category": "phone"},
        {"name": "Apple iPhone 14", "tags": "phone, smartphone, screen, iOS", "category": "phone"},
        {"name": "Apple iPhone 15", "tags": "phone, smartphone, screen, iOS", "category": "phone"},
        {"name": "Samsung Galaxy S24", "tags": "phone, smartphone, screen, Android", "category": "phone"},
        {"name": "Apple iPod", "tags": "music, screen, iOS", "category": "music player"},
        {"name": "Samsung Microwave", "tags": "kitchen, cooking, electric", "category": "household"}
    ]
    
    indexer = SolrIndexer()
    
    solr_documents = []
    for doc in documents:
        solrdoc = indexer.create_document(doc['name'], doc['tags'], doc['category'])
        solr_documents.append(solrdoc)
        print(f"Created document: {solrdoc['name']}")
        print(f"Vector length: {len(solrdoc['vector'])}")
        
    indexer.index_documents(solr_documents)

    embedder = DocumentEmbedder()
    embeddings = embedder.get_embedding("song player")
    print(f"Embeddings: {str(embeddings)}")

if __name__ == "__main__":
    main()


</code></pre>



<p>Kilka słów komentarza odnośnie kodu:</p>



<ul class="wp-block-list">
<li>Klasa <em>DocumentEmbedder</em> odpowiada za stworzenie wektora, jeżeli chcielibyśmy zmienić model wystarczy zmienić wartość<strong> self.model_name</strong>, warto jednak pamiętać, iż stworzyliśmy konfigurację Solr, która działa z wektorami o 384 wymiarach, jeżeli zmieniony model będzie produkował wektory o innej charakterystyce konieczna będzie inna konfiguracja Solr.</li>



<li>Klasa <strong>SolrIndexer</strong> odpowiada za indeksowanie dokumentów.</li>



<li>Same dokumenty, które zostaną zindeksowane zdefiniowane są w zmiennej <strong>documents</strong>.</li>
</ul>



<p>Aby uruchomić kod będziesz zależności, które muszą zostać zainstalowane. Testowaliśmy kod na MacOS działającym na komputerze z procesorem w architekturze ARM, a sam plik <strong>requirements.txt</strong> wygląda następująco:</p>



<pre class="wp-block-code"><code class="">transformers==4.37.2
torch&gt;=2.2.0
numpy&gt;=1.24.3
pandas&gt;=2.1.4
sentencepiece==0.1.99 --extra-index-url https://download.pytorch.org/whl/cpu
pysolr==3.9.0
uuid==1.30
</code></pre>



<p>Możesz zainstalować zależności i uruchomić kod za pomocą następujących poleceń:</p>



<pre class="wp-block-code"><code class="">$ pip install -r requirements.txt

$ python index.py</code></pre>



<h2 class="wp-block-heading">Zapytania</h2>



<p>Po zindeksowaniu dokumentów możemy zacząć zadawać zapytania. Aby to zrobić możemy skorzystać z jednego z dwóch parserów:</p>



<ul class="wp-block-list">
<li><em>knn</em></li>



<li><em>vectorSimilarity</em></li>
</ul>



<p>W przypadku korzystania z parsera <em>knn</em> dostajemy top K dokumentów, w przypadku korzystania z parsera <em>vectorSimilarity</em> dostajemy dokumenty powyżej założonej granicy podobieństwa wektorów. Przykładowe zapytanie wygląda następująco:</p>



<pre class="wp-block-code"><code class="">http://localhost:8983/solr/test/query?q={!knn f=vector topK=10}[...]</code></pre>



<p>Jak widać powyższe zapytanie korzysta z parsera <strong>knn</strong>, działa w oparciu o dane z pola o nazwie <strong>vector</strong>, pobiera 10 dokumentów i w sekcji <strong>[&#8230;]</strong> powinien się znaleźć nasz wektor o 384 wymiarach. Jak go wygenerować? Możemy zmodyfikować poprzedni kod i w funkcji <strong>main</strong> wyliczyć wektor dla naszego zapytania:</p>



<pre class="wp-block-code"><code class="">.
.
.

def main():
    embedder = DocumentEmbedder()
    embeddings = embedder.get_embedding("song player").flatten().tolist()
    print(f"Embeddings: {str(embeddings)}")

if __name__ == "__main__":
    main()</code></pre>



<p>Tak wygenerowany wektor będziemy mogli wykorzystać już w Solr. Spróbujmy zatem zadać zapytanie <strong>song player</strong> do pola <strong>tags</strong>, czyli następujące zapytanie:</p>



<pre class="wp-block-code"><code class="">http://localhost:8983/solr/test/select?q=tags:"song player"</code></pre>



<p>Zapytanie takie zwróci puste wyniki wyszukiwania:</p>



<pre class="wp-block-code"><code class="">{
  "responseHeader":{
    "zkConnected":true,
    "status":0,
    "QTime":1,
    "params":{
      "q":"tags:\"song player\""
    }
  },
  "response":{
    "numFound":0,
    "start":0,
    "numFoundExact":true,
    "docs":[ ]
  }
}</code></pre>



<p>Jest to sytuacja, której mogliśmy się spodziewać &#8211; nie mamy dokumentu z takim tagiem. Nasz <strong>Apple iPod</strong> ma jedną z wartości pola <strong>tags</strong> ustawioną na <strong>music</strong>. Blisko, ale to nie to samo co <strong>song player</strong>, przynajmniej z punktu widzenia wyszukiwania pełnotekstowego, a oczywiście nie korzystamy z żadnych synonimów. </p>



<p>Zobaczmy zatem, jak poradzi sobie wyszukiwanie semantyczne. Do tego musimy wygenerować wektor z naszej frazy. Skorzystamy z kodu, który już widzieliśmy powyżej, i nasze zapytanie <strong>song player</strong> po wygenerowaniu wektora będzie wyglądało następująco:</p>



<pre class="wp-block-code"><code class="">http://localhost:8983/solr/test/query?fl=name,tags,score,*&amp;q={!knn f=vector topK=10}[-2.00312417e-02,-8.43894295e-03,4.63341828e-03,-7.11187869e-02,-5.41122817e-02,8.86119753e-02,1.43207222e-01,6.48824051e-02,-2.42583733e-02,5.58690503e-02,1.17570441e-02,-8.10808595e-03,5.87443374e-02,-8.77943709e-02,-2.66626403e-02,9.57359672e-02,-3.80967893e-02,6.07994124e-02,-2.05643158e-02,-4.67214771e-02,-1.52253941e-01,-9.71766468e-03,-6.08050935e-02,2.88447887e-02,-2.94179078e-02,1.03829138e-01,-8.31280462e-03,1.05928726e-01,-5.67206480e-02,-7.29391426e-02,8.77882075e-03,3.76622789e-02,1.22778863e-01,7.63954269e-03,-1.07958838e-01,-2.39758939e-02,-4.40737829e-02,-1.58879627e-02,-8.71627405e-02,-1.86017044e-02,-1.60255414e-02,3.48703228e-02,-3.19783390e-02,-1.32465595e-03,-6.66615218e-02,-5.59806228e-02,-3.83943357e-02,-3.26939276e-03,3.50921564e-02,8.62423256e-02,-4.67025265e-02,3.27739231e-02,1.36479884e-02,2.14965921e-02,9.94029175e-03,8.50510597e-03,6.12449907e-02,1.26747936e-01,1.20850094e-02,8.25305879e-02,-3.12836142e-03,-7.23205507e-02,-1.25491228e-02,-4.12022136e-02,8.32084790e-02,-4.56760749e-02,1.24245733e-02,2.99928896e-03,-1.72226392e-02,1.40204923e-02,-5.27366474e-02,8.62174854e-02,-1.23909842e-02,-5.11718355e-03,-5.19047305e-03,-3.26551199e-02,3.38410065e-02,-2.57184170e-02,4.93465960e-02,2.16306932e-02,5.44718355e-02,-5.13004661e-02,-6.29040822e-02,-1.14791520e-01,2.42656711e-02,-5.12294583e-02,-1.41068548e-02,-1.69403590e-02,-3.93591821e-02,2.21420880e-02,-7.29445517e-02,3.97052318e-02,4.06186096e-02,2.38778368e-02,-2.60719825e-02,4.29015867e-02,8.55240896e-02,-1.29460618e-01,-6.10361844e-02,1.73135296e-01,5.50046675e-02,2.11313833e-02,8.09541941e-02,8.49610151e-05,1.86103079e-02,-7.11245313e-02,-2.20125029e-03,8.21124241e-02,-3.17892991e-02,-5.61453328e-02,3.08496933e-02,-2.24805139e-02,-6.99905232e-02,-1.38647379e-02,2.40420792e-02,1.32726450e-02,3.10667846e-02,1.00519679e-01,-3.77691817e-03,1.09965838e-02,-2.87625156e-02,-3.69284451e-02,-4.66967747e-02,1.17862746e-02,-6.05449453e-02,1.80788971e-02,-7.05301017e-03,-2.43471990e-33,4.00696788e-03,-8.39618146e-02,4.76996899e-02,4.31838222e-02,6.59582578e-03,-2.13763528e-02,1.40098054e-02,4.72629964e-02,-7.40025416e-02,-1.17481779e-02,4.74894270e-02,-2.08981428e-02,-4.10624146e-02,6.36240691e-02,1.19205341e-01,-6.63071573e-02,-5.37127964e-02,1.66804232e-02,-5.87310642e-03,-1.00764409e-02,2.95753870e-02,7.51189962e-02,3.69122699e-02,4.02592048e-02,3.01825050e-02,-3.49015556e-02,3.05062942e-02,-5.49735427e-02,7.76632428e-02,-1.31180901e-02,1.32043369e-03,-7.29682148e-02,4.55190837e-02,-3.90759930e-02,-2.00935621e-02,2.90088002e-02,-4.90487851e-02,-4.68467623e-02,-3.78382057e-02,-8.62234011e-02,-8.21669679e-03,-1.02636190e-02,-4.22154590e-02,-1.01420805e-02,-9.29822177e-02,-4.51174043e-02,1.08679747e-02,4.72158194e-02,1.13880262e-02,2.26492099e-02,1.08083403e-02,4.73796055e-02,-2.11352482e-02,2.94824075e-02,3.32471356e-02,-6.10656738e-02,6.39934689e-02,7.52783706e-03,2.01779045e-02,8.42025783e-03,7.92021900e-02,7.49795884e-02,3.60657759e-02,-5.19200675e-02,-2.61804424e-02,4.30282280e-02,9.71625224e-02,-5.95051460e-02,9.75757018e-02,2.11117323e-02,-3.37768793e-02,-2.30997894e-02,5.96088655e-02,3.78986225e-02,-8.59187767e-02,2.64272131e-02,-5.98726794e-02,-7.63988122e-02,-8.67564604e-03,-4.02248465e-02,-7.74084628e-02,-7.17599411e-03,-6.49854094e-02,5.07407561e-02,-4.55319695e-02,-2.79121399e-02,-3.54578048e-02,-8.02342817e-02,-1.16430726e-02,-3.30321328e-03,-2.42636316e-02,4.07439955e-02,8.91774427e-03,1.66885648e-02,-5.11291474e-02,2.29379760e-33,-2.25291476e-02,-3.75496261e-02,7.23349378e-02,2.80879121e-02,1.11529857e-01,9.52087902e-03,3.09992954e-02,3.44425067e-02,-1.44962538e-02,3.70973274e-02,-3.42840664e-02,-4.15693037e-03,4.41893972e-02,-5.78483054e-03,2.27168929e-02,2.73053981e-02,-7.61534553e-03,3.05881090e-02,2.22553536e-02,-2.11785771e-02,-8.38738605e-02,-3.54573503e-02,5.55671491e-02,-3.25255133e-02,-6.17663078e-02,-3.31615508e-02,1.28180429e-01,-1.09783243e-02,-4.22303416e-02,8.85861460e-03,1.03141055e-01,-1.19012371e-02,-5.68600930e-02,-9.76827294e-02,-3.66187817e-03,6.69727027e-02,5.01095466e-02,3.45795639e-02,-4.87284325e-02,-2.88722366e-02,3.69899273e-02,4.80774455e-02,9.51483287e-03,1.05140977e-01,9.12032463e-03,7.83540029e-03,3.00276410e-02,1.10446520e-01,2.02773716e-02,6.35717139e-02,-6.48668930e-02,-3.63331586e-02,5.26458211e-02,-8.43572319e-02,-6.71359748e-02,-3.95149644e-03,-3.19298953e-02,-3.83070633e-02,-1.30710788e-02,3.45022231e-02,3.50211672e-02,-1.17928414e-02,-2.96711233e-02,-1.07177338e-02,-3.55232134e-02,1.01115681e-01,3.25268582e-02,3.25036608e-02,-1.87526215e-02,-2.26221383e-02,1.12875113e-02,5.46618700e-02,-9.37254541e-03,5.56712523e-02,-4.75139245e-02,8.30354728e-03,-1.24001637e-01,7.75059313e-02,-2.27639657e-02,-7.71744251e-02,3.50985602e-02,-1.18714431e-02,2.85322741e-02,-1.23035219e-02,3.05858813e-03,-3.02043278e-02,7.86332041e-02,2.63012294e-02,-2.10798401e-02,-2.86972001e-02,4.53165434e-02,5.47545776e-02,-1.12766981e-01,4.01742896e-03,-7.09661469e-03,-1.23211663e-08,-8.58379975e-02,1.30043477e-02,2.08473746e-02,-4.64787520e-02,5.16857654e-02,1.19483555e-02,3.52647863e-02,-1.09199435e-01,3.83973494e-02,3.78849730e-02,3.93284820e-02,-7.54635110e-02,8.52666888e-03,7.53920805e-03,2.69943215e-02,-2.04220396e-02,-5.33770137e-02,9.14978534e-02,-5.69638461e-02,4.34034877e-02,1.58696324e-02,6.27059937e-02,1.16019472e-02,-3.03725917e-02,2.53463928e-02,-1.08486209e-02,-4.15410772e-02,6.96198866e-02,1.25364019e-02,8.21540307e-04,3.80332284e-02,7.23319054e-02,2.98110046e-03,-6.22513592e-02,6.56076372e-02,2.09525451e-02,2.19415948e-02,-1.34995428e-03,-9.06020775e-02,2.12142467e-02,-1.63224037e-03,9.72291678e-02,-3.10167447e-02,-3.09801828e-02,-1.90541670e-02,-3.96139771e-02,2.09502075e-02,-9.64867976e-03,1.07652992e-02,2.92998832e-02,-6.14691451e-02,2.42477246e-02,-4.87532094e-02,2.83491425e-02,8.26478079e-02,4.19540368e-02,-3.42742465e-02,5.80971912e-02,-2.19746046e-02,1.28769483e-02,-1.10125542e-02,3.03942598e-02,2.98435502e-02,5.43411188e-02]</code></pre>



<h2 class="wp-block-heading">Wyniki</h2>



<p>Zapytanie, które zadaliśmy zwróci nam następujące wyniki:</p>



<pre class="wp-block-code"><code class="">{
  "responseHeader":{
    .
    .
    .
  },
  "response":{
    "numFound":6,
    "start":0,
    "maxScore":0.7036504,
    "numFoundExact":true,
    "docs":[{
      "id":"0030eb55-95ee-4042-b574-1f1f4d2d0dd4",
      "name":"Apple iPod",
      "tags":["music","screen","iOS"],
      "_version_":1815543355046625280,
      "_root_":"0030eb55-95ee-4042-b574-1f1f4d2d0dd4",
      "score":0.7036504
    },{
      "id":"f5122858-22bf-47fe-84b4-f0fa5e608d22",
      "name":"Samsung Galaxy S24",
      "tags":["phone","smartphone","screen","Android"],
      "_version_":1815543355045576704,
      "_root_":"f5122858-22bf-47fe-84b4-f0fa5e608d22",
      "score":0.5939434
    },{
      "id":"9b6aa3b5-f02a-4969-ae21-daaafc58ece4",
      "name":"Apple iPhone 13",
      "tags":["phone","smartphone","screen","iOS"],
      "_version_":1815543355005730816,
      "_root_":"9b6aa3b5-f02a-4969-ae21-daaafc58ece4",
      "score":0.5706577
    },{
      "id":"bbb744ae-ca90-4663-8a8e-fdbe8e00d935",
      "name":"Apple iPhone 14",
      "tags":["phone","smartphone","screen","iOS"],
      "_version_":1815543355042430976,
      "_root_":"bbb744ae-ca90-4663-8a8e-fdbe8e00d935",
      "score":0.5706577
    },{
      "id":"f750c960-229b-4fd2-a9f7-c891afbb19d3",
      "name":"Apple iPhone 15",
      "tags":["phone","smartphone","screen","iOS"],
      "_version_":1815543355044528128,
      "_root_":"f750c960-229b-4fd2-a9f7-c891afbb19d3",
      "score":0.5706577
    },{
      "id":"756e14fc-6320-4fee-9713-7d0c6123fa8a",
      "name":"Samsung Microwave",
      "tags":["kitchen","cooking","electric"],
      "_version_":1815543355047673856,
      "_root_":"756e14fc-6320-4fee-9713-7d0c6123fa8a",
      "score":0.5591891
    }]
  }
}</code></pre>



<p>Jak widać dostaliśmy wszystkie dokumenty, nasz <strong>Apple iPod</strong> ma najwyższy współczynnik <strong>score</strong>, który wyliczony jest już nie na podstawie podobieństwa dokumentów do zapytania, ale podobieństwa wektorów. Oczywiście w naszym wypadku jest to bardzo skromna próbka dokumentów oraz pole z tagami, które jest dalekie od ideału, ale mamy działający, w miarę prosty przykład. Parser <strong>knn</strong> zwrócił top 10 dokumentów, czyli w naszym wypadku wszystkie.</p>



<p>Jeżeli chcielibyśmy pobrać tylko te dokumenty, które mają score powyżej pewnej granicy możemy skorzystać z parsera <strong>vectorSimilarity</strong>. Załóżmy, że chcemy pobrać dokumenty których współczynnik podobieństwa jest równy, bądź większy od <strong>0.7</strong>, wtedy nasze zapytanie wyglądałoby następująco:</p>



<pre class="wp-block-code"><code class="">http://localhost:8983/solr/test/query?q={!vectorSimilarity f=vector minReturn=0.7}[...]</code></pre>



<p>A wynik tego zapytania wyglądałby tak:</p>



<pre class="wp-block-code"><code class="">{
  "responseHeader":{
    .
    .
    .
  },
  "response":{
    "numFound":1,
    "start":0,
    "maxScore":0.7036504,
    "numFoundExact":true,
    "docs":[{
      "id":"0030eb55-95ee-4042-b574-1f1f4d2d0dd4",
      "name":"Apple iPod",
      "tags":["music","screen","iOS"],
      "_version_":1815543355046625280,
      "_root_":"0030eb55-95ee-4042-b574-1f1f4d2d0dd4",
      "score":0.7036504
    }]
  }
}</code></pre>



<p>I teraz mamy już tylko jeden dokument. </p>



<h2 class="wp-block-heading">Filtrowanie wstępne</h2>



<p>Wydajności rozwiązania będzie zależeć od ilości kandydatów, czyli dokumentów, które Solr będzie musiał przetworzyć. Aby zmniejszyć ich liczbę możemy skorzystać z filtrowania i parametru <strong>preFilter</strong>. Np. jeżeli chcielibyśmy otrzymać tylko telefony (czyli pole <strong>category</strong> równe <strong>phone</strong>) nasze zapytanie wyglądałoby następująco:</p>



<pre class="wp-block-code"><code class="">http://localhost:8983/solr/test/query?q={!knn f=vector topK=10 preFilter=category:phone}[...]</code></pre>



<p>A odpowiedź tym razem wyglądałaby następująco:</p>



<pre class="wp-block-code"><code class="">{
  "responseHeader":{
    .
    .
    .
  },
  "response":{
    "numFound":4,
    "start":0,
    "maxScore":0.5939434,
    "numFoundExact":true,
    "docs":[{
      "id":"a0f6fe9b-e82a-437e-b8ef-c12b6ee8d4a3",
      "name":"Samsung Galaxy S24",
      "tags":["phone","smartphone","screen","Android"],
      "category":"phone",
      "_version_":1815544743947403264,
      "_root_":"a0f6fe9b-e82a-437e-b8ef-c12b6ee8d4a3",
      "score":0.5939434
    },{
      "id":"ff2a471f-6d31-4dc1-9ec0-f5eacbfc1540",
      "name":"Apple iPhone 13",
      "tags":["phone","smartphone","screen","iOS"],
      "category":"phone",
      "_version_":1815544743943208960,
      "_root_":"ff2a471f-6d31-4dc1-9ec0-f5eacbfc1540",
      "score":0.5706577
    },{
      "id":"9d2955b2-ba6d-4f5d-a477-66909725b362",
      "name":"Apple iPhone 14",
      "tags":["phone","smartphone","screen","iOS"],
      "category":"phone",
      "_version_":1815544743945306112,
      "_root_":"9d2955b2-ba6d-4f5d-a477-66909725b362",
      "score":0.5706577
    },{
      "id":"93e5bd31-78b5-45b9-9c61-a0d8e8c0badb",
      "name":"Apple iPhone 15",
      "tags":["phone","smartphone","screen","iOS"],
      "category":"phone",
      "_version_":1815544743946354688,
      "_root_":"93e5bd31-78b5-45b9-9c61-a0d8e8c0badb",
      "score":0.5706577
    }]
  }
}</code></pre>



<p>Tym razem dostaliśmy tylko telefony, czyli same dokumenty zostały odpowiednio przefiltrowane. </p>



<p>Samo zachowanie Solr jest także definiowane przez zapytanie. Na przykład w przypadku braku parametru <strong>preFilter</strong> Solr będzie korzystać ze standardowych parametrów znanych z wyszukiwania pełnotekstowego, np. <strong>fq</strong>. Nasze zapytanie z filtrowaniem możemy skonstruować z wykorzystaniem parametru <strong>fq</strong> i wtedy wyglądałoby następująco:</p>



<pre class="wp-block-code"><code class="">http://localhost:8983/solr/test/query?fq=category:phone&amp;q={!knn f=vector topK=10}[...]</code></pre>



<p>Rezultat będzie taki sam &#8211; cztery dokumenty, które już widzieliśmy. </p>



<h2 class="wp-block-heading">Podsumowanie</h2>



<p>Zobaczyliśmy część możliwości jakie daje Solr jeżeli chodzi o wykorzystanie wektorów. Zobaczyliśmy na co pozwalają dwa parsery i jak z nich skorzystać. Oczywiście przykład jest bardzo prosty, a co za tym idzie, jeżeli chcielibyśmy zacząć pracę w naszych projektach po pierwsze musimy włożyć pracę w znalezienie lub przygotowanie modelu i jego strojenie. W zależności od potrzeb konieczne może być użycie innego modelu. Istnieje szereg modeli, które mogą zostać wykorzystane, np. model <a href="https://huggingface.co/Snowflake/snowflake-arctic-embed-m-v1.5">https://huggingface.co/Snowflake/snowflake-arctic-embed-m-v1.5</a>, czy też opublikowane modele takie jak <a href="https://huggingface.co/Marqo/marqo-ecommerce-embeddings-B">https://huggingface.co/Marqo/marqo-ecommerce-embeddings-B</a> lub <a href="https://huggingface.co/Marqo/marqo-ecommerce-embeddings-L">https://huggingface.co/Marqo/marqo-ecommerce-embeddings-L</a>. Warto poświęcić czas na wybranie odpowiedniego modelu, jego testy i ewaluację &#8211; w przypadku wyszukiwania semantycznego wybór modelu jest kluczowy bo to on odpowiada za stworzenie wektorów, które będą wykorzystywane do wyszukiwania.</p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://solr.pl/2024/11/18/apache-solr-i-wektory-jak-sie-do-tego-zabrac/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
