Há um cenário em que todas as técnicas vistas até aqui falham por um motivo trivial: não existe classe negativa para treinar. Você quer detectar falhas num equipamento, mas ele nunca falhou. Quer detectar intrusão numa rede, mas o ataque que importa é o que ninguém viu ainda. Quer detectar fraude num produto novo, e o histórico tem zero casos confirmados.

Reamostragem não ajuda — não há o que reamostrar. Pesos não ajudam — não há classe para pesar. O problema mudou de natureza: em vez de aprender a separar duas classes, é preciso aprender a descrever uma e sinalizar tudo que não se parece com ela.

Reconhecer quando o problema é de uma classe e não de classificação binária, aplicar One-Class SVM, Isolation Forest e Local Outlier Factor, ajustar o nível de contaminação com o pouco rótulo disponível, e avaliar esses modelos sem cair nas armadilhas específicas do cenário não-supervisionado.

Três problemas que costumam ser confundidos

O que você tem O que busca
Classificação desbalanceadarótulos das duas classes, uma raraseparar as duas
Classificação de uma classe (OCC)rótulos só da classe normaldescrever o normal, sinalizar o resto
Detecção de outliersdados sem rótulo, possivelmente contaminadosencontrar o que destoa

A diferença entre a segunda e a terceira é sutil e importante. Na OCC, o treino é limpo por construção — você separou exemplos que sabe serem normais. Na detecção de outliers, o conjunto já contém as anomalias, e o algoritmo precisa encontrá-las ali dentro.

Isso muda o que se pode assumir. Com treino limpo, a fronteira aprendida descreve o normal com fidelidade. Com treino contaminado, o modelo aprende parte da anomalia como se fosse normal — e é por isso que quase todos esses algoritmos pedem um parâmetro de contaminação.

Isolation Forest: começar por aqui

A intuição é elegante e vale entender antes do código. Escolha um atributo ao acaso e um ponto de corte ao acaso; repita. Um ponto anômalo — afastado dos demais — tende a ficar isolado com poucos cortes, porque há muito espaço vazio ao redor dele. Um ponto normal, no meio da massa, exige muitos cortes.

A pontuação de anomalia é, portanto, a profundidade média em que o ponto é isolado numa floresta de árvores aleatórias. Nenhuma distância é calculada, o que explica a principal vantagem: escala bem, inclusive em dimensão alta.

from sklearn.ensemble import IsolationForest

detector = IsolationForest(
    n_estimators=300,
    contamination=0.01,     # proporção esperada de anomalias
    max_samples=256,        # o padrão do método; amostras pequenas bastam
    random_state=42,
)
detector.fit(X_treino)

rotulo = detector.predict(X_novo)              # 1 = normal, -1 = anomalia
score  = -detector.score_samples(X_novo)       # maior = mais anômalo

Duas observações práticas:

  • score_samples vale mais que predict. A pontuação contínua permite ordenar os casos por suspeita e revisar os mais graves primeiro — que é como uma equipe de investigação realmente trabalha. O rótulo binário joga essa ordenação fora.
  • O sinal é invertido na convenção do scikit-learn: score_samples devolve valores mais baixos para os mais anômalos. Negar deixa a leitura intuitiva.

One-Class SVM: a fronteira apertada

O OC-SVM procura a fronteira que envolve os dados normais, deixando uma fração nu deles do lado de fora. Com kernel RBF, essa fronteira pode ser bem irregular, o que permite descrever regiões de formato complexo.

from sklearn.svm import OneClassSVM
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline

oc = make_pipeline(
    StandardScaler(),                    # obrigatório: o kernel é sensível à escala
    OneClassSVM(kernel="rbf", nu=0.02, gamma="scale"),
)
oc.fit(X_treino_limpo)

O StandardScaler não é opcional. O kernel RBF mede distância euclidiana, e um atributo em reais (milhares) domina completamente um em proporção (0 a 1). Sem padronizar, o OC-SVM olha essencialmente para uma variável só.

Os dois parâmetros importam:

  • nu é simultaneamente um limite superior para a fração de treino tratada como fora da fronteira e um limite inferior para a fração de vetores de suporte. Na prática, é a sua estimativa de contaminação.
  • gamma controla a flexibilidade. Alto demais, a fronteira envolve cada ponto individualmente (decora o treino); baixo demais, vira quase uma esfera e perde a forma dos dados.

A limitação séria: o custo de treino cresce de forma quadrática a cúbica com o número de amostras. Acima de algumas dezenas de milhares de pontos, fica impraticável — e é aí que o Isolation Forest ganha.

Local Outlier Factor: anomalia é relativa à vizinhança

Os dois anteriores usam uma noção global de "normal". O LOF usa uma local: um ponto é anômalo se a densidade ao redor dele é bem menor que a densidade ao redor dos seus vizinhos.

Isso resolve um caso que os outros erram. Imagine dados com dois agrupamentos, um denso e um esparso. Um ponto na borda do agrupamento esparso pode estar longe de tudo em termos absolutos e ainda assim ser perfeitamente normal para aquela região. O LOF entende isso; uma fronteira global, não.

from sklearn.neighbors import LocalOutlierFactor

# modo detecção de outliers: encontra anomalias no próprio conjunto
lof = LocalOutlierFactor(n_neighbors=20, contamination=0.02)
rotulos = lof.fit_predict(X)

# modo novidade: precisa de novelty=True e treino limpo
lof_novidade = LocalOutlierFactor(n_neighbors=20, novelty=True)
lof_novidade.fit(X_treino_limpo)
lof_novidade.predict(X_novo)

A distinção entre os dois modos é a pegadinha do LOF: sem novelty=True ele não tem predict, só fit_predict, porque foi desenhado para analisar um conjunto fechado, não para pontuar dados novos.

Escolher entre eles

Isolation Forest One-Class SVM LOF
Volumecentenas de milharesaté ~10 milmédio (custo do kNN)
Dimensão altabomrazoáveldegrada
Densidade variávelnão distinguenão distinguesim
Precisa padronizarnãosimsim
Custo de treinobaixoaltomédio
Pontua dado novosimsimsó com novelty=True

O ponto de partida sensato é Isolation Forest: barato, poucos parâmetros, tolera dimensão alta. Passe ao LOF se houver razão para crer que a densidade varia entre regiões; ao OC-SVM quando o volume é pequeno e a fronteira, complicada.

E vale registrar a alternativa que não está na tabela: quando existem alguns exemplos rotulados da anomalia, mesmo que poucos, um classificador supervisionado com peso costuma superar qualquer método de uma classe. Dez fraudes confirmadas valem mais que zero. Use OCC quando realmente não há rótulo positivo — não por comodidade.

Avaliar sem rótulo (e com o pouco que houver)

Este é o ponto mais delicado: sem rótulo, não há acurácia, precisão nem revocação. Três caminhos, em ordem de preferência:

1. Conjunto de validação rotulado, ainda que pequeno. Mesmo 50 anomalias confirmadas permitem calcular precisão nos k primeiros e comparar configurações. É o caminho mais valioso, e frequentemente basta uma revisão manual para construí-lo.

2. Precisão no topo (precision@k). A métrica que reflete a operação real: a equipe revisa as k ocorrências mais suspeitas por dia. O que importa é quantas dessas k eram de fato anômalas.

import numpy as np

def precisao_no_topo(y_real, score, k=100):
    ordem = np.argsort(score)[::-1][:k]
    return y_real[ordem].mean()

3. Anomalias sintéticas. Na ausência total de rótulo, injetar anomalias plausíveis — ruído extremo, combinações impossíveis de atributos, valores fora de faixa — dá um piso de comparação. É frágil, porque o método pode ficar bom em detectar o tipo de anomalia que você inventou e continuar cego para o real. Use como diagnóstico, não como evidência.

O que não fazer: reportar a fração de pontos sinalizados como se fosse desempenho. Se você definiu contamination=0.01, o modelo vai sinalizar 1% — isso é aritmética do parâmetro, não medida de qualidade.

Deriva: o normal de hoje não é o de ontem

Um detector de anomalias aprende uma fotografia do normal, e o normal muda — sazonalidade, novos produtos, mudança de comportamento. Um modelo treinado em janeiro começa a sinalizar dezembro inteiro como anômalo, e a equipe aprende a ignorar os alertas.

Três medidas práticas: retreinar em janela deslizante, com dados recentes; monitorar a taxa de alertas, porque um salto súbito costuma indicar deriva antes de indicar ataque; e registrar o retorno da revisão manual — cada alerta confirmado ou descartado é um rótulo que, acumulado, permite migrar para o supervisionado, que é sempre melhor quando viável.

Quando não há exemplos da classe rara, o problema deixa de ser separar classes e passa a ser descrever o normal. A classificação de uma classe pressupõe treino limpo, enquanto a detecção de outliers trabalha com um conjunto já contaminado — daí o parâmetro contamination. O Isolation Forest isola pontos com cortes aleatórios, e o que se isola raso é anomalia: escala bem, dispensa padronização, tem poucos parâmetros, e é o ponto de partida natural. O One-Class SVM traça a fronteira por kernel com nu no papel de contaminação, mas exige padronização e fica caro acima de umas dez mil amostras. O LOF trabalha com densidade local e é o único dos três que lida bem com regiões de densidades diferentes — sem novelty=True, porém, não pontua dados novos. Use score_samples em vez de predict: quem revisa os alertas trabalha pela ordenação de suspeita, não pelo corte binário. E se houver alguns rótulos positivos, o supervisionado com peso costuma vencer qualquer método de uma classe. Para avaliar, a ordem de preferência é validação rotulada pequena, depois precision@k, depois anomalias sintéticas; a fração sinalizada não mede qualidade nenhuma — ela é o parâmetro que você escolheu. E como o normal muda, retreine em janela, monitore a taxa de alertas e acumule o retorno da revisão manual.

Exercícios

  1. Por que reamostragem e class_weight não resolvem o caso sem exemplos da classe rara?

    ✓ Resposta:

    Porque as duas técnicas operam sobre exemplos existentes da classe minoritária. Reamostragem replica ou sintetiza a partir do que há — com zero exemplos, não há o que replicar nem interpolar (o SMOTE, por exemplo, interpola entre vizinhos da classe rara, o que exige pelo menos alguns). class_weight multiplica a contribuição desses exemplos na função de perda — multiplicar zero exemplos por qualquer peso continua dando zero. O problema é de natureza diferente: não há fronteira entre duas classes a aprender, e a formulação correta passa a ser descrever a região do normal e medir o afastamento em relação a ela.

  2. Explique a intuição do Isolation Forest e por que ela escala melhor que métodos baseados em distância.

    ✓ Resposta:

    A ideia é que anomalias são fáceis de isolar. Cortando o espaço repetidamente por atributos e limiares sorteados ao acaso, um ponto afastado da massa fica sozinho em sua partição após poucos cortes, porque há muito espaço vazio ao redor dele; um ponto no centro da distribuição exige muitos cortes. A pontuação é a profundidade média de isolamento numa floresta dessas árvores. Escala melhor porque nunca calcula distância entre pares — não há matriz de distâncias, nem busca de vizinhos, cujo custo cresce quadraticamente com o número de pontos e degrada com a dimensão. Cada árvore usa ainda uma subamostra pequena (256 pontos costuma bastar), o que torna o treino praticamente linear no tamanho da base.

  3. Um colega aplicou OC-SVM direto sobre colunas de "valor em reais" e "taxa de conversão". O resultado ficou sem sentido. Por quê?

    ✓ Resposta:

    Falta de padronização. O kernel RBF é função da distância euclidiana entre pontos, e "valor em reais" vive na casa dos milhares enquanto "taxa de conversão" vive entre 0 e 1. Na soma dos quadrados das diferenças, a contribuição da taxa é desprezível — uma variação de 0,3 na taxa pesa como 0,09, contra 250.000 de uma variação de R$ 500. Na prática o modelo enxerga uma variável só, e a fronteira aprendida ignora completamente a segunda. A correção é um StandardScaler (ou RobustScaler, se houver outliers extremos) dentro de um Pipeline, para que a padronização seja ajustada apenas no treino e aplicada consistentemente.

  4. Em que situação o LOF acerta e o Isolation Forest erra?

    ✓ Resposta:

    Quando os dados têm regiões de densidades diferentes. Imagine dois agrupamentos: um denso e compacto, outro esparso e espalhado. Um ponto na periferia do agrupamento esparso está, em termos absolutos, longe de quase tudo — e um método com noção global de normalidade, como o Isolation Forest, tende a isolá-lo rapidamente e marcá-lo como anômalo. Mas ele é perfeitamente típico para aquela região. O LOF compara a densidade ao redor do ponto com a densidade ao redor dos seus próprios vizinhos, então um ponto esparso cercado de pontos igualmente esparsos recebe LOF próximo de 1, isto é, normal. O caso real correspondente é comum: segmentos de clientes com padrões de volume muito distintos.

  5. Você tem 30 fraudes confirmadas e 2 milhões de transações normais. OCC ou supervisionado?

    ✓ Resposta:

    Supervisionado, com peso de classe ou custo — e vale testar contra um OCC como linha de base. Trinta exemplos positivos são poucos, mas carregam informação que nenhum método de uma classe tem acesso: eles dizem como a anomalia que importa se parece, e não apenas que ela difere do normal. Isso é decisivo, porque a maior parte do que destoa do normal não é fraude — é cliente atípico, erro de digitação, comportamento sazonal. Um modelo com peso 1:1000 aprende a direção certa; um OCC sinaliza tudo que é incomum e afoga a equipe em falsos positivos. O caminho prático é validação cruzada estratificada (com 30 positivos, cada dobra tem poucos — use repetição), avaliação por precision@k ou por custo, e uso do OCC como complemento para capturar tipos de fraude ainda não vistos.

Fontes e leituras recomendadas