Verständnis / Spelling Correction

@maik_froebe
Ich versuche mich gerade an dem IR Projekt. Ich versuche seit 3h mich in eines der Themen ein zu arbeiten doch ich finde kein Verständis in auch nur einem. Der Tutorial Code scheint die Lösung zu sein und irgendwie auch nicht, mir fehlt die Sicht der Möglichkeiten aus dem Tutorialcode, vorallem da die 1:1 Anwendung des Codes meines erachtens nicht funktioniert und ja auch iwo meines erachtens das Ziel nicht sein kann das ich Code copie paste. Vieles aus dem Code ruft auch nur noch mehr Fragezeichen auf, welche Funktion macht genau was, was für inputs kann ich mit geben etc.
Zuletzt bin ich eben auf das Thema “Spelling Correction” gestoßen, wobei meine Frage wäre wie ich das Testen soll da alle querys korrekt geschrieben sind?

Hi Florian,

Danke für deine Nachricht.
Manche der Tutorials sind für die Anwendung im IR Projekt dieses Jahr nicht geeignet, zum beispiel spelling correction ist nur hilfreich, wenn die Suchanfragen Rechtschreibfehler haben, das ist für das Projekt dieses Jahr aber eher nicht der Fall.

Insgesamt ist es so gedacht, dass du eine Hypothese/Idee deiner Wahl die dich aus der Vorlesung interessiert als Projekt umsetzen kannst.
Viele Sachen sind dabei in PyTerrier schon eingebaut und oft mit vergleichsweise wenig Aufwand umsetzbar, ich kann dafür dieses Tutorial empfehlen: GitHub - terrier-org/ecir2021tutorial

In welche Richtung soll das Projekt bei euch gehen, was interessiert euch? Vielleicht kann ich euch damit noch ein Paar Tipps geben?

Viele Grüße,

Maik

Hi, meine Gruppe möchte vorallem Wert auf Entity Ranking legen, wobei Umsetzung über: “Pipeline so anpassen, dass Entitäten als Phrasen gesucht werden” und “Entitäten als Hilfsmittel für Query Expansion nutzen, die Terme die die Entität beschreiben mit in die query expande.”, da das über zwei andere Mitglieder umgesetzt wird und ich gerne selber etwas coden würde war ich auf der Suche nach etwas anderem.

Wobei ich selbst Interesse an Stemming hätte,
0
dabei verstehe ich vorallem diesen Ausschnitt nicht, woher weiß die Funktion hier z.B. bei übergabe “PorterStemmer” dass es die Funktion def stem_porter(t) verwenden soll.

Danke für die schnelle Antwort.

Mfg
Florian Hofmeister

@maik_froebe
Nachtrag, also die Dokumentation hilft schonmal sehr gut.
Nun habe ich Stemming wie im Tutorial implementiert, jedoch bekomme ich bei stemmer=None und stemmer=“PorterStemmer” jeweils folgenden output:


Des weiterin soll Kovertz stemmer und Lemmatization eingebaut werden (laut Tutorial und https://tira-io.github.io/) beides sind jedoch keine Option des Inputs “stemmer”

, angenommen ich implementiere es wie im Tutorial
3
wie kann ich es dann mit geben / verwenden

Mfg
Florian Hofmeister

Das klingt doch schon mal nach einem super start!

Ich hab das gerade nicht mehr ganz im Kopf, wenn ich mich recht errinnere nutzt PyTerrier das, was in stemmer="XY" übergeben wird um die Klasse XY im package org.terrier.terms zu laden, die dafür im ClassPath sein muss.

D.h., wenn sich lemmatizer = autoclass("org.terrier.terms.StanfordLemmatizer")() laden lässt, müsste ``stemmer=“StanfordLemmatizer”` auch funktionieren.

Ich bin gerade nicht am laptop und kann es deswegen gerade nicht ausprobieren, wenn es bei dir nicht klappt kannst du gerne mir einen link zum Jupyter Notebook senden, später am Abend hab ich bestimmt Zeit nochmal rein zu schauen.

Was hier dann sicherlich auch interessant wird: ist für jede Query der gleiche stemmer mit dem gleichen retrieval modell das beste? Oder wechselt das zwischen den Queries? Wenn es wechselt, hätte es bestimmt das potential dass man zwei oder drei solche pipelines miteinander kombinieren kann. Um damit etwas rumzuspielen könntest du bei pt.Experiment ein Parameter perquery setzen.

Ich melde mich heute später am Abend nochmal zurück wegen dem Stacktrace den du geschrieben hast (wenn du es schaffst mir den link zu dem notebook zu senden kann ich auch direkt dort gucken).

Viele Grüße,

Maik

ir-lab-sose-2024-ir-nfmj/baseline-retrieval-system/baseline-retrieval-system.ipynb at 83722128443af7524948375a178c7a381082c885 · tira-io/ir-lab-sose-2024-ir-nfmj · GitHub, ich merk gerade das beim indexing irgendetwas noch nicht geht, da kommt " Indexed 3 empty documents"

Hi, die Warnung sagt nur, dass es 3 Dokumente gibt, die leer sind. Das ist ok, das kann passieren, sind ja insgesamt knapp 150 000 Dokumente, dass da mal ein paar leer sind (vielleicht auch nur nach stopword removal) klingt plausibel.

Was ich dort noch sehe: PyTerrier schneidet die Document-IDs ab, das ist der Grund, warum dort ein ndcg von 0 erzielt wird. (z.b., die Dokument-ID ist 2004.cikm_conferenc, da fehlt noch etwas.)

Um das zu lösen, müsstest du diese Zeile:

indexer = pt.IterDictIndexer("/tmp/index", overwrite=True, stemmer='PorterStemmer')

Zu dem hier ändern;

indexer = pt.IterDictIndexer("/tmp/index", overwrite=True, stemmer='PorterStemmer', meta={'docno': 75, 'text': 4096})

Das meta feld sagt hier, dass die docno 75 zeichen lang sein soll, was für den corpus hier ausreichend ist.

Ansonsten: Ich hab die nacht noch etwas drüber geschlafen, und ich finde mittlerweile, dass es super spannend sein könnte wenn du Enity-Regognition mit Spacy o.ä. einbaust. Dann wird die geschichte in meinen augen rund, weil deine Komilitonen machen entity linking in der query (in der query hab ich zu wenig text um spacy drauf zu werfen, deswegen dort die existierende spezialisierte Lösung), wenn du dann auf der Dokumenten-Seite auch etwas mit Entities machst ist das in meinen Augen eine Runde Story, das passt besser als wenn du dich auf tokenization oder stemming spezialisierst, oder?

Ein einfacher approach könnte sein, dass du die normalen BM25 scores mit BM25 scores (oder ein anderes retrieval model) auf nur bestimmten entity-arten kombinierst (oder mehrere Varainten davon, im einfachsten Fall als Linearkombination). Wenn du zum Beispiel mal schaust, welche typen von entities in den abstracts in der Regel gefunden werden (hier wäre ein guter Start: https://spacy.io/usage/linguistic-features#named-entities), könntest du zum beispiel probieren, ob du eine (oder mehrere) alternative dokument-repräsentationen baust, die dann jeweils nur eine menge von entitiy-typen enthalten. Also meinetwegen in dem Spacy Link von oben könnte ich sagen, alles was eine Geopolitical entity ist, kann vermutlich problemlos entfernt werden.

Ein systematisches Vorgehen könnte hier sein:

  • Verarbeite alle Dokumente mit Spacy
  • Zähle, wie oft welche Entity-Typen vorkommen
  • Für die die oft vorkommen: denkst du, dass diese Art fürs retrieval hilfreich ist, oder nicht? Wenn es hilfreich sein wird, dann würde ich es in die Dokumentrepräsentation aufnehmen, wenn nicht, entfernen. Ggfs. kann das noch gewichtet werden, also zum Beispiel, dass man Entitäten vom Typ X 10 mal speichert, entitäten vom Typ Z nur 2 mal oder ähnlich.
  • Basierend auf einer ersten Analyse könntest du damit dann auf dem Trainingsdatensatz versuchen, den nDCG score zu erhöhen. Da du vollen zugriff auf die Trainingsdaten hast, sollte das gehen, weil die Entitys ja zusätzliches wissen mit reinbringen, das vorher nicht da war. Im Zweifel sollte es in der Regel gehen, den BM5 score mit einem “BM25 nur auf Entities vom Typ X und Z” zu kombinieren, dass man auf dem Trainingsdatensatz besser wird. Und ob das dann auf dem Testdatensatz noch der Fall ist, wäre eine interessante Hypothese.

Was denkst du dazu?

Viele Grüße,

Maik

Hi,
danke für die guten Tipps und entschuldige die späte Antwort ich bin gerade erst von der Arbeit heim gekommen.
Ich halte mal Rücksprache mit meinen Gruppenmitgliedern, ob das nicht schon jemand mit macht. Könnte aber ne weile dauern bis ich Antwort erhalte. Falls es nicht gemacht wird, würde ich mich dran versuchen.

Mfg
Florian Hofmeister

@maik_froebe
Wie greife ich auf die abstracts zu?

Mfg
Florian Hofmeister

Hi, das Beispiel-Jupyter-Notebook hat am anfang ein pyterrier datensatz geladen.
Auf diesem PyTerrier Datensatz gibt es eine get_corpus_iter() methode die genutzt werden kann (siehe hier: Importing Datasets — PyTerrier 0.10.1 documentation).

Alternativ gab es noch ein anderes notebook in eurem repo, das zeigt, wie man mit ir_datasets über die dokumente iteriert, beides würde analog funktionieren.

Viele grüße,

Maik

Morgen,
ja aber nlp also die Funktion von spacy die den Text engegen nimmt erlaubt ja nur “Expected a string, Doc, or bytes as input” und Iter gibt ja nur ein Iterator wieder, alle anderen der get_… Funktionen scheinen auch nicht geeignet zu sein. Deswegen meine Frage wie ich an den Text der Abstracts komme, denn die Texte soll ich ja verarbeiten.

Mfg
Florian Hofmeister

Achso, alles klaro.

Der Iterator ist über dictionaries der Dokumente, also jeder eintrag (d.h. jedes dictionary) entspricht einem Dokument.
Um dieses dictionary zu inspizieren, ist die einfachste Variante, das dictionary mit print auszugeben.

Also grob so:

for i in pt_dataset.get_corpus_iter():
    print(i)
    #for dev purposes only show the first document, hence do a break
    break

Aus meinem Kopf (habs gerade nicht nachgeschaut) sollte jedes dictionary ein feld text und ein feld für die Dokument ID haben.
D.h., du könntest wenn du ein processing festgelegt hast einfach das text feld überschreiben.

Viele Grüße,

Maik

@maik_froebe
Hi,
ich habe es nun soweit das ich ein dict habe indem ich die Entity Typen habe mit ihrer Vorkommensanzahl.

Kann ich das iwie etwas beschleunigen wenn ich das einmal durchlaufen lasse dauert das ca 30min.

Zum nächsten Schritt ihres Vorschlags:

  • Für die die oft vorkommen: denkst du, dass diese Art fürs retrieval hilfreich ist, oder nicht? Wenn es hilfreich sein wird, dann würde ich es in die Dokumentrepräsentation aufnehmen, wenn nicht, entfernen. Ggfs. kann das noch gewichtet werden, also zum Beispiel, dass man Entitäten vom Typ X 10 mal speichert, entitäten vom Typ Z nur 2 mal oder ähnlich.

Sind Entity Typen nicht nur interessant wenn sie auch in der Query vorkommen, um dann explizit nach den Entity Typen zu suchen oder wie funktioniert das?

Was ist die Dokumentenrepräsentation? Wieder i aus:

for i in pt_dataset.get_corpus_iter():

Aktueller Stand

Hi,

Super cool!
So richtig gut beschleunigen lässt sich das nicht, aber du musst das ja nur ein mal bauen. Eine alternative Möglichkeit könnte sein, dass du nur die top-100 oder so results von BM25 so verarbeitest, oder nur die top-1000, das sollte es etwas schneller machen, weil dann weniger Dokumente verarbeitet werden, aber insgesamt lohnt sich das vielleicht nicht so stark. Aber da man den Index ja jeweils nur ein mal bauen muss, sind 30 min ja vertretbar.

Für den nächsten Schritt: das müsste man ausprobieren, ob und was hier funktioniert, hängt von den Daten ab. Da hier alle Queries für einen domänenspezifischen Datensatz gebaut sind, kann ich mir sehr gut vorstellen, dass es eine statische Filter-Regel geben kann, die sinvoll ist. Wenn du es abhängig von der Query machen möchtest hat das sicher insgesamt das größere potential, aber ist bestimmt auch etwas schwieriger. Aber da deine komilitonen ja an entity-linking auf der Query Seite arbeiten, könnt ihr darüber ggfs- gut alles zusammenführen.

Insgesamt ist aber die übersicht, die du von den entities gemacht hast schon ein sehr guter start. Jetzt kannst du dich fragen: Sind ORG,TIME, DATE, MONEY, o.ä. entitäten für das Retrieval Szenario hilfreich? Meine Vermutung wäre eher nein. Aber das muss man testen. Die einfachste Variante, das zu testen wäre (nachdem du ein mal die terme alle annotiert hast): retrieva mal nur auf ORG, retrieve mal nur auf TIME und messe wie ist die retrieval effektivität, wenn sie sehr niedrig ist, kann man es entfernen. Das im besten fall über alle möglichen Entitäten, so dass im nächsten schritt das ganze ausgenutzt werden kann, um die effektivität der retrieval pipeline zu verbessern (zum beispiel alle entitiy typen entfernen die nicht hilfreich sind).

Die Dokumentenrepräsentation ist für mich der Text des Dokuments, über dem retrievt wird.

Am Schluss kannst du dann deine entitiy-pipeline mit den anderen kombinieren: Examples of Retrieval Pipelines — PyTerrier 0.10.1 documentation

Viele Grüße,

Maik

Wie retrieval ich denn nur auf ein Entity Typen?

Dazu kannst du den Text der Dokumente so anpassen, dass nur der eine entity typ vorhanden ist, alles andere wird rausgefiltert.

Also, zum Beispiel speicherst du dir alle vorverarbeiteten Dokumente in einer Liste ab.
Dann baust du einen Index, passt aber den Dokumenttext so an, dass nur der eine entity-typ in den Text übernommen wird.

Als Pseudo-Code:

docs_to_index = []
for document in documents:
    document_id = ...
    document_text = ''
    for term in document.terms:
        if term.entity_type == target_entity_type:
            document_text += ' ' + term
    docs_to_index += [{'id': document_id, 'text': document_text}]
indexer.index(docs_to_index)

So in der Art sollte es passen, hilft das?

Viele Grüße,

Maik

Also so ganz verstehe ichs noch nicht, also ich habe nun die absctracts der Dokumente durch die Entity Typen ersetzt (Duplikatfrei).

# entity Recognition 
nlp = spacy.load("en_core_web_sm")


# Erstelle ein Dictionary {}
EntityTypeDict = dict()
documents = []
# gehe über alle abstracts mit spacy und ließ deren Entitäten
for i in pt_dataset.get_corpus_iter():
    doc = nlp(i.get("text"))
    i["text"] = ""
    for ent in doc.ents:
        #print("entity:")
        #print(ent.text, ent.start_char, ent.end_char, ent.label_)
        # wenn der Entity Type bereits im Dictionary ist dann ...
        if ent.label_ in EntityTypeDict:
            EntityTypeDict[ent.label_] += 1
        # falls nicht füge den Entity Typen in den Dictionary mit Initial 1 vorkommen    
        else: 
            EntityTypeDict[ent.label_] = 1   
        # ersetze die abstracts mit den vorkommenden entity typen    
        if ent.label_ not in i["text"]:
            i["text"] += ' ' + ent.label_   
    documents.append(i)
    print(i["text"])    
    break 
print(EntityTypeDict)

Und nun führe ich folgendes aus

docs_to_index = []
for document in documents:
    document_id = document.get("docno")
    document_text = ''
    if "ORG" in document.get("text"):
        document_text += "ORG"
        docs_to_index += [{'text': document_text, 'docno': document_id}]
indexer.index(docs_to_index)

Ganzes

Also so filter ich jetzt auf den Entity Typen im Dokument aber wie Filter ich in der retrieval pipeline. Also wie wird die query denn nun auf diese Liste ausgeführt. Außerdem so wie ich es mir Vorstelle nimmt er eine query und sucht nach ergebnissen doch findet nur entity Typen und kann damit dann nichts anfangen oder? Außerdem gibt es ein Fehler beim run von pt.Experiment()

Tut mir leid wenn ich einfach unglaublich wenig peil.

Mfg
Florian Hofmeister

Hi,

alles gut, es geht ja schon in die richtige Richtung!
Das Problem in dem code oben ist, dass Dokumente am ende ja nur aus dem Texten wie “ORG ORG ORG” bestehen, und des dadurch nicht mit der Query treffen kann. Das Ziel ist, die “Lücke zwischen Suchanfrage und relevanten Dokumenten” so klein wie möglich zu machen, aber wenn Dokumente nur aus “ORG ORG ORG” bestehen kann keine der üblichen Queries überhaupt dokumente zurückliefern.

Ich habe hier ein kleines Google Colab Notebook für dich vorbereitet, was die Idee die oben besprochen wurde ganz rudimentär umsetzt, und damit eine Verbesserung gegenüber BM25 auf den Trainingsdaten erzielt: Google Colab

Das Notebook arbeitet, damit es zum entwickeln schnell geht, nur auf den gejudgten Dokumenten, ich hab aber die entsprechende Stelle, die angepasst werden müsste mit kommentaren versehen.

Bitte schau dir das mal an, wenn du weitere Fragen hast sag gern bescheid.

Viele grüße,

Maik

Kann es sein das die Dokumentemasse zu groß ist um sie in processed_documents_store zu speichern? Mir stürtzt der Kernel nach ca der hälfte der Dukumente ab.

Das könnte sein, dass es zu viel memory braucht (du kanst den memory verbrauch zum beispiel mit htop im terminal anschauen, während das programm läuft).

Aber die processed_documents_store abzuspeichern ist ja nur für die entwicklung wichtig, gewesen, oder? Da würde ja vielleicht schon der Teil reichen, den wir oben im notebook hatten.

Auf dem kompletten corpus reicht es vielleicht aus, wenn dort direkt nur der gewollte text abgespeichert wird, oder?