Machine Learning: Fundamentos de NLP
Última atualização: 2026-08-26
Por trás de cada avaliação há uma emoção — NLP permite que as máquinas leiam a raiva, decepção e alegria ocultas no feedback dos clientes.
1. O que você vai aprender
- Pré-processamento de texto: tokenização (NLTK), remoção de stopwords, stemming/lemmatização
- Representação de texto: Bag-of-Words, TF-IDF, N-grams (capturando frases)
- Embeddings de palavras: Word2Vec (Gensim) princípios e treinamento, cálculo de similaridade de palavras
- Classificação de texto na prática: análise de sentimento em avaliações de produtos com TF-IDF + LogisticRegression
- Insights de avaliações do Bob: identificar automaticamente palavras-chave de problemas em avaliações negativas para guiar melhorias do produto
2. A história real de um gerente de atendimento ao cliente de e-commerce
(1) O problema: 5 mil avaliações por dia, impossíveis de analisar à mão
A plataforma do Bob recebe 5 mil avaliações de produtos todos os dias — positivas, negativas e sugestões, todas misturadas. Sua equipe de atendimento consegue analisar apenas 1% por amostragem, perdendo um grande número de problemas de qualidade. No mês passado, um lote defeituoso gerou 2 mil avaliações negativas, mas só foi descoberto 3 semanas depois, quando já havia causado 500 mil USD em perdas com devoluções. Dados massivos de texto são tanto uma mina de ouro quanto um campo minado.
(2) A solução com NLP
NLP pode analisar automaticamente o sentimento das avaliações, extrair palavras-chave de problemas e monitorar tendências de avaliações negativas em tempo real.
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
pipe = Pipeline([
("tfidf", TfidfVectorizer(max_features=5000, ngram_range=(1, 2))),
("clf", LogisticRegression(max_iter=500)),
])
pipe.fit(X_train_reviews, y_train_sentiment)
print(f"Acurácia do sentimento: {pipe.score(X_test, y_test):.3f}")
(3) O resultado: problemas encontrados em 3 dias, taxa de devolução caiu 40%
Depois que o Bob começou a usar NLP para analisar avaliações automaticamente, o tempo entre descobrir um problema de qualidade e responder caiu de 3 semanas para 3 dias, a taxa de devolução caiu 40%, e ele economiza cerca de 1 milhão de USD por ano.
3. Pré-processamento de texto
(1) Tokenização e limpeza
▶ Exemplo: Pipeline de pré-processamento de texto
import re
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
from nltk.stem import PorterStemmer, WordNetLemmatizer
import nltk
nltk.download("punkt_tab", quiet=True)
nltk.download("stopwords", quiet=True)
nltk.download("wordnet", quiet=True)
def preprocess_text(text):
# Minúsculas
text = text.lower()
# Remover caracteres especiais e números
text = re.sub(r"[^a-zA-Z\s]", "", text)
# Tokenizar
tokens = word_tokenize(text)
# Remover stopwords
stop_words = set(stopwords.words("english"))
tokens = [t for t in tokens if t not in stop_words and len(t) > 2]
# Stemming
stemmer = PorterStemmer()
tokens_stemmed = [stemmer.stem(t) for t in tokens]
return " ".join(tokens_stemmed)
# Teste
reviews = [
"This product is absolutely amazing! Best purchase ever.",
"Terrible quality, broke after 2 days. Very disappointed.",
"Decent product for the price, but shipping was slow.",
]
for review in reviews:
cleaned = preprocess_text(review)
print(f"Original: {review}")
print(f"Limpo: {cleaned}\n")
Saída:
# Função definida com sucesso
▶ Exemplo: Tokenização de palavras (NLTK)
# Tokenização de palavras com NLTK
# import nltk
# nltk.download('punkt')
# from nltk.tokenize import word_tokenize
# text = "This product has great quality and fast shipping"
# tokens = word_tokenize(text)
# print(tokens) # ['This', 'product', 'has', 'great', 'quality', 'and', 'fast', 'shipping']
# Para avaliações em português (cenário internacional)
from sklearn.feature_extraction.text import CountVectorizer
reviews = [
"Ótimo notebook desempenho rápido",
"Qualidade de tela terrível",
"Bom preço entrega rápida",
]
vectorizer = CountVectorizer()
X_bow = vectorizer.fit_transform(reviews)
print(f"Vocabulário: {vectorizer.vocabulary_}")
print(f"Shape da matriz BoW: {X_bow.shape}")
Saída:
# Executado com sucesso
| Passo de pré-processamento | Inglês | Português |
|---|---|---|
| Tokenização | NLTK/spaCy (divisão por espaços) | NLTK/spaCy (com regras para acentos) |
| Stopwords | NLTK stopwords | NLTK stopwords (português) |
| Stemming | PorterStemmer | RSLPStemmer (removedor de sufixos) |
| Lemmatização | WordNetLemmatizer | spaCy lemmatizer (português) |
4. Métodos de Representação de Texto
Transformar texto de uma "string" em um "vetor numérico" requer um pipeline de transformação de representação:
graph LR
TEXT[Texto Bruto] --> TOKEN[Tokenizar<br/>split / jieba] --> REMOVE[Remover Stopwords] --> STEM[Stem/Lematizar] --> BOW[Bag-of-Words<br/>Vetor de Contagem] --> TFIDF[TF-IDF<br/>Vetor Ponderado] --> EMBED[Word2Vec<br/>Embedding Denso]
style BOW fill:#fff3cd
style TFIDF fill:#d1ecf1
style EMBED fill:#d4edda
(1) Bag-of-Words e TF-IDF
▶ Exemplo: Comparando três representações de texto
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
import numpy as np
reviews = [
"great product fast delivery happy customer",
"terrible quality broke disappointed waste money",
"great quality decent price fast shipping",
"awful product terrible experience never buy again",
"excellent quality great value fast delivery",
]
labels = [1, 0, 1, 0, 1] # 1=positivo, 0=negativo
# Método 1: Bag-of-Words (contagem)
bow = CountVectorizer()
X_bow = bow.fit_transform(reviews)
print(f"Tamanho do vocabulário BoW: {len(bow.vocabulary_)}")
# Método 2: TF-IDF
tfidf = TfidfVectorizer(max_features=1000)
X_tfidf = tfidf.fit_transform(reviews)
print(f"Shape TF-IDF: {X_tfidf.shape}")
# Método 3: N-gram (capturando frases)
tfidf_ngram = TfidfVectorizer(ngram_range=(1, 2), max_features=1000)
X_ngram = tfidf_ngram.fit_transform(reviews)
print(f"Shape TF-IDF com bigramas: {X_ngram.shape}")
print(f"Top bigramas: {[w for w in tfidf_ngram.get_feature_names_out() if ' ' in w][:10]}")
Saída:
# Executado com sucesso
| Representação | Informação | Dimensionalidade | Caso de uso |
|---|---|---|---|
| BoW (frequência) | Baixa | Alta | Classificação simples |
| TF-IDF | Média | Alta | Padrão para classificação de texto |
| N-gram | Média | Muito alta | Captura frases |
| Word2Vec | Alta | Baixa (100-300) | Tarefas semânticas |
(2) Intuição do TF-IDF
- TF (Frequência do Termo): com que frequência uma palavra aparece em um documento
- IDF (Frequência Inversa do Documento): o quão rara é uma palavra (palavras mais raras recebem peso maior)
- TF-IDF = TF × IDF: palavras que são comuns neste documento, mas raras em outros, recebem o maior peso
5. Embeddings de Palavras com Word2Vec
(1) Como o Word2Vec funciona
Word2Vec aprende vetores de palavras prevendo palavras de contexto (CBOW) ou prevendo contexto a partir de uma palavra (Skip-gram), de modo que palavras semanticamente similares terminem com vetores próximos.
▶ Exemplo: Treinando Word2Vec e similaridade de palavras
from gensim.models import Word2Vec
import numpy as np
# Treinar Word2Vec em corpus de exemplo
# Na prática, use corpus real de avaliações
sentences = [
["great", "product", "fast", "delivery", "happy"],
["terrible", "quality", "broke", "disappointed"],
["great", "quality", "decent", "price"],
["awful", "product", "terrible", "experience"],
["excellent", "quality", "great", "value"],
["fast", "shipping", "good", "price", "recommend"],
["poor", "quality", "slow", "delivery", "angry"],
["amazing", "product", "best", "purchase", "happy"],
]
model = Word2Vec(sentences, vector_size=50, window=3, min_count=1, workers=4, epochs=50)
# Similaridade de palavras
print("Similar a 'great':")
for word, sim in model.wv.most_similar("great", topn=5):
print(f" {word}: {sim:.3f}")
# Vetor de palavra
print(f"\nVetor para 'quality': {model.wv['quality'][:5]}... (50-dim)")
# Analogia: great - good + terrible = ?
# result = model.wv.most_similar(positive=["terrible"], negative=["good"], topn=3)
Saída:
Similar a
| Parâmetro | Significado | Valor recomendado |
|---|---|---|
| vector_size | Dimensionalidade do vetor | 100-300 |
| window | Tamanho da janela de contexto | 5 |
| min_count | Frequência mínima da palavra | 5 |
| sg | 0=CBOW, 1=Skip-gram | 1 (poucos dados) |
▶ Exemplo: Visualizando embeddings de palavras
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
words = ["great", "terrible", "happy", "angry", "fast", "slow",
"quality", "price", "product", "delivery"]
vectors = [model.wv[w] for w in words]
vectors_2d = PCA(n_components=2).fit_transform(vectors)
fig, ax = plt.subplots(figsize=(10, 8))
ax.scatter(vectors_2d[:, 0], vectors_2d[:, 1], s=100)
for i, word in enumerate(words):
ax.annotate(word, (vectors_2d[i, 0], vectors_2d[i, 1]), fontsize=12)
ax.set_title("Visualização de Embeddings Word2Vec")
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("word2vec_vis.png", dpi=150)
Saída:
# Executado com sucesso
6. Análise de Sentimento em Avaliações de Produtos na Prática
▶ Exemplo: Fluxo completo de classificação de sentimento
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.metrics import classification_report, confusion_matrix
import numpy as np
# Simular avaliações de produtos
rng = np.random.default_rng(42)
positive_templates = [
"great product love it highly recommend",
"excellent quality fast delivery satisfied",
"amazing value best purchase ever happy",
"good product decent price works well",
"love this item perfect condition great",
]
negative_templates = [
"terrible quality broke after one day",
"awful product waste money very disappointed",
"poor quality slow delivery never again",
"worst purchase defective product angry",
"bad product cheap material not recommend",
]
# Gerar 2000 avaliações
reviews = []
labels = []
for _ in range(1000):
reviews.append(rng.choice(positive_templates))
labels.append(1)
reviews.append(rng.choice(negative_templates))
labels.append(0)
X_train, X_test, y_train, y_test = train_test_split(reviews, labels, test_size=0.2, random_state=42)
# Pipeline TF-IDF + Regressão Logística
pipe = Pipeline([
("tfidf", TfidfVectorizer(max_features=5000, ngram_range=(1, 2), min_df=2)),
("clf", LogisticRegression(max_iter=500, C=1.0)),
])
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)
print(f"Acurácia: {pipe.score(X_test, y_test):.4f}")
print(f"\nRelatório de Classificação:\n{classification_report(y_test, y_pred, target_names=['Negativo', 'Positivo'])}")
# Extrair palavras mais importantes para cada sentimento
feature_names = pipe.named_steps["tfidf"].get_feature_names_out()
coefs = pipe.named_steps["clf"].coef_[0]
top_positive = np.argsort(coefs)[-10:][::-1]
top_negative = np.argsort(coefs)[:10]
print("\nTop palavras positivas:")
for idx in top_positive:
print(f" {feature_names[idx]:20s}: {coefs[idx]:.3f}")
print("\nTop palavras negativas:")
for idx in top_negative:
print(f" {feature_names[idx]:20s}: {coefs[idx]:.3f}")
Saída:
Top palavras positivas:
Top palavras negativas:
❓ Perguntas Frequentes
P: Devo usar TF-IDF ou Word2Vec? R: Use TF-IDF para classificação de texto (simples, eficaz e interpretável); use Word2Vec para tarefas semânticas (similaridade/analogia); use embeddings pré-treinados (BERT, etc.) para aprendizado profundo. Comece com TF-IDF como iniciante.
P: Como escolher n para N-grams? R: Para classificação de texto, use (1,2), ou seja, unigramas + bigramas; bigramas importam muito para análise de sentimento (por exemplo, "não bom"); n maior (3+) geralmente não vale a pena (a dimensionalidade explode e os dados ficam esparsos).
P: Que ferramentas devo usar para tokenização em português? R: NLTK (com suporte para português), spaCy (com modelo
pt_core_news_sm), ou Stanza. Para avaliações de e-commerce, adicione nomes de marcas e termos de domínio com um dicionário personalizado.
P: Quanta dados o Word2Vec precisa para treinar? R: Pelo menos 10 milhões de palavras para aprender vetores significativos. Com poucos dados, use vetores pré-treinados (por exemplo, Word2Vec do NILC) ou fique com TF-IDF.
P: Que acurácia conta como boa para análise de sentimento? R: Para classificação binária simples (positivo/negativo), TF-IDF + regressão logística normalmente alcança 80-85%; modelos profundos como BERT podem chegar a 90-95%. Mas em configurações de negócio reais com muitos dados ruidosos, 70-80% já é utilizável.
P: Como lidar com sarcasmo e negação implícita? R: Modelos simples (TF-IDF) têm dificuldade com isso. Você precisa — 1) adicionar mais features de N-gram; 2) usar modelos de linguagem pré-treinados (BERT); 3) anotar manualmente amostras sarcásticas para treinar um modelo dedicado.
📖 Resumo
- Pipeline de pré-processamento de texto: minúsculas → remover caracteres especiais → tokenizar → remover stopwords → stem/lemmatizar
- TF-IDF é a representação padrão para classificação de texto: frequência do termo × frequência inversa do documento, dando o maior peso a palavras raras, mas importantes
- N-grams capturam informações de frases (por exemplo, "não bom"), mas a dimensionalidade explode, então controle max_features
- Word2Vec aprende vetores de palavras através da previsão de contexto; palavras semanticamente similares têm vetores próximos
- Pipeline de classificação de texto: TfidfVectorizer + LogisticRegression — simples e eficiente
- Os coeficientes do modelo permitem extrair palavras-chave — palavras indicadoras positivas/negativas orientam diretamente melhorias de negócio
📝 Exercícios
- Básico (Dificuldade ⭐): Use TfidfVectorizer para vetorizar 5 avaliações de produtos, depois imprima o vocabulário e a matriz TF-IDF. Dica: consulte o exemplo na Seção 4.
- Intermediário (Dificuldade ⭐⭐): Construa um pipeline TF-IDF + LogisticRegression de classificação de sentimento e compare a diferença de F1 score entre apenas unigrama e (1,2)-grama. Dica: ngram_range=(1,1) vs (1,2).
- Desafio (Dificuldade ⭐⭐⭐): Implemente o sistema de insights de avaliações do Bob — depois de treinar o classificador de sentimento, extraia as top 20 palavras-chave negativas, ordene por frequência e produza um ranking de "problemas mais urgentes a corrigir". Dica: ordene coef_ + mapeie para nomes de features + conte frequências.
← Lição Anterior: Redução de Dimensionalidade | Próxima Lição: Introdução ao Aprendizado Profundo →