Comparando Diferentes Embeddings Vetoriais
Este artigo foi originalmente publicado em The New Stack e é republicado aqui com permissão.
Como os embeddings vetoriais gerados por diferentes redes neurais diferem, e como você pode avaliá-los no seu Jupyter Notebook?
Modelos de linguagem grandes (LLMs) estão em alta e estamos diante de um novo paradigma de aplicações de linguagem como o ChatGPT. Bancos de dados vetoriais serão uma parte central da stack. Portanto, é importante entender vetores e por que eles são tão importantes.
Este projeto demonstra a diferença nos embeddings vetoriais entre modelos e mostra como usar várias coleções de dados vetoriais em um Jupyter Notebook. Neste post, abordaremos o que são embeddings vetoriais, por que eles são importantes e como comparamos diferentes embeddings vetoriais no seu Jupyter Notebook.
O que são embeddings vetoriais e por que são importantes?
De onde vêm os embeddings vetoriais?
Nos termos mais simples, embeddings vetoriais são representações numéricas de dados. Eles são usados principalmente para representar dados não estruturados. Dados não estruturados são imagens, vídeos, áudio, texto, imagens moleculares e outros tipos de dados que não têm uma estrutura formal. Embeddings vetoriais são gerados ao passar dados de entrada por uma rede neural pré-treinada e obter a saída da penúltima camada.
Redes neurais têm arquiteturas diferentes e são treinadas em diferentes conjuntos de dados, tornando único o embedding vetorial de cada modelo. É por isso que trabalhar com dados não estruturados e embeddings vetoriais é desafiador. Veremos mais adiante como modelos com a mesma base ajustados em diferentes conjuntos de dados podem produzir embeddings vetoriais diferentes.
As diferenças nas redes neurais também significam que devemos usar modelos distintos para processar diversas formas de dados não estruturados e gerar seus embeddings. Por exemplo, você não pode usar um modelo sentence transformer para gerar embeddings para uma imagem. Por outro lado, você não gostaria de usar ResNet50, um modelo de imagem, para gerar embeddings para frases. Portanto, é importante encontrar modelos que sejam apropriados para o seu tipo de dado.
Como comparamos embeddings vetoriais?
Em seguida, vamos ver como compará-los. Esta seção compara três modelos multilíngues diferentes baseados no MiniLM da Hugging Face. Há muitas maneiras de comparar vetores. Neste exemplo, usamos a métrica de distância L2 e um índice de arquivo invertido como índice vetorial.
Para este projeto, desenvolvi alguns dos meus dados, inspirado pelo recente lançamento de álbum da Taylor Swift, definidos diretamente no Jupyter Notebook. Você pode usar meus exemplos ou criar suas próprias frases. Depois que temos os dados, obtemos os diferentes embeddings e armazenamos dois conjuntos de embeddings em um banco de dados vetorial como o Milvus. Fazemos consultas neles para realizar as comparações usando os embeddings do terceiro modelo.
Estamos procurando ver se os resultados da busca são diferentes e quão distantes os resultados da busca estão uns dos outros. Em um ambiente de produção, você gostaria de saber os resultados que quer ver e, então, verificar isso em relação aos resultados retornados.
Comparando embeddings vetoriais de diferentes modelos
Os três modelos que comparamos são o modelo base multilíngue de paráfrase usando MiniLM da Sentence Transformers, uma versão que foi ajustada para detecção de intenção, e um que a Sprylab ajustou sem detalhes sobre para que ele foi ajustado. Encontrar três modelos compatíveis que eu pudesse rodar no meu laptop foi uma das partes mais difíceis deste projeto.
Precisamos de vetores do mesmo comprimento/dimensão para comparar embeddings vetoriais. Neste exemplo, estamos usando vetores de 384 dimensões, conforme o modelo MiniLM Sentence Transformer. Observe que isso não significa que estamos criando 384 “recursos” ou “categorias” para cada pedaço de dado, mas sim gerando uma representação abstrata dos dados em um espaço de 384 dimensões. Por exemplo, nenhuma das dimensões representará algo como uma classe gramatical, o número de palavras na frase, se algo é um substantivo próprio ou qualquer coisa conceitual desse tipo.
Dados de comparação de embeddings vetoriais
Estamos usando modelos sentence transformer, o que significa que nossos dados devem estar na forma de frases. Recomendo ter pelo menos 50 frases para comparar. O notebook de exemplo contém 51. Também recomendo usar dados que tenham alguma semelhança entre si. Neste exemplo, usei letras de quatro músicas da Taylor Swift: “Speak Now,” “Starlight,” “Sparks Fly,” e “Haunted.”
Escolhi essas músicas porque muitas das letras formam frases completas, então é fácil transformar do formato de letra de música para o formato de frase. Eu também queria testar uma hipótese. As três primeiras músicas são mais ou menos canções de amor, enquanto a última, “Haunted,” é mais parecida com uma canção de término. Então, minha hipótese é que é menos provável que “Haunted” apareça nos resultados retornados para as três primeiras músicas do que elas entre si.
Comparando embeddings vetoriais no seu Jupyter Notebook
Vamos ao código. Com o Milvus Lite, uma versão leve do Milvus, você pode comparar seus embeddings vetoriais diretamente em um Jupyter Notebook. Para este código de exemplo, você precisará de ! pip install pymilvus milvus sentence-transformers. Você só precisa de alguns imports para este projeto; a biblioteca time é opcional. Depois de executar os imports, inicie o default_server do Milvus e configure uma conexão.
from sentence_transformers import SentenceTransformer
from milvus import default_server
from pymilvus import connections, utility, FieldSchema, CollectionSchema, DataType, Collection
from time import time
default_server.start()
connections.connect(host="127.0.0.1", port=default_server.listen_port)
Obtenha os modelos sentence transformer do Hugging Face
O passo 1 é obter nossos modelos de embeddings vetoriais. Usamos a série MiniLM multilíngue de paráfrase para este exemplo. O primeiro é a versão canônica. Os dois seguintes são as versões ajustadas de forma diferente. Essa escolha de modelo nos dá um exemplo claro de como o ajuste fino pode alterar visivelmente seus vetores.
v12 = SentenceTransformer("sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")
ft3_v12 = SentenceTransformer("Sprylab/paraphrase-multilingual-MiniLM-L12-v2-fine-tuned-3")
ft5_v12 = SentenceTransformer("hroth/psais-paraphrase-multilingual-MiniLM-L12-v2-5shot")
Carregue os conjuntos de dados em duas coleções em um banco de dados vetorial
Listei 51 frases do álbum “Speak Now” da Taylor Swift abaixo. Sinta-se à vontade para copiar e colar este trecho de código. Ou leia, se quiser; ela é uma ótima letrista.
# inspirado por Speak Now (Taylor's Version)
# Letras de: Speak Now, Starlight, Sparks Fly, Haunted
sentences = [
"Eu não sou o tipo de garota que deveria estar invadindo grosseiramente uma ocasião de véu branco, mas você não é o tipo de garoto que deveria estar se casando com a garota errada",
"Eu entro de fininho e vejo seus amigos e a familiazinha esnobe dela, todos vestidos em tons pastel, e ela está gritando com uma madrinha em algum lugar lá dentro de uma sala, usando um vestido em forma de doce",
"Isso certamente não é o que você pensou que seria.",
"Eu me perco em um devaneio em que me levanto e digo: 'Não diga sim, fuja agora Eu vou te encontrar quando você estiver fora da igreja, na porta dos fundos.'",
"Não espere, nem diga um único voto você precisa me ouvir e eles disseram: 'Fale agora'.",
"Gestos afetuosos são trocados.",
"E o órgão começa a tocar uma canção que soa como uma marcha fúnebre.",
"E eu estou me escondendo nas cortinas, parece que fui desconvidada pela sua adorável futura noiva.",
"Ela flutua pelo corredor como uma rainha de concurso de beleza.",
"Mas eu sei que você queria que fosse eu você queria que fosse eu, não queria?",
"Eu ouço o pregador dizer: 'Fale agora ou cale-se para sempre'",
"Lá está o silêncio, lá está minha última chance.",
"Eu me levanto com as mãos trêmulas, todos os olhos em mim",
"Olhares horrorizados de todos na sala, mas eu só estou olhando para você.",
"E você dirá: 'Vamos fugir agora' Eu vou te encontrar quando eu estiver fora do meu smoking, na porta dos fundos",
"Amor, eu não disse meus votos Tão feliz que você estava por perto Quando eles disseram: 'Fale agora'",
"Eu disse: 'Meu Deus, que melodia maravilhosa'",
"Foi a melhor noite, jamais esqueceria como nos movíamos.",
"O lugar inteiro estava impecável e nós estávamos dançando, dançando como se fôssemos feitos de luz das estrelas",
"Conheci Bobby no calçadão no verão de 45",
"Ele me buscou tarde uma noite pela janela nós tínhamos dezessete anos e éramos loucos correndo selvagens, selvagens.",
"Não consigo lembrar que música ele estava tocando quando entramos.",
"A noite em que entramos escondidos em uma festa de iate clube fingindo ser uma duquesa e um príncipe.",
"Ele disse: 'Olhe para você, se preocupando tanto com coisas que não pode mudar Você vai passar a vida inteira cantando tristeza Se continuar pensando assim'",
"Ele estava tentando jogar pedrinhas no oceano dizendo para mim 'Você não vê a luz das estrelas, luz das estrelas você não sonha com coisas impossíveis'",
"Ooh, ooh ele está falando loucuras Ooh, ooh dançando comigo Ooh, ooh poderíamos nos casar Ter dez filhos e ensiná-los a sonhar",
"O jeito que você se move é como uma tempestade completa.",
"E eu sou um castelo de cartas",
"Você é o tipo de imprudente que deveria me fazer sair correndo, mas eu meio que sei que não vou chegar longe",
"E você ficou ali na minha frente perto o bastante para tocar",
"Perto o bastante para esperar que você não pudesse ver no que eu estava pensando",
"Largue tudo agora",
"Encontre-me na chuva torrencial",
"Beije-me na calçada",
"Leve embora a dor",
"Porque eu vejo faíscas voarem, sempre que você sorri",
"Pegue-me com esses olhos verdes, amor, enquanto as luzes se apagam",
"Me dê algo que vá me assombrar quando você não estiver por perto",
"Minha mente se esquece de me lembrar que você é uma má ideia",
"Você me toca uma vez e é realmente algo você descobre que sou ainda melhor do que imaginou que eu seria",
"Estou em guarda para o resto do mundo, mas com você, sei que não adianta"
"E eu poderia esperar pacientemente, mas eu realmente queria que você viesse"
"Passo meus dedos pelo seu cabelo e vejo as luzes enlouquecerem",
"Continue mantendo seus olhos em mim é errado o suficiente para fazer parecer certo",
"E me leve escada acima, você não vai sussurrar suave e devagar, estou cativada por você, amor, como um show de fogos de artifício",
"Você e eu caminhamos por uma linha frágil eu soube disso o tempo todo, Mas nunca pensei que viveria para vê-la se romper",
"Está ficando escuro e está tudo quieto demais E não posso confiar em nada agora E isso está vindo sobre você como se tudo fosse um grande erro",
"Oh, estou prendendo a respiração Não vou te perder de novo",
"Algo fez seus olhos ficarem frios",
"Vamos, vamos, não me deixe assim Eu pensei que tinha entendido você",
"Algo deu terrivelmente errado você é tudo o que eu queria",
"Não consigo respirar sempre que você se vai não dá para voltar agora, estou assombrada",
"Eu só sei Você não se foi, você não pode ter ido, não",
]
Primeiro, precisamos definir os esquemas para inserir dados em um banco de dados vetorial. Temos o mesmo esquema para ambas as coleções neste exemplo, então só precisamos criar um. No entanto, certifique-se de criar duas coleções.
Depois que as coleções estiverem prontas, codificamos todas as frases em embeddings para seus modelos e definimos os parâmetros do índice vetorial. Usamos L2 como nossa métrica de distância e um índice de arquivo invertido com quatro centroides. Afinal, há apenas 51 entradas. Então passamos a formatar os dados e carregá-los no Milvus.
# object should be inserted in the format of (title, date, location, speech embedding)
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="sentence", dtype=DataType.VARCHAR, max_length=500),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=DIMENSION),
]
schema = CollectionSchema(fields=fields)
collection_v12 = Collection(name=COLLECTION_V12, schema=schema)
collection_v12_ft5 = Collection(name=COLLECTION_V12_Q, schema=schema)
v12_embeds = {}
v12_q_embeds = {}
for sentence in sentences:
v12_embeds[sentence] = v12.encode(sentence)
v12_q_embeds[sentence] = ft5_v12.encode(sentence)
index_params = {
"index_type": "IVF_FLAT",
"metric_type": "L2",
"params": {"nlist": 4},
}
collection_v12.create_index(field_name="embedding", index_params=index_params)
collection_v12.load()
collection_v12_ft5.create_index(field_name="embedding", index_params=index_params)
collection_v12_ft5.load()
for sentence in sentences:
v12_insert = [
{
"sentence": sentence,
"embedding": v12_embeds[sentence]
}
]
ft_insert = [
{
"sentence": sentence,
"embedding": v12_q_embeds[sentence]
}
]
collection_v12.insert(v12_insert)
collection_v12_ft5.insert(ft_insert)
collection_v12.flush()
collection_v12_ft5.flush()
Consulte os armazenamentos vetoriais para comparar os embeddings
Em seguida, é hora de comparar. Neste exemplo, usaremos as duas primeiras frases. Usamos o terceiro modelo para gerar os embeddings vetoriais delas.
search_embeds = {}
search_data = []
for sentence in sentences[0:2]:
vector_embedding = ft3_v12.encode(sentence)
search_embeds[sentence] = vector_embedding
search_data.append(vector_embedding)
Agora consultamos o Milvus. Também estou monitorando o tempo aqui. Os tempos das buscas que obtive são mostrados abaixo. Certifique-se de passar o mesmo tipo de métrica nos parâmetros de busca.
start1 = time()
res_v12 = collection_v12.search(
data=search_data, # Embeded search value
anns_field="embedding", # Search across embeddings
param={"metric_type": "L2",
"params": {"nprobe": 2}},
limit = 3, # Limit to top_k results per search
output_fields=["sentence"])
time1 = time() - start1
print(f"Time for first search: {time1}")
start2 = time()
res_v12_ft5 = collection_v12_ft5.search(
data=search_data, # Embeded search value
anns_field="embedding", # Search across embeddings
param={"metric_type": "L2",
"params": {"nprobe": 2}},
limit = 3, # Limit to top_k results per search
output_fields=["sentence"])
time2 = time() - start2
print(f"Time for second search: {time2}")
Agora vamos analisar os resultados e comparar. Vemos resultados surpreendentemente semelhantes para o modelo original em comparação com o ajustado pela Sprylab. A única diferença é que o primeiro resultado retornado é a própria frase. Isso nos mostra que os resultados em dois e três são mais semelhantes às duas frases de busca de exemplo do que entre si nesses dois espaços vetoriais.
for i, hits in enumerate(res_v12):
for hit in hits:
print(f"Query sentence: {sentences[i]}")
print(f"Nearest Neighbor Number {i}: {hit.entity.get('sentence')} ---- {hit.distance}\n")
A seguir, vamos comparar os dois modelos ajustados por fine-tuning. A partir desses resultados, veremos que a frase que começa com “I’m on my guard for the rest of the world …“ é semanticamente semelhante às nossas frases de busca, porque aparece em ambas as comparações. Dois pontos interessantes aqui são: 1) resultados diferentes da primeira consulta, e 2) a frase da segunda consulta não aparece entre as três primeiras da primeira, mas o inverso é verdadeiro.
for hit in hits:
print(f"Query sentence: {sentences[i]}")
print(f"Nearest Neighbor Number {i}: {hit.entity.get('sentence')} ---- {hit.distance}\n")
Resumo
Neste tutorial, aprendemos sobre embeddings vetoriais e como compará-los, e fizemos algumas comparações nós mesmos com nossos dados personalizados. Como bônus, também mostramos um exemplo de como trabalhar com duas coleções diferentes ao mesmo tempo. É assim que você pode consultar diferentes espaços vetoriais latentes.
Mostramos a diferença entre um modelo e algumas versões dele ajustadas por fine-tuning. Também vimos como um resultado apareceu em ambos os espaços de embedding. Um resultado de consulta em várias representações vetoriais significa que essa consulta deve ser semanticamente semelhante de várias maneiras. Experimente fazer isso com modelos de imagem, modelos de linguagem com dimensionalidades diferentes ou seus dados para os próximos passos.
Continue lendo

Introducing Customer-Managed Encryption Keys (CMEK) on Zilliz Cloud
We're announcing the general availability of Customer-Managed Encryption Keys (CMEK) on Zilliz Cloud.

Zilliz Cloud Just Landed in Claude Code
The Zilliz Cloud Plugin brings the full power of Zilliz Cloud directly into your Claude Code terminal as natural-language conversations.

3 Easiest Ways to Use Claude Code on Your Mobile Phone
Run Claude Code from your phone with Remote Control, Happy Coder, or SSH + Tailscale. Comparison table, setup steps, and tools for typing, memory, and parallel tasks.



