Problem

Normalerweise basiert eine Suche innerhalb eines Webauftritts auf SQL-Datenbankabfragen, um Suchworte innerhalb des Webauftritts einzelnen Seiten zuordnen zu können.

Beispiel: Codeauszug der Suche innerhalb einer WordPress-Installation:

 
$wild = '%';
$find = 'only 43% of planets';
$like = $wild . $wpdb->esc_like( $find ) . $wild;
$sql  = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_content LIKE %s", $like );

Quelle: wpdb::esc_like() | Method | WordPress Developer Resources   (10.03.2022)

Nun haben Suchmaschinen natürlich keinen Zugang zu den Datenbanken der Internet Service Provider. Wie also können Suchworte mit Webseiten abgeglichen werden?

Lösungsansatz

Web Crawler

Suchmaschinen müssen daher zwangsläufig die Webseiten selbst lesen: Sogenannte Internet-Robots oder kurz Bots sind Programme die selbstständig Internetlinks folgen und hier für die Suchmaschinen-Anbieter die Inhalte auslesen und übertragen. Sie heißen auch "Crawler", weil sie quasi durch das Internet kriechen (engl. to crawl). Das automatisierte Auslesen von Webseiten wird auch als Web Scraping (kratzen =  engl. to scrape) bezeichnet.

Natural Language Processing NLP

Die von den Bots übertragenen Webseiteninhalte müssen aufbereitet und dann abgespeichert werden. Die großen Suchmaschinen-Anbieter bereiten die Texte mit Hilfe von Natural Language Processing auf, z.B.: 

  • Es werden unrelevante Wörter (und, zu, dem, mein, ich ..) entfernt.
  • Die Häufigkeit aller Wörter im gesamten Wortschatz wird ermittelt und das Gesamtvokabular nach Vokabelhäufigkeit sortiert
  • Das Auftreten der Worte einer Seite wird damit abgeglichen und als Zahlenreihe (Vektor) abgespeichert.
  • Die Relevanz der Worte einer Seite wird entsprechend gewichtet. (Worte einer Seite, die in vielen anderen Seiten vorkommen sind für die Suche weniger relevant.)
  • Alle Webseiten überhaupt ergeben jeweils eine Zahlenreihe die zusammen als Bag of Words bezeichnet wird (Array). Die zu einer Zeile dazugehörige Internetadresse wird natürlich auch gespeichert.
    Es ist davon auszugehen, dass keine herkömmlichen relationalen Datenbanken verwendet werden. Die Datenmenge ist für eine DB viel zu groß. SQL-Abfragen würden zu lange dauern. 
    Quelle: Welche Datenbank verwendet Google? (qastack.com.de)    (10.03.2022)

Der Vergleich erfolgt danach:

  • Die Suchworte werden ebenso mit den gleichen Methoden des NLP mathematisch aufbereitet und die sich so ergebende Zahlenreihe wird mit allen anderen Zahlenreihen der Bag of  Words mit mathematischen Methoden verglichen (Cosinus-Vergleich von Vektoren).
  • Diejenigen Zahlenreihen aus der Bag of Words mit den größten Ähnlichkeiten werden ermittelt und die entsprechenden Links dazu ausgegeben.

 

Demonstration: NLP-Suche über 4 Seiten 

Dieses Verfahren wird hier beispielhaft angewandt:

Die 4 Webseiten sind auf github gehostet: https://ateachment.github.io/nlp-demo/index.html

Der kommentierte Code steht als Jupyter-Notebook bereit: https://gist.github.com/ateachment/7d3c6893b1e6a3b38b92e86ce7a94d20

Je nach Plattform müssen zuvor bestimmte Libraries installiert werden, z.B. 

pip install requests
pip install bs4
pip install html5lib

Web Crawling

Zunächst ruft der Bot bzw. der Crawler eine Webseite aufgerufen und mit Hilfe Python-Bibliothek Beautiful Soup (HTML Parser) werden, HTML-Tags herausgefiltert, relevanter Text erfasst und dann rekursiv den Links die auf dieser Seite enthalten sind, gefolgt. So wühlt sich der Crawler so durch alle verlinkten Webseiten.

Es muss dabei verhindert werden, dass die Rekursion zu Endlosschleifen führt. Daher wird die Rekursionstiefe überwacht und auch Seiten kein 2. Mal durchlaufen.

# HTTP-Anfragen an Webseiten senden (zum Crawlen)
import requests

# HTML-Dokumente parsen und Inhalte extrahieren
from bs4 import BeautifulSoup

# URLs korrekt zusammensetzen und analysieren (z. B. relative Links)
from urllib.parse import urljoin, urlparse

# NumPy wird für numerische Berechnungen und Vektor-/Matrixoperationen verwendet
import numpy as np

# ----------------------------
# NLP-Bibliotheken
# ----------------------------

# Natural Language Toolkit (Grundlage für Textverarbeitung)
import nltk

# Deutsche Stopwörter (z. B. "und", "der", "die")
from nltk.corpus import stopwords

# Zerlegung eines Textes in einzelne Wörter (Tokenisierung)
from nltk.tokenize import word_tokenize

# Deutscher Snowball-Stemmer (Wortstammreduktion)
from nltk.stem.snowball import GermanStemmer

# Stemmer-Objekt erzeugen
stemmer = GermanStemmer()


# ----------------------------
# Lemmatisierung mit HanTa
# ----------------------------

# Installation der HanTa-Bibliothek (Hanover Tagger)
# Führt Wörter auf ihre Grundform (Lemma) zurück
!pip install HanTa

# Import des HanoverTagger
from HanTa import HanoverTagger as ht




def crawl_recursive(url, base_domain, visited=None, results=None, depth=0, max_depth=10):
    """
    Rekursiver Webcrawler.

    Parameter:
    url          : Start- oder Ziel-URL, die gecrawlt werden soll
    base_domain  : Domain-Beschränkung (z. B. ateachment.github.io)
                   → verhindert, dass externe Seiten gecrawlt werden
    visited      : Menge (set) bereits besuchter URLs
                   → verhindert Endlosschleifen
    results      : Dictionary zur Speicherung der gecrawlten Inhalte
                   → {url: textinhalt}
    depth        : Aktuelle Rekursionstiefe
    max_depth    : Maximale Tiefe, um das Crawling zu begrenzen
    """

    # Initialisierung beim ersten Funktionsaufruf
    if visited is None:
        visited = set() # speichert bereits besuchte Seiten
    if results is None:
        results = []    # speichert URL → extrahierter Text

    # Abbruchbedingungen
    # 1. Maximale Tiefe erreicht
    # 2. URL wurde bereits besucht
    if url in visited:
        return results
    
    if depth > max_depth:
        return results

    print("  " * depth + "Crawling:", url)

    # URL als besucht markieren
    visited.add(url)

    try:
        # Browser vortäuschen
        headers = {<span style="background-color: #fff0f0;">"User-Agent"</span>: <span style="background-color: #fff0f0;">"Mozilla/5.0"</span>}
        # HTML-Seite abrufen
        r = requests.get(url, headers=headers, timeout=5)
        r.encoding = "utf-8"
    except Exception as e:
        print("Fehler:", e)
        return results

    # HTML parsen
    soup = BeautifulSoup(r.text, "html.parser")

    # ---- reinen Text extrahieren (h1, h2, p) ----
    h1 = " ".join(h.get_text(strip=True) for h in soup.find_all("h1"))
    h2 = " ".join(h.get_text(strip=True) for h in soup.find_all("h2"))
    p  = " ".join(p.get_text(strip=True) for p in soup.find_all("p"))


    # einfach werten
    text = h1 + " " + h2 + " " + p
    # Überschriften künstlich verstärken
    # text = (h1 + " ") * 3 + (h2 + " ") * 2 + p

    results.append((url, text))

    # ---- Links rekursiv verfolgen ----
    for link in soup.find_all("a"):
        href = link.get("href")
        if not href:
            continue
        # Relative Links in absolute URLs umwandeln
        full_url = urljoin(url, href)
        parsed = urlparse(full_url)

        # Nur interne Links (innerhalb derselben Domain bleiben)
        if parsed.netloc == base_domain:
            # Funktion ruft sich selbst auf
            crawl_recursive(full_url, base_domain,
                            visited, results,
                            depth + 1, max_depth)

    return results

start_url = "https://ateachment.github.io/nlp-demo/index.html"
base_domain = "ateachment.github.io"

data = crawl_recursive(start_url, base_domain)

documents = []
urls = []

for url, text in data:
    urls.append(url)
    documents.append(text)
    print("\n", url, "\n", text)

print("\nAnzahl gecrawlter Seiten:", len(documents))

In diesem Demo wird so der Text von 4 Webseiten erfasst:

 https://ateachment.github.io/nlp-demo/index.html 
 Informatik NLP-Suchmaschinen-Demonstration Diese Webseite enthält drei thematische Unterseiten zur Demonstration einer NLP-basierten Suchmaschine.

 https://ateachment.github.io/nlp-demo/ki.html 
 Informatik Künstliche Intelligenz Künstliche Intelligenz ist ein Teilgebiet der Informatik. Maschinelles Lernen ist eine zentrale Methode der künstlichen Intelligenz. Neuronale Netze werden im Bereich Deep Learning eingesetzt. Künstliche Intelligenz findet Anwendung in Medizin, Industrie und Forschung. Zurück zur Übersicht

 https://ateachment.github.io/nlp-demo/anaconda.html 
 Informatik Installation von Anaconda Anaconda ist eine Python-Distribution für Data Science. Die Installation von Anaconda erfolgt über ein Installationsprogramm. Anaconda enthält Python, Jupyter Notebook und viele wissenschaftliche Bibliotheken. Nach der Installation kann Anaconda über den Navigator gestartet werden. Zurück zur Übersicht

 https://ateachment.github.io/nlp-demo/datenbanken.html 
 Informatik Relationale Datenbanken Relationale Datenbanken speichern strukturierte Daten. SQL wird verwendet, um Abfragen in Datenbanken durchzuführen. Tabellen bestehen aus Zeilen und Spalten. SQL ermöglicht Insert, Update und Select Operationen. Zurück zur Übersicht

Natural Language Processing NLP

NLP-Vorverarbeitung

Ausgangstext (hier: index.html):
Informatik NLP-Suchmaschinen-Demonstration Diese Webseite enthält drei thematische Unterseiten zur Demonstration einer NLP-basierten Suchmaschine.

1. Tokenisierung (in Liste überführen)
["Informatik", "NLP-Suchmaschinen-Demonstration", "Diese", "Webseite", "enthält", "drei", "thematische", "Unterseiten", "zur", "Demonstration",  "einer", "NLP-basierten", "Suchmaschine"]

2. Lemmatisierung (mit HanTa auf die Grundformen zurückführen)
["Informatik", "NLP-Suchmaschinen-Demonstration", "Diese", "Webseite", "enthalten", "drei", "thematisch", "Unterseite", "zu",
 "Demonstration", "eine", "NLP-basiert", "Suchmaschine"]

3. Kleinschreibung
["informatik", "nlp-suchmaschinen-demonstration", "diese", "webseite", "enthalten", "drei", "thematisch", "unterseite", "zu", "demonstration", "eine",
 "nlp-basiert", "suchmaschine"]
4. Entfernen nicht-alphabetischer Tokens (isalpha entfernt auch Worte mit Bindestrich)
["informatik", "diese", "webseite", "enthalten", "drei", "thematisch", "unterseite", "zu", "demonstration", "eine", "suchmaschine"]

5. Stopword-Entfernung (z.B. "diese", "zu", "eine", ...)
["informatik", "webseite", "enthalten", "drei", "thematisch", "unterseite",  "demonstration", "suchmaschine"]
6. Stemming
["informat", "webseit", "enthalt", "drei", "themat", "unterseit", "demonstration", "suchmaschin"]

def process_text(text):
  
    # 1. Tokenisierung
    tokens = word_tokenize(text, language="german")

    processed_tokens = []

    for word in tokens:
        # 2. Nur alphabetische Wörter betrachten
        if not word.isalpha():
            continue

        # 3. Lemmatisierung
        lemma = hannover.analyze(word)[0]

        # 4. Kleinschreibung
        lemma = lemma.lower()

        # 5. Stopwords entfernen
        if lemma in stop:
            continue

        # 6. Zusätzliches Stemming 
        stem = stemmer.stem(lemma)

        processed_tokens.append(stem)

    return processed_tokens

processed_docs = [process_text(doc) for doc in documents]
processed_docs

[['informat', 'webseit', 'enthalt', 'drei', 'themat', 'unterseit', 'demonstration', 'suchmaschin'], 
['informat', 'kunstlich', 'intelligenz', 'kunstlich', 'intelligenz', 'teilgebiet', 'informat', 'maschinell', 'lern', 'zentral', 'method', 'kunstlich', 'intelligenz', 'neuronal', 'netz', 'bereich', 'deep', 'learning', 'einsetz', 'kunstlich', 'intelligenz', 'find', 'anwend', 'medizin', 'industri', 'forschung', 'zuruck', 'ubersicht'],
['informat', 'installation', 'anaconda', 'anaconda', 'data', 'scienc', 'installation', 'anaconda', 'erfolg', 'installationsprogramm', 'anaconda', 'enthalt', 'pytho', 'jupyt', 'notebook', 'wissenschaft', 'bibliothek', 'installation', 'anaconda', 'navigator', 'start', 'zuruck', 'ubersicht'],
['informat', 'relational', 'datenbank', 'relational', 'datenbank', 'speich', 'strukturi', 'datum', 'sql', 'verwend', 'abfrag', 'datenbank', 'durchfuhr', 'tabell', 'besteh', 'zeil', 'spalt', 'sql', 'ermog', 'insert', 'updat', 'select', 'operation', 'zuruck', 'ubersicht']]

Durch diese Maßnahmen wird die Datenmenge bereits erheblich reduziert.

Wort-Häufigkeit bestimmen (Bag Of Words vorbereiten)

Der nächste Schritt besteht darin, die Häufigkeit aller Worte des gesamten Wortschatzes zu bestimmen:

from collections import Counter

all_words = []
for doc in processed_docs:
    all_words.extend(doc)

word_freq = Counter(all_words)
word_freq


Counter({'informat': 5, 'webseit': 1, 'enthalt': 2, 'drei': 1, 'themat': 1, 'unterseit': 1, 'demonstration': 1, 'suchmaschin': 1, 'kunstlich': 4, 'intelligenz': 4, 'teilgebiet': 1, 'maschinell': 1, 'lern': 1, 'zentral': 1, 'method': 1, 'neuronal': 1, 'netz': 1, 'bereich': 1, 'deep': 1, 'learning': 1, 'einsetz': 1, 'find': 1, 'anwend': 1, 'medizin': 1, 'industri': 1, 'forschung': 1, 'zuruck': 3, 'ubersicht': 3, 'installation': 3, '<a class="autolink" title="Anaconda" href="https://moodle.eick-at.de/mod/page/view.php?id=1139">anaconda</a>': 5, 'data': 1, 'scienc': 1, 'erfolg': 1, 'installationsprogramm': 1, 'pytho': 1, 'jupyt': 1, 'notebook': 1, 'wissenschaft': 1, 'bibliothek': 1, 'navigator': 1, 'start': 1, 'relational': 2, 'datenbank': 3, 'speich': 1, 'strukturi': 1, 'datum': 1, 'sql': 2, 'verwend': 1, 'abfrag': 1, 'durchfuhr': 1, 'tabell': 1, 'besteh': 1, 'zeil': 1, 'spalt': 1, 'ermog': 1, 'insert': 1, 'updat': 1, 'select': 1, 'operation': 1})

Das Wort 'künstlich' bzw 'kunstlich' kommt also genau 4 Mal im gesamten Wortschatz vor.

Vokabular nach Häufigkeit sortieren und begrenzen

Jetzt wird der gesamte Wortschatz, nach Häufigkeit des Vorkommens der Wörter absteigend sortiert und begrenzt. Hier wird aus didaktischen bzw. der besseren Anschauung wegen der Wortschatz auf nur 15 Wörter insgesamt begrenzt.
Die Größe des Wortschatzes begrenzt dabei die Länge der Liste, also auch die Datenmenge und den damit im Folgenden verbundenen Rechenaufwand. Dabei wird davon ausgegangen, das selten vorkommende Worte weniger relevant sind und so eher abgeschnitten werden dürfen als häufig vorkommende Begriffe, die vorne in der Liste stehen. Wichtige Worte können durchaus sehr selten vorkommen und werden so von der Verarbeitung ausgeschlossen. Dieser Parameter muss also sorgfältig in Abhängigkeit von Rechenleistung bzw. Verarbeitungsdauer angepasst werden.

max_features = 15
vocab = [word for word, freq in word_freq.most_common(max_features)]
vocab

['informat', 'anaconda', 'kunstlich', 'intelligenz', 'zuruck', 'ubersicht', 'installation', 'datenbank', 'enthalt', 'relational', 'sql', 'webseit', 'drei', 'themat', 'unterseit']

Bag of Words erstellen

Die mathematische Verarbeitung von Worten ist direkt nicht möglich. Sie müssen erst in Zahlen umgewandelt werden. Dafür wird die sogenannte Bag Of Words erstellt:
Jeder Text wird zu einem Vektor gemacht. In dem sein Vorkommen vor diesem Gesamtwortschatz abgebildet wird. Es entstehen 4 Vektoren × 15 Dimensionen.

def bag_of_words(tokens, vocab):
    vector = np.zeros(len(vocab))
    for i, word in enumerate(vocab):
        vector[i] = tokens.count(word)
    return vector

bag_matrix = np.array([bag_of_words(doc, vocab) for doc in processed_docs])
bag_matrix
 
array([[1., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 1., 1., 1., 1.],
       [2., 0., 4., 4., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [1., 5., 0., 0., 1., 1., 3., 0., 1., 0., 0., 0., 0., 0., 0.],
       [1., 0., 0., 0., 1., 1., 0., 3., 0., 2., 2., 0., 0., 0., 0.]])

Im 2. Text kommt so z.B. das Wort 'kunstlich' und 'intelligenz' jeweils 4x vor.

 

TF-IDF berechnen

Die Bag Of Words sind hier Listen mit 15 Elementen und jede stellt einen Vektor im 15-dimensionalen Raum dar. Im folgenden werden solche Vektoren miteinander mathematisch verglichen, um Ähnlichkeiten berechnen zu können.

 
Inverse Document Frequency

Bevor der Vergleich des Such-Bag Of Words mit den Bag Of Words jeder Seite vorgenommen wird, muss die Relevanz der Worte berücksichtigt werden, die in den Bag Of Words abgebildet werden: 

Begriffe, die auf jeder Seite vorkommen sind nicht relevant. Diejenigen Worte, die nur auf einzelnen Seiten vorkommen, sind für eine Suchfunktion viel aussagekräftiger.

Die Gewichtung der Worte, in den Bags Of Words muss entsprechend angepasst werden. Worte, die in allen Seiten vorkommen werden weniger gewichtet. Begriffe die nur auf wenigen Seiten vorkommen werden stärker gewichtet. Gleichzeitig bleibt es allerdings wichtig, wie oft ein Begriff überhaupt auf einer Seite vorkommt.

TF-IDF steht für Term Frequency Inverse Document Frequency of Records. Ein Term meint hier umgangssprachlich Wort oder Begriff (alles was eine Bedeutung hat).

TF

Die Termhäufigkeit tf (term frequency) beschreibt, wie oft ein Wort in einem Dokument (hier auf einer Seite), bezogen auf die Gesamtanzahl der Worte in diesem Dokument, vorkommt. Je öfter ein Wort auf einem Dokument vorkommt, um so größer ist die Bedeutung für die Suche. Allerdings nimmt die Relevanz auch ab, je mehr Worte diese Dokument enthält:

\( ( Relevanz\ eines\ Wortes\ \sim tf = \frac{Anzahl\ eines\ Wortes\ im\ Dokument}{Gesamtwortzahl\ des\ Dokuments} \)

IDF

Die Dokumentenhäufigkeit df (document frequency) besagt, in wie vielen Dokumenten ein Term vorhanden ist:

\(df = Anzahl\ der\ Dokumente\ in\ denen\ ein\ bestimmtes\ Wort\ vorkommt \)

Die Relevanz eines Wortes ist aber umgekehrt proportional zu df, also der Anzahl der Dokumente, in denen das Wort vorkommt:

\( Relevanz\ eines\ Wortes\ \sim \frac{1}{df} \)  (umgekehrt proportional = engl. inverse proportional)

Außerdem ist dabei relevant, wieviele Dokumente es überhaupt gibt N:

\( Relevanz\ eines\ Wortes\ \sim Gesamtzahl\ aller\ Dokumente\ N \)

Aus beiden Schlussfolgerungen ergibt sich die inverse Dokumentenhäufigkeit idf (inverse document frequency) eines Wortes:

\( Relevanz\ eines\ Wortes\ = idf = \frac{N}{df} \)

Kommt ein Wort in allen Dokumenten vor, dann ergibt sich hier der Wert 1. Tatsächlich ist die Relevanz, die Bedeutung für die Suche = 0, denn es ist völlig unbrauchbar. Daher wird der Logarithmus (zur Basis 2) dieses Bruches bestimmt, denn es gilt log(1) = 0. Die Gewichtung geht also gegen 0, wenn ein Wort in vielen Dokumenten vorkommt. Außerdem wird die Relevanz selten vorkommenden Worten etwas gedämpft:

Logarithmusfunktion (zur Basis 2)

Logarithmusfunktion (zur Basis 2)   https://en.wikipedia.org/wiki/Binary_logarithm  (12.03.2022)

Beispiel bei :

df N/df log₂(N/df)
1000   1 0
100 10 3.32
10 100 6.64
1 1000  9.97

Es muss jedoch außerdem berücksichtigt werden, wie oft ein bestimmtes Wort in einem Dokument vorkommt tf (term frequency). Daher wird zur Berechnung der Relevanz tf mit idf mulitpliziert:

\( tfidf = tf \cdot log_2(\frac{N}{df}) \)

Im Code kann das manuell so programmiert werden. (Die Python-Bibliothek sklearn bietet allerdings auch einen fertigen Algorithmus an.)

N = len(processed_docs)

# df berechnen
df = np.sum(bag_matrix > 0, axis=0)

idf = np.log(N / df)

tfidf_matrix = bag_matrix * idf
tfidf_matrix

array([[0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.69314718, 0.        ,
        0.        , 1.38629436, 1.38629436, 1.38629436, 1.38629436],
       [0.        , 0.        , 5.54517744, 5.54517744, 0.28768207,
        0.28768207, 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 6.93147181, 0.        , 0.        , 0.28768207,
        0.28768207, 4.15888308, 0.        , 0.69314718, 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.28768207,
        0.28768207, 0.        , 4.15888308, 0.        , 2.77258872,
        2.77258872, 0.        , 0.        , 0.        , 0.        ]])

Worte, die nur auf wenigen Seiten vorkommen sind nun stärker gewichtet.

Diese Daten könnten gespeichert werden, so dass sie nicht jedes Mal mit Webcrawling und NLP ermittelt werden müssen (Das könnte als zeitgesteuerter Task einmal am Tag erledigt werden. Webcrawler durchlaufen das Netz vorzugsweise, wenn die Netzwerkbelastung gering ist.)

Diese Daten können in einem kompakten binären Format gespeichert werden, da sie nicht für Menschen lesbar sein müssen.

filename = "tfidf.npy"   # Die Dateierweitung muss "npy" sein
np.save(filename,tfidf)  # NumPy enthält Dateifunktionen

Das Lesen der Datei erfolgt mit:

tfidf=np.load(filename)

 
Suchanfrage verarbeiten

Suchanfragen werden genauso behandelt wie Dokumente:

search = "Anaconda installieren künstlich"

processed_search = process_text(search)
search_vector = bag_of_words(processed_search, vocab)

search_tfidf = search_vector * idf
search_tfidf

array([0.        , 1.38629436, 1.38629436, 0.        , 0.        ,
       0.        , 0.        , 0.        , 0.        , 0.        ,
       0.        , 0.        , 0.        , 0.        , 0.        ])
 

Interpretation dieses Suchvektors: 'Anaconda' und "künstlich" sind zweit- und dritthäufigste Wort und gleichrelevant für die Suche. 'installieren' ist gar nicht im 15 Worte langen Gesamtvokabular vorhanden und kann also auch nicht abgebildet, bzw. es kann nicht danach gesucht werden. (Interessanterweise ist 'installation' vorhanden. Aber das ist kein Treffer.)
 
 
Kosinus-Ähnlichkeit

Nun wird der Such-Vektor mit allen Seiten-Vektoren verglichen. Dafür wird jeweils die Kosinus-Ähnlichkeit der Vektoren berechnet:

\( cos( \theta )= \frac{ \Sigma{a_i \cdot b_i }}{\sqrt{\Sigma{ a_i^2}} \cdot \sqrt{\Sigma{ b_i^2} }} = \frac{a_1b_1+a_2b_2+...+a_nb_n}{\sqrt{ a_1^2 + a_2^2+...+a_n^2} \cdot \sqrt{ b_1^2 + b_2^2+...+b_n^2}} \)

(Skalarprodukt / Produkt der Vektorlängen)

Werden z.B. zur Veranschaulichung die folgenden Vektoren im zweidimensionalen Raum miteinander verglichen:

3 Vektoren im zweidimensionalen Raum

3 Vektoren im zweidimensionalen Raum
Cosine Similarity Explained Using Python | by Misha Sv | Towards Data Science   (16.03.2022)

So ergeben die Kosinus-Ähnlichkeiten:

\( Ähnlichkeit(A,B)=cos( \theta )= \frac{1 \cdot 2+4 \cdot 4 }{\sqrt{1^2 +4^2} \cdot \sqrt{ 2^2 + 4^2}}= \frac{18}{ \sqrt{17}} = \frac{18}{4,14} =0,976\\ Ähnlichkeit(A,C) = 0,74 \)

Das bedeutet, dass sich die Vektoren A und B ähnlicher sind als A und C. Je weiter die Ähnlichkeit von cos(θ) gegen 1 geht, um so ähnlicher sind sich zwei Vektoren:


Kosinus-Ähnlichkeit
Kosinus-Ähnlichkeit

Der Vergleich wird hier mit einer fertigen Funktion mit 15-dimensionalen Vektoren über das gesamte Array mit einem einzigen Befehl durchgeführt. 

from sklearn.metrics.pairwise import cosine_similarity

similarities = cosine_similarity(tfidf_matrix, search_tfidf.reshape(1, -1))
similarities

array([[0.        ],
       [0.49932848],
       [0.60336393],
       [0.        ]])

Der Vergleich liefert eine Liste mit Kosinus-Ähnlichkeiten (Anzahl der Elemente = Anzahl der "gescrapten" Seiten).

Nun wird der Index des höchsten Wertes wird ermittelt. Damit wird der relevanteste Link indiziert.

Das Ergebnis der Suche mit 'Anaconda installieren künstlich' wird ausgegeben:

best_index = similarities.argmax() # liefert den Index des größten Werts in der Liste

print("Am relevantesten ist das " + str(best_index + 1) + ". Dokument:")
print(documents[best_index])

print(urls[best_index])

Am relevantesten ist das 3. Dokument:

Informatik Installation von Anaconda Anaconda ist eine Python-Distribution für Data Science. Die Installation von Anaconda erfolgt über ein Installationsprogramm. Anaconda enthält Python, Jupyter Notebook und viele wissenschaftliche Bibliotheken. Nach der Installation kann Anaconda über den Navigator gestartet werden. Zurück zur Übersicht

https://ateachment.github.io/nlp-demo/anaconda.html


Damit hat sich das relevanteste Suchwort 'Anaconda' hier durchgesetzt, denn 'künstlich' kommt auf der Zielseite gar nicht vor.

Last modified: Wednesday, 25 February 2026, 10:46 PM