1. 首页
  2. 后端

AI 驱动的推荐系统:从 Embeddings 到相似度计算

  AI 驱动的推荐系统:从 Embeddings 到相似度计算

==============================

引言

==

在当今信息爆炸的时代,如何快速准确地获取所需信息变得尤为重要。推荐系统作为解决信息过载问题的利器,在各大平台上得到了广泛应用。今天呆同学将分享如何使用 OpenAI 的 Embeddings 接口构建一个简单而高效的推荐系统。通过详细的代码解析和步骤说明,带领大家从理论到实践,深入理解推荐系统的实现过程。

Embeddings

Embeddings 是一种将高维数据(如文本、图像等)映射到低维空间的技术。它将原始数据转换为一组实数向量,使其能够进行数学计算和比较。通过 Embeddings,复杂的非结构化数据可以在向量空间中进行处理,方便实现相似度计算、聚类和分类等任务。

OpenAI 提供了一系列强大的 Embeddings 模型,这些模型能够将文本转换为高维向量表示,使其可以进行语义比较和分析。在接下来的实例中,我们使用的是 OpenAI 提供的 text-embedding-ada-002 模型。

设计思路

  • 数据准备:收集并整理需要推荐的内容数据,确保数据质量和格式一致性。
  • 向量化表示:利用 OpenAI 的 Embeddings 接口,将文本数据转换为向量表示,使其能够进行数学计算和比较。
  • 相似度计算:通过计算向量之间的余弦相似度,评估不同内容之间的相似程度。
  • 推荐系统:根据相似度计算结果,筛选出与用户输入最相关的内容,并进行推荐。

实现步骤

准备工作

  1. 首先创建一个后端项目,初始化并安装相关依赖。
//初始化后端 
npm init -y 
//安装OpenAI依赖 
npm i openai 
//安装dotenv依赖 
npm i dotenv
  1. 创建好一个posts.json文件用来存放我们准备好的数据,下面展示一部分数据。

image.png

  1. 创建.env文件,存好我们的API密钥。

测试接口

  1. 导入所需包
import OpenAI from "openai";
import dotenv from "dotenv";
dotenv.config({ path: ".env" });
  1. 创建 OpenAI 实例对象
const client = new OpenAI({
    apiKey: process.env.OPENAI_KEY,
    baseURL: "https://api.302.ai/v1",
})
  1. 调用embeddings接口,并输出响应结果
const response = await client.embeddings.create({
    model: 'text-embedding-ada-002',
    input: '如何创建vue组件'
})
console.log(response.data[0].embedding)
  1. 向量化结果,接口调用成功

image.png

正式使用接口

  1. 首先我们将上述创建的 OpenAI 实例对象封装到一个新的app.service.mjs文件中并且抛出,方便后续直接引入。

image.png

  1. 创建create-embedding.mjs文件,在里面我们开始通过调用embeddings接口,将我们的数据将进行模块化的向量化输出,并存入一个新的文件中保存起来,以便后续的相似度计算。
// 先把所有的内容计算向量
// 更年轻化的fs模块
import fs from 'fs/promises';
// openai 实例 openai 封装
import {client} from './app.service.mjs';
// 源数据,麻烦
const inputFilePath = './data/posts.json';
// 整整 多一个 1536维的向量数字
const outputFilePath = './data/posts_with_embeddings.json';
// promisify
const data = await fs.readFile(inputFilePath, 'utf8');
const posts = JSON.parse(data);
// 新数组
const postsWidthEmbeddings = [];

for (const {title, category} of posts) {
    const response = await client.embeddings.create({
        model: 'text-embedding-ada-002',
        input: `标题:${title}  分类:${category}`,
    });
    postsWidthEmbeddings.push({
        title,
        category,
        embedding: response.data[0].embedding,
    });
}

await fs.writeFile(outputFilePath, JSON.stringify(postsWidthEmbeddings));
  • fs/promises:Node.js 的文件系统模块,以 Promise 的方式使用文件操作方法(如读取和写入文件)。
  • client:从 app.service.mjs 导入的 OpenAI 客户端实例,用于调用 OpenAI 的 API。
  • inputFilePath:这是我们准备好的数据文件的路径,包含需要进行向量计算的内容。
  • outputFilePath:输出文件的路径,将计算得到的向量保存到该文件中。
  • fs.readFile:读取数据文件,并以 UTF-8 编码格式解析成字符串。
  • JSON.parse:将读取到的 JSON 字符串解析成 JavaScript 对象。
  • postsWidthEmbeddings:用于存储包含向量的内容数据的数组。
for (const {title, category} of posts) {
    const response = await client.embeddings.create({
        model: 'text-embedding-ada-002',
        input: `标题:${title}  分类:${category}`,
    });
    postsWidthEmbeddings.push({
        title,
        category,
        embedding: response.data[0].embedding,
    });
}
await fs.writeFile(outputFilePath, JSON.stringify(postsWidthEmbeddings));
  • for (const {title, category} of posts):遍历每个帖子,解构出 titlecategory
  • client.embeddings.create:调用 OpenAI 的 Embeddings 接口,将标题和分类作为输入,生成对应的向量。
  • postsWidthEmbeddings.push:将每个帖子的标题、分类和生成的向量存储到数组中。
  • fs.writeFile:最后将包含向量的内容数据数组转换成 JSON 字符串,并写入到输出文件中。

一起来看看新存入的数据文件:

image.png

可以看到新的 json 文件中每个数据都多了它自己的空间向量,通过这些步骤,代码实现了将文本内容转换为向量表示并保存的功能,为后续的相似度计算和推荐系统提供了基础数据。

计算余弦相似度

在我们完成了所有内容的向量化之后,接下来需要进行的是余弦相似度计算,以实现内容的相似性匹配和推荐。

  1. 首先还是同样导入文件系统模块和我们地OpenAI实例,同时读取我们之前生成好被用来存储数据的新json文件,并将它解析为JSON对象。
// nlp 相似性搜索
import fs from 'fs/promises';
import { client } from './app.service.mjs';

const inputFilePath = './data/posts_with_embeddings.json';
// select * 内存中
const data = await fs.readFile(inputFilePath, 'utf8');
const posts = JSON.parse(data);
  1. 接下便是计算余弦相似度,分别计算出向量点积、向量长度,根据余弦相似度计算公式计算余弦相似度。
    余弦相似度的公式如下:

cosinesimilarity(v1,v2)=v1⋅v2∣∣v1∣∣×∣∣v2∣∣cosine_similarity(v1, v2) = \frac{v1 \cdot v2}{||v1|| \times ||v2||}cosines​imilarity(v1,v2)=∣∣v1∣∣×∣∣v2∣∣v1⋅v2​
其中,v1⋅v2v1 \cdot v2v1⋅v2 表示向量的点积,∣∣v1∣∣||v1||∣∣v1∣∣ 和 ∣∣v2∣∣||v2||∣∣v2∣∣ 分别表示向量的长度。

// 计算向量的余弦相似度
const cosineSimilarity = (v1, v2) => {
    // 计算向量的点积
    const dotProduct = v1.reduce((acc, curr, i) => acc + curr * v2[i], 0);

    // 计算向量的长度
    const lengthV1 = Math.sqrt(v1.reduce((acc, curr) => acc + curr * curr, 0));
    const lengthV2 = Math.sqrt(v2.reduce((acc, curr) => acc + curr * curr, 0));

    // 计算余弦相似度
    const similarity = dotProduct / (lengthV1 * lengthV2);

    return similarity;
};
  • similarity:计算余弦相似度,值在 -1 到 1 之间,1 表示完全相似,-1 表示完全不相似

  • 然后是记录用户输入的搜索文本,调用 OpenAI 的 Embeddings 接口,将搜索文本转换为向量表示。

// vue | 组件 | 开发  LLM 语义搜索,而不是简单的文字配
const searchText = 'vue组件开发';
const response = await client.embeddings.create({
    model: 'text-embedding-ada-002',
    input: searchText,
})
// 要推荐的原文emdedding
const { embedding } = response.data[0];
  • embedding:提取搜索文本的向量表示。

  • 最后便是遍历每一份数据,计算其与搜索文本的相似度,也就是要计算要搜索的文本与每一条数据的余弦相似度,再将收集到的数据按相似度从高到低进行一个排序,这里我们支取相似度最高的前三个结果,我们给个编号,将它们的标题和分类都输出出来。

// posts每一项embedding 进行cosin 计算
const results = posts.map(item => ({
    ...item,
    similarity: cosineSimilarity(embedding, item.embedding),
}))
    .sort((a, b) => a.similarity - b.similarity)
    .reverse()
    .slice(0, 3)
    .map((item, index) => `${index + 1}. ${item.title}, ${item.category}`)
    .join('\n');

console.log(results);
  • sortreverse:按相似度从高到低排序。
  • slice:取出相似度最高的前三个结果。
  • mapjoin:格式化输出结果,显示前三个相似度最高的帖子标题和分类。

结果展示

测试1: const searchText = 'Vue组件开发';

image.png

测试2:const searchText = 'React组件开发';

image.png

通过上述步骤,代码实现了一个简单而高效的相似性搜索功能,能够根据用户输入的搜索文本,推荐与之最相关的内容。这种基于语义的搜索比传统的关键词匹配更加智能和准确,有助于提高用户体验和内容推荐的质量 。

结语

通过今天的学习,我们不仅掌握了 Embeddings 接口的使用方法,还深入理解了如何利用向量计算和余弦相似度实现内容推荐。这为我们在实际项目中应用 AI 技术提供了宝贵的经验和启示。希望在未来的学习和实践中,呆同学能够继续探索更多的 AI 应用场景,为大家带来更智能和便捷的体验。

点赞.jfif

原文链接: https://juejin.cn/post/7390682536743698466

文章收集整理于网络,请勿商用,仅供个人学习使用,如有侵权,请联系作者删除,如若转载,请注明出处:http://www.cxyroad.com/17154.html

QR code