基于 GPT 和 Qdrant DB 向量数据库, 我构建了一个电影推荐系统

电影推荐系统自从机器学习时代开始以来就不断发展,逐步演进到当前的 transformers 和向量数据库的时代。

在本文中,我们将探讨如何在向量数据库中高效存储数千个视频文件,以构建最佳的推荐引擎。

在众多可用的向量数据库中,我们将关注 Qdrant DB,因为它具有独特的特性——HNSW ANN 搜索算法,正如我在之前的文章中讨论的那样。

传统推荐系统

随着支持向量机(SVM)等机器学习算法的发展,引入 transformers 到人工智能领域,传统电影推荐系统得以形成。电影推荐系统利用机器学习算法预测用户对电影的偏好和评分。这些系统主要分为三种类型:

  • 协同过滤:通过收集许多具有相似观点的用户的偏好来预测用户的兴趣。
  • 基于内容的过滤:根据物品的属性和描述推荐物品,重点关注用户的过去互动。
  • 混合系统:结合协同和基于内容的方法,以提高效果,并解决冷启动和数据稀疏等问题。


各种机器学习技术,如最近邻算法用于基于实例的学习,矩阵分解用于协同过滤,以及使用神经网络的深度学习,有助于提高推荐系统的质量。这些系统面临冷启动问题和数据稀疏等挑战。伦理考虑、可扩展性以及整合背景信息进一步增加了设计有效和负责任的推荐系统的复杂性。

向量数据库的引入

向量数据库已经成为进行高效相似性搜索的有益工具。在电影推荐系统中,使用相似性搜索特别有用,其目标是找到与用户已经观看并喜欢的电影相似的电影。

通过将电影表示为高维空间中的向量,我们可以利用距离度量(如余弦相似性或欧氏距离)来识别彼此“接近”的电影,表示它们相似。

随着电影和用户数量的增长,数据库的规模也在增大。向量数据库旨在处理大规模数据,并保持高查询性能。这种可扩展性对于电影推荐系统至关重要,特别是对于那些使用庞大的电影库和用户基础的大型流媒体平台。

在这个背景下,我们将使用 Qdrant 数据库,因为它利用快速的近似最近邻搜索,具体来说是 HNSW 算法与余弦相似性搜索。

通俗易懂讲解大模型系列

  • 做大模型也有1年多了,聊聊这段时间的感悟!

  • 用通俗易懂的方式讲解:大模型算法工程师最全面试题汇总

  • 用通俗易懂的方式讲解:我的大模型岗位面试总结:共24家,9个offer

  • 用通俗易懂的方式讲解:大模型 RAG 在 LangChain 中的应用实战

  • 用通俗易懂的方式讲解:一文讲清大模型 RAG 技术全流程

  • 用通俗易懂的方式讲解:如何提升大模型 Agent 的能力?

  • 用通俗易懂的方式讲解:ChatGPT 开放的多模态的DALL-E 3功能,好玩到停不下来!

  • 用通俗易懂的方式讲解:基于扩散模型(Diffusion),文生图 AnyText 的效果太棒了

  • 用通俗易懂的方式讲解:在 CPU 服务器上部署 ChatGLM3-6B 模型

  • 用通俗易懂的方式讲解:使用 LangChain 和大模型生成海报文案

  • 用通俗易懂的方式讲解:ChatGLM3-6B 部署指南

  • 用通俗易懂的方式讲解:使用 LangChain 封装自定义的 LLM,太棒了

  • 用通俗易懂的方式讲解:基于 Langchain 和 ChatChat 部署本地知识库问答系统

  • 用通俗易懂的方式讲解:在 Ubuntu 22 上安装 CUDA、Nvidia 显卡驱动、PyTorch等大模型基础环境

  • 用通俗易懂的方式讲解:Llama2 部署讲解及试用方式

  • 用通俗易懂的方式讲解:基于 LangChain 和 ChatGLM2 打造自有知识库问答系统

  • 用通俗易懂的方式讲解:一份保姆级的 Stable Diffusion 部署教程,开启你的炼丹之路

  • 用通俗易懂的方式讲解:对 embedding 模型进行微调,我的大模型召回效果提升了太多了

  • 用通俗易懂的方式讲解:LlamaIndex 官方发布高清大图,纵览高级 RAG技术

  • 用通俗易懂的方式讲解:使用 LlamaIndex 和 Eleasticsearch 进行大模型 RAG 检索增强生成

  • 用通俗易懂的方式讲解:基于 Langchain 框架,利用 MongoDB 矢量搜索实现大模型 RAG 高级检索方法

  • 用通俗易懂的方式讲解:使用Llama-2、PgVector和LlamaIndex,构建大模型 RAG 全流程

技术交流&资料

技术要学会分享、交流,不建议闭门造车。一个人可以走的很快、一堆人可以走的更远。

成立了大模型技术交流群,本文完整代码、相关资料、技术交流&答疑,均可加我们的交流群获取,群友已超过2000人,添加时最好的备注方式为:来源+兴趣方向,方便找到志同道合的朋友。

方式①、微信搜索公众号:机器学习社区,后台回复:加群
方式②、添加微信号:mlc2060,备注:来自CSDN + 技术交流

在这里插入图片描述

推荐系统架构

在使用向量数据库的同时,让我们了解一下这里的推荐系统是如何工作的。电影的推荐基于模型在一个电影中观察到的情感。该架构分为两个部分:

候选生成

候选生成是推荐系统功能的最重要部分。对于数十万个视频,初始步骤涉及根据口音或语言对内容进行筛选。例如,对于一部西班牙电影,它将在推荐中仅显示西班牙电影。这个筛选过程称为启发式筛选。

第二步是根据视频的转录将其转换为文本嵌入。Hugging Face上有许多模型可以将文本信息转换为向量嵌入。然而,为了获得文本信息,我们首先需要提取视频的音频格式。使用像 Whisper 或 SpeechRecognition 这样的音频转文本模型,我们可以检索文本信息作为转录。

利用嵌入模型,我们将文本信息转换为向量嵌入。将这些向量存储在一个安全可靠的数据库中至关重要。此外,向量数据库简化了我们的相似性搜索。我们将保存嵌入到 Qdrant 数据库中。

在非常短的响应时间内,我们将基于Qdrant数据库的余弦相似性搜索获取相似的视频。这检索相似视频构成了候选生成的最后一步。

重新排序

重新排序主要是在推荐系统中进行的,以根据文本信息中表达的情感来排列电影。借助大型语言模型,我们将能够获得文本信息的意见分数。根据意见分数,电影将被重新排名进行推荐。

代码实现与 Qdrant

在了解了推荐系统的架构之后,现在是时候在代码中实现理论了。我们理解了理论,知道如何分析电影转录的情感,但关键问题是如何将mp4格式的视频文件转换为文本嵌入。

对于这个代码实现,我从YouTube上提取了30个电影预告片。我们需要安装将来使用的重要库。

!pip install -q torch 
!pip install -q openai moviepy
!pip install SpeechRecognition
!pip install -q transformers
!pip install -q datasets
!pip install -q qdrant_client

然后,我们将导入在代码实现中需要的所有包。

import os
import moviepy.editor as mp
import os
import glob
import speech_recognition as sr
import csv
import numpy as np
import pandas as pd
from qdrant_client import QdrantClient
from qdrant_client.http import models
from transformers import AutoModel, AutoTokenizer
import torch

现在,我们将创建一个目录,将在其中保存我们的音频转录。

# 指定您的路径
path = "/content/my_directory"# 创建目录
os.makedirs(path, exist_ok=True)

在创建目录之后,我们将使用以下代码将视频转换为文本信息:

# 包含视频文件的目录
source_videos_file_path = r"/content/drive/MyDrive/qdrant_videos"# 用于存储音频文件的目录
destination_audio_files_path = r"/content/my_directory/audios"# 存储转录的CSV文件
csv_file_path = r"/content/my_directory/transcripts.csv"# 如果目标目录不存在,则创建目录
os.makedirs(destination_audio_files_path, exist_ok=True)# 初始化识别器类(用于识别语音)
r = sr.Recognizer()# 以写模式打开CSV文件
with open(csv_file_path, 'w', newline='') as csvfile:# 创建CSV写入器writer = csv.writer(csvfile)# 写入标题行writer.writerow(["Video File", "Transcript"])# 逐帧处理视频for video_file in glob.glob(os.path.join(source_videos_file_path, '*.mp4')):# 将视频转换为音频video_clip = mp.VideoFileClip(video_file)audio_file_path = os.path.join(destination_audio_files_path, os.path.basename(video_file).replace("'", "").replace(" ", "_") + '.wav')video_clip.audio.write_audiofile(audio_file_path)# 将音频转录为文本with sr.AudioFile(audio_file_path) as source:# 读取音频文件audio_text = r.listen(source)# 将语音转换为文本try:transcript = r.recognize_google(audio_text)except sr.UnknownValueError:print("Google Speech Recognition could not understand audio")transcript = "Error: Could not understand audio"except sr.RequestError as e:print("Could not request results from Google Speech Recognition service; {0}".format(e))transcript = "Error: Could not request results from Google Speech Recognition service; {0}".format(e)# 将转录写入CSV文件writer.writerow([video_file, transcript])

然后,我们将在数据框格式中查看我们的转录。

data = pd.read_csv('/content/my_directory/transcripts.csv')
data.head()

有一些“SpeechRecognition”无法理解的转录,因此我们将从数据框中消除这一行。

data = data[~data['Transcript'].str.startswith('Error')]
data.head()

现在,我们将创建一个具有内存数据库的QdrantClient实例。

client = QdrantClient(":memory:")

我们将创建一个集合,其中将存储我们的向量嵌入,使用余弦相似性搜索测量距离。

my_collection = "text_collection"
client.recreate_collection(collection_name=my_collection,vectors_config=models.VectorParams(size=768, distance=models.Distance.COSINE)
)

我们将使用一个预训练模型来帮助我们从数据集中提取嵌入层。我们将使用transformers库和GPT-2模型来完成这个任务。

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
tokenizer = AutoTokenizer.from_pretrained('gpt2')
model = AutoModel.from_pretrained('gpt2')#.to(device) # 切换到GPU

我们需要提取电影名称并创建一个新列,以便我们知道哪些嵌入属于哪部电影。

def extract_movie_name(file_path):file_name = file_path.split("/")[-1]  # 获取路径的最后一部分movie_name = file_name.replace(".mp4", "").strip()return movie_name# 应用该函数以创建新列
data['Movie_Name'] = data['Video File'].apply(extract_movie_name)# 显示数据框
data[['Video File', 'Movie_Name', 'Transcript']]

现在,我们将创建一个帮助函数,通过它我们将为每个电影预告片转录获取嵌入。

def get_embeddings(row):tokenizer = AutoTokenizer.from_pretrained('gpt2')tokenizer.add_special_tokens({'pad_token': '[PAD]'})inputs = tokenizer(row['Transcript'], padding=True, truncation=True, max_length=128, return_tensors="pt")# 对以下操作禁用梯度计算。with torch.no_grad():outputs = model(**inputs).last_hidden_state.mean(dim=1).cpu().numpy()# 返回计算得到的嵌入。return outputs

然后,我们将嵌入函数应用于我们的数据集。之后,我们将保存嵌入,以便不必再次加载它们。

data['embeddings'] = data.apply(get_embeddings, axis=1)
np.save("vectors", np.array(data['embeddings']))

现在,我们将为每个电影转录创建一个包含元数据的负载。

payload = data[['Transcript', 'Movie_Name', 'embeddings']].to_dict(orient="records")

我们将创建一个用于标记化嵌入的辅助函数。然后,我们将循环遍历“Transcript”列中的每个转录,以创建文本嵌入。

# 设置向量嵌入的预期大小
expected_vector_size = 768# 定义用于标记化的均值池化函数
def mean_pooling(model_output, attention_mask):# 从模型输出中提取标记嵌入token_embeddings = model_output[0]# 将关注掩码扩展到与标记嵌入的大小匹配input_mask_expanded = (attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float())# 计算考虑到关注掩码的标记嵌入的总和sum_embeddings = torch.sum(token_embeddings * input_mask_expanded, 1)# 计算关注掩码的总和(夹紧以避免除零错误)sum_mask = torch.clamp(input_mask_expanded.sum(1), min=1e-9)# 返回均值池化的嵌入return sum_embeddings / sum_mask# 初始化列表以存储文本嵌入
text_embeddings = []# 遍历“data”变量的“Transcript”列中的每个转录
for transcript in data['Transcript']:# 对转录进行标记化,确保填充和截断,并返回PyTorch张量inputs = tokenizer(transcript, padding=True, truncation=True, max_length=128, return_tensors="pt")# 使用模型和标记化的输入执行推断with torch.no_grad():embs = model(**inputs)# 使用定义的函数计算均值池化的嵌入embedding = mean_pooling(embs, inputs["attention_mask"])# 确保嵌入的大小正确,通过修剪或填充embedding = embedding[:, :expected_vector_size]# 将得到的嵌入追加到列表中text_embeddings.append(embedding)

为了在Qdrant数据库集合中为每个转录分配明确的ID,我们将创建一个ID列表,然后更新组合的ID、向量和负载。

ids = list(range(len(data)))# 将PyTorch张量转换为浮点数列表
text_embeddings_list = [[float(num) for num in emb.numpy().flatten().tolist()[:expected_vector_size]] for emb in text_embeddings]client.upsert(collection_name=my_collection,points=models.Batch(ids=ids,vectors=text_embeddings_list,payloads=payload))

使用情感分析模型,您可以生成情感分数,其中情感极性在-1和1之间计算。分数为-1表示负面情感,0表示中性情感,1表示正面情感。

from textblob import TextBlobdef calculate_sentiment_score(text):# 创建TextBlob对象blob = TextBlob(text)# 获取情感极性(-1到1,其中-1为负面,0为中性,1为正面)sentiment_score = blob.sentiment.polarityreturn sentiment_score# 示例用法:
text_example = data['Transcript'].iloc[0]
sentiment_score_example = calculate_sentiment_score(text_example)
print(f"Sentiment Score: {sentiment_score_example}")

对于此示例,生成的情感分数将为0.75。现在,我们将将计算情感分数的辅助函数应用于“data”数据框。

data['Sentiment Score'] = data['Transcript'].apply(calculate_sentiment_score)
data.head()

您可以取每个电影转录的向量嵌入的平均值,并与情感分数结合以获得最终的意见分数。

data['avg_embeddings'] = data['embeddings'].apply(lambda x: np.mean(x, axis=0))
data['Opinion_Score'] = 0.7 * data['avg_embeddings'] + 0.3 * data['Sentiment']

在上述代码中,我为嵌入分配了更大的权重,因为它们捕获语义内容和电影转录之间的相似性。内在内容相似性在确定总体意见分数时更为关键。 “Sentiment”列定义了电影转录的情感语气。我为情感分数分配了较低的权重,因为情感作为因素在计算总体意见分数时不像语义内容那样关键。权重是任意的


然后创建一个电影推荐函数,其中您传递电影名称并获取所需数量的推荐电影。

def get_recommendations(movie_name):# 找到对应于给定电影名称的行query_row = data[data['Movie_Name'] == movie_name]if not query_row.empty:# 将'Opinion_Score'列转换为NumPy数组opinion_scores_array = np.array(data['Opinion_Score'].tolist())# 将'Opinion_Score'向量上插入到Qdrant集合opinion_scores_ids = list(range(len(data)))# 将'Opinion_Score'数组转换为列表的列表opinion_scores_list = opinion_scores_array.reshape(-1, 1).tolist()client.upsert(collection_name=my_collection,points=models.Batch(ids=opinion_scores_ids,vectors=opinion_scores_list))# 基于要查找相似电影的意见分数定义查询向量query_opinion_score = np.array([0.8] * 768)  # 根据需要进行调整# 执行相似性搜索search_results = client.search(collection_name=my_collection,query_vector=query_opinion_score.tolist(),limit=3)# 从搜索结果中提取电影推荐recommended_movie_ids = [result.id for result in search_results]recommended_movies = data.loc[data.index.isin(recommended_movie_ids)]# 显示推荐电影print("Recommended Movies:")print(recommended_movies[['Movie_Name', 'Opinion_Score']])else:print(f"Movie '{movie_name}' not found in the dataset.")# 示例用法:
get_recommendations("Star Wars_ The Last Jedi Trailer (Official)")

通过这样,我们能够使用Qdrant数据库创建一个电影推荐系统。

结论

矢量数据库有许多用途。在这些用例中,电影推荐系统在余弦相似性搜索和大型语言模型的帮助下取得了显著的改进。

使用 Qdrant 数据库创建电影推荐系统非常有趣、令人兴奋,也很容易。

借助 Qdrant 的最佳近似最近邻搜索和处理大型负载的能力,您可以创建自己的数据集,并尽情尝试基于矢量搜索的电影推荐系统。来自akriti.upadhyay

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/638890.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Tomcat的maxParameterCountmaxPostSize参数

Tomcat的maxParameterCount&maxPostSize参数 Tomcat的maxParameterCount&maxPostSize参数1.问题1.1问题现象1.2 参数总结1.3 问题总结 2 Tomcat官网的解释2.1 到https://tomcat.apache.org/找到文档入口2.2 找到文档的Reference2.3 查看配置文件的参数 3 文档看不明白&…

GIS项目实战08:JetBrains IntelliJ IDEA 2022 激活

为什么选择 IntelliJ IDEA 使用编码辅助功能更快地编写高质量代码,这些功能可在您键入时搜索可能的错误并提供改进建议,同时无缝地向您介绍编码、新语言功能等方面的社区最佳实践。 IntelliJ IDEA 了解您的代码,并利用这些知识通过在每种上…

Istio

1、Istio介绍 Istio 是由 Google、IBM 和 Lyft 开源的微服务管理、保护和监控框架。 官网:https://istio.io/latest/zh/ 官方文档:https://istio.io/docs/ 中文官方文档:https://istio.io/zh/docs Github地址:https://github.com…

vectorCast添加边界值分析测试用例

1.1创建项目成功后会自动生成封装好的函数,在这些封装好的函数上点击右键,添加边界值分析测试用例,如下图所示。 1.2生成的用例模版是不可以直接运行的,需要我们分别点击它们,让它们自动生成相应测试用例。如下图所示,分别为变化前和变化后。 1.3点击选中生成的测试用例,…

【动态规划】【广度优先搜索】【状态压缩】847 访问所有节点的最短路径

作者推荐 视频算法专题 本文涉及知识点 动态规划汇总 广度优先搜索 状态压缩 LeetCode847 访问所有节点的最短路径 存在一个由 n 个节点组成的无向连通图,图中的节点按从 0 到 n - 1 编号。 给你一个数组 graph 表示这个图。其中,graph[i] 是一个列…

如何用“VMware安装Ubuntu”win11系统?

一、 下载Ubuntu 企业开源和 Linux |Ubuntu的 二、 安装 三、 启动虚拟机 选中Try or Install Ubuntu Server,按回车

数据结构与算法:图

文章目录 图1) 概念有向 vs 无向度权路径环图的连通性 2) 图的表示3) Java 表示4) DFS5) BFS6) 拓扑排序7) 最短路径DijkstraBellman-FordFloyd-Warshall 8) 最小生成树PrimKruskal 图 1) 概念 图是由顶点(vertex)和边(edge)组成…

Mysql学习笔记系列(一)

本次mysql系列不会讲解具体的查询语句,而是放在mysql的一些性能优化和一些特性上,是学习笔记,供大家参考补充。 慢查询 MySQL的慢查询,全名是慢查询日志,是MySQL提供的一种日志记录,用来记录在MySQL中响应…

Meta 标签的力量:如何利用它们提高网站的可见性(上)

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

如何在ubuntu22.04安装ROS2

ubuntu22.04安装ROS2 教程 选择对应版本进行安装设置编码添加源安装ROS2设置环境变量 运行ROS2 选择对应版本 通过官方网站,查询Ubuntu与ros对应的版本,版本不一致也会出现安装不成功。 https://wiki.ros.org/ROS/Installation 每一个都可以进行点击&a…

解决电脑文件大小写不敏感问题

第一步:以管理员的身份运行 CMD 第二步: 输入下面命令 fsutil file setCaseSensitiveInfo 路径 enable 路径改成目标文件夹的路径,比如说我也下面 Less-24 这个文件夹里面的文件全部都大小写敏感 这样就 OK 了,注意路径最后要加…

GitFlow工作流

基于 Git 这一版本控制系统,通过定义不同的分支,探索合适的工作流程来完成开发、测试、修改等方面的需求。 例如:在开发阶段,创建 feature 分支,完成需求后,将此分支合并到 develop 分支上;在发…

深度学习常用代码总结(k-means, NMS)

目录 一、k-means 算法 二、NMS 一、k-means 算法 k-means 是一种无监督聚类算法,常用的聚类算法还有 DBSCAN。k-means 由于其原理简单,可解释强,实现方便,收敛速度快,在数据挖掘、数据分析、异常检测、模式识别、金…

PHP+vue+Mysql家庭理财管理系统演5x6nf

本文着重阐述了收支管理系统的分析、设计与实现,首先介绍开发系统和环境配置、数据库的设计,对系统的功能需求作出分析,根据需求对系统进行设计,明确各个部分的规范,来完成系统的设计。最后在对设计的系统进行一系列的…

k8s1.27.2版本二进制高可用集群部署

文章目录 环境软件版本服务器系统初始化设置关于etcd签名证书etcd集群部署负载均衡器组件安装设置关于k8s自签证书自签CAkube-apiserver 自签证书kube-controller-manager自签证书kube-scheduler自签证书kube-proxy 自签证书admin 自签证书 控制平面节点组件部署**部署kube-api…

Spring 事务原理一

从本篇博客开始,我们将梳理Spring事务相关的知识点。在开始前,想先给自己定一个目标:通过此次梳理要完全理解事务的基本概念及Spring实现事务的基本原理。为实现这个目标我想按以下几个步骤进行: 讲解事务中的一些基本概念使用Sp…

x-cmd pkg | jq - 命令行 JSON 处理器

目录 简介首次用户功能特点类似工具进一步探索 简介 jq 是轻量级的 JSON 处理工具,由 Stephen Dolan 于 2012 年使用 C 语言开发。 它的功能极为强大,语法简洁,可以灵活高效地完成从 JSON 数据中提取特定字段、过滤和排序数据、执行复杂的转…

Java NIO (三)NIO Channel类

1 概述 前面提到,Java NIO中一个socket连接使用一个Channel来表示。从更广泛的层面来说,一个通道可以表示一个底层的文件描述符,例如硬件设备、文件、网络连接等。然而,远不止如此,Java NIO的通道可以更加细化。例如&a…

【GitHub项目推荐--GitHub 上的考研神器】【转载】

如果有打算考研的读者,这些开源项目不能错过。把各个学校近几年考研初试真题分享给大家(包括 408),应该能帮上大家,文末有下载方式。 同时,我把盘点的开源相关的学习项目更新到 Awesome GiHub Repo&#xf…

【GitHub项目推荐--智能家居项目】【转载】

如果你具备硬件、软件知识,这个项目肯定符合你的胃口。 物美智能是一套软硬件结合的开源项目,该系统可助你快速搭建自己的智能家居系统。你可以学习到设备的集成和软硬件交互。 PC 端或者手机与服务端通信,单片机可以接受遥控设备和服务器的…