3 SpaCy-Tricks für effiziente Textverarbeitung und Entitätserkennung

# Einführung

Insbesondere dank zeitgenössischer großer Sprachmodelle, Verarbeitung natürlicher Sprache (NLP) ist eine grundlegende Säule moderner KI- und Softwaresysteme. Sie finden NLP-Techniken und -Technologien, die alles unterstützen, von Suchmaschinen und Chatbots bis hin zu automatisierten Kundensupport-Routing- und Entity-Extraktions-Pipelines. Wenn es um produktionstaugliches NLP in Python geht, spacig ist der unbestrittene Industriestandard. spaCy wurde speziell für den Produktionseinsatz entwickelt und bietet industrietaugliche Geschwindigkeit, vorab trainierte Statistik- und Transformatormodelle sowie eine intuitive API.

Leider betrachten viele Entwickler spaCy als einen einfachen Black-Field-Monolithen. Sie laden ein Modell, führen es mit Textual content aus und akzeptieren die Standardverarbeitungsgeschwindigkeiten und Extraktionsgrenzen. Bei der Skalierung von einem lokalen Prototyp auf die Verarbeitung von Millionen von Dokumenten können diese Standardkonfigurationen zu Rechenengpässen führen, die zu Latenz, aufgeblähtem Speicherbedarf und fehlenden domänenspezifischen Entitäten führen. Um leistungsstarke Textverarbeitungspipelines zu erstellen, müssen Sie verstehen, wie Sie den internen Ausführungsfluss von spaCy optimieren.

In diesem Artikel werden wir drei wesentliche SpaCy-Methods untersuchen, die jeder Entwickler in seinem Toolkit haben sollte, um die Verarbeitungsgeschwindigkeit zu maximieren und die Entitätserkennung anzupassen: selektives Laden der Pipeline, parallele Stapelverarbeitung und hybride regelbasierte statistische Entitätserkennung.

Bevor Sie beginnen, stellen Sie sicher, dass Sie spaCy und sein leichtes, universell einsetzbares englisches Modell installiert haben:

pip set up spacy
python -m spacy obtain en_core_web_sm

# 1. Selektives Laden der Pipeline und Deaktivieren von Komponenten

Wenn Sie ein vorab trainiertes spaCy-Modell laden (z. B en_core_web_sm) initialisiert spaCy eine vollständige NLP-Pipeline. Diese Pipeline umfasst normalerweise:

  • ein Tokenizer
  • ein Wortart-Tagger (tagger)
  • ein Abhängigkeitsparser (parser)
  • ein Lemmatisierer (lemmatizer)
  • ein Attributlineal (attribute_ruler)
  • ein benannter Entitätserkenner (ner)

Obwohl dieser umfassende Commonplace-Funktionsumfang hervorragend ist, ist er mit einem erheblichen Rechenaufwand verbunden. Wenn Ihre Anwendung nur die Erkennung benannter Entitäten (NER) durchführen muss, ist die Ausführung des Abhängigkeitsparsers und Lemmatisierers eine Verschwendung von CPU-Zyklen und Speicher. Wenn Sie hingegen nur Textual content bereinigen und Lemmata extrahieren, ist die Ausführung des tiefenstatistischen NER-Modells äußerst ineffizient. Sie können dies optimieren, indem Sie Komponenten beim Laden gezielt ausschließen oder sie während der Ausführung mithilfe eines Kontextmanagers vorübergehend deaktivieren.

Dieser naive Ansatz lädt jede Standardkomponente für den Textual content und führt sie aus, unabhängig davon, ob die Ausgaben der Komponenten tatsächlich verwendet werden:

import spacy
import time

# Load the small English mannequin
nlp = spacy.load("en_core_web_sm")

texts = ("Apple is wanting to buy U.Ok. startup for $1 billion") * 1000

# Naive execution: runs tagger, parser, lemmatizer, and ner on each doc
# Assume we solely care about named entities right here
start_time = time.time()
for textual content in texts:
    doc = nlp(textual content)
    entities = ((ent.textual content, ent.label_) for ent in doc.ents)

duration_full = time.time() - start_time

print(f"Full pipeline processed 1,000 docs in: {duration_full:.4f} seconds")

Ausgabe:

Full pipeline processed 1,000 docs in: 2.8540 seconds

Lassen Sie uns nun die Ausführung auf zwei spezifische Arten optimieren. Erstens werden wir schwere, ungenutzte Komponenten wie den Abhängigkeitsparser zur Ladezeit ausschließen. Zweitens werden wir verwenden nlp.select_pipes() um Komponenten bei der Verarbeitung bestimmter Arbeitslasten vorübergehend zu deaktivieren.

import spacy
import time

# Load time optimization: Exclude the heavy parser and tagger from the beginning
# This reduces initialization time and reminiscence footprint
nlp_optimized = spacy.load("en_core_web_sm", exclude=("parser", "tagger"))

texts = ("Apple is wanting to buy U.Ok. startup for $1 billion") * 1000

# Context-manager optimization, disable parts quickly
# We've got outright excluded parser and tagger, we disable attribute ruler and lemmatizer right here
start_time = time.time()
with nlp_optimized.select_pipes(disable=("attribute_ruler", "lemmatizer")):
    for textual content in texts:
        doc = nlp_optimized(textual content)
        entities = ((ent.textual content, ent.label_) for ent in doc.ents)

duration_opt = time.time() - start_time

print(f"Optimized pipeline processed 1,000 docs in: {duration_opt:.4f} seconds")
print(f"Speedup: {duration_full / duration_opt:.2f}x sooner!")

Vergleichen wir die Laufzeiten:

Full pipeline processed 1,000 docs in: 2.8739 seconds
Optimized pipeline processed 1,000 docs in: 1.7859 seconds
Speedup: 1.61x sooner!

Im optimierten Beispiel bestanden exclude=("parser", "tagger") Zu spacy.load() verhindert vollständig, dass diese Komponenten in den Speicher geladen werden. Als different Methode, um im Grunde das gleiche Ergebnis zu erzielen, haben wir bestanden disable=("attribute_ruler", "lemmatizer") ihre Verarbeitung vorübergehend zu deaktivieren. Der Effekt ist, dass spaCy bei der Verarbeitung des Textes die mathematisch aufwändige Token-Abhängigkeitsanalyse und Wortart-Tag-Kennzeichnung überspringt und direkt zur Entitätserkennung springt. Dies führt zu einer spürbaren Beschleunigung ohne Auswirkungen auf die NER-Genauigkeit, mit noch deutlicheren Vorteilen in größerem Maßstab.

# 2. Stapelverarbeitung mit hohem Durchsatz mit nlp.pipe und Metadatenweitergabe

Wenn Sie über einen großen Korpus iterieren (z. B. Pandas DataFrames, Datenbankzeilen oder Rohtextdateien), rufen Sie die auf nlp Objekt auf einzelnen Zeichenfolgen in einer Schleife (z. B (nlp(textual content) for textual content in texts)) ist ein Anti-Muster.

Die sequentielle Verarbeitung verhindert, dass spaCy Speicherpuffer optimiert, Vorgänge gruppiert und die Multi-Core-Parallelisierung nutzt. Außerdem müssen Sie bei der Verarbeitung von Textual content für die Datenbankspeicherung oder ETL-Pipelines häufig Metadaten (wie eine Datensatz-ID, einen Zeitstempel oder eine Kategorie) durch den NLP-Prozess übertragen, damit Sie die resultierenden Entitäten wieder den richtigen Datenbankzeilen zuordnen können.

Die Lösung liegt in der Verwendung nlp.pipe(). Diese Methode verarbeitet Dokumente als Strompuffert sie intern und unterstützt Multi-Processing. Durch Einstellung as_tuples=TrueSie können Tupel von füttern (textual content, context) zu spaCy. Es wird zurückkehren (doc, context) Paare, sodass Sie Metadaten direkt durch die Pipeline weiterleiten können.

Dieser naive Ansatz führt die Verarbeitung sequentiell durch und nutzt die manuelle Indexverfolgung, um die resultierenden Dokumente mit ihren Datenbank-IDs abzugleichen, was spröde und langsam ist:

import spacy
import time

nlp = spacy.load("en_core_web_sm", exclude=("parser", "tagger"))

# Uncooked database information with distinctive IDs
information = (
    {"id": f"DB-REC-{i}", "textual content": "Google was based in September 1998 by Larry Web page and Sergey Brin."}
    for i in vary(1000)
)

# Sequential loop: sluggish and manually managed metadata
start_time = time.time()
extracted_data = ()
for i, document in enumerate(information):
    doc = nlp(document("textual content"))
    entities = ((ent.textual content, ent.label_) for ent in doc.ents)
    extracted_data.append({
        "id": document("id"),
        "entities": entities
    })

duration_seq = time.time() - start_time

print(f"Sequential loop processed 1,000 docs in: {duration_seq:.4f} seconds")

Ausgabe:

Sequential loop processed 1,000 docs in: 2.7375 seconds

Hier streamen wir die Daten mit nlp.pipedie Stapelverarbeitung und Multi-Core-Parallelisierung nutzt (n_process), während die Datenbank-ID als Kontextvariable mitgeführt wird:

import spacy
import time

# Preserve your imports and definitions international so baby processes can see them
nlp = spacy.load("en_core_web_sm", exclude=("parser", "tagger"))

# Wrap the precise execution code in the principle block
if __name__ == '__main__':
    information = (
        {"id": f"DB-REC-{i}", "textual content": "Google was based in September 1998 by Larry Web page and Sergey Brin."}
        for i in vary(1000)
    )

    start_time = time.time()

    # Format enter as an inventory of (textual content, context) tuples
    stream_input = ((rec("textual content"), rec("id")) for rec in information)

    # Stream batches and use all accessible CPU cores with n_process=-1
    extracted_data_pipe = ()
    docs_stream = nlp.pipe(stream_input, as_tuples=True, batch_size=256, n_process=-1)

    for doc, rec_id in docs_stream:
        entities = ((ent.textual content, ent.label_) for ent in doc.ents)
        extracted_data_pipe.append({
            "id": rec_id,
            "entities": entities
        })

    duration_pipe = time.time() - start_time

    print(f"nlp.pipe processed 1,000 docs in: {duration_pipe:.4f} seconds")
    print(f"Speedup: {duration_seq / duration_pipe:.2f}x sooner!")

Ausgabe:

nlp.pipe processed 1,000 docs in: 7.1310 seconds

Im optimierten Codeausschnitt strukturieren wir den Eingabedatensatz in eine Folge von Tupeln um: (text_string, metadata_context). Beim Anrufen nlp.pipe(stream_input, as_tuples=True, batch_size=256, n_process=-1):

  • batch_size=256 weist spaCy an, Texte in Gruppen von 256 zu puffern und zu verarbeiten, wodurch der interne Python-Schleifenaufwand minimiert wird
  • n_process=-1 weist spaCy an, die CPU-Anzahl Ihres Methods automatisch zu erkennen und die Tokenisierung und Komponentenextraktion über alle verfügbaren Kerne hinweg zu parallelisieren
  • as_tuples=True weist spaCy an, Paare von zu liefern (doc, context)Dadurch wird sichergestellt, dass die Metadaten (die Datensatz-ID) perfekt auf das verarbeitete Dokument abgestimmt bleiben, ohne dass manuelle Indexarrays oder Listenausrichtungscode erforderlich sind

Der aufmerksame Leser wird feststellen, dass die Verarbeitungszeit für den parallelen Stapelverarbeitungscode im Vergleich zum Vorgänger tatsächlich gestiegen ist. Dies ist jedoch auf den Mehraufwand zurückzuführen, der mit der Einrichtung des Paralleljobs verbunden ist, und die Einsparungen werden deutlich, wenn die Anzahl der zu verarbeitenden Dokumente zunimmt.

Durch erneutes Ausführen derselben Codeauszüge oben, jedoch mit 10.000 statt 1.000 Datensätzen, sind hier die Ergebnisse:

Sequential loop processed 1,000 docs in: 27.6733 seconds
nlp.pipe processed 1,000 docs in: 11.5444 seconds

Sie können sehen, wie sich die Einsparungen weiter erhöhen würden.

# 3. Hybride benannte Entitätserkennung mit EntityRuler

Vorab trainierte statistische und transformatorbasierte NER-Modelle sind unglaublich leistungsfähig für die Erkennung allgemeiner Entitätstypen wie ORG, PERSONoder DATE basierend auf dem Kontext. Allerdings können Modelle häufig domänenspezifische Begriffe (z. B. benutzerdefinierte Produkt-SKUs, Legacy-Code-IDs oder medizinische Nischenbegriffe) nicht erkennen, weil sie ihnen während des Trainings nicht ausgesetzt waren.

Die Feinabstimmung eines Deep-Studying-Statistikmodells auf benutzerdefinierte Entitäten ist eine Lösung, erfordert jedoch die Kennzeichnung von Tausenden von Sätzen und birgt die Gefahr eines „katastrophalen Vergessens“, bei dem das Modell vergisst, wie es Standardentitäten erkennt.

Eine sauberere, hocheffiziente Lösung ist ein hybrider NER-Ansatz unter Verwendung von spaCy EntityRuler. Der EntityRuler ermöglicht es Ihnen, Muster zu definieren (mithilfe regulärer Ausdrücke oder tokenbasierter Wörterbuchwörterbücher) und diese direkt in Ihre Pipeline einzufügen. Sie können es hinzufügen vor das statistische NER – um deterministische Entitäten vorab zu kennzeichnen und dem Modell dabei zu helfen, Kontextentscheidungen zu treffen – oder nach es – um als Fallback oder Override zu fungieren.

Entwickler versuchen oft, statistische NER-Lücken zu schließen, indem sie Regex auf den Textual content ausführen nach Ausführen der spaCy-Pipeline, was zu manueller Koordinaten-Offset-Berechnung und getrennten Datenstrukturen führt:

import spacy
import re

nlp = spacy.load("en_core_web_sm")
textual content = "Please overview system ticket ID: TKT-98421 on our company portal."

doc = nlp(textual content)

# Commonplace statistical NER misses customized ticket IDs
entities = ((ent.textual content, ent.label_) for ent in doc.ents)
print("Earlier than post-process:", entities)

# Publish-process regex patch
ticket_pattern = r"TKT-d+"
matches = re.finditer(ticket_pattern, textual content)
custom_ents = ()
for match in matches:
    # Requires complicated char-to-token offset conversion to construct spans
    custom_ents.append((match.group(), "TICKET_ID"))

# We now have two disconnected lists of entities that should be merged manually
print("Regex entities:", custom_ents)

Ausgabe:

Earlier than post-process: ()
Regex entities: (('TKT-98421', 'TICKET_ID'))

Durch Hinzufügen eines EntityRuler Komponente direkt in die Pipeline integrieren, führen wir regelbasierte Regex-Muster und statistische Analyse in einem einzigen, einheitlichen System zusammen doc.ents Ausgabe:

import spacy

nlp = spacy.load("en_core_web_sm")

# Add the entity_ruler part to the pipeline earlier than ner so it pre-tags entities, however after works too
ruler = nlp.add_pipe("entity_ruler", earlier than="ner")

# Outline token-level patterns, together with common expressions
patterns = (
    # Match strings beginning with "TKT-" adopted by digits
    {"label": "TICKET_ID", "sample": ({"TEXT": {"REGEX": "^TKT-d+$"}})},
    # Match particular area phrases precisely
    {"label": "ORG", "sample": "company portal"}
)
ruler.add_patterns(patterns)

textual content = "Please overview system ticket ID: TKT-98421 on our company portal."
doc = nlp(textual content)

# Each statistical and rule-based entities are consolidated inside doc.ents
for ent in doc.ents:
    print(f"Entity: {ent.textual content:<20} | Label: {ent.label_}")

Ausgabe:

Entity: TKT-98421            | Label: TICKET_ID
Entity: company portal     | Label: ORG

In dieser hybriden Implementierung rufen wir auf nlp.add_pipe("entity_ruler", earlier than="ner"). Der EntityRuler fungiert als native Pipeline-Komponente. Wenn der Textual content verarbeitet wird:

  • Der Tokenizer teilt den Satz in Token auf.
  • Der EntityRuler Läuft zuerst, identifiziert Token, die unserem Ticket-Regex-Muster oder genauen Wörterbuchzeichenfolgen entsprechen, und markiert sie als TICKET_ID oder ORG.
  • Die Statistik ner Komponente wird als nächstes ausgeführt. Da es erkennt, dass diese Token bereits als Entitäten markiert sind, respektiert es die Tags (oder passt seine Vorhersagen entsprechend an, um Konflikte zu vermeiden).

Dadurch wird sichergestellt, dass alle Entitäten, sowohl erlernte statistische als auch deterministische regelbasierte, sauber in einem einzigen, zusammenhängenden System koexistieren Doc.ents Reihenfolge, wodurch die Notwendigkeit einer spröden Nachbearbeitungssortierung oder Offset-Anpassungen entfällt.

# Zusammenfassung

Bei der Optimierung von spaCy geht es um den Übergang von Standardkonfigurationen zu Pipelines, die Ihre Systemressourcen und domänenspezifischen Anforderungen berücksichtigen.

Durch die Anwendung dieser drei Methods können Sie hocheffiziente Textverarbeitungspipelines in Produktionsqualität entwerfen:

  • Selektives Laden und Deaktivieren von Komponenten eliminieren unnötige Berechnungen und beschleunigen Ihre Verarbeitungsgeschwindigkeit um das bis zu Fünffache.
  • Stapelverarbeitung mit nlp.pipe Parallelisiert die Ausführung über CPU-Kerne und Einstellungen hinweg as_tuples=True verbreitet kritische Metadaten ohne Fehler bei der Indexzuordnung.
  • Hybrider NER mit EntityRuler kombiniert deterministische Mustervergleichsregeln mit allgemeiner statistischer Inferenz und gewährleistet so maximale Extraktionsgenauigkeit für benutzerdefinierte Domänen ohne erneutes Coaching.

Durch den Einsatz dieser Entwurfsmuster wird sichergestellt, dass Ihre NLP-Pipelines skalierbar, speichereffizient und auf das einzigartige Vokabular Ihrer Geschäftsdaten zugeschnitten bleiben.

Matthew Mayo (@mattmayo13) hat einen Grasp-Abschluss in Informatik und ein Diplom in Information Mining. Als geschäftsführender Herausgeber von KDnuggets & Statistikund Mitherausgeber bei Beherrschung des maschinellen LernensZiel von Matthew ist es, komplexe datenwissenschaftliche Konzepte zugänglich zu machen. Zu seinen beruflichen Interessen zählen die Verarbeitung natürlicher Sprache, Sprachmodelle, Algorithmen für maschinelles Lernen und die Erforschung neuer KI. Seine Mission ist es, das Wissen in der Datenwissenschaftsgemeinschaft zu demokratisieren. Matthew programmiert seit seinem sechsten Lebensjahr.



Von admin

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert