Introdução ao RAG (Retrieval-Augmented Generation)- parte 2

Entendendo Bancos de Dados Vetoriais

Petrus 
TOTVS Developers
Published in
10 min readNov 20, 2023

--

Introdução

Imagine um mundo onde as máquinas não apenas respondem às nossas perguntas, mas entendem e antecipam nossas necessidades com precisão surpreendente. Esse é o poder da Inteligência Artificial Generativa (GenAI), uma fronteira tecnológica cada vez mais explorada por empresas inovadoras ao redor do mundo. Nos últimos seis meses, essa exploração acelerou, com modelos GenAI exigindo algo fascinante: uma ‘memória de longo prazo’. Isso transformou os bancos de dados vetoriais de uma necessidade técnica em uma revolução empresarial, permitindo que esses modelos acessem informações em tempo real e forneçam respostas contextualizadas e profundamente relevantes aos usuários. Neste artigo, mergulharemos no coração dessa revolução: como os bancos de dados vetoriais estão capacitando a GenAI e redefinindo as aplicações empresariais.

A Ascensão da Inteligência Artificial Generativa

Empresas ao redor do mundo têm implantado soluções de Inteligência Artificial Generativa (GenAI) customizadas para seu negócio. Essa nova onda de IA está redefinindo o que os sistemas computacionais podem fazer, desde a criação de arte até a resposta a consultas complexas em um contexto empresarial.

O Papel Crucial dos Bancos de Dados Vetoriais

Para que esses modelos de GenAI funcionem eficientemente, eles precisam de uma ‘memória de longo prazo’, algo que é amplamente alcançado através de bancos de dados vetoriais. Esses bancos de dados não são apenas um repositório de informações, mas uma fonte dinâmica de contexto e compreensão. Estes dados são geralmente dados das empresas que não são públicos e que são vitais para viabilizar a solução interna de GenAI. Para manter estes dados seguros, as duas estratégias mais utilizadas são: Treinar um base model com seus dados, ou criar uma camada de software entre o cliente ( usuário interno ou software) e a api direta do chatgpt o RAG.

Entendendo os Embeddings: O Coração dos Bancos de Dados Vetoriais

Introdução aos Embeddings

Os embeddings são a chave para a eficácia dos bancos de dados vetoriais. Eles representam um avanço significativo na forma como as máquinas interpretam e armazenam dados, sendo fundamentais em modelos de IA como o ChatGPT.

Desafios Históricos e a Evolução dos Embeddings

A barreira que os pesquisadores de IA, se depararam por décadas, era o problema como os computadores armazenam os dados, no formato binário, só entendem ‘1s’ e ‘0s’. As letras e palavras são conjuntos de números binários. Como expressar informações e relações que as máquinas podem entender rapidamente ? A primeira abordagem foi a codificação, vetores de bits para expressar dados. ‘cão’ seria [1, 0, 0], ‘gato’ seria [0, 1, 0], ‘elefante’ seria [0, 0, 1] e assim vai. Mas os pesquisadores começaram a se deparar com os classificadores para similaridade e diferença. Como dizer que São Bernardo e doberman são cães? E que cão e gatos são animais, mas não da mesma espécie? Os cães poe exemplo 450 espécies Ok, vamos adicionar uma dimensão extra, então uma seria [1, 0, 0, 1], mas e gatos? e passáros que são 10000 espécies, não haveriam bits suficientes.

Assim, para contabilizar todas as múltiplas combinações de elementos existentes em nosso mundo, precisaríamos que esses vetores estivessem nas trilhões ou em dimensões ainda maiores, algo completamente inviável. Felizmente, a solução era clara, tornar os vetores densos. Em outras palavras, ‘cão’ teria que ser [1, -0.5, 2, 0.21,…], o que significa que, em vez de usar um vetor simples para descrever o cão como um todo, um cão seria formado por um vetor muito específico com vários números diferentes. Uma maneira de interpretar esses números é como ‘atributos’. Assim como um personagem de RPG tem 90 de destreza, 65 de força, 43 de defesa, etc. Mas isso era uma façanha impossível de fazer manualmente, simplesmente havia combinações demais. E havia um problema adicional, esses vetores ainda não significavam nada. Sim, a máquina poderia diferenciar um conceito de outro, mas como poderíamos ensinar um modelo que um ‘cão’ e um ‘gato’ são coisas ‘mais semelhantes’ do que ‘cão’ e ‘mesa’? Aplicar similaridade entre conceitos é uma característica chave para entender nosso mundo.

Word2Vec: Uma Revolução nos Embeddings

A grande virada veio em 2013 com o advento do Word2Vec, uma rede neural desenvolvida pelo Google. Este modelo foi capaz de transformar palavras em embeddings densos e ricos — vetores com muitas dimensões que capturam nuances e relações semânticas. Por exemplo, um ‘cão’ não seria mais apenas [1, -0.5, 2, 0.21,…], mas um conjunto complexo de atributos, similar a um personagem de RPG com várias estatísticas. Essa nova e revolucionária rede neural foi capaz de analisar automaticamente milhões de textos e gerar vetores densos de palavras chamados embeddings. Esses embeddings capturavam finalmente as relações semânticas complexas de forma muito rica. Por exemplo, modelos treinados com Word2Vec poderiam entender analogias e relações como “rei — homem + mulher = rainha”.

Ou seja, foi possível ensinar às máquinas a entender e relacionar conceitos de uma forma parecida com os humanos.

Essa capacidade de gerar embeddings que capturam relações semânticas de maneira automatizada a partir de grandes volumes de texto foi uma verdadeira revolução no campo de processamento de linguagem natural. Os embeddings abriram as portas para muitos dos impressionantes avanços em PLN que vimos na última década.

Aplicação em Modelos Modernos: O Exemplo do ChatGPT

Nos modelos modernos de IA, como o ChatGPT, os embeddings desempenham um papel crucial. Eles permitem que o modelo calcule relações e similaridades entre conceitos, palavras e frases. Essa capacidade é a base do mecanismo de ‘Attention’, permitindo que o ChatGPT entenda e responda de maneira contextualizada e semântica.

Importância dos Embeddings em Bancos de Dados Vetoriais e IA Generativa

Em bancos de dados vetoriais, esses embeddings transformam a maneira como armazenamos e acessamos informações. Quando um usuário faz uma pergunta, ela é convertida em um embedding e comparada com outros embeddings no banco de dados usando cálculos de similaridade, como a distância euclidiana ou a similaridade de cosseno. Isso permite encontrar e utilizar informações semanticamente relevantes rapidamente, uma capacidade essencial para a IA generativa em ambientes empresariais.

Aplicação Prática: O Caso do ChatGPT e HyperVectorDB

Para ilustrar de forma prática como os bancos de dados vetoriais funcionam e sua aplicação em soluções de IA, vamos explorar um exemplo concreto. O seguinte trecho de código utiliza a biblioteca HyperVectorDB (https://github.com/deatos/HyperVectorDB), demonstrando a criação, indexação e consulta de um banco de dados vetorial. Este exemplo é particularmente relevante para oferecer uma visão prática sobre a implementação dessas tecnologias no desenvolvimento de produtos baseados em IA.

using OpenAI.Files;
using System.Diagnostics;

namespace HyperVectorDBExample {
internal class Program {
// Inicializando a base de dados HyperVectorDB
public static HyperVectorDB.HyperVectorDB ?DB;

static void Main() {
// Obtendo a chave de API da OpenAI
var openaiapikey = "";
var openaiapikey2 = System.Environment.GetEnvironmentVariable("openaiapikey");
if (openaiapikey2 != null) openaiapikey = openaiapikey2;
var filepath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile), "oak.txt");
if ((openaiapikey == "") && (System.IO.File.Exists(filepath))) openaiapikey = System.IO.File.ReadAllText(filepath).Trim();
// Solicitando a chave de API se não for encontrada
while(openaiapikey == "") {
Console.WriteLine("Openapi key not found in environment variable or file, please enter it now:");
openaiapikey = Console.ReadLine();
}
if (openaiapikey is null) return;

// Criando ou carregando o banco de dados
DB = new HyperVectorDB.HyperVectorDB(new HyperVectorDB.Embedder.EmbedderOpenAI_ADA_002(openaiapikey), "TestDatabase");
if(Directory.Exists("TestDatabase")) {
Console.WriteLine("Loading database");
DB.Load();
} else {
Console.WriteLine("Creating database");
// Criando um índice e adicionando documentos de teste
DB.CreateIndex("TestIndex");
DB.IndexDocument("TestIndex", "This is a test document about dogs");
DB.IndexDocument("TestIndex", "This is a test document about cats");
DB.IndexDocument("TestIndex", "This is a test document about fish");
DB.IndexDocument("TestIndex", "This is a test document about birds");
DB.IndexDocument("TestIndex", "This is a test document about dogs and cats");
DB.IndexDocument("TestIndex", "This is a test document about cats and fish");
DB.IndexDocument("TestIndex", "This is a test document about fish and birds");
DB.IndexDocument("TestIndex", "This is a test document about birds and dogs");
DB.IndexDocument("TestIndex", "This is a test document about dogs and cats and fish");
DB.IndexDocument("TestIndex", "This is a test document about cats and fish and birds");
DB.IndexDocument("TestIndex", "This is a test document about fish and birds and dogs");
DB.IndexDocument("TestIndex", "This is a test document about birds and dogs and cats");
DB.IndexDocument("TestIndex", "This is a test document about dogs and cats and fish and birds");
DB.IndexDocument("TestIndex", "This is a test document about cats and fish and birds and dogs");
DB.IndexDocument("TestIndex", "This is a test document about fish and birds and dogs and cats");
DB.IndexDocument("TestIndex", "This is a test document about birds and dogs and cats and fish");
DB.Save();
}

// Loop para busca de termos
while(true) {
Console.WriteLine("Enter a search term:");
var searchterm = Console.ReadLine();
if(searchterm == "exit") break;
if (searchterm is null) break;
if (searchterm is "") continue;
// Medindo o tempo da consulta
var sw = new Stopwatch();sw.Start();
var result = DB.QueryCosineSimilarity(searchterm);
sw.Stop();
// Exibindo os resultados da consulta
Console.WriteLine("Results:");
for (var i = 0; i < result.Documents.Count; i++) Console.WriteLine(result.Documents[i].DocumentString + " " + result.Distances[i]);
Console.WriteLine("Time taken: " + sw.ElapsedMilliseconds + "ms");
}
Console.WriteLine("Done, press enter to exit");
Console.ReadLine();
}
}
}

O exemplo de código acima demonstra a prática de indexação e recuperação de informações em um banco de dados vetorial, usando a biblioteca HyperVectorDB. Através deste processo, é possível ver como os conceitos de embeddings e similaridade vetorial são aplicados na realidade. Ao indexar documentos com base em seus vetores semânticos, podemos realizar buscas eficientes, encontrando documentos que são semanticamente relacionados a um termo de busca. Isso exemplifica como bancos de dados vetoriais permitem que soluções de IA, como o ChatGPT, acessem e interpretem grandes quantidades de dados de forma eficaz, facilitando aplicações empresariais complexas.

Para os leitores que não estão profundamente envolvidos com programação, o essencial a entender do exemplo de código acima é como ele demonstra a aplicação prática dos bancos de dados vetoriais em sistemas de IA. Em resumo, o código ilustra como documentos (ou dados) são convertidos em vetores que representam seu significado semântico. Esses vetores permitem que o sistema faça buscas eficientes, encontrando documentos que têm um significado similar ao de uma consulta específica. Isso é crucial em aplicações de IA, pois permite que o sistema acesse e interprete grandes volumes de informações de maneira rápida e eficiente, uma habilidade essencial para muitas aplicações empresariais de IA, como a personalização de recomendações ou a análise de tendências de mercado.

Vamos entrar em 2 pontos importantes:

    DB.IndexDocument("TestIndex", "This is a test document about dogs");

No acima inserimos um documento que será transformado em um array de vetores.

        public bool IndexDocument(string indexName, string document) {
if (!Indexs.ContainsKey(indexName)) return false;
var index = Indexs[indexName];
var vector = _embedder.GetVector(document);
var doc = new HVDBDocument(document);
index.Add(vector, doc);
return true;
}

// E temos

public Double[] GetVector(String Document) {
var result = this.Client.EmbeddingsEndpoint.CreateEmbeddingAsync(Document, OpenAI.Models.Model.Embedding_Ada_002).GetAwaiter().GetResult();
TotalTokens += result.Usage.TotalTokens;
var vect = result.Data[0].Embedding.ToArray<double>();
return vect;
}

Veja que o texto é enviado para uma api Embedding_Ada_002 da OpenAI (cobrada) que retorna a representação vetorial do texto.

E depois temos a busca

var result = DB.QueryCosineSimilarity(searchterm);

detalhando:

        public HVDBQueryResult QueryCosineSimilarity(string query, int topK = 5) {
var vector = _embedder.GetVector(query);
var results = new List<HVDBQueryResult>();
Parallel.ForEach(Indexs, index =>
{
var result = index.Value.QueryCosineSimilarity(vector, topK);
results.Add(result);
});
var docs = new List<HVDBDocument>();
var distances = new List<double>();
foreach (var result in results) {
docs.AddRange(result.Documents);
distances.AddRange(result.Distances);
}
var sorted = distances.Select((x, i) => new KeyValuePair<double, HVDBDocument>(x, docs[i])).OrderByDescending(x => x.Key).ToList().Take(topK);
var newresult = new HVDBQueryResult(sorted.Select(x => x.Value).ToList(), sorted.Select(x => x.Key).ToList());
return newresult;
}
}

Codifica a query e depois com os vetores faz a busca index.Value.QueryCosineSimilarity.

        public HVDBQueryResult QueryCosineSimilarity(double[] queryVector, int topK = 5) {
if (queryVector == null) throw new ArgumentNullException(nameof(queryVector));
if (topK <= 0) throw new ArgumentException("Number of results requested (k) must be greater than zero.", nameof(topK));
// first check _tryCache
HVDBQueryResult? cachedResult = TryHitCacheCosineSimilarity(queryVector, topK);
if (cachedResult != null) {
return cachedResult;
}
var similarities = new ConcurrentBag<KeyValuePair<HVDBDocument, double>>();
Parallel.For(0, vectors.Count, i =>
{
//double similarity = 1 - Distance.Cosine(queryVector, vectors[i]);

double similarity = 1 - GalaxyBrainedMathsLOL.CosineSimilarity(queryVector, vectors[i]);
similarities.Add(new KeyValuePair<HVDBDocument, double>(documents[i], similarity));
});
var orderedData = similarities
.OrderByDescending(pair => pair.Value)
.Take(topK)
.ToList();

return new HVDBQueryResult(
orderedData.Select(pair => pair.Key).ToList(),
orderedData.Select(pair => pair.Value).ToList()
);
}

Onde a pesquisa é feita da seguinte forma:

        public static double CosineSimilarity(double[] x, double[] y)
{
double dotProduct = 0.0;
double xMagnitude = 0.0;
double yMagnitude = 0.0;

for (int i = 0; i < x.Length; i++)
{
dotProduct += x[i] * y[i];
xMagnitude += x[i] * x[i];
yMagnitude += y[i] * y[i];
}

if (dotProduct == 0.0)
{
return 1.0;
}

return 1.0 - dotProduct / (Math.Sqrt(xMagnitude) * Math.Sqrt(yMagnitude));
}

os resultados mais próximos terão similaridade semântica mais próxima.

No RAG estes texto serão incorporados no prompt para a LLM, aumentando a informação especializada e o resultado da resposta.

Concluindo

À medida que nos aprofundamos na era da Inteligência Artificial, entendo cada vez mais que os bancos de dados vetoriais são fundamentais, não apenas como ferramentas, mas como verdadeiros catalisadores de inovação. Para nós, programadores, gestores de projetos de TI e product managers, aprofundar nosso conhecimento nessas tecnologias não é um luxo, mas uma necessidade urgente. Dominar a aplicação dos bancos de dados vetoriais não só nos capacita a manipular grandes volumes de dados eficientemente, mas também nos abre portas para insights mais ricos e decisões estratégicas mais informadas.

Convido você a se juntar a mim nesta jornada de exploração e inovação. Seja para enriquecer a experiência do cliente, otimizar processos internos ou abrir novos caminhos em seu campo, o potencial é vasto e inspirador. Os bancos de dados vetoriais são mais do que uma solução técnica; eles são um passo em direção a um futuro mais inteligente e conectado. Estou ansioso para ver como você irá utilizar esses poderosos recursos em suas próximas iniciativas de tecnologia. Vamos avançar juntos, inovar e transformar o mundo com a IA.

Sessão de Referências

  1. HyperVectorDB Documentation. (2023). GitHub Repository. Disponível em: https://github.com/deatos/HyperVectorDB
  2. Introdução ao Word Embeddings em C#. (2023). Medium. Disponível em: https://medium.com/totvsdevelopers/introdu%C3%A7%C3%A3o-a-word-embeddings-em-c-s%C3%A9rie-tech-d3710458943d
  3. Introdução ao RAG (Retrieval-Augmented Generation): Ampliando Modelos de Linguagem com Conhecimento Externo. (2023). Medium. Disponível em: https://medium.com/%40petrusje/introdu%C3%A7%C3%A3o-ao-rag-retrieval-augmented-generation-046b1310b96a
  4. Mikolov, T., Chen, K., Corrado, G., & Dean, J. (2013). Efficient Estimation of Word Representations in Vector Space. arXiv preprint arXiv:1301.3781. Disponível em: https://arxiv.org/abs/1301.3781
  5. Devlin, J., Chang, M. W., Lee, K., & Toutanova, K. (2018). BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding. arXiv preprint arXiv:1810.04805. Disponível em: https://arxiv.org/abs/1810.04805
  6. Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., … & Polosukhin, I. (2017). Attention Is All You Need. arXiv preprint arXiv:1706.03762. Disponível em: https://arxiv.org/abs/1706.03762
  7. Brown, T. B., Mann, B., Ryder, N., Subbiah, M., Kaplan, J., Dhariwal, P., … & Amodei, D. (2020). Language Models are Few-Shot Learners. arXiv preprint arXiv:2005.14165. Disponível em: https://arxiv.org/abs/2005.14165

--

--