generative-ai-for-beginners 第 08 课实战:用文本嵌入与余弦相似度构建语义搜索应用
generative-ai-for-beginners 第 08 课实战用文本嵌入与余弦相似度构建语义搜索应用【免费下载链接】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 课程仓库第 08 课文档 translations/bn/08-building-search-applications/README.md 编写。你将围绕一个教育初创公司的真实场景学会区分语义搜索与关键词搜索、理解文本嵌入Embeddings与余弦相似度的原理并完整走一遍“构建嵌入索引 → 部署 Azure OpenAI 嵌入模型 → 用 Jupyter Notebook 实现语义搜索”的实战流程同时结合仓库中scripts/目录的索引构建流水线脚本与解决方案 Notebook 源码深入理解每一环的实际实现。课程场景让学员“问一句话直达视频答案”LLM 的能力不止于聊天机器人和文本生成。借助嵌入Embeddings——数据的数值表示也称为向量——还可以构建语义搜索应用。本课的目标场景是我们假设有一家教育初创公司它是一家非营利组织向发展中国家的学生提供免费教育并拥有大量教授 AI 知识的 YouTube 视频该初创公司希望构建一个搜索应用学生输入一个问题例如“什么是 Jupyter Notebook”或“什么是 Azure ML”应用就返回与问题相关的视频列表更进一步应用会返回视频时间戳链接直接定位到回答该问题的片段位置。本课覆盖内容与学习目标本课讨论四个核心主题语义搜索Semantic search与关键词搜索Keyword search的区别什么是文本嵌入如何创建文本嵌入索引如何搜索文本嵌入索引。完成本课之后你应当能够说清语义搜索与关键词搜索的差异解释文本嵌入是什么使用嵌入构建一个可检索数据的应用。为什么值得动手构建搜索应用因为它能让你真正理解“如何用嵌入检索数据”并产出一个可被学生用来快速查找信息的可用工具。本课附带的素材是微软 AI Show YouTube 频道字幕的嵌入索引AI Show 是一个教授 AI 与机器学习的 YouTube 频道。该索引包含截至 2023 年 10 月每条视频字幕的嵌入。上面的示例截图中查询 “can you use rstudio with azure ml?” 返回的 YouTube 链接带有时间戳参数可直接跳转到答案所在片段——这正是本课要实现的检索体验。语义搜索与关键词搜索语义搜索Semantic search是一种利用查询中词语的**语义含义**而非字面匹配来返回相关结果的检索技术。文档给出的经典例子假设你要买一辆车搜索 “my dream car”我的梦想车。语义搜索能理解你并不是在做梦dreaming想车而是要购买你理想的ideal车——它理解你的意图返回相关结果。相对地关键词搜索Keyword search会按字面去匹配“汽车 梦”结果往往毫不相干。这一差异的根源在于关键词搜索工作在“词表”空间而语义搜索工作在“向量”空间——这正是下一节文本嵌入要解决的问题。什么是文本嵌入文本嵌入是自然语言处理中的文本表示技术它是文本的语义化数值表示把数据转换成机器易于处理的形式。构建文本嵌入的模型有很多本课聚焦使用 OpenAI Embedding Model 生成嵌入。文档举了一个直观例子假设 AI Show 某期视频的字幕中有这样一句话——Today we are going to learn about Azure Machine Learning.把这段文本传给 OpenAI Embedding API会返回一个由1536 个浮点数组成的向量embedding向量中的每个数表示文本的某个不同侧面。文档展示了向量的前 10 个数[-0.006655829958617687, 0.0026128944009542465, 0.008792596869170666, -0.02446001023054123, -0.008540431968867779, 0.022071078419685364, -0.010703742504119873, 0.003311325330287218, -0.011632772162556648, -0.02187200076878071, ...]这个 1536 维的数值并不是随机的语义相近的文本其向量在高维空间中的夹角更小。这一点在课程的练习 Notebook 08-building-search-applications/python/aoai-assignment.ipynb 中有专门的验证实验分别计算automobile与automobile应为 1.0完全相同、automobile与vehicle相近介于 0 和 1 之间、automobile与dinosaur、automobile与stick的余弦相似度通过对比数值直接感受嵌入的语义特性text the quick brown fox jumped over the lazy dog model os.getenv(AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT, ).strip() client.embeddings.create(input[text], modelmodel).data[0].embedding automobile_embedding client.embeddings.create(inputautomobile, modelmodel).data[0].embedding vehicle_embedding client.embeddings.create(inputvehicle, modelmodel).data[0].embedding dinosaur_embedding client.embeddings.create(inputdinosaur, modelmodel).data[0].embedding stick_embedding client.embeddings.create(inputstick, modelmodel).data[0].embedding print(cosine_similarity(automobile_embedding, automobile_embedding)) # 1.0 print(cosine_similarity(automobile_embedding, vehicle_embedding)) print(cosine_similarity(automobile_embedding, dinosaur_embedding)) print(cosine_similarity(automobile_embedding, stick_embedding))嵌入索引是如何构建的五步流水线本课的嵌入索引由一系列 Python 脚本生成脚本与说明位于 08-building-search-applications/scripts/README.md。完成课程不需要运行这些脚本因为嵌入索引文件 08-building-search-applications/embedding_index_3m.json 已经随仓库提供。文档描述的五个处理步骤下载字幕下载 AI Show 播放列表中每个 YouTube 视频的字幕提取讲者姓名利用 OpenAI Functions函数调用从每条视频字幕的前 3 分钟内容中尝试识别讲者姓名姓名存入名为embedding_index_3m.json的嵌入索引切分为 3 分钟文本段字幕文本被切分为3 分钟一段的文本块segment每段尾部会带入下一段约 20 个词的重叠文本确保段的嵌入语义不被截断、搜索上下文更完整生成 60 词摘要每个文本段送入 OpenAI Chat API 压缩成约 60 词的摘要摘要同样存入embedding_index_3m.json生成嵌入向量段文本送入 OpenAI Embedding API返回 1536 维向量与段文本一起写入embedding_index_3m.json。源码印证脚本流水线如何实现仓库中scripts/目录下的脚本与上述五步一一对应可以从源码结构看整条链路的实现细节流水线总控08-building-search-applications/scripts/prepare_transcripts_ai_show.sh 按顺序串联全部步骤——下载字幕 → 讲者识别 → 分桶-m 3即 3 分钟段→ 摘要 → 嵌入 → 精简最后把产物重命名为embedding_index_full_3m.json与embedding_index_3m.json字幕下载08-building-search-applications/scripts/transcript_download.py 通过 YouTube Data API v3 分页每页 50 条拉取播放列表用youtube_transcript_api抓取字幕并保存为 JSON 化的 WebVTT同时为每个视频写入含title/videoId/description/speaker的元数据文件从源码结构看它使用 40 个工作线程并发处理队列并依赖环境变量GOOGLE_DEVELOPER_API_KEY讲者识别08-building-search-applications/scripts/transcript_enrich_speaker.py 定义了一个名为get_speaker_name的函数工具从标题、描述和字幕前 3 分钟SEGMENT_MIN_LENGTH_MINUTES 3中抽取讲者姓名写回元数据的speaker字段默认模型部署名为gpt-4o-mini并设置了temperature0.0以保证抽取稳定分桶切段08-building-search-applications/scripts/transcript_enrich_bucket.py 将字幕按分钟数参数切段。注意文档表述为“约 20 词重叠”而从源码看重叠是通过PERCENTAGE_OVERLAP 0.05实现的——即把下一段约 5% 的词追加到上一段末尾以平滑上下文过渡同时用 tiktoken 按MAX_TOKENS 2048控制段长为后续摘要请求预留空间嵌入生成08-building-search-applications/scripts/transcript_enrich_embeddings.py 使用AzureOpenAI客户端api_version2024-10-21嵌入部署名默认text-embedding-ada-002为每个段生成向量并写入ada_v2字段从源码看它做了若干健壮性处理超过 8191 个 token 的超长段直接跳过、文本做空白/标点归一化、6 线程并发、用 tenacity 做指数退避重试BadRequestError不重试最后按videoId 起始时间排序输出。索引文件的实际结构随课提供的 08-building-search-applications/embedding_index_3m.json 是一个 JSON 数组共1409 个文本段。每个条目包含如下字段以真实数据为例字段含义示例值speaker讲者姓名步骤 2 抽取Seth Juarez, Josh Lovejoy, Sarah Birdtitle视频标题Youre Not Solving the Problem...videoIdYouTube 视频 ID用于拼链接-tJQm4mSh1sstart/seconds段的起始时间HH:MM:SS/ 秒数用于生成时间戳链接00:00:00/0summary约 60 词的段摘要Join Seth Juarez as he discusses...ada_v21536 维嵌入向量text-embedding-ada-002生成[0.0043573323637247086, ...]seconds字段正是搜索应用能“跳转到视频具体位置”的关键——解决方案会用videoId和seconds拼出带时间戳的链接。向量数据库教学与生产的取舍为简化本课嵌入索引存放在 JSON 文件中并加载进 Pandas DataFrame。文档同时指出生产环境中嵌入索引应存放在向量数据库里例如 Azure Cognitive Search、Redis、Pinecone、Weaviate 等。JSON DataFrame 方案适合教学与中小规模数据而向量数据库提供 ANN 索引、持久化和规模化检索能力——这也是本仓库第 15 课RAG 与向量数据库继续深入的主题。理解余弦相似度学完嵌入之后下一步是学习如何用嵌入检索数据给定一个查询找出索引中与之最相似的嵌入这就是余弦相似度检索也常被称为“最近邻搜索”nearest neighbor search。执行流程是用 OpenAI Embedding API 把查询文本向量化计算查询向量与嵌入索引中每一个段向量的余弦相似度索引中每个字幕段都有一个向量按相似度降序排序得分最高的文本段即与查询最相似的结果。从数学角度看余弦相似度衡量的是两个向量投影到多维空间后夹角的余弦值。它的优点是即使两个文档因长度差异在欧氏距离上很远只要方向接近夹角小余弦相似度依然可以很高——因此它天然适合比较“语义方向”而非“向量长度”。仓库中两种实现都可以对照阅读练习版08-building-search-applications/python/aoai-assignment.ipynb是最朴素的两行公式def cosine_similarity(a, b): return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))解决方案版08-building-search-applications/python/aoai-solution.ipynb在公式基础上增加了向量长度对齐的防御性处理——当两个向量长度不一致时用零填充np.pad避免索引数据异常时直接报错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))构建你的第一个搜索应用解决方案 Notebook 源码解析文档说明该解决方案在 Windows 11、macOS、Ubuntu 22.04 上使用 Python 3.10 或更高版本构建并测试过Notebook 内核版本为 3.10.13。以下是解决方案 Notebook 08-building-search-applications/python/aoai-solution.ipynb 的完整实现脉络可复制到本地环境运行。1. 初始化客户端与常量import os import pandas as pd import numpy as np from openai import AzureOpenAI from dotenv import load_dotenv load_dotenv() client AzureOpenAI( api_keyos.environ[AZURE_OPENAI_API_KEY], # 也可省略默认读取该环境变量 api_version 2024-10-21, azure_endpoint os.environ[AZURE_OPENAI_ENDPOINT] ) model os.environ[AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT] SIMILARITIES_RESULTS_THRESHOLD 0.75 # 相似度阈值低于 0.75 的结果直接过滤 DATASET_NAME ../embedding_index_3m.json注意这里多了一个环境变量AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT它指向你在 Azure 上部署的嵌入模型部署名如text-embedding-ada-002。Notebook 开头也明确提示需要先部署以text-embedding-ada-002为底模的模型并在.env文件中设置好该部署名。2. 加载嵌入索引def load_dataset(source: str) - pd.core.frame.DataFrame: pd_vectors pd.read_json(source) return pd_vectors.drop(columns[text], errorsignore).fillna()用pd.read_json把 1409 个段读入 DataFrame丢弃text原文列节省内存空值填充为空串。3. 核心检索函数get_videosdef get_videos(query: str, dataset: pd.core.frame.DataFrame, rows: int) - pd.core.frame.DataFrame: video_vectors dataset.copy() # 1. 拷贝索引 query_embeddings client.embeddings.create( # 2. 查询向量化 inputquery, modelmodel).data[0].embedding video_vectors[similarity] video_vectors[ada_v2].apply( # 3. 逐段算余弦相似度 lambda x: cosine_similarity(np.array(query_embeddings), np.array(x)) ) mask video_vectors[similarity] SIMILARITIES_RESULTS_THRESHOLD # 4. 过滤 0.75 video_vectors video_vectors[mask].copy() video_vectors video_vectors.sort_values(bysimilarity, # 5. 降序取 Top N ascendingFalse).head(rows) return video_vectors.head(rows)这五步与前面“理解余弦相似度”一节描述的流程完全对应且引入了两个工程细节相似度阈值 0.75低于该值的结果视为不相关直接丢弃避免返回噪声结果Top N 截断默认取前 5 条主循环中get_videos(query, pd_vectors, 5)。4. 结果展示拼出带时间戳的视频链接def display_results(videos: pd.core.frame.DataFrame, query: str): def _gen_yt_url(video_id: str, seconds: int) - str: convert time in format 00:00:00 to seconds return fhttps://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]})_gen_yt_url利用videoIdseconds生成?t{秒数}时间戳链接——这正是开篇承诺的“直达答案所在片段”能力。摘要只打印前 15 个词保持输出紧凑。5. 主循环与输入框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)运行时会出现一个交互式查询输入框输入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?实操用 Azure Cloud Shell 创建 Azure OpenAI 服务课程作业要求在 Azure 上创建搜索应用所需的 Azure OpenAI 服务需要一个 Azure 订阅。以下步骤完整继承自文档启动 Azure Cloud Shell登录 Azure 门户点击 Azure 门户右上角的 Cloud Shell 图标环境类型选择Bash。创建资源组文档示例使用 East US 区域的semantic-video-search资源组。资源组名可以修改但更换区域时必须先确认对应区域是否可用所需模型参考 Azure 的模型可用性表。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 模型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部署完成后把端点、密钥和部署名写入运行环境.env或系统环境变量。从源码看不同脚本需要的环境变量略有差异运行对象需要的环境变量依据解决方案 NotebookAZURE_OPENAI_API_KEY、AZURE_OPENAI_ENDPOINT、AZURE_OPENAI_EMBEDDINGS_DEPLOYMENTaoai-solution.ipynb索引构建流水线脚本AZURE_OPENAI_API_KEY、AZURE_OPENAI_ENDPOINT、AZURE_OPENAI_MODEL_DEPLOYMENT_NAME讲者/摘要用的对话模型、GOOGLE_DEVELOPER_API_KEYscripts/README.md此外如果你要复现完整的索引构建流水线scripts/README.md还建议同时部署gpt-4o-mini用于讲者识别与摘要步骤并将 Azure CLI 升级到最新版本以保证与 OpenAI 服务的兼容性。小结与继续学习本课以“教育初创公司视频搜索”为主线完整覆盖了语义搜索应用的核心链路语义/关键词搜索的区别 → 文本嵌入原理 → 五步索引构建流水线下载字幕、讲者识别、3 分钟切段、60 词摘要、1536 维嵌入→ 余弦相似度检索 → 阈值过滤 Top-N 时间戳链接。仓库中的embedding_index_3m.json1409 段、scripts/目录下的流水线脚本与两个 Notebook 提供了可直接阅读和运行的完整证据链生产化时则应把 JSON 索引替换为 Azure Cognitive Search、Redis、Pinecone、Weaviate 等向量数据库。完成本课之后可以继续学习仓库第 09 课了解如何构建图像生成应用09-building-image-applications/README.md。说明本文所依据的课程文档由 AI 翻译服务生成孟加拉语译本可能存在自动翻译的误差或不一致引用技术细节时建议以仓库中的英文原文 08-building-search-applications/README.md 与源码为准。【免费下载链接】generative-ai-for-beginners21 Lessons, Get Started Building with Generative AI项目地址: https://gitcode.com/GitHub_Trending/ge/generative-ai-for-beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考