首页
/ 使用文本嵌入与余弦相似度构建语义搜索应用:generative-ai-for-beginners 第 08 课完整指南

使用文本嵌入与余弦相似度构建语义搜索应用:generative-ai-for-beginners 第 08 课完整指南

2026-09-04 14:22:28作者:牧宁李

本文基于 generative-ai-for-beginners 仓库第 08 课《Building a Search Applications》编写,系统讲解如何用文本嵌入(Embeddings)替代关键词匹配、构建可定位到视频时间戳的语义搜索应用。读完你既能掌握嵌入向量、余弦相似度与向量索引的核心原理,也能完整复现课程配套的 Azure OpenAI 资源部署命令,并读懂仓库中索引构建脚本与求解 Notebook 的实际实现。

问题 "can you use rstudio with Azure ML" 的语义查询结果示例,返回带视频时间戳链接的 YouTube 视频列表

课程背景:为教育机构构建“按问题搜索视频”的应用

大语言模型(LLM)的应用远不止聊天机器人与文本生成——利用嵌入(Embeddings)构建搜索应用同样是核心场景。嵌入是数据的数值表示,也称作向量(vector),可用于数据的语义检索。

本课程的场景设定:一家面向发展中国家学生提供免费教育的非营利创业公司,拥有大量可用来学习 AI 的 YouTube 视频(微软 AI Show 频道)。团队希望构建一个搜索应用:学生输入一个问题(例如 “What are Jupyter Notebooks?” 或 “What is Azure ML”),应用即返回与该问题相关的视频列表;更进一步,返回的链接会直接跳转到视频中包含答案的位置。

课程提供了 AI Show 频道 YouTube 字幕的现成嵌入索引(覆盖至 2023 年 10 月的全部视频字幕),你无需自己跑索引构建流程,直接用它就能完成搜索应用。下面的截图就是针对问题 “can you use rstudio with azure ml?” 的一次语义查询结果,注意返回的 YouTube URL 带有时间戳参数,可直达视频中回答问题的那一段。

课程覆盖内容与学习目标

本课程覆盖四个主题:语义搜索与关键词搜索的区别、什么是文本嵌入、如何创建文本嵌入索引、如何在索引中检索。

完成课程后,你将能够:

  • 区分语义搜索(semantic search)与关键词搜索(keyword search);
  • 解释文本嵌入是什么;
  • 使用嵌入构建一个可以检索数据的应用。

什么是语义搜索?

语义搜索是一种利用查询中词语的语义(即含义)来返回相关结果的检索技术。

课程用一个买车例子说明:你搜索 “my dream car”(我梦想的车),语义搜索理解到你并不是想找“关于车的梦”,而是想购买你的“理想”座驾——它理解你的意图并返回相关结果。与之相对的是关键词搜索,它会字面上去匹配“车 + 梦想”这类词,往往返回不相关的结果。这个例子也点出了向量检索的核心价值:匹配的是“意图”,而不是“字面词”。

什么是文本嵌入?

文本嵌入(Text Embeddings)是自然语言处理(NLP)中常用的文本表示技术,是文本的语义化数值表示,把数据转换成机器易于理解的形式。业界有多种构建嵌入的模型,本课程聚焦 OpenAI 嵌入模型。

课程给出的示例:假设 AI Show 某期视频的字幕里有一句:

Today we are going to learn about Azure Machine Learning.

把这句话传给 OpenAI 嵌入 API,会返回一个由 1536 个数字构成的向量。向量中的每个数字都代表文本的某个不同侧面。为便于阅读,原文档只列出向量的前 10 个数字:

[-0.006655829958617687, 0.0026128944009542465, 0.008792596869170666, -0.02446001023054123, -0.008540431968867779, 0.022071078419685364, -0.010703742504119873, 0.003311325330287218, -0.01161632772162556648, -0.02187200076878071, ...]

这个 1536 维的设定在仓库的数据里可以直接验证:课程索引文件 embedding_index_3m.json 中每个片段的 ada_v2 字段就是一个长度 1536 的浮点数数组。

嵌入索引是如何创建的?

课程索引由一组 Python 脚本生成,脚本和说明位于 scripts/README.md。完成课程不需要自己运行这些脚本(索引已提供),但理解流水线是深入掌握 RAG 与向量检索的关键。脚本执行以下五步操作:

  1. 下载字幕:下载 AI Show 播放列表中每个 YouTube 视频的字幕;
  2. 提取演讲者:利用 OpenAI Functions,从每个视频前 3 分钟的字幕中尝试提取演讲者姓名,并存入索引 embedding_index_3m.json
  3. 按 3 分钟切分:把字幕文本切分为 3 分钟文本片段,每个片段会包含下一个片段约 20 个词的重叠内容,以确保片段的嵌入不被“截断”、并提升搜索上下文质量;
  4. 生成摘要:把每个片段传给 OpenAI 聊天 API,总结为 60 词的摘要,摘要同样存入 embedding_index_3m.json
  5. 生成嵌入:把片段文本传给 OpenAI 嵌入 API,得到代表该片段的 1536 维语义向量,与片段文本一起存入索引。

结合仓库源码看索引流水线的实现细节

仓库中这条流水线是真实可执行的,入口脚本为 prepare_transcripts_ai_show.sh。它设置了 TRANSCRIPT_FOLDER=transcripts_the_ai_showTRANSCRIPT_BUCKET_MINUTES=3(即 3 分钟一个时间桶),然后按顺序调用六个脚本:

步骤 脚本 职责
1 transcript_download.py 从播放列表 PLlrxD0HtieHi0mwteKBOfEeOYf0LJU4O1 下载字幕
2 transcript_enrich_speaker.py 用 OpenAI Function Calling 提取演讲者姓名
3 transcript_enrich_bucket.py 按 N 分钟切分片段(-m 参数,脚本内默认 5 分钟,Shell 入口覆盖为 3)
4 transcript_enrich_summaries.py 生成 60 词摘要
5 transcript_enrich_embeddings.py 生成 1536 维嵌入向量
6 transcript_enrich_lite.py 生成去除文本字段的轻量索引

从源码结构看,几个关键参数与教学文档的描述互相印证,且提供了文档未展开的工程细节:

  • 片段切分与重叠transcript_enrich_bucket.py 中定义 PERCENTAGE_OVERLAP = 0.05(上一片段尾部追加下一片段约 5% 的词数,即文档所说的“约 20 个词重叠”)和 MAX_TOKENS = 2048(单片段 token 上限,同时为下一步摘要请求预留 1024 token)。切分前先拼入演讲者、标题、描述作为上下文前缀,再逐条解析 .json.vtt 字幕文件;
  • 摘要生成transcript_enrich_summaries.py 的系统提示词要求“写一个权威的 60 词摘要,避免以 ‘This video’ 开头”,temperature=0.7top_p=0.0,默认模型 gpt-4o-mini(可通过环境变量 AZURE_OPENAI_MODEL_DEPLOYMENT_NAME 覆盖);
  • 嵌入生成transcript_enrich_embeddings.py 使用 6 个线程并发处理队列,默认嵌入部署名 text-embedding-ada-002(可用 AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT 覆盖),并用 tiktoken 检查文本——超过 8191 token 的片段直接跳过,这正是嵌入模型输入上限的工程保护;调用通过 tenacity 重试装饰器包裹(指数退避,最多 20 次);
  • 产物结构:流水线最后把结果重命名为 embedding_index_full_3m.jsonembedding_index_3m.json。仓库中的 embedding_index_3m.json 共含 1409 个片段,每条记录包含 speakertitlevideoIdstartsecondssummaryada_v2(1536 维向量)七个字段——seconds 字段正是搜索应用能生成“跳转到视频某一秒”链接的数据来源;
  • 依赖清单scripts/requirements.txt 列出了流水线所需库,包括 openai>=1.54.0pandastiktokenyoutube-transcript-apitenacityrichscikit-learnscipy 等。

脚本运行所需的环境变量(Windows 用户变量或 Linux/macOS 的 ~/.bashrc/~/.zshrc)在 scripts/README.md 中有完整说明:AZURE_OPENAI_API_KEYAZURE_OPENAI_ENDPOINTAZURE_OPENAI_MODEL_DEPLOYMENT_NAME,以及用于下载 YouTube 字幕的 GOOGLE_DEVELOPER_API_KEY

向量数据库

出于教学简化,课程索引保存在 JSON 文件 embedding_index_3m.json 中,并加载进 Pandas DataFrame。而在生产环境中,索引应存储在专门的向量数据库里,例如 Azure Cognitive Search、Redis、Pinecone、Weaviate 等——从 JSON + DataFrame 到向量数据库,是这类应用走向生产时的标准演进路径。

理解余弦相似度

学会了文本嵌入之后,下一步是学会如何用嵌入检索数据——核心是找到与查询最相似的嵌入,即余弦相似度(cosine similarity)

什么是余弦相似度?

余弦相似度衡量两个向量之间的相似程度,它也常被称为“最近邻搜索(nearest neighbor search)”。执行一次余弦相似度检索的完整流程是:

  1. 用 OpenAI 嵌入 API 把查询文本向量化
  2. 计算查询向量与索引中每个向量的余弦相似度(索引中每个 YouTube 字幕片段都有一个向量);
  3. 按相似度排序,相似度最高的文本片段就是与查询最相似的结果。

从数学角度看,余弦相似度度量的是两个向量投影到高维空间中夹角的余弦值。这一度量的好处在于:即使两份文档因长度差异导致欧氏距离很远,只要夹角小,余弦相似度依然可以很高——也就是说,相似度由“方向”而非“模长”决定,天然适合比较长短不一的文本片段。

求解 Notebook 中的实现

课程的官方解法在 aoai-solution.ipynb 中,其中的 cosine_similarity 函数用 NumPy 实现了标准公式:先对长度不齐的向量做零填充,再计算点积除以两向量模长之积(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))。配套的 get_videos 函数完整演示了检索主流程:

model = os.environ['AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT']
SIMILARITIES_RESULTS_THRESHOLD = 0.75   # 相似度过滤阈值
DATASET_NAME = "../embedding_index_3m.json"

def get_videos(query: str, dataset, rows: int):
    video_vectors = dataset.copy()
    # 1. 查询文本向量化
    query_embeddings = client.embeddings.create(
        input=query, model=model).data[0].embedding
    # 2. 对索引每一行计算余弦相似度
    video_vectors["similarity"] = video_vectors["ada_v2"].apply(
        lambda x: cosine_similarity(np.array(query_embeddings), np.array(x)))
    # 3. 阈值过滤:只保留相似度 >= 0.75 的行
    mask = video_vectors["similarity"] >= SIMILARITIES_RESULTS_THRESHOLD
    video_vectors = video_vectors[mask].copy()
    # 4. 按相似度降序,取前 N 条(rows=5)
    video_vectors = video_vectors.sort_values(
        by="similarity", ascending=False).head(rows)
    return video_vectors

可以看到,课程描述的“计算—过滤—排序”三步在代码里一一对应,且给出了两个可复用的关键参数:相似度阈值 0.75(低于它的结果视为不相关,直接丢弃)和返回条数 5(返回最相关的前 5 个片段)。display_results 函数则把命中片段转成带时间戳的链接:https://youtu.be/{videoId}?t={seconds},其中 seconds 正是索引里记录的字幕片段起始秒数——这就是“搜索应用能把用户带到视频中答案所在位置”的实现原理。

运行求解 Notebook 后提示用户输入查询的交互界面截图

构建你的第一个搜索应用

环境要求

课程解法在 Windows 11、macOS、Ubuntu 22.04 上使用 Python 3.10 或更高版本构建并测试,可从 python.org 下载 Python。

实战任务:创建 Azure OpenAI 资源

课程的动手任务要求用 Azure OpenAI 服务构建搜索应用(需要一个 Azure 订阅)。完整操作步骤如下:

启动 Azure Cloud Shell:登录 Azure 门户,点击右上角的 Cloud Shell 图标,环境类型选择 Bash

创建资源组(课程使用位于 East US 的资源组 semantic-video-search;可以改名字,但更换区域时须核对模型可用性表):

az group create --name semantic-video-search --location eastus

创建 Azure OpenAI 服务资源

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 嵌入模型

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"

补充说明:脚本流水线 scripts/README.md 中还额外部署了 gpt-4o-mini 模型(用于摘要与演讲者提取),因为完整重建索引需要这两个模型协同工作。

运行求解 Notebook

在 GitHub Codespaces 中打开 aoai-solution.ipynb,按 Notebook 内提示配置 .envAZURE_OPENAI_API_KEYAZURE_OPENAI_ENDPOINTAZURE_OPENAI_EMBEDDINGS_DEPLOYMENT,即指向你部署的 text-embedding-ada-002),运行后会提示输入查询。主循环逻辑是:加载索引 → 等待用户输入 → 调用 get_videos 检索 → display_results 输出结果 → 继续等待下一次输入,输入 exit 退出。

可尝试的查询示例(来自 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?

输出会列出每条命中视频的标题、15 词摘要、带时间戳的 YouTube 链接、相似度分数与演讲者。

课程小结与延伸

这一课建立了生成式 AI 搜索应用的完整知识链:语义搜索理念 → 文本嵌入(1536 维向量)→ 索引构建流水线(字幕下载、演讲者提取、3 分钟切分、60 词摘要、嵌入生成)→ 余弦相似度检索(阈值 0.75、Top-5)→ 时间戳精准定位。仓库中从 脚本流水线1409 条片段的真实索引数据求解 Notebook 形成了可对照验证的完整证据链;英文原版课程文档在 08-building-search-applications/README.md

完成本课之后,可以继续第 09 课,学习构建图像生成应用:09-building-image-applications/README.md

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
981
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384