AI 驱动的推荐系统:从 Embeddings 到相似度计算
==============================
引言
==
在当今信息爆炸的时代,如何快速准确地获取所需信息变得尤为重要。推荐系统作为解决信息过载问题的利器,在各大平台上得到了广泛应用。今天呆同学将分享如何使用 OpenAI 的 Embeddings 接口构建一个简单而高效的推荐系统。通过详细的代码解析和步骤说明,带领大家从理论到实践,深入理解推荐系统的实现过程。
Embeddings
Embeddings 是一种将高维数据(如文本、图像等)映射到低维空间的技术。它将原始数据转换为一组实数向量,使其能够进行数学计算和比较。通过 Embeddings,复杂的非结构化数据可以在向量空间中进行处理,方便实现相似度计算、聚类和分类等任务。
OpenAI 提供了一系列强大的 Embeddings 模型,这些模型能够将文本转换为高维向量表示,使其可以进行语义比较和分析。在接下来的实例中,我们使用的是 OpenAI 提供的 text-embedding-ada-002
模型。
设计思路
- 数据准备:收集并整理需要推荐的内容数据,确保数据质量和格式一致性。
- 向量化表示:利用 OpenAI 的 Embeddings 接口,将文本数据转换为向量表示,使其能够进行数学计算和比较。
- 相似度计算:通过计算向量之间的余弦相似度,评估不同内容之间的相似程度。
- 推荐系统:根据相似度计算结果,筛选出与用户输入最相关的内容,并进行推荐。
实现步骤
准备工作
- 首先创建一个后端项目,初始化并安装相关依赖。
//初始化后端
npm init -y
//安装OpenAI依赖
npm i openai
//安装dotenv依赖
npm i dotenv
- 创建好一个
posts.json
文件用来存放我们准备好的数据,下面展示一部分数据。
- 创建
.env
文件,存好我们的API密钥。
测试接口
- 导入所需包
import OpenAI from "openai";
import dotenv from "dotenv";
dotenv.config({ path: ".env" });
- 创建 OpenAI 实例对象
const client = new OpenAI({
apiKey: process.env.OPENAI_KEY,
baseURL: "https://api.302.ai/v1",
})
- 调用embeddings接口,并输出响应结果
const response = await client.embeddings.create({
model: 'text-embedding-ada-002',
input: '如何创建vue组件'
})
console.log(response.data[0].embedding)
- 向量化结果,接口调用成功
正式使用接口
- 首先我们将上述创建的 OpenAI 实例对象封装到一个新的
app.service.mjs
文件中并且抛出,方便后续直接引入。
- 创建
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)
:遍历每个帖子,解构出title
和category
。client.embeddings.create
:调用 OpenAI 的 Embeddings 接口,将标题和分类作为输入,生成对应的向量。postsWidthEmbeddings.push
:将每个帖子的标题、分类和生成的向量存储到数组中。fs.writeFile
:最后将包含向量的内容数据数组转换成 JSON 字符串,并写入到输出文件中。
一起来看看新存入的数据文件:
可以看到新的 json 文件中每个数据都多了它自己的空间向量,通过这些步骤,代码实现了将文本内容转换为向量表示并保存的功能,为后续的相似度计算和推荐系统提供了基础数据。
计算余弦相似度
在我们完成了所有内容的向量化之后,接下来需要进行的是余弦相似度计算,以实现内容的相似性匹配和推荐。
- 首先还是同样导入文件系统模块和我们地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);
- 接下便是计算余弦相似度,分别计算出向量点积、向量长度,根据余弦相似度计算公式计算余弦相似度。
余弦相似度的公式如下:
cosinesimilarity(v1,v2)=v1⋅v2∣∣v1∣∣×∣∣v2∣∣cosine_similarity(v1, v2) = \frac{v1 \cdot v2}{||v1|| \times ||v2||}cosinesimilarity(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);
sort
和reverse
:按相似度从高到低排序。slice
:取出相似度最高的前三个结果。map
和join
:格式化输出结果,显示前三个相似度最高的帖子标题和分类。
结果展示
测试1: const searchText = 'Vue组件开发';
测试2:const searchText = 'React组件开发';
通过上述步骤,代码实现了一个简单而高效的相似性搜索功能,能够根据用户输入的搜索文本,推荐与之最相关的内容。这种基于语义的搜索比传统的关键词匹配更加智能和准确,有助于提高用户体验和内容推荐的质量 。
结语
通过今天的学习,我们不仅掌握了 Embeddings 接口的使用方法,还深入理解了如何利用向量计算和余弦相似度实现内容推荐。这为我们在实际项目中应用 AI 技术提供了宝贵的经验和启示。希望在未来的学习和实践中,呆同学能够继续探索更多的 AI 应用场景,为大家带来更智能和便捷的体验。
原文链接: https://juejin.cn/post/7390682536743698466
文章收集整理于网络,请勿商用,仅供个人学习使用,如有侵权,请联系作者删除,如若转载,请注明出处:http://www.cxyroad.com/17154.html