Inteligência artificial, sem ruído.
Tutoriais5 min

Classificação de texto interpretável: como investigar embeddings de LLMs com Scikit-LLM

Aprenda a usar probing classifiers, UMAP e SHAP para interpretar e avaliar a qualidade dos embeddings gerados por LLMs com Scikit-LLM e Ollama.

Classificação de texto interpretável: como investigar embeddings de LLMs com Scikit-LLM

Modelos de linguagem (LLMs) revolucionaram a forma como classificadores de texto são construídos — mais potentes e precisos, mas com um efeito colateral: a falta de interpretabilidade, já que LLMs são caixas-pretas. Quando usamos um LLM para converter texto cru em embeddings (representações vetoriais densas) antes de uma tarefa de classificação, capturamos informação semântica. Mas fica a pergunta desafiadora: o que exatamente o modelo aprendeu sobre o texto, e como esse aprendizado interno guia as previsões?

Este tutorial mostra, na prática, como gerar embeddings com a biblioteca Scikit-LLM e um modelo local do Ollama, treinar um classificador de sondagem (probing classifier) e abrir a caixa-preta usando UMAP e SHAP — duas técnicas populares de IA explicável.

Por que isso importa agora

Há alguns anos, interpretar um modelo de classificação era razoavelmente direto com regressão logística ou árvores de decisão sobre features tabulares. Hoje, quando embeddings de LLMs alimentam esses classificadores, perdemos a noção do que cada dimensão representa. Entender a qualidade dos embeddings — se eles de fato separam classes — virou um passo obrigatório antes de confiar em qualquer pipeline de classificação em produção, especialmente em cenários regulados ou de alto risco, como análise de sentimentos em avaliações, triagem de conteúdo ou detecção de fraude.

Configuração inicial

O código abaixo roda em Google Colab e mantém o custo zero usando modelos locais. Instalamos as bibliotecas, corrigimos dependências e subimos o servidor Ollama com o modelo de embeddings all-minilm:

# 1. Instalando bibliotecas Python
!pip install -q scikit-llm umap-learn shap

# 2. Corrigindo dependências de sistema do Colab
!apt-get update -qq && apt-get install -y -qq zstd

# 3. Instalando o Ollama
!curl -fsSL https://ollama.com/install.sh | sh

# 4. Subindo o servidor local em background
!nohup ollama serve > ollama.log 2>&1 &
!sleep 5

# 5. Baixando o modelo de embeddings gratuito
!ollama pull all-minilm

Em seguida, importamos tudo o que vamos precisar e configuramos o Scikit-LLM para apontar para o servidor local:

from skllm.config import SKLLMConfig
from skllm.models.gpt.vectorization import GPTVectorizer
# ... demais imports (numpy, pandas, umap, shap, sklearn, datasets)

SKLLMConfig.set_gpt_url("http://localhost:11434/v1/")
SKLLMConfig.set_openai_key("dummy_key")  # formato exigido, ignorado localmente

Carregando e balanceando o dataset

Usamos o dataset público IMDB de resenhas de filmes, carregando 1.000 exemplos — 500 positivos e 500 negativos, uma amostra perfeitamente balanceada. Com amostragem estratificada, separamos 80% para treino e 20% para teste. A parte mais pesada é gerar embeddings para os 1.000 textos com o GPTVectorizer, cuja sintaxe imita as transformações do scikit-learn. Em Colab, espere de 5 a 10 minutos, pois são 1.000 chamadas a um LLM local.

vectorizer = GPTVectorizer(model="all-minilm")
X_train_vec = vectorizer.fit_transform(X_train)
X_test_vec = vectorizer.transform(X_test)

Treinando o classificador de sondagem

Um probing classifier é uma ferramenta de diagnóstico: um classificador simples (regressão logística) treinado sobre os embeddings. Se ele alcança boas métricas, os embeddings são ricos o bastante para a tarefa. Usar um modelo raso também isola a contribuição dos próprios embeddings.

clf = LogisticRegression(random_state=42, max_iter=1000)
clf.fit(X_train_vec, y_train)
print(classification_report(y_test, clf.predict(X_test_vec)))

O resultado — acurácia de 0,77 com precisão e recall equilibrados por classe — é respeitável para um classificador linear simples, considerando o tamanho modesto do dataset frente à dimensionalidade dos embeddings.

Visualizando a estrutura semântica com UMAP

O UMAP projeta os embeddings para 2 dimensões usando similaridade de cosseno (a métrica padrão para embeddings de texto). O gráfico de dispersão revela se há agrupamento natural entre resenhas positivas e negativas:

reducer = umap.UMAP(n_components=2, metric='cosine',
                    n_neighbors=30, min_dist=0.1, random_state=42)
X_umap = reducer.fit_transform(X_train_vec)

O resultado não é extraordinário à primeira vista — não há separação perfeita entre classes — mas, considerando embeddings gerados por LLM fortemente comprimidos em duas dimensões, um agrupamento sutil é visível: a metade sul do gráfico concentra resenhas negativas, enquanto a metade superior tem maioria positiva.

Identificando dimensões influentes com SHAP

O SHAP (SHapley Additive exPlanations) mostra quais dimensões latentes dos embeddings mais influenciaram as previsões do classificador de sondagem. O summary plot exibe as 20 features de maior impacto, com cor indicando se cada feature contribui para a classe positiva ou negativa:

explainer = shap.LinearExplainer(clf, X_train_vec)
shap_values = explainer.shap_values(X_test_vec)
if isinstance(shap_values, list):
    shap_values = shap_values[1]
shap.summary_plot(shap_values, X_test_vec, show=False)

A análise conclui que a dimensão 208 é o sinal primário de resenhas negativas, seguida pela dimensão 317; já a dimensão 139 é o principal motor das positivas, empurrando a previsão bruta do modelo para valores mais altos.

Conclusão

Este fluxo — gerar embeddings com Scikit-LLM, sondar com regressão logística, visualizar com UMAP e explicar com SHAP — transforma a caixa-preta dos LLMs em algo inspecionável. Antes de colocar qualquer classificador baseado em embeddings em produção, vale rodar esse pipeline para confirmar que os embeddings realmente codificam a estrutura semântica que a tarefa exige.


Descubra mais sobre noticiAI

Assine para receber nossas notícias mais recentes por e-mail.

R
Sobre o autorRedação Noticiai

Equipe editorial dedicada a explicar inteligência artificial com clareza, independência e contexto.