Faza 05 · lecția 01
Prelucrarea textului — tokenizare, stemming, lematizare
Scopul lecției: Tip: Construire Limbaje: Python Cerințe preliminare: Faza 2 · 14 (Naive Bayes) Timp: ~45 de minute
Versiunea curentă AlexBred.com: primele 100 de lecții ale programului în limba română.
Cuprinsul lecției
- Problema
- Conceptul
- Construiți
- Pasul 1: un tokenizer de cuvinte cu expresii regulate
- Pasul 2: un stemmer Porter (doar pasul 1a)
- Pasul 3: un lematizator bazat pe căutare
- Pasul 4: legați pașii într-o conductă
- Folosiți
- NLTK
- spaCy
- Când alegeți fiecare opțiune
- Cele două moduri de eșec despre care nu vă avertizează nimeni
- Livrați
- Exerciții
- Termeni-cheie
- Lecturi suplimentare
Limbajul este continuu. Modelele sunt discrete. Preprocesarea este puntea.
Tip: Construire Limbaje: Python Cerințe preliminare: Faza 2 · 14 (Naive Bayes) Timp: ~45 de minute
Problema
Un model nu poate citi „The cats were running.” El citește numere întregi.
Fiecare sistem NLP începe cu aceleași trei întrebări: de unde începe un cuvânt? Care este rădăcina cuvântului? Când tratăm „run”, „running” și „ran” ca același lucru și când ca lucruri diferite?
Dacă tokenizarea este greșită, modelul învață din date nefolositoare. Dacă tokenizerul tratează don't ca un token, iar do n't ca două, distribuția de antrenare se fragmentează. Dacă stemmerul reduce organization și organ la același stem, modelarea subiectelor devine inutilă. Dacă lematizatorul are nevoie de contextul părții de vorbire, dar acesta nu este furnizat, verbele sunt tratate drept substantive.
Această lecție construiește de la zero cei trei pași de preprocesare, apoi arată cum NLTK și spaCy fac aceeași muncă, pentru a evidenția compromisurile.
Conceptul
Trei operații. Fiecare are un rol și un mod de eșec.
Tokenizarea împarte un șir în tokenuri. „Token” este intenționat vag, deoarece granularitatea potrivită depinde de sarcină: la nivel de cuvânt pentru NLP clasic, de subcuvânt pentru Transformere, de caracter pentru limbile fără spații albe.
Stemmingul taie sufixe folosind reguli. Este rapid, agresiv și simplist. running -> run. organization -> organ. Al doilea exemplu este chiar modul de eșec.
Lematizarea reduce un cuvânt la forma sa de dicționar folosind cunoștințe gramaticale. Este mai lentă și mai precisă; are nevoie de un tabel de căutare sau de un analizor morfologic. ran -> run (trebuie să știe că „ran” este timpul trecut al lui „run”). better -> good (trebuie să știe formele comparative).
Regulă practică: folosiți stemming când viteza contează și zgomotul este tolerabil (indexare pentru căutare, clasificare aproximativă). Folosiți lematizarea când contează sensul (întrebări și răspunsuri, căutare semantică, orice text pe care îl va citi utilizatorul).
edit-distance
Notă tehnică a traducerii: Expresia regulată de mai jos definește în mod explicit literele ASCII
A–Zșia–z; nu este un tokenizer general pentru text Unicode, alte alfabete sau alte convenții lingvistice. Alegeți reguli și teste pentru limbile acceptate, nu extrapolați acest exemplu englezesc. Consultați documentația Python pentru expresii regulate.
Construiți
Pasul 1: un tokenizer de cuvinte cu expresii regulate
Cel mai simplu tokenizer util separă după caractere nealfanumerice, păstrând punctuația ca tokenuri proprii. Nu este perfect și nici final, dar se execută într-o singură linie.
import re
def tokenize(text):
return re.findall(r"[A-Za-z]+(?:'[A-Za-z]+)?|[0-9]+|[^\sA-Za-z0-9]", text)
Trei tipare, în ordinea priorității: cuvinte cu apostrof interior opțional (don't, it's), numere pure și orice caracter individual nealfanumeric care nu este spațiu alb, ca token independent (punctuația).
>>> tokenize("The cats weren't running at 3pm.")
['The', 'cats', "weren't", 'running', 'at', '3', 'pm', '.']
Observați modurile de eșec. 3pm devine ['3', 'pm'], deoarece se alternează secvențe de litere și secvențe de cifre. Este suficient de bun pentru multe sarcini. URL-urile, e-mailurile și hashtagurile se strică toate. În producție, adăugați tipare înaintea celor generale.
Pasul 2: un stemmer Porter (doar pasul 1a)
Algoritmul Porter complet are cinci etape de reguli. Numai pasul 1a acoperă cele mai frecvente sufixe englezești și arată tiparul.
def stem_step_1a(word):
if word.endswith("sses"):
return word[:-2]
if word.endswith("ies"):
return word[:-2]
if word.endswith("ss"):
return word
if word.endswith("s") and len(word) > 1:
return word[:-1]
return word
>>> [stem_step_1a(w) for w in ["caresses", "ponies", "caress", "cats"]]
['caress', 'poni', 'caress', 'cat']
Citiți regulile de sus în jos. Regula ies -> i explică ponies -> poni, nu pony. Porter-ul real are pasul 1b, care ar corecta această formă. Regulile concurează; cele anterioare câștigă. Ordinea contează mai mult decât orice regulă individuală.
Notă tehnică a traducerii: Afirmația despre corectarea lui
ponitrebuie nuanțată: în algoritmul Porter original, pasul 1b tratează sufixeleedșiing, nu transformă în mod generalponiînpony. Un stem nu este obligat să fie un cuvânt de dicționar, iarponieste un rezultat intenționat al regulilor de stemming.
Pasul 3: un lematizator bazat pe căutare
Lematizarea propriu-zisă are nevoie de morfologie. O versiune didactică ușor de urmărit folosește un tabel mic de leme și o rezervă de siguranță.
LEMMA_TABLE = {
("running", "VERB"): "run",
("ran", "VERB"): "run",
("runs", "VERB"): "run",
("better", "ADJ"): "good",
("best", "ADJ"): "good",
("cats", "NOUN"): "cat",
("cat", "NOUN"): "cat",
("were", "VERB"): "be",
("was", "VERB"): "be",
("is", "VERB"): "be",
}
def lemmatize(word, pos):
key = (word.lower(), pos)
if key in LEMMA_TABLE:
return LEMMA_TABLE[key]
if pos == "VERB" and word.endswith("ing"):
return word[:-3]
if pos == "NOUN" and word.endswith("s"):
return word[:-1]
return word.lower()
>>> lemmatize("running", "VERB")
'run'
>>> lemmatize("cats", "NOUN")
'cat'
>>> lemmatize("better", "ADJ")
'good'
>>> lemmatize("watched", "VERB")
'watched'
Ultimul caz este momentul didactic esențial. watched nu este în tabel, iar rezerva noastră tratează numai ing. Lematizarea reală acoperă ed, verbe neregulate, adjective comparative și plurale cu schimbări de sunet (children -> child). De aceea sistemele de producție folosesc WordNet, lematizatorul spaCy sau un analizor morfologic complet.
Notă tehnică a traducerii: POS-ul este important când aceeași formă are leme diferite, însă nu este o condiție universală pentru orice lematizare: un tabel poate avea o singură intrare, iar
WordNetLemmatizeracceptă și un POS implicit. Calitatea și ambiguitatea rezultatului depind atunci de lexicon, limbă și sarcină; nu transformați regula didactică într-o interdicție absolută.
Pasul 4: legați pașii într-o conductă
def preprocess(text, pos_tagger=None):
tokens = tokenize(text)
stems = [stem_step_1a(t.lower()) for t in tokens]
tags = pos_tagger(tokens) if pos_tagger else [(t, "NOUN") for t in tokens]
lemmas = [lemmatize(word, pos) for word, pos in tags]
return {"tokens": tokens, "stems": stems, "lemmas": lemmas}
Piesa lipsă este un etichetator POS. Faza 5 · 07 (etichetare POS) construiește unul. Deocamdată, tratați implicit toate tokenurile ca NOUN și recunoașteți limitarea.
Notă tehnică a traducerii: În această rezervă implicită, un verb precum
runningnu se potrivește cu intrarea("running", "VERB")din tabel și rămânerunning; rezultatul nu este o lematizare funcțională fără un etichetator POS. Folosiți această conductă numai ca demonstrație și testați contractul dintre etichetator și lematizator.
Folosiți
NLTK și spaCy oferă versiunile pentru producție. Câteva linii pentru fiecare.
NLTK
import nltk
nltk.download("punkt_tab")
nltk.download("wordnet")
nltk.download("averaged_perceptron_tagger_eng")
from nltk.tokenize import word_tokenize
from nltk.stem import PorterStemmer, WordNetLemmatizer
from nltk import pos_tag
text = "The cats were running."
tokens = word_tokenize(text)
stems = [PorterStemmer().stem(t) for t in tokens]
lemmatizer = WordNetLemmatizer()
tagged = pos_tag(tokens)
def nltk_pos_to_wordnet(tag):
if tag.startswith("V"):
return "v"
if tag.startswith("J"):
return "a"
if tag.startswith("R"):
return "r"
return "n"
lemmas = [lemmatizer.lemmatize(t, nltk_pos_to_wordnet(tag)) for t, tag in tagged]
word_tokenize gestionează contracțiile, Unicode și cazurile-limită pe care expresia regulată le omite. PorterStemmer execută toate cele cinci etape. WordNetLemmatizer are nevoie ca POS-ul să fie tradus din schema Penn Treebank a NLTK în setul de abrevieri WordNet. Adaptarea de mai sus este partea pe care multe tutoriale o omit.
Notă tehnică a traducerii: Acest exemplu depinde de resurse NLTK descărcate, de versiunea bibliotecii, de cache și de accesul la rețea la prima rulare; numele resurselor trebuie verificate pentru versiunea instalată.
word_tokenizeși WordNet sunt orientate în principal spre engleză. Mai mult, funcția de adaptare trimite orice etichetă nerecunoscută la"n", deci nu este o mapare generală; POS-ul și domeniul trebuie validate. Consultați descărcătorul NLTK și API-ul WordNetLemmatizer.
spaCy
import spacy
nlp = spacy.load("en_core_web_sm")
doc = nlp("The cats were running.")
for token in doc:
print(token.text, token.lemma_, token.pos_)
The the DET
cats cat NOUN
were be AUX
running run VERB
. . PUNCT
spaCy ascunde întreaga conductă în spatele nlp(text). Tokenizarea, etichetarea POS și lematizarea se execută toate. Este mai rapid decât NLTK la scară mare și mai precis imediat după instalare. Compromisul este că nu puteți înlocui ușor componente individuale.
Notă tehnică a traducerii: Viteza și acuratețea nu sunt proprietăți universale; ele depind de limbă, model, componentele active, hardware și măsurătoare.
en_core_web_smeste un pachet de model separat, care trebuie să fie compatibil cu versiunea spaCy și fixat pentru reproducibilitate. Conductele spaCy sunt modulare: componentele pot fi dezactivate, configurate sau înlocuite. Consultați modelele spaCy și documentația despre conducte.
Când alegeți fiecare opțiune
| Situație | Alegeți |
|---|---|
| Predare, cercetare, înlocuirea componentelor | NLTK |
| Producție, mai multe limbi, viteza contează | spaCy |
| Conductă Transformer (oricum tokenizați cu tokenizerul modelului) | Folosiți tokenizers / transformers și săriți peste preprocesarea clasică |
Notă tehnică a traducerii: Un model Transformer trebuie alimentat cu tokenizerul, vocabularul și regulile de normalizare cu care a fost antrenat. Aceasta nu înseamnă că orice normalizare sau validare a intrării poate fi omisă; verificați contractul tokenizerului ales și efectul asupra sarcinii. Consultați documentația Transformers pentru tokenizatoare.
Cele două moduri de eșec despre care nu vă avertizează nimeni
Cele mai multe tutoriale predau algoritmii și se opresc. Două lucruri afectează o conductă reală de preprocesare și sunt aproape întotdeauna omise.
Deriva reproductibilității. NLTK și spaCy schimbă comportamentul tokenizării și al lematizatorului între versiuni. Ceea ce producea ['do', "n't"] în spaCy 2.x poate produce ["don't"] în 3.x. Modelul a fost antrenat pe o distribuție, iar inferența rulează acum pe alta. Acuratețea se degradează discret și nimeni nu știe de ce. Fixați versiunile bibliotecilor în requirements.txt. Scrieți un test de regresie pentru preprocesare, care îngheață tokenizarea a 20 de propoziții eșantion. Rulați-l la fiecare actualizare.
Notă tehnică a traducerii: Comparația exactă dintre spaCy 2.x și 3.x pentru
don'tnu trebuie folosită ca fapt general fără un test reproductibil; regulile engleze actuale spaCy tratează în mod normal contracția cadoșin't. Deriva de versiune sau de configurație rămâne un risc real, dar trebuie demonstrată pentru dependențele și exemplele proiectului. Consultați documentația spaCy despre tokenizare.
Nepotrivirea dintre antrenare și inferență. Antrenați cu preprocesare agresivă (litere mici, eliminarea cuvintelor de oprire, stemming), lansați pe intrare brută de la utilizator și urmăriți cum performanța se prăbușește. Acesta este cel mai comun eșec NLP în producție. Dacă preprocesați la antrenare, trebuie să rulați funcția identică la inferență. Livrați preprocesarea ca funcție în pachetul modelului, nu ca celulă de notebook rescrisă de echipa de servire.
Notă tehnică a traducerii: Nu există o clasificare universală care să demonstreze că acesta este „cel mai comun” eșec NLP de producție. Totuși, transformările de intrare și resursele lor fac parte din contractul modelului: versiunea, testele și comportamentul de antrenare/inferență trebuie păstrate sau schimbarea trebuie evaluată explicit pe date reprezentative.
Livrați
Un prompt reutilizabil care îi ajută pe ingineri să aleagă o strategie de preprocesare fără a citi trei manuale.
Salvați-l ca outputs/prompt-preprocessing-advisor.md:
---
name: preprocessing-advisor
description: Recomandă o configurație de tokenizare, stemming și lematizare pentru o sarcină NLP.
phase: 5
lesson: 01
---
Oferiți consultanță despre preprocesarea NLP clasică. Pentru o descriere a sarcinii, produceți:
1. Alegerea tokenizării (regex, NLTK word_tokenize, spaCy sau tokenizer Transformer). Explicați de ce.
2. Dacă trebuie aplicat stemming, lematizare, ambele sau niciuna. Explicați de ce.
3. Apelurile specifice de bibliotecă. Numiți funcțiile. Includeți traducerea etichetelor POS dacă este implicat NLTK.
4. Un mod de eșec pe care utilizatorul trebuie să îl testeze.
Refuzați să recomandați stemming pentru text vizibil utilizatorului. Refuzați să recomandați lematizarea fără etichete POS. Semnalați intrările care nu sunt în engleză ca necesitând o conductă diferită.
Exerciții
- Ușor. Extindeți
tokenizeastfel încât URL-urile să rămână tokenuri unice. Test:tokenize("Visit https://example.com today.")trebuie să producă un token URL. - Mediu. Implementați pasul 1b Porter. Dacă un cuvânt conține o vocală și se termină în
edsauing, eliminați sufixul. Tratați regula consoanei duble (hopping -> hop, nuhopp). - Dificil. Construiți un lematizator care folosește WordNet ca tabel de căutare, dar revine la stemmerul Porter când WordNet nu are intrare. Măsurați acuratețea pe un corpus etichetat față de WordNet simplu și Porter simplu.
Notă tehnică a traducerii: Cerința exercițiului 2 descrie numai un schelet didactic, nu pasul 1b complet al algoritmului Porter. Implementarea oficială are condiții suplimentare privind măsura, terminațiile și transformările; comparați rezultatul cu specificația Porter, nu presupuneți că regula scurtă este echivalentă cu ea.
Termeni-cheie
| Termen | Ce spun oamenii | Ce înseamnă de fapt |
|---|---|---|
| Token | Un cuvânt | Orice unitate consumată de model: cuvânt, subcuvânt, caracter sau octet. |
| Stem | Rădăcina unui cuvânt | Rezultatul eliminării sufixelor pe bază de reguli. Nu este întotdeauna un cuvânt real. |
| Lemă | Forma de dicționar | Forma pe care ați căuta-o. Pentru calcul corect necesită context gramatical. |
| Etichetă POS | Parte de vorbire | Categorie precum NOUN, VERB, ADJ. Este necesară pentru lematizare precisă. |
| Morfologie | Reguli despre forma cuvintelor | Felul în care un cuvânt își schimbă forma după timp, număr sau caz. Lematizarea depinde de aceasta. |
Lecturi suplimentare
- Porter, M. F. (1980). An algorithm for suffix stripping — lucrarea originală, cinci pagini, încă cea mai clară explicație.
- spaCy 101 — linguistic features — cum este legată o conductă reală.
- Cartea NLTK, capitolul 3 — cazuri-limită ale tokenizării la care probabil nu v-ați gândit.
Sursă: Originalul în limba engleză
Navigare: ← Lecția 04.28 — Modele ale lumii și difuzia video · Faza 5 — NLP: de la fundamente la subiecte avansate · Lecția 05.02 — Sacul de cuvinte, TF-IDF și reprezentarea textului → · Catalog complet