如何利用OpenAI Embedding实现网页“相关推荐”功能?
在当今互联网的时代里,个性化已经成为吸引用户关注和提升用户体验的重要策略之一。当我们浏��网站时,经常可以看到一个名为“相关推荐”的版块,它能精准地展示出符合我们兴趣的内容。但是,您是否好奇过这些推荐是如何生成的,它们又是如何如此准确地捕捉到我们的偏好 在本文中我将为大家介绍OpanAI中的embedding,向量计算模型实现相似度/推荐等功能。
正文
首先我们先阐述一下向量计算模型实现相似度/推荐等功能的理论依据。我们使用OpenAI的embedding接口,把我们输入的关键字变成数学向量,然后在一个空间中是可以存在多个向量的,向量与向量之间存在角度,这里我们使用cosin(cos)表示向量与向量之间的关系,众所周知cos越大,其角度越小,当两个向量的cos值越大,我们就可以理解为这两条数据的相似度越高,也就是文章内容越接近,然后我们把相似度高的数据拿出来,就可以实现推荐功能了。
接下来我们就正式开始实现这个功能了。第一步我们先在Vscode中初始化一个后端项目,使用npm执行,然后完成一些配置项
//初始化后端
npm init -y
//安装OpenAI依赖
npm i openai
//安装dotenv依赖
npm i dotenv
创建文件app.service.mjs
在这个文件中我们把模块引入项目中
// 模块化输出client 给各项ai任务调用
import OpenAI from 'openai'
import dotenv from 'dotenv'
dotenv.config({path:'.env'})
export const client = new OpenAI({
apiKey: process.env.OPENAI_KEY,
baseURL: 'https://api.302.ai/v1'
})
最后要记得抛出client
.
接下来我们创建一个embedding
,创建文件create-embeddings.mjs
,这里我们的读取的数据已经写好在一个json文件中,所以这里我们使用fs
文件系统读取数据
// 更年轻的fs模块
import fs from 'fs/promises';
获取app.service.mjs
抛出的client
import {client} from './app.service.mjs'
在我们文件create-embedding.mjs
中的主要任务是将源数据posts.json
里面的所有内容计算向量,所以这里我们准备了两个变量,一个存放源数据inputFilePath
,一个存放向量计算后的数据outputFilePath
.现在我们读取源数据中的数据并JSON化数据,使用fs
文件系统中的readFile
const data = await fs.readFile(inputFilePath, 'utf8');
const posts = JSON.parse(data)
这里我们加了一个await
是因为fs/promises
返回的是一个Promise
对象,这里我们需要使用到await
.
定义一个新数组postsWithEmbedding
存放向量计算后的数据,现在我们遍历JSON化后的源数据posts
并且解构数据title
,category
.然后使用client.embedding.create()
创建一个embedding
接口,这里我们需要给一个模型model
和输入input
,当接口embedding
接口数据处理完成之后,我们把解构出来的title
,category
,以及向量计算后的数据存入postsWithEmbedding
中。最后把数据写入outputFilePath
中,我们这个创建embedding
接口这个文件就算是写好了。
// 先把所有的内容计算向量
// 更年轻的fs模块
import fs from 'fs/promises';
//将openai的实例封装
import {client} from './app.service.mjs'
//源数据,麻烦
const inputFilePath = './data/posts.json';
// 整整数据 让每一个数据多一个1536维的向量数字
const outputFilePath = './data/posts_with_embedding.json';
// promisify 重点
const data = await fs.readFile(inputFilePath, 'utf8');
const posts = JSON.parse(data)
// 新数组
const postsWithEmbedding = [];
for(const {title,category} of posts){
const response = await client.embeddings.create({
model: 'text-embedding-ada-002',
// 当搜索vue是,也可以搜索到vue相关的文章 搜索vue+分类
input: `标题 ${title} 分类 ${category}`
})
postsWithEmbedding.push({
title,
category,
embedding: response.data[0].embedding // 取得embedding的第一个元素
})
}
await fs.writeFile(outputFilePath, JSON.stringify(postsWithEmbedding))
// console.log(data);
最后一部分我们实现数据查询就好了,创建文件semantic-search.mjs
获取所有的向量计算后的数据
const posts = JSON.parse(await fs.readFile(inputFilePath));
计算向量的余弦相似度函数(这个函数我们直接在网上找就好了)
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;
};
然后我再定义一个变量。用来存放我们想要查询的内容,并进行向量计算。最后解构出向量值
const searchText = 'vue组件开发'
//先向量化
const response = await client.embeddings.create({
model:'text-embedding-ada-002',
input: searchText
})
//要推荐的原文embedding
const { embedding } = response.data[0]
最后一步就是遍历数据,对数据中进行一个排序,取得前三项
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);
完整代码
// nlp 相似性搜索
import fs from 'fs/promises'
import { client} from './app.service.mjs'
const inputFilePath = './data/posts_with_embedding.json';
// select * 让数据在内存之中
const posts = JSON.parse(await fs.readFile(inputFilePath));
// 计算向量的余弦相似度 cosine
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;
};
// vue | 组件 | 开发 LLM 基于语义搜索,而不是简单的文字匹配
const searchText = 'vue组件开发'
//先向量化
const response = await client.embeddings.create({
model:'text-embedding-ada-002',
input: searchText
})
//要推荐的原文embedding
const { embedding } = response.data[0]
// 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);
感谢大家阅读,若有不足,恳请各位指出!!!
转载自:https://juejin.cn/post/7390319492120985636