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

2024-01-22 16:44

本文主要是介绍基于 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

这篇关于基于 GPT 和 Qdrant DB 向量数据库, 我构建了一个电影推荐系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/633587

相关文章

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

MySQL常用字符串函数示例和场景介绍

《MySQL常用字符串函数示例和场景介绍》MySQL提供了丰富的字符串函数帮助我们高效地对字符串进行处理、转换和分析,本文我将全面且深入地介绍MySQL常用的字符串函数,并结合具体示例和场景,帮你熟练... 目录一、字符串函数概述1.1 字符串函数的作用1.2 字符串函数分类二、字符串长度与统计函数2.1

Oracle数据库定时备份脚本方式(Linux)

《Oracle数据库定时备份脚本方式(Linux)》文章介绍Oracle数据库自动备份方案,包含主机备份传输与备机解压导入流程,强调需提前全量删除原库数据避免报错,并需配置无密传输、定时任务及验证脚本... 目录说明主机脚本备机上自动导库脚本整个自动备份oracle数据库的过程(建议全程用root用户)总结

使用Python构建智能BAT文件生成器的完美解决方案

《使用Python构建智能BAT文件生成器的完美解决方案》这篇文章主要为大家详细介绍了如何使用wxPython构建一个智能的BAT文件生成器,它不仅能够为Python脚本生成启动脚本,还提供了完整的文... 目录引言运行效果图项目背景与需求分析核心需求技术选型核心功能实现1. 数据库设计2. 界面布局设计3

SQL Server跟踪自动统计信息更新实战指南

《SQLServer跟踪自动统计信息更新实战指南》本文详解SQLServer自动统计信息更新的跟踪方法,推荐使用扩展事件实时捕获更新操作及详细信息,同时结合系统视图快速检查统计信息状态,重点强调修... 目录SQL Server 如何跟踪自动统计信息更新:深入解析与实战指南 核心跟踪方法1️⃣ 利用系统目录

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

深入浅出SpringBoot WebSocket构建实时应用全面指南

《深入浅出SpringBootWebSocket构建实时应用全面指南》WebSocket是一种在单个TCP连接上进行全双工通信的协议,这篇文章主要为大家详细介绍了SpringBoot如何集成WebS... 目录前言为什么需要 WebSocketWebSocket 是什么Spring Boot 如何简化 We

Mysql中设计数据表的过程解析

《Mysql中设计数据表的过程解析》数据库约束通过NOTNULL、UNIQUE、DEFAULT、主键和外键等规则保障数据完整性,自动校验数据,减少人工错误,提升数据一致性和业务逻辑严谨性,本文介绍My... 目录1.引言2.NOT NULL——制定某列不可以存储NULL值2.UNIQUE——保证某一列的每一

解密SQL查询语句执行的过程

《解密SQL查询语句执行的过程》文章讲解了SQL语句的执行流程,涵盖解析、优化、执行三个核心阶段,并介绍执行计划查看方法EXPLAIN,同时提出性能优化技巧如合理使用索引、避免SELECT*、JOIN... 目录1. SQL语句的基本结构2. SQL语句的执行过程3. SQL语句的执行计划4. 常见的性能优

SQL Server 中的 WITH (NOLOCK) 示例详解

《SQLServer中的WITH(NOLOCK)示例详解》SQLServer中的WITH(NOLOCK)是一种表提示,等同于READUNCOMMITTED隔离级别,允许查询在不获取共享锁的情... 目录SQL Server 中的 WITH (NOLOCK) 详解一、WITH (NOLOCK) 的本质二、工作