Phase 05 - Lesson 01
Processamento de Texto — Tokenizacao, Stemming, Lematizacao
This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.
A linguagem e continua. Os modelos sao discretos. O pre-processamento e a ponte.
Tipo: Build Linguagens: Python Pre-requisitos: Fase 2 · 14 (Naive Bayes) Tempo: ~45 minutos
O Problema
Um modelo nao consegue ler "The cats were running." Ele le inteiros.
Todo sistema de NLP comeca com as mesmas tres perguntas. Onde uma palavra comeca. Qual e a raiz da palavra. Como tratar "run", "running", "ran" como a mesma coisa quando isso ajuda, e como coisas diferentes quando nao ajuda.
Erre a tokenizacao e o modelo aprende a partir de lixo. Se o seu tokenizador trata don't como um unico token mas do n't como dois, a distribuicao de treino se divide. Se o seu stemmer reduz organization e organ ao mesmo radical, a modelagem de topicos morre. Se o seu lematizador precisa de contexto de classe gramatical mas voce nao o passa, verbos acabam tratados como substantivos.
Esta licao constroi as tres primitivas de pre-processamento do zero e, em seguida, mostra como NLTK e spaCy fazem o mesmo trabalho, para que voce veja os tradeoffs.
O Conceito
Tres operacoes. Cada uma tem uma funcao e um modo de falha.
Tokenizacao divide uma string em tokens. "Token" e deliberadamente vago, porque a granularidade certa depende da tarefa. Nivel de palavra para NLP classico. Subpalavra para transformers. Caractere para idiomas sem espacos em branco.
Stemming corta sufixos com regras. Rapido, agressivo, burro. running -> run. organization -> organ. Esse segundo caso e o modo de falha.
Lematizacao reduz uma palavra a sua forma de dicionario usando conhecimento gramatical. Mais lento, preciso, precisa de uma tabela de consulta ou de um analisador morfologico. ran -> run (precisa saber que "ran" e o passado de "run"). better -> good (precisa conhecer formas comparativas).
Regra de ouro. Faca stemming quando a velocidade importa e voce pode tolerar ruido (indexacao de busca, classificacao grosseira). Faca lematizacao quando o significado importa (perguntas e respostas, busca semantica, qualquer coisa que o usuario vai ler).
Construa
Passo 1: um tokenizador de palavras com regex
O tokenizador util mais simples divide em caracteres nao alfanumericos enquanto mantem a pontuacao como tokens proprios. Nao e perfeito, nao e definitivo, mas roda em uma linha.
import re
def tokenize(text):
return re.findall(r"[A-Za-z]+(?:'[A-Za-z]+)?|[0-9]+|[^\sA-Za-z0-9]", text)
Tres padroes em ordem de precedencia. Palavras com apostrofo interno opcional (don't, it's). Numeros puros. Qualquer caractere unico nao branco e nao alfanumerico como token isolado (pontuacao).
>>> tokenize("The cats weren't running at 3pm.")
['The', 'cats', "weren't", 'running', 'at', '3', 'pm', '.']
Modos de falha a notar. 3pm se divide em ['3', 'pm'] porque alternamos entre sequencias de letras e sequencias de digitos. Bom o suficiente para a maioria das tarefas. URLs, e-mails e hashtags quebram. Para producao, adicione padroes antes dos gerais.
Passo 2: um stemmer de Porter (apenas o passo 1a)
O algoritmo completo de Porter tem cinco fases de regras. O passo 1a sozinho cobre os sufixos mais frequentes do ingles e ensina o padrao.
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']
Leia as regras de cima para baixo. A regra ies -> i e o motivo de ponies -> poni, e nao pony. O Porter real tem o passo 1b que corrigiria isso. As regras competem. As regras anteriores vencem. A ordem importa mais do que qualquer regra isolada.
Passo 3: um lematizador baseado em consulta
A lematizacao propriamente dita precisa de morfologia. Uma versao didatica tratavel usa uma pequena tabela de lemas e um fallback.
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'
O ultimo caso e o momento didatico chave. watched nao esta na nossa tabela e o nosso fallback so trata ing. A lematizacao real cobre ed, verbos irregulares, adjetivos comparativos, plurais com mudancas sonoras (children -> child). E por isso que sistemas de producao usam WordNet, o morfologizador do spaCy ou um analisador morfologico completo.
Passo 4: ligue tudo num pipeline
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}
A peca que falta e um POS tagger. A Fase 5 · 07 (POS Tagging) constroi um. Por enquanto, deixe tudo como NOUN por padrao e reconheca a limitacao.
Use
NLTK e spaCy trazem as versoes de producao. Poucas linhas cada.
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 lida com contracoes, Unicode e casos extremos que a sua regex perde. PorterStemmer roda todas as cinco fases. WordNetLemmatizer precisa que a tag de POS seja traduzida do esquema Penn Treebank do NLTK para o conjunto de abreviacoes do WordNet. O encanamento de traducao acima e a parte que a maioria dos tutoriais pula.
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
O spaCy esconde todo o pipeline atras de nlp(text). Tokenizacao, POS tagging e lematizacao rodam todos. Mais rapido que o NLTK em escala. Mais preciso de fabrica. O tradeoff e que voce nao consegue trocar componentes individuais com facilidade.
Quando escolher qual
| Situacao | Escolha |
|---|---|
| Ensino, pesquisa, troca de componentes | NLTK |
| Producao, multilingue, velocidade importa | spaCy |
| Pipeline de transformer (voce vai tokenizar com o tokenizador do modelo de qualquer forma) | Use tokenizers / transformers e pule o pre-processamento classico |
Os dois modos de falha que ninguem te avisa
A maioria dos tutoriais ensina os algoritmos e para por ai. Duas coisas vao morder um pipeline de pre-processamento real, e quase nunca sao abordadas.
Deriva de reprodutibilidade. NLTK e spaCy mudam o comportamento da tokenizacao e do lematizador entre versoes. O que produzia ['do', "n't"] no spaCy 2.x pode produzir ["don't"] no 3.x. Seu modelo foi treinado em uma distribuicao. A inferencia agora roda em outra. A acuracia degrada silenciosamente e ninguem sabe por que. Fixe as versoes das bibliotecas no requirements.txt. Escreva um teste de regressao de pre-processamento que congele a tokenizacao esperada de 20 frases de exemplo. Rode-o a cada atualizacao.
Incompatibilidade entre treino e inferencia. Treine com pre-processamento agressivo (minusculas, remocao de stopwords, stemming), implante sobre entrada bruta do usuario e veja o desempenho despencar. Essa e a falha de NLP em producao mais comum de todas. Se voce pre-processa durante o treino, voce precisa rodar a funcao identica durante a inferencia. Entregue o pre-processamento como uma funcao dentro do pacote do modelo, nao como uma celula de notebook que o time de serving reescreve.
Entregue
Um prompt reutilizavel que ajuda engenheiros a escolher uma estrategia de pre-processamento sem ler tres livros didaticos.
Salve como outputs/prompt-preprocessing-advisor.md:
---
name: preprocessing-advisor
description: Recommends a tokenization, stemming, and lemmatization setup for an NLP task.
phase: 5
lesson: 01
---
You advise on classical NLP preprocessing. Given a task description, you output:
1. Tokenization choice (regex, NLTK word_tokenize, spaCy, or transformer tokenizer). Explain why.
2. Whether to stem, lemmatize, both, or neither. Explain why.
3. Specific library calls. Name the functions. Quote the POS-tag translation if NLTK is involved.
4. One failure mode the user should test for.
Refuse to recommend stemming for user-visible text. Refuse to recommend lemmatization without POS tags. Flag non-English input as needing a different pipeline.
Exercicios
- Facil. Estenda
tokenizepara manter URLs como tokens unicos. Teste:tokenize("Visit https://example.com today.")deve produzir um unico token de URL. - Medio. Implemente o passo 1b de Porter. Se uma palavra contem uma vogal e termina em
edouing, remova o sufixo. Trate a regra de consoante dupla (hopping -> hop, e naohopp). - Dificil. Construa um lematizador que use o WordNet como tabela de consulta mas recorra ao seu stemmer de Porter quando o WordNet nao tiver entrada. Meca a acuracia em um corpus etiquetado contra o WordNet puro e o Porter puro.
Termos-Chave
| Termo | O que as pessoas dizem | O que realmente significa |
|---|---|---|
| Token | Uma palavra | Qualquer unidade que o modelo consome. Pode ser palavra, subpalavra, caractere ou byte. |
| Radical (stem) | Raiz de uma palavra | Resultado da remocao de sufixos baseada em regras. Nem sempre uma palavra real. |
| Lema | Forma de dicionario | A forma que voce procuraria no dicionario. Requer contexto gramatical para ser computada corretamente. |
| Tag de POS | Classe gramatical | Categoria como NOUN, VERB, ADJ. Necessaria para lematizar com precisao. |
| Morfologia | Regras de forma das palavras | Como uma palavra muda de forma com base em tempo, numero, caso. A lematizacao depende disso. |
Leitura Adicional
- Porter, M. F. (1980). An algorithm for suffix stripping — o artigo original, cinco paginas, ainda a explicacao mais clara.
- spaCy 101 — linguistic features — como um pipeline real e montado.
- NLTK book, chapter 3 — casos extremos de tokenizacao em que voce ainda nao tinha pensado.