
基于 SkyPilot 与 OpenAI CLIP 构建大规模图像语义搜索的向量数据库实战【免费下载链接】skypilotThe AI Compute Platform for frontier teams. SkyPilot turns fragmented AI compute into one AI supercomputer, so frontier AI teams build custom intelligence faster.项目地址: https://gitcode.com/GitHub_Trending/sk/skypilot本文以 SkyPilot 仓库中的examples/vector_database为例完整讲解如何把海量图像通过 CLIP 模型转换为向量嵌入Embedding使用 ChromaDB 构建可实时查询的向量数据库并以 HTTP 服务的形式对外提供以文搜图的语义检索能力。读完本文你将掌握一套可复用的三步工作流分布式向量计算SkyPilot Managed Jobs→ 向量入库ChromaDB→ 服务化部署Sky Launch / Sky Serve并理解每一步背后的 YAML 配置与源码实现细节。一、为什么大规模图像搜索需要向量数据库随着图像数据量的增长基于关键词或元数据的传统检索方式越来越难以捕获图像的完整语义。例如查询云朵的照片传统标签搜索只能命中显式打了cloud标签的图片而向量数据库则能把图像和文本映射到同一个语义空间中实现真正的语义匹配。在本示例中向量数据库带来的核心收益可以归纳为三点可扩展性现代应用可能面对数百万甚至数十亿张图片传统数据库方案在大数据量下会变慢且难以管理而向量数据库专为高维最近邻Nearest Neighbor查询而设计。灵活性把图像存储为向量嵌入后可以灵活适配不同检索场景从找相似商品到找包含特定物体或风格的图片。性能向量数据库针对高维空间的最近邻查询做了深度优化能够在千万级甚至亿级向量上实现实时或近实时的检索。而 SkyPilot 的价值在于把跑大规模计算任务这件事的云上基础设施复杂度抽象掉它负责找机器、挂载数据、调度任务帮助用户以高效且经济的方式运行这类计算密集型任务。整个示例的工作流清晰分为三步这也是本文的主线步骤目标产物Step 1用 OpenAI CLIP 将图像批量编码为向量Parquet 格式的嵌入文件Step 2将嵌入批量写入 ChromaDB持久化的向量数据库Step 3将数据库封装为 HTTP 服务对外提供语义检索可查询的/searchAPI 端点对应的完整代码与配置均位于仓库的 examples/vector_database 目录下。二、Step 0环境准备2.1 安装 SkyPilot 并通过云端检查首先确保 SkyPilot 已正确安装并运行以下命令验证云端凭据与资源配置可用sky checksky check会探测当前环境可用的所有云提供商AWS、GCP、Azure、Kubernetes 等及其配额情况。该命令必须成功返回后续所有sky jobs launch、sky launch、sky serve up操作才具备执行前提。2.2 配置 Hugging Face Token本示例的数据集来自 Hugging Face Hub下载数据集需要 HF Token。有两种配置方式方式一在~/.env文件中写入HF_TOKENhf_xxxxx方式二直接设置环境变量export HF_TOKENhf_xxxxx值得注意的是这个 Token 不仅用于本地下载还会被安全地传递到云端任务。在 batch_compute_vectors.py 的源码中可以看到脚本会优先从环境变量HF_TOKEN读取若不存在则解析~/.env文件两种来源都取不到时会直接抛出ValueError终止执行。而 compute_vectors.yaml 中通过secrets: HF_TOKEN: null声明该密钥由task_copy.update_secrets({HF_TOKEN: hf_token})在启动任务时注入避免了把 Token 硬编码进 YAML 或明文写入日志。三、Step 1用 OpenAI CLIP 计算图像向量3.1 一键启动分布式向量计算CLIP 模型能够把图像与文本映射到同一个嵌入空间从而让一张云朵的照片这样的文本查询与相关图像在向量空间中距离更近。本步骤的目标就是把整个图像数据集的每一张图片编码成向量。直接运行仓库中的启动脚本即可python3 batch_compute_vectors.py脚本会自动寻找可用的云上机器并行计算。运行后你会在任务日志中看到类似输出(clip-batch-compute-vectors, pid2523) 2025-01-27 23:57:27,387 - root - INFO - Saved partition 2 to /output/embeddings_90000_100000.parquet_part_2/data.parquet (clip-batch-compute-vectors, pid2523) 2025-01-27 23:59:39,720 - root - INFO - Saved partition 3 to /output/embeddings_90000_100000.parquet_part_3/data.parquet (clip-batch-compute-vectors, pid2523) 2025-01-28 00:01:56,707 - root - INFO - Saved partition 4 to /output/embeddings_90000_100000.parquet_part_4/data.parquet注意embeddings_90000_100000这类命名每个分区负责数据集的一个索引区间编码结果以 Parquet 格式落盘。3.2 任务分区逻辑把大任务拆成并行小任务batch_compute_vectors.py 是整个 Step 1 的调度核心它做了三件事按索引区间切分数据calculate_job_range()将全局[start_idx, end_idx)区间按任务数均分并把余数均匀分配给前几个任务保证负载均衡。脚本默认参数为--start-idx 0、--end-idx 1000000、--num-jobs 100即默认把 100 万张图片切给 100 个并行任务。动态注入每个任务的环境变量通过task.update_envs({START_IDX: ..., END_IDX: ...})为每个任务写入各自的索引区间。批量提交 Managed Jobs循环调用sky.jobs.launch(task_copy, namefvector-compute-{job_start}-{job_end})一次性向云上提交多个托管任务。3.3 compute_vectors.yaml 配置解读Step 1 的任务定义在 compute_vectors.yaml这是理解整个流程的关键配置文件值得逐段拆解name: clip-batch-compute-vectors workdir: . resources: accelerators: # 按价格排序最便宜到最贵 T4: 1 L4: 1 A10G: 1 A10: 1 V100: 1 memory: 32 any_of: - use_spot: true - use_spot: false num_nodes: 1accelerators列表SkyPilot 会按声明顺序优先选用最便宜的可用 GPU这里是 T4只有最便宜的不可用时才依次尝试 L4、A10G 等。这正体现了 SkyPilot找到性价比最优机器的调度策略与 serve_vectordb.yaml 中的声明方式完全一致。any_of: use_spot二元选择允许任务在 Spot抢占式实例更便宜与按需实例之间自由选择让调度器以成本为导向决策。memory: 32要求机器至少 32GB 内存为 CLIP 模型加载与批量编码预留足够空间。文件挂载与任务运行部分同样关键file_mounts: /output: name: sky-demo-embedding # 必须与 build_vectordb.yaml 中的 source 一致 mode: MOUNT /images: name: sky-demo-image mode: MOUNT envs: START_IDX: END_IDX: secrets: HF_TOKEN: null # 启动时注入 run: | python scripts/compute_vectors.py \ --output-path /output/embeddings_${START_IDX}_${END_IDX}.parquet \ --start-idx ${START_IDX} \ --end-idx ${END_IDX} \ --batch-size 64 \ --checkpoint-size 1000file_mounts把云端对象存储桶挂载到任务节点/images提供原始图像数据/output承接编码结果。因为多个任务写同一个桶SkyPilot 使用sky-demo-embedding与sky-demo-image两个命名存储桶Sky Storage来跨任务共享数据。mode: MOUNT表示以挂载方式访问存储而非每次启动拷贝快照避免海量数据的传输开销。断点续算--checkpoint-size 1000让计算脚本每处理 1000 张图片写一次检查点Spot 实例被回收后可从中断处继续配合 Managed Jobs 的重试机制实现弹性容错。3.4 监控计算任务计算任务提交后可以通过 SkyPilot 的两条命令查看状态sky jobs queue # 查看所有托管任务的排队与运行状态 sky dashboard # 打开可视化 Dashboard可看到任务分布在哪些区域从 batch_compute_vectors.py 的实现可以看到这些任务是彼此独立的 Managed JobsSkyPilot 会在不同区域、不同可用区自动调度它们充分利用 Spot 价格差降低总成本。四、Step 2把嵌入构建成向量数据库4.1 为什么需要专用向量引擎有了图像嵌入之后还需要一个专门的高维向量检索引擎来支撑毫秒级的相似度查询。本示例选用ChromaDB作为向量数据库负责把 Step 1 产出的嵌入批量写入并持久化从而支持在百万级向量上的实时或近实时检索。4.2 build_vectordb.yaml 配置解读构建数据库的任务定义在 build_vectordb.yamlname: vectordb-build workdir: . file_mounts: /clip_embeddings: name: sky-demo-embedding # 与 compute_vectors.yaml 的 /output 是同一个桶 mode: MOUNT /vectordb: name: sky-vectordb # 与 serve_vectordb.yaml 的 /vectordb 是同一个桶 mode: MOUNT /images: name: sky-demo-image mode: MOUNT setup: | pip install chromadb pandas tqdm pyarrow run: | python scripts/build_vectordb.py \ --collection-name clip_embeddings \ --persist-dir /vectordb/chroma \ --embeddings-dir /clip_embeddings \ --batch-size 1000三个挂载桶的分工非常清晰且相互之间存在严格的命名约定YAML 注释也反复强调必须与另一份 YAML 中的 source 一致/clip_embeddings→sky-demo-embedding读入 Step 1 的计算结果/vectordb→sky-vectordb写入构建好的 ChromaDB 持久化目录供 Step 3 读取/images→sky-demo-image保留原始图片供服务端返回检索结果的图像内容。这条一个桶写、另一个桶读的传递链是三步流水线能在不同任务、不同集群间无缝衔接的关键。4.3 源码实现细节scripts/build_vectordb.py 的实现揭示了构建过程的核心逻辑扫描 Parquet 文件list_local_parquet_files()用glob递归搜索挂载桶下所有**/*.parquet文件说明 Step 1 的多个分区输出会被一次性全部纳入。多进程批处理process_parquet_file()通过ProcessPoolExecutor并行处理每个 Parquet 文件——用pandas.read_parquet读取按--batch-size默认 1000分片解包其中的 pickle 序列化数据拆出idx、向量和 base64 编码的图像。--batch-size的调优含义该参数需要能放进内存源码注释原话批量越大单次写库效率越高但对节点内存要求越高需与 YAML 中的memory: 32配合权衡。运行构建任务sky jobs launch build_vectordb.yaml日志会显示逐个处理 Parquet 文件的过程(vectordb-build, pid2457) INFO:__main__:Processing /clip_embeddings/embeddings_0_500.parquet_part_0/data.parquet Processing batches: 100%|██████████| 1/1 [00:0000:00, 1.19it/s] Processing files: 100%|██████████| 12/12 [00:0500:00, 2.04it/s]4.4 注意ChromaDB 采用--collection-name clip_embeddings作为集合名此名称在 Step 3 的建库与服务配置中必须保持一致build_vectordb.yaml 与 serve_vectordb.yaml 均使用clip_embeddings否则服务端将无法找到集合。五、Step 3把向量数据库服务化构建完成后需要把数据库封装成一个 API 服务让本地客户端或其他应用如图像搜索引擎、推荐系统能够调用它执行语义检索。本示例提供两种部署方式CLI与SDK。5.1 方式一CLI 部署模式 A常规集群部署sky launch -c vecdb_serve serve_vectordb.yaml这会启动一个名为vecdb_serve的集群在集群上常驻运行向量数据库服务。查询部署地址sky status --ip vecdb_serve模式 BSky Serve 服务化部署sky serve up serve_vectordb.yaml -n vectordb这种方式把向量数据库部署为云上的托管服务Sky Serve 会自动完成健康检查与弹性扩缩容并通过公网端点对外提供服务。查询服务端点sky serve status vectordb --endpoint5.2 方式二SDK 部署serve_vectordb.py 提供了等价的 Python SDK 方式python3 serve_vectordb.py # 以集群方式启动 python3 serve_vectordb.py --serve # 以 Sky Serve 服务方式启动脚本内部通过sky.Task.from_yaml(serve_vectordb.yaml)加载任务定义然后依据是否带--serve参数分别调用serve_sdk.up(task, service_namevectordb-serve)或sky.launch(task, cluster_namevectordb-serve)最后用sky.stream_and_get(req_id)同步等待部署完成并自动从状态中解析出endpoint打印出来if args.serve: serve_status sky.get(serve_sdk.status(service_names_SERVICE_NAME)) endpoint serve_status[0][endpoint] else: cluster_status sky.get(sky.endpoints(cluster_SERVICE_NAME)) endpoint cluster_status[int(port)] print(fendpoint: {endpoint})脚本还会自动从任务资源中解析出ports字段只支持单个端口无需手动硬编码端口号。5.3 serve_vectordb.yaml服务端配置详解serve_vectordb.yaml 与前两个配置最明显的差异是多了service段resources: accelerators: T4: 1 L4: 1 A10G: 1 A10: 1 V100: 1 memory: 32 ports: 8000 use_spot: true file_mounts: /vectordb: name: sky-vectordb mode: MOUNT /images: name: sky-demo-image mode: MOUNT service: replicas: 1 readiness_probe: path: /healthports: 8000声明服务监听端口Sky Serve 据此自动完成负载均衡器的端口映射。service.readiness_probe.path: /healthSky Serve 通过轮询/health路径判断副本是否就绪只有就绪的副本才会接收流量——这正是 README 中自动健康检查的落地配置。为什么服务端必须要有 GPUYAML 注释明确说明serve requires a GPU to compute the embeddings——在线推理阶段用户的文本查询需要实时用 CLIP 编码为向量因此需要 GPU 而非仅做存储查询。5.4 在线查询的源码链路scripts/serve_vectordb.py 实现了完整的 FastAPI 服务其查询链路可以拆解为四步文本编码encode_text()用 CLIP 的tokenizer处理查询文本model.encode_text(text_tokens)得到特征向量并在torch.no_grad()下做 L2 归一化text_features / text_features.norm(dim-1, keepdimTrue)确保与建库阶段存储的向量处于同一度量空间。向量检索query_collection()调用 ChromaDB 的collection.query(query_embeddings..., n_results..., include[metadatas, distances, documents])找出最近邻向量。距离转相似度由于 ChromaDB 默认返回 L2 距离源码用similarity 1 - (distance / 2)将其换算为余弦相似度分值返回给调用方一个直观的匹配分数。API 暴露app.post(/search)端点接收SearchQuerytext查询词 可选n_results默认返回 5 条结果返回SearchResult列表每项包含命中的image_path与similarity。拿到上一步打印的 endpoint 后即可用任意 HTTP 客户端发起语义检索例如向http://endpoint/searchPOST 一条{text: a photo of a cloud}服务会返回语义最接近的图像及其相似度。整个服务还可嵌入更大的应用如图像搜索引擎、推荐系统作为检索后端。六、三步流水线背后的设计要点6.1 存储桶贯穿始终跨任务的数据传递契约细读三份 YAML 可以发现整个流水线没有走任何本地文件传输全靠三个命名存储桶接力存储桶名写入方读取方承载数据sky-demo-image用户数据集Step 1 / Step 2 / Step 3原始图像sky-demo-embeddingStep 1/outputStep 2/clip_embeddingsCLIP 嵌入Parquetsky-vectordbStep 2/vectordbStep 3/vectordbChromaDB 持久化目录由于不同步骤运行在不同的集群与任务上这种以云端对象存储为中介的设计让任务之间彻底解耦任何一步都可以独立重跑、横向扩容而不会破坏后续步骤的数据完整性。6.2 成本优化贯穿始终从 Step 1 到 Step 3成本优化的手段层层叠加多 GPU 候选 按价格排序T4 → L4 → A10G → A10 → V100SkyPilot 自动选择当前可用且最便宜的加速器Spot 优先Step 1 通过any_of允许 Spot/按需二选一Step 3 直接use_spot: true充分利用可抢占实例的低价断点续算--checkpoint-size配合 Managed Jobs 自动重启让 Spot 回收不再导致整段任务返工。6.3 托管任务与托管服务的分工批处理Step 1/2使用sky jobs launch的Managed Jobs适合有始有终、可并行切分、支持自动重试的离线任务在线服务Step 3使用sky serve up的Sky Serve适合需要常驻、对外暴露端点、具备就绪探针与扩缩容能力的服务型负载。两者在 compute_vectors.yaml、build_vectordb.yaml 与 serve_vectordb.yaml 三份配置中形成了清晰的分工模式可作为同类离线建库 在线检索应用的通用范本。七、总结从一张张原始图片到可实时语义检索的在线 API这个示例完整覆盖了向量检索应用的全生命周期python3 batch_compute_vectors.py把百万级图像数据集切分成上百个并行任务用 CLIP 编码为向量落盘到共享存储桶sky jobs launch build_vectordb.yaml读取嵌入以多进程批处理方式构建 ChromaDB 集合并持久化sky launch/sky serve up/ SDK把数据库包装成带健康检查与扩缩容能力的 HTTP 服务通过/search端点实现以文搜图。本示例的完整代码、三份 YAML 配置与全部 Python 脚本均可在 examples/vector_database 目录中复现运行其中 batch_compute_vectors.py、scripts/build_vectordb.py 与 scripts/serve_vectordb.py 分别对应上述三个步骤的核心实现可以作为构建自有图像语义检索系统的起点。【免费下载链接】skypilotThe AI Compute Platform for frontier teams. SkyPilot turns fragmented AI compute into one AI supercomputer, so frontier AI teams build custom intelligence faster.项目地址: https://gitcode.com/GitHub_Trending/sk/skypilot创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考