嵌入的魔力
#javascript #openai #convex #vectordatabase

我关心坚固的酸保证和我喜欢交易数据库有多相似?”尽管有多种方法可以从语法上或语法上比较这些字符串,例如,AI模型给我们的一种强大的东西是使用一种称为 embeddings 的东西对这些语义进行比较的能力。给定一个模型,例如Openai的text-embedding-ada-002,我可以告诉您,上述两个字符串的相似性为0.784,并且比我关心强酸保证更相似,我喜欢Mongodb”。 ð。使用嵌入,我们可以做一套有力的事情: 1

  • 搜索â(在与查询字符串相关的结果中排名结果)
  • 聚类(其中文本字符串以相似性分组)
  • 建议(建议使用相关文本字符串的项目)
  • 异常检测(在几乎没有相关性的异常值)
  • 多样性测量(分析相似性分布)
  • 分类(其中文本字符串由其最相似的标签分类)

本文将考虑使用RAW OpenAi Embeddings。

什么是嵌入?

嵌入最终是描述给定模型的数字列表。在Openai的模型中,它始终是1,536个元素长的数字。此外,对于OpenAI,数字全部介于-1和1之间,如果将阵列视为1,536维空间中的矢量,则其幅度为1(即,将其标准化为长度为1-在线性代数术语中)。

在概念层面上,您可以将数组中的每个数字视为捕获文本的某些方面。两个数组被认为与数组中每个元素中具有相似值的程度相似。您不必知道任何个人价值观与嵌入的美和奥秘相对应的您只需要比较产生的阵列即可。我们将研究如何计算以下相似性。

取决于您使用的模型,您可以得到截然不同的数组,因此比较来自同一模型的数组才有意义。这也意味着不同的模型可能会对类似内容不同意。您可以想象,一个模型对弦乐押韵是否更敏感。您可以为您的特定用例微调一个模型,但是我建议从一通用的用途开始,原因是为什么通常会选择聊天GPT,而不是微调的文本生成模型。

这超出了这篇文章的范围,但还值得一提的是,我们只是在这里查看文本嵌入,但是也有一些模型可以将图像和音频变成嵌入,具有相似的含义。

我如何嵌入?

有几个模型可以将文本变成嵌入。要使用API​​背后的托管模型,我推荐OpenAI,这就是我们在本文中使用的内容。对于开源选项,您可以查看all-MiniLM-L6-v2all-mpnet-base-v2

假设您在environment variables中有一个API key,则可以通过简单的fetch嵌入。

export async function fetchEmbedding(text: string) {
  const result = await fetch("https://api.openai.com/v1/embeddings", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: "Bearer " + process.env.OPENAI_API_KEY,
    },
    body: JSON.stringify({
      model: "text-embedding-ada-002",
      input: [text],
    }),
  });
  const jsonresults = await result.json();
  return jsonresults.data[0].embedding;
}

为了提高效率,我建议在一批中立即获取多个嵌入。

export async function fetchEmbeddingBatch(text: string[]) {
  const result = await fetch("https://api.openai.com/v1/embeddings", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: "Bearer " + process.env.OPENAI_API_KEY,
    },

    body: JSON.stringify({
      model: "text-embedding-ada-002",
      input: [text],
    }),
  });
  const jsonresults = await result.json();
  const allembeddings = jsonresults.data as {
    embedding: number[];
    index: number;
  }[];
  allembeddings.sort((a, b) => b.index - a.index);
  return allembeddings.map(({ embedding }) => embedding);
}

我应该在哪里存放它?

一旦您拥有嵌入矢量,您可能想用它做两件事之一:

  1. 使用它搜索类似的字符串(即搜索类似的嵌入)。
  2. 将其存储将来要对其进行搜索。

如果您打算存储数千个向量,我建议使用像Pinecone这样的专用矢量数据库。这使您可以快速找到给定输入的附近向量,而无需每次都与每个向量进行比较。请继续关注以后的帖子,以了解与凸面旁边的Pinecone。

如果您没有许多向量,但是,您只能将它们直接存储在普通数据库中。就我而言,如果我想建议Stack帖子类似于给定的帖子或搜索,我只需要与少于100个矢量进行比较,因此我只需使用Convex数据库就可以获取它们并在毫秒内进行比较。

我应该如何存储嵌入?

如果您将嵌入在松果中,请继续关注其中的专用帖子,但是简短的答案是您配置Pinecone -index并将元数据与矢量一起存储一些元数据,因此从Pinecone获取结果,您可以轻松地将它们与应用程序数据重新相关。例如,您可以存储要与向量相关联的行的文档ID。

如果您将嵌入在凸的嵌入,我建议将其存储为二进制斑点,而不是数字的JavaScript数组。凸建议到not store arrays longer than 1024 elements。我们可以通过在JavaScript中轻松地将其转换为float32array来实现这一目标:

const numberList = await fetchEmbedding(inputText); // number[]
const floatArray = Float32Array.from(numberList); // Float32Array
const floatBytes = floatArray.buffer; // ArrayBuffer
// Save floatBytes to the DB
// Later, after you read the bytes back out:
const arrayAgain = new Float32Array(bytesFromDB); // Float32Array

您可以表示嵌入为表in your schema中的字段:

vectors: defineTable({
  float32Buffer: v.bytes(),
  textId: v.id("texts"),
}),

在这种情况下,我将矢量与文档表格一起存储在文本表中。

如何比较JavaScript中的嵌入

您希望在不使用矢量数据库的情况下比较OpenAI的两个嵌入,这很简单。有a few ways of comparing vectors,包括欧几里得距离,点产品和余弦相似性。值得庆幸的是,由于Openai将所有向量均标准化为长度1,因此它们都会给出相同的排名!使用简单的dot product,您可以获得从-1(完全无关)到1(非常相似)的相似性得分。有优化的库可以做,但是出于我的目的,这个简单的功能就足够了:

/**
 * Compares two vectors by doing a dot product.
 *
 * Assuming both vectors are normalized to length 1, it will be in [-1, 1].
 * @returns [-1, 1] based on similarity. (1 is the same, -1 is the opposite)
 */
export function compare(vectorA: Float32Array, vectorB: Float32Array) {
  return vectorA.reduce((sum, val, idx) => sum + val * vectorB[idx], 0);
}

例子

在此示例中,让我们制作一个函数(在这种情况下在这种情况下是凸的query),该函数根据某些查询向量返回所有向量及其相似性得分,假设如上所述,如上所述,和我们刚刚定义的compare函数。

export const compareTo = query(async ({ db }, { vectorId }) => {
  const target = await db.get(vectorId);
  const targetArray = new Float32Array(target.float32Buffer);
  const vectors = await db.query("vectors").collect();
  const scores = await Promise.all(
    vectors
      .filter((vector) => !vector._id.equals(vectorId))
      .map(async (vector) => {
        const score = compare(
          targetArray,
          new Float32Array(vector.float32Buffer)
        );
        return { score, textId: vector.textId, vectorId: vector._id };
      })
  );
  return scores.sort((a, b) => b.score - a.score);
});

概括

在这篇文章中,我们查看了嵌入,为什么它们有用,以及如何将它们存储和使用它们。我将发布更多有关使用嵌入的帖子,包括将长输入输入到多个嵌入式中,并尽快将Pinecone与凸面一起使用。在our Discord中让我们知道您的想法!


  1. OpenAI’s guide

    复制