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.

Pipeline de pre-processamento: texto bruto → tokens → radicais ou lemas → modelo

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

  1. Facil. Estenda tokenize para manter URLs como tokens unicos. Teste: tokenize("Visit https://example.com today.") deve produzir um unico token de URL.
  2. Medio. Implemente o passo 1b de Porter. Se uma palavra contem uma vogal e termina em ed ou ing, remova o sufixo. Trate a regra de consoante dupla (hopping -> hop, e nao hopp).
  3. 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

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).