DEV Community

coda
coda

Posted on

Como agrupamos milhares de figurinhas do WhatsApp automaticamente: K-Means vs DBSCAN vs HDBSCAN

Se você já construiu um diretório ou uma plataforma de conteúdo gerado por usuários (UGC), sabe que a organização vira um caos muito rápido.

Recentemente, eu estava escalando o backend do Figurinha WhatsApp — uma plataforma para descobrir e baixar pacotes de stickers. A premissa era simples: os usuários enviam figurinhas, e nós precisamos organizá-las em pacotes temáticos para facilitar a busca (ex: "Bom dia", "Gatos", "Memes de TI").

O problema? Fazer isso na mão é impossível. Precisávamos de um pipeline que olhasse para milhares de imagens, entendesse o contexto visual delas e as agrupasse automaticamente.

Foi assim que entramos no buraco negro dos algoritmos de clusterização. E se você acha que o bom e velho K-Means resolve tudo, prepare-se para passar raiva.

A Abordagem Ingênua: Embeddings + K-Means
O primeiro passo foi transformar as imagens em números. Usamos modelos de visão computacional modernos (como o SigLIP) para gerar embeddings vetoriais de cada figurinha. Se duas figurinhas são visualmente e semanticamente parecidas, seus vetores estarão próximos no espaço.

A primeira tentativa de agrupar esses vetores foi usar o K-Means. É o padrão da indústria, certo?
Erro fatal.

O K-Means exige que você passe o parâmetro K (o número exato de clusters/grupos que você quer formar). Mas em uma plataforma aberta de figurinhas, como eu vou saber quantos memes diferentes existem hoje na internet? Podem ser 50 ou 5.000. Além disso, o K-Means assume que os clusters são esféricos e têm tamanhos parecidos, o que definitivamente não reflete a realidade da internet.

O Upgrade: Tentando o DBSCAN
Precisávamos de um algoritmo que descobrisse o número de clusters sozinho, baseado puramente na densidade dos pontos (imagens parecidas muito juntas). Foi aí que migramos para o DBSCAN (Density-Based Spatial Clustering of Applications with Noise).

O DBSCAN foi um salto gigantesco. Ele não precisa do número de clusters. Ele encontra regiões densas e isola o "ruído" (aquelas figurinhas aleatórias que não se encaixam em lugar nenhum — o que é ótimo para filtrar lixo).

Mas logo esbarramos no "Calcanhar de Aquiles" do algoritmo: o parâmetro Epsilon (eps).
O eps define o raio de busca ao redor de um ponto. O problema é que o DBSCAN assume uma densidade global.

No mundo real das figurinhas, a densidade varia absurdamente:

Cluster Denso: Figurinhas de "Bom dia com café". São todas muito iguais, formando um grupo super denso e apertado.

Cluster Esparso: Figurinhas de "Reações de Anime". Elas têm o mesmo tema, mas visualmente são mais variadas e dispersas.

Se configurássemos o eps muito pequeno, o DBSCAN separava perfeitamente o café, mas quebrava os animes em 50 grupos minúsculos. Se aumentássemos o eps, ele juntava os animes, mas misturava as de café com "Boa noite". Era um cobertor curto.

A Solução Final: Por que o HDBSCAN destruiu a concorrência
Foi rodando testes no nosso servidor VPS que decidimos testar o HDBSCAN (Hierarchical DBSCAN). A letra "H" faz toda a diferença.

O HDBSCAN não exige um eps global. Ele cria uma hierarquia de todos os clusters possíveis em diferentes escalas de densidade e, de forma inteligente, extrai os clusters mais estáveis.

O impacto no Figurinha WhatsApp foi imediato:

Lida com densidades variáveis: Ele conseguiu agrupar o cluster super denso de "Bom Dia" e, ao mesmo tempo, reconhecer o cluster mais disperso de "Memes do Brasileirão", sem que um interferisse no outro.

Menos "babá" de parâmetros: Só precisamos definir o min_cluster_size (ex: queremos que um pacote de figurinhas tenha no mínimo 5 imagens). O algoritmo faz o resto.

Filtragem de Ruído impecável: Imagens muito fora do padrão continuaram sendo marcadas como outliers, mantendo a qualidade dos nossos pacotes altíssima.

import hdbscan
import numpy as np

# 'image_embeddings' são os vetores gerados pelo nosso modelo de visão
embeddings = np.array(image_embeddings)

# Inicializando o HDBSCAN
# Exigimos no mínimo 10 figurinhas para formar um pacote válido
clusterer = hdbscan.HDBSCAN(min_cluster_size=10, metric='euclidean')

# Fit e predição
cluster_labels = clusterer.fit_predict(embeddings)

# As imagens com label -1 são consideradas "ruído" e descartadas da home
Enter fullscreen mode Exit fullscreen mode

Quando você está lidando com dados comportamentais ou UGC, a realidade é bagunçada e tem densidades variáveis. Tentar forçar o K-Means ou brigar com o eps do DBSCAN só vai atrasar seu roadmap.

Se você estiver construindo qualquer sistema de recomendação, galeria de imagens, ou agrupamento de textos, faça um favor a si mesmo e vá direto para o HDBSCAN. No nosso caso, foi a diferença entre um diretório de figurinhas caótico e uma navegação fluida que retém os usuários.

Se quiserem ver o resultado desse pipeline de clusterização na prática, deem uma olhada em como os pacotes estão sendo formados dinamicamente lá no Figurinha WhatsApp.

Vocês já usaram HDBSCAN em produção? Qual métrica de distância vocês preferem para imagens? Deixem aí nos comentários!

Top comments (0)