generative-ai-for-beginners 实战:使用文本嵌入(Embeddings)与余弦相似度构建语义视频搜索应用
【免费下载链接】generative-ai-for-beginners21 Lessons, Get Started Building with Generative AI项目地址: https://gitcode.com/GitHub_Trending/ge/generative-ai-for-beginners
本教程基于开源课程仓库 generative-ai-for-beginners 第 8 课「构建搜索应用」,完整讲解如何利用 OpenAI 文本嵌入模型将 YouTube 视频字幕转化为可检索的 Embedding 索引,并据此构建一个能理解自然语言问题、返回相关视频以及精确答案时间点的语义搜索应用。学完后你将掌握语义搜索与关键词搜索的区别、文本嵌入的原理与生成方式、嵌入索引的构建流水线,以及用 Pandas + 余弦相似度实现搜索应用的完整技能。
为什么需要构建搜索应用
大语言模型(LLM)的能力远不止聊天机器人和文本生成,借助Embeddings(文本嵌入)同样可以构建搜索应用。嵌入是数据的数值化表示,也叫向量,可用于对数据进行语义搜索。
本课的应用场景来自一个教育创业公司:这是一家非营利组织,为发展中国家的学生提供免费 AI 教育,拥有大量 YouTube 教学视频。公司希望构建一个搜索应用,让学生通过输入一个问题来搜索相关视频。例如学生输入 "What are Jupyter Notebooks?" 或 "What is Azure ML",应用不仅会返回与问题相关的视频列表,更能返回视频中答案所在位置的精确链接(带时间戳)。
课程的素材是微软 AI Show YouTube 频道的字幕嵌入索引,该索引覆盖截至 2023 年 10 月的所有视频字幕。下图展示了对 "can you use rstudio with azure ml?" 进行语义查询的结果,注意 YouTube URL 中带有时间戳,可直接跳到答案所在位置:
语义搜索与关键词搜索
语义搜索是一种利用查询词中词语的语义(含义)来返回相关结果的搜索技术。例如你想买车,搜索 "my dream car",语义搜索会理解你并不是在"做梦(dreaming)"关于一辆车,而是想购买你的"理想(ideal)"汽车——它理解你的意图并返回相关结果。而关键词搜索则字面地搜索关于汽车的"梦",往往会返回不相关的结果。
这正是向量检索的核心价值:即使查询文本与文档文本没有相同的字面词汇,只要语义相近,也能被检索命中。
什么是文本嵌入
文本嵌入(Text Embeddings)是自然语言处理(NLP)中的文本表示技术,是文本的语义数值表示,用机器易于理解的方式表示数据。构建文本嵌入的模型有很多,本课聚焦于OpenAI Embedding 模型。
以一个 AI Show 字幕片段为例:
Today we are going to learn about Azure Machine Learning.将该文本传入 OpenAI Embedding API,会返回一个由1536 个数字组成的嵌入(即向量),向量中的每个数字代表文本的不同语义方面。为简洁起见,这里展示前 10 个数字:
[-0.006655829958617687, 0.0026128944009542465, 0.008792596869170666, -0.02446001023054123, -0.008540431968867779, 0.022071078419685364, -0.010703742504119873, 0.003311325330287218, -0.011632772162556648, -0.02187200076878071, ...]可以这样理解:语义相近的文本,其向量在 1536 维空间中彼此靠近(夹角更小);语义无关的文本则相距较远。这正是后续一切检索计算的基础。
嵌入索引是如何创建的
本课的嵌入索引由一系列 Python 脚本构建,脚本与说明位于 08-building-search-applications/scripts/README.md。完成课程不需要运行这些脚本,因为索引文件已随仓库提供(即 embedding_index_3m.json)。但理解流水线对生产实践至关重要。
整个流水线由 prepare_transcripts_ai_show.sh 编排,依次执行以下 6 步:
- 下载字幕:transcript_download.py 通过 YouTube Data API(v3)分页拉取播放列表中的视频元数据,并使用
youtube_transcript_api下载每段字幕,保存为.json.vtt文件;脚本用 40 个线程并发处理队列,提高大批量下载效率。 - 提取演讲者:transcript_enrich_speaker.py 利用 OpenAI Functions(函数调用)从视频标题、描述和前 3 分钟字幕中提取演讲者姓名。源码中定义了一个名为
get_speaker_name的函数工具,要求模型返回逗号分隔的演讲者列表,并通过temperature=0.0保证提取的确定性,结果写入每个视频的元数据。 - 按 3 分钟分块:transcript_enrich_bucket.py 将字幕切分为3 分钟的文本段(segment)。每个分段会从下一个分段重叠约 20 个词,目的是确保分段的嵌入不被截断,并提供更好的检索上下文。源码还用
tiktoken统计 token 数(MAX_TOKENS=2048),为下一步摘要预留空间;每个分段记录start(形如00:00:00的时间字符串)和seconds(秒数)字段,这正是应用能生成时间戳链接的关键。 - 生成摘要:transcript_enrich_summaries.py 将每个分段文本传给 OpenAI Chat API,生成约 60 词的权威摘要(系统提示要求避免以 "This video" 开头),摘要同样存入索引。
- 生成嵌入:transcript_enrich_embeddings.py 将分段文本传给 OpenAI Embedding API(部署名为
text-embedding-ada-002),返回 1536 维向量并存入分段记录的ada_v2字段。脚本先对文本做归一化(去除多余空格、换行、..等噪声),并用tiktoken的cl100k_base编码器跳过超过 8191 token 的超长文本。 - 生成精简索引:transcript_enrich_lite.py 从富化结果中移除
text和description字段,得到体积更小的索引文件,最终由 shell 脚本重命名为embedding_index_3m.json。
最终索引中每条记录的结构如下(来自仓库内 embedding_index_3m.json 的实际数据):
{ "speaker": "Seth Juarez, Josh Lovejoy, Sarah Bird", "title": "You're Not Solving the Problem You Think You're Solving", "videoId": "-tJQm4mSh1s", "start": "00:00:00", "seconds": 0, "summary": "Join Seth Juarez as he discusses ethical concerns with AI...", "ada_v2": [0.0043573323637247086, -0.02840915322303772, ...] }向量数据库
为简化课程,嵌入索引存储为 JSON 文件并加载到 Pandas DataFrame 中。但在生产环境中,嵌入索引应存储在向量数据库中,例如 Azure Cognitive Search、Redis、Pinecone、Weaviate 等,以便在海量向量上提供高性能的相似度检索与持久化管理。
理解余弦相似度
知道文本嵌入之后,下一步是学会用嵌入搜索数据:即用余弦相似度找出与给定查询最相似的嵌入。
余弦相似度是衡量两个向量相似度的指标,也常被称为最近邻搜索(nearest neighbor search)。执行余弦相似度搜索的步骤是:
- 用 OpenAI Embedding API 对查询文本进行向量化;
- 计算查询向量与索引中每个向量(索引中每个 YouTube 字幕分段都有一个向量)之间的余弦相似度;
- 按余弦相似度排序,相似度最高的文本分段即与查询最相似。
从数学角度看,余弦相似度度量的是投影在多维空间中的两个向量之间夹角的余弦。这一度量很有价值:如果两个文档因规模(长度)原因在欧氏距离上相距很远,它们之间的夹角仍可能很小,从而获得更高的余弦相似度——也就是说,它关注的是"方向"而非"距离"。
构建你的第一个搜索应用
现在正式构建搜索应用:允许学生输入问题来搜索视频,返回相关问题视频列表,并给出答案所在位置的视频链接。
该解决方案已在 Windows 11、macOS 和 Ubuntu 22.04 上使用Python 3.10 或更高版本构建并测试通过。
任务:创建 Azure OpenAI 服务
构建搜索应用需要 Azure OpenAI 服务,首先在Azure Cloud Shell中完成资源创建(需要 Azure 订阅):
登录 Azure 门户,点击右上角的 Cloud Shell 图标,选择Bash环境类型。
创建资源组(本课使用 East US 的
semantic-video-search;可改名,但更换资源位置时需查询模型可用性表):
az group create --name semantic-video-search --location eastus- 创建 Azure OpenAI Service 资源:
az cognitiveservices account create --name semantic-video-openai --resource-group semantic-video-search \ --location eastus --kind OpenAI --sku s0- 获取端点与密钥(供应用配置使用):
az cognitiveservices account show --name semantic-video-openai \ --resource-group semantic-video-search | jq -r .properties.endpoint az cognitiveservices account keys list --name semantic-video-openai \ --resource-group semantic-video-search | jq -r .key1- 部署 OpenAI Embedding 模型(部署名
text-embedding-ada-002,版本 2):
az cognitiveservices account deployment create \ --name semantic-video-openai \ --resource-group semantic-video-search \ --deployment-name text-embedding-ada-002 \ --model-name text-embedding-ada-002 \ --model-version "2" \ --model-format OpenAI \ --sku-capacity 100 --sku-name "Standard"说明:若要自行重跑字幕数据准备流水线,还需额外部署
gpt-4o-mini(用于演讲者提取与摘要生成),并配置AZURE_OPENAI_API_KEY、AZURE_OPENAI_ENDPOINT、AZURE_OPENAI_MODEL_DEPLOYMENT_NAME、GOOGLE_DEVELOPER_API_KEY四个环境变量,详见 08-building-search-applications/scripts/README.md。
解决方案:语义搜索 notebook
打开解决方案 notebook 08-building-search-applications/python/aoai-solution.ipynb,在 GitHub Codespaces 中按 Jupyter Notebook 指引运行。运行时需要.env中配置好AZURE_OPENAI_API_KEY、AZURE_OPENAI_ENDPOINT和AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT,依赖库见 08-building-search-applications/python/requirements.txt。
notebook 的核心代码逻辑如下:
1. 加载嵌入索引到 Pandas DataFrame,并丢弃体积较大的text字段:
def load_dataset(source: str) -> pd.core.frame.DataFrame: # Load the video session index pd_vectors = pd.read_json(source) return pd_vectors.drop(columns=["text"], errors="ignore").fillna("")2. 定义余弦相似度函数与搜索函数get_videos,执行流程为:复制索引 → 用 Embedding API 计算查询向量 → 新增similarity列(每行分段的余弦相似度)→ 按阈值0.75过滤 → 按相似度降序排序并返回前 5 条:
SIMILARITIES_RESULTS_THRESHOLD = 0.75 DATASET_NAME = "../embedding_index_3m.json" def cosine_similarity(a, b): if len(a) > len(b): b = np.pad(b, (0, len(a) - len(b)), 'constant') elif len(b) > len(a): a = np.pad(a, (0, len(b) - len(a)), 'constant') return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) def get_videos(query: str, dataset: pd.core.frame.DataFrame, rows: int) -> pd.core.frame.DataFrame: # create a copy of the dataset video_vectors = dataset.copy() # get the embeddings for the query query_embeddings = client.embeddings.create(input=query, model=model).data[0].embedding # create a new column with the calculated similarity for each row video_vectors["similarity"] = video_vectors["ada_v2"].apply( lambda x: cosine_similarity(np.array(query_embeddings), np.array(x)) ) # filter the videos by similarity mask = video_vectors["similarity"] >= SIMILARITIES_RESULTS_THRESHOLD video_vectors = video_vectors[mask].copy() # sort the videos by similarity video_vectors = video_vectors.sort_values(by="similarity", ascending=False).head(rows) # return the top rows return video_vectors.head(rows)3. 展示结果:display_results使用记录中的videoId与seconds字段拼接https://youtu.be/{video_id}?t={seconds}形式的带时间戳链接,并打印标题、摘要前 15 个词、相似度分数与演讲者:
def display_results(videos: pd.core.frame.DataFrame, query: str): def _gen_yt_url(video_id: str, seconds: int) -> str: return f"https://youtu.be/{video_id}?t={seconds}" print(f"\nVideos similar to '{query}':") for _, row in videos.iterrows(): youtube_url = _gen_yt_url(row["videoId"], row["seconds"]) print(f" - {row['title']}") print(f" Summary: {' '.join(row['summary'].split()[:15])}...") print(f" YouTube: {youtube_url}") print(f" Similarity: {row['similarity']}") print(f" Speakers: {row['speaker']}")4. 交互查询循环:加载索引后反复提示用户输入查询,输入exit退出:
pd_vectors = load_dataset(DATASET_NAME) while True: query = input("Enter a query: ") if query == "exit": break videos = get_videos(query, pd_vectors, 5) display_results(videos, query)运行 notebook 后会出现查询输入框:
可以尝试以下示例查询:
- What is Azure Machine Learning?
- How do convolutional neural networks work?
- What is a neural network?
- Can I use Jupyter Notebooks with Azure Machine Learning?
- What is ONNX?
小结:从视频到可检索的知识
本课完成了从"视频字幕"到"可语义检索知识库"的完整闭环:通过 3 分钟分段 + 重叠词 + 摘要 +text-embedding-ada-002嵌入构建了 embedding_index_3m.json 索引;搜索端则利用 Pandas 与余弦相似度实现语义查询,并借助seconds字段生成直达答案位置的时间戳链接。这套"嵌入索引 + 相似度检索"的模式,正是 RAG(检索增强生成)、企业知识库搜索等生产级 AI 应用的核心基石,区别仅在于生产环境会将 JSON 索引替换为 Redis、Pinecone、Weaviate 或 Azure Cognitive Search 等向量数据库。
继续学习可前往下一课 构建图像生成应用,掌握多模态生成能力。
【免费下载链接】generative-ai-for-beginners21 Lessons, Get Started Building with Generative AI项目地址: https://gitcode.com/GitHub_Trending/ge/generative-ai-for-beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考