You need to enable JavaScript to run this app.
文档中心
云搜索服务

云搜索服务

复制全文
下载 pdf
应用构建
基于云搜索服务 + Doubao-1.5-pro 视觉理解模型构建图文检索应用
复制全文
下载 pdf
基于云搜索服务 + Doubao-1.5-pro 视觉理解模型构建图文检索应用
在云搜索服务的 AI 搜索中,支持创建 Image Embedding 模型,该模型是将图像转换为低维的向量表示,以便于在各种机器学习任务中进行处理、分析和比较。豆包的视觉理解模型通过分析和理解图像内容,将视觉信息转化为可识别、可处理的语义信息,从而实现对图像的智能化理解和应用。本文介绍基于云搜索服务 Image Embedding 模型和豆包视觉理解模型,快速搭建一套支持以图搜图、以文搜图、图文混合搜图的图文检索应用。​
说明​
豆包大模型当前为加白功能,如需使用,请提交工单申请。​
背景信息​
图文检索在电商、广告、设计、搜索引擎等热门领域被广泛应用。常见的图文检索包括以图搜图和以文搜图,用户通过文字描述或上传图片就可以在海量的图片库中快速找到同款或者相似图片。​
图片的文本描述和图片作为检索对象,分别对 image 和 text 进行特征提取,并在模型中对文本和图片建立相关联系,然后在海量图片数据库进行特征向量检索,返回与检索对象最相关的记录集合。其中文本描述由豆包视觉理解模型 Doubao-1.5-vision-pro-32k 生成,特征提取部分采用 CLIP 模型,向量检索采用火山引擎云搜索服务在海量图片特征中进行快速搜索。​
​​​
前提条件​
  • 已创建用于存储图片的 TOS 存储桶和文件夹,其中存储桶名称为 doc-test,Endpoint 域名节点为 tos-cn-beijing.ivolces.com,图片存放路径为 picture/image_temp。具体操作,请参见创建存储桶和创建文件夹。​
注意事项​
  • 目前仅 OpenSearch 2.9.0 及更新版本实例支持 AI 搜索。如何创建 OpenSearch 实例,具体操作,请参见创建实例。​
  • 创建豆包视觉理解模型 Doubao-1.5-vision-pro-32k 的在线推理接入点来生成图片描述。如何创建在线推理接入点,具体操作,请参见创建在线推理接入点。​
  • 豆包大模型当前为加白功能,如需使用,请提交工单申请。​
部署图文检索应用​
创建 AI 搜索​
OpenSearch 实例创建后,您可以在实例中创建 AI 搜索,用于后续的模型创建和部署。​
如何创建 AI 搜索,请参见创建 AI 搜索。​
说明​
  • 目前支持 CPU 和 GPU 两种资源类型,本文选择使用 CPU 类型资源。如需了解各参数含义,请参见创建 AI 搜索。​
  • 使用 CPU 资源类型时,建议选择 1:4 资源比例,能够保障模型服务应用的性能更佳。​
创建并启动模型​
您可以在 AI 搜索中创建 Image Embedding 模型,也可以选择使用豆包的图文向量化模型。AI 搜索中的 Image Embedding 模型类型支持 CLIP 和 ChineseCLIP 及其衍生模型,本文以 CLIP 模型为例。​
云搜索服务为您提供了模型示例文件,您可以自行下载上传。​
​
​
model.zip
​
​
  1. 上传 Image Embedding 模型文件到 TOS Bucket。​
  1. 下载模型文件。​
  1. 登录对象存储控制台,将文件上传到存储桶。​
  1. 假设模型文件完整的存放路径为 tos://doc-test/ImageEmbedding/。具体操作,请参见创建存储桶和上传文件。​
  1. 创建模型。​
  1. 登录云搜索服务控制台,在左侧导航栏选择 AI 搜索,查找并单击目标服务名称。​
  1. 在左侧导航栏选择模型管理,然后选择我的模型页签,再单击创建模型。​
  1. 选择创建 Image Embedding 模型,然后设置模型相关参数,再单击确定。​
  • ​
    参数​
    说明​
    模型名称​
    自定义设置模型名称,例如 ImageSearch-Demo。Image Embedding 模型具备以下两种能力:支持直接对图片和文字进行向量化,返回向量化结果。如何获取向量化,请参见调用 Embedding 模型。代理 OpenSearch,使用类似 OpenSearch 接口同时进行向量化和写入操作。​
    模型类型​
    选择创建 Image Embedding 模型。​
    选择存储桶​
    从下拉列表中选择存放模型文件的对象存储桶。​
    模型文件​
    选择对象存储文件,表示从选择的对象存储桶中选择模型文件,此时还需要设置对象存储文件夹,即从该文件夹中读取已经存在的模型文件。​
    描述​
    设置模型的描述语句。​
    ​
  1. 创建并启动推理服务。​
  • 您可以创建一个推理服务,关联新建的 Image Embedding 模型,并根据业务需求配置网络、资源和自定义参数等信息。​
  1. 在左侧导航栏选择推理服务,然后单击创建推理服务。​
  1. 在创建推理服务面板,配置推理服务相关参数,然后单击确定。​
  • ​
    配置​
    说明​
    服务名称​
    自定义设置推理服务的名称,比如“image-search”。首字符仅支持字母或下划线(_)。可包含字母、数字、特殊字符仅支持英文句号(.)、下划线(_)、短横线(-)、反斜杠(/),长度为 1~128 个字符。最多只能包含一个反斜杠(/)字符。​
    选择模型​
    从拉列表框选择新建的 Image Embedding 模型。​
    可用区​
    选择需要部署推理服务的可用区。如果 OpenSearch 实例是单可用区,那么您的推理服务也只有一个可用区。如果需要多可用区部署推理服务,则需要确保 OpenSearch 实例有多个可用区。如何为实例添加可用区,请参见添加可用区。​
    是否开启高可用区​
    单可用区部署推理服务时,支持为推理服务开启高可用。​
    开启高可用的推理服务,节点数量至少为 2。​
    资源类型​
    从下拉列表中选择推理服务使用的资源类型,您可以选择 CPU 和 GPU 两种资源类型。​
    如果需要使用的资源类型还未启用,或资源规格太低,您可以选择先变配 AI 搜索。具体操作,请参见变配 AI 搜索。​
    规格​
    从下拉列表中选择规格。​
    节点数量​
    设置推理服务的节点数量。单可用区部署,未开启高可用:节点数量可设范围为 1~512。单可用区部署,开启高可用:节点数量为 2 的整数倍,最大值为 512。多可用区部署:节点数量为可用区个数的整数倍,最大值为 512。比如可用区个数为 2,节点数量就为 2 的整数倍。​
    网络配置​
    是否开通私网访问。​
    开通后,系统将为推理服务关联的模型分配一个私网访问地址,允许与 AI 搜索在相同 VPC 环境内的客户端访问关联的模型。​
    描述​
    自定义设置推理服务的描述信息。​
    高级选项​
    为推理服务配置自定义参数,默认提供 UserConfig、MaxReplicasPerNode 两个参数。​
    本文关联 Image Embedding 模型,无需填写。​
    ​
  1. 启动推理服务。​
  1. 推理服务创建后,单击操作列中的启动。​
  1. 在弹出的对话框,单击确定。​
  1. 获取 Image Embedding 模型调用信息。​
  • 推理服务启动后,在推理服务详情页面单击查看调用信息,然后获取模型的调用信息。​
创建 Ingest Pipeline​
创建 Ingest Pipeline,需要指定使用的 Image Embedding 模型,可以将指定字段(图片信息)转换为向量后嵌入回去。​
  1. 登录可视化工具,进入命令行。​
  1. 登录可视化工具。使用公网访问地址登录可视化工具,请参见公网访问 Kibana/Dashboards。​
  1. 在左侧导航栏选择 Management > Dev Tools。​
  1. 执行以下命令,创建 ID 为 image_remote_embedding 的 Ingest Pipeline。​
  • 创建 Ingest Pipeline 时,请注意填写以下信息:​
  • url:设置为从模型服务的调用信息中获取模型的 url。​
  • model:设置从模型服务的调用信息中获取模型的 model,例如 ImageSearch-Demo。​
  • "vector_source_image": "vector":索引中的 vector_source_image 字段含义为图片的 TOS Bucket 存放路径,该配置表示将 vector_source_image 字段转为向量存储到 vector 中。​
  • ​
    PUT _ingest/pipeline/image_remote_embedding​
    {​
    "description": "image embedding pipeline for remote inference",​
    "processors": [​
    {​
    "remote_text_embedding": {​
    "remote_config": {​
    "method": "POST",​
    "url": "http://d-182800***-serve-svc.r-00g**:8000/v1/embeddings",​
    "params": {​
    ​
    },​
    "headers": {​
    "Content-Type": "application/json"​
    },​
    "advance_request_body": {​
    "model": "ImageSearch-Demo"​
    }​
    },​
    "field_map": {​
    "vector_source_image": "vector"​
    }​
    }​
    }​
    ]​
    }​
    ​
创建 Search Pipeline​
创建 Search Pipeline,需要指定使用的 Image Embedding 模型。​
  1. 登录可视化工具,进入命令行。​
  1. 登录可视化工具。使用公网访问地址登录可视化工具,请参见公网访问 Kibana/Dashboards。​
  1. 在左侧导航栏选择 Management > Dev Tools。​
  1. 执行以下命令,创建 ID 为 image_remote_embedding_search_pipeline 的 Search Pipeline。​
  • 创建 Search Pipeline 时,请注意填写以下信息:​
  • url:设置为从模型服务的调用信息中获取模型的 url。​
  • model:设置从模型服务的调用信息中获取模型的 model,例如 ImageSearch-Demo。​
  • ​
    PUT _search/pipeline/image_remote_embedding_search_pipeline​
    {​
    "description": "image embedding pipeline for remote inference",​
    "request_processors": [​
    {​
    "remote_embedding": {​
    "remote_config":​
    {​
    "method": "POST",​
    "url": "http://d-1828002***-serve-svc.r-00g***:8000/v1/embeddings",​
    "params":​
    {},​
    "headers":​
    {​
    "Content-Type": "application/json"​
    },​
    "advance_request_body":​
    {​
    "model": "ImageSearch-Demo"​
    }​
    }​
    }​
    }​
    ]​
    }​
    ​
创建索引​
OpenSearch 实例创建后,您可以在实例中创建一个索引,用于存储图片的向量信息。​
  1. 登录可视化工具,进入命令行。​
  1. 登录可视化工具。使用公网访问地址登录可视化工具,请参见公网访问 Kibana/Dashboards。​
  1. 在左侧导航栏选择 Management > Dev Tools。​
  1. 执行以下命令,创建一个名称为 image_search 的索引。​
  • 创建索引时,指明使用的默认管道 default_pipeline 为新建的 Ingest Pipeline,ID 为 image_remote_embedding。​
  • id:图片的 ID 字段,可理解为图片名称或 ID,起标识作用。​
  • description:图片的描述信息,由豆包视觉理解模型生成。​
  • vector_source_image:表示图片在 TOS Bucket 上的存储路径,通过该路径可以获取到具体图片。​
  • vector:表示图片生成的 embedding 向量字段,将 vector设置为 knn_vector,dimension 需要和使用的模型保持相同维度,例如 768。​
  • ​
    PUT imagesearch​
    {​
    "settings": {​
    "default_pipeline": "image_remote_embedding",​
    "index": {​
    "refresh_interval": "60s",​
    "number_of_shards": "3",​
    "knn.space_type": "cosinesimil",​
    "knn": "true",​
    "number_of_replicas": "1"​
    }​
    },​
    "mappings": {​
    "properties": {​
    "id": {​
    "type": "text"​
    },​
    "description": {​
    "type": "text"​
    },​
    "vector_source_image": {​
    "type": "text"​
    },​
    "vector": {​
    "type": "knn_vector",​
    "dimension": 768,​
    "method": {​
    "name": "hnsw",​
    "engine": "nmslib",​
    "space_type": "cosinesimil"​
    }​
    }​
    }​
    }​
    }​
    ​
创建图文搜索应用​
使用 Python 语言利用 Gradio 库搭建交互式的图文检索应用。​
  1. 安装依赖。​
  • ​
    pip install gradio opensearch-py tos openai​
    ​
  1. 生成图片描述。​
  1. 配置环境变量,将 <ARK_API_KEY> 替换为您的方舟 API Key。方舟 API Key 获取方式,请参见 API Key 管理。​
  • ​
    export ARK_API_KEY="ARK_API_KEY"​
    ​
  1. 利用豆包视觉模型推理接入点生成图片描述,注意填写以下信息:​
  • ark_model:设置为豆包视觉模型推理接入点 ID。​
  • ​
    import os​
    import base64​
    from openai import OpenAI​
    from PIL import Image​
    ​
    def gen_image_description(image, prompt="提取图中商品的关键词,直接输出结果", image_path=None):​
    ark_model = 'ep-****'​
    llm = OpenAI(api_key=os.environ.get("ARK_API_KEY"), base_url="https://ark.cn-beijing.volces.com/api/v3")​
    ​
    if image_path:​
    image = Image.open(image_path)​
    if image is None:​
    return ""​
    response = llm.chat.completions.create(​
    model=ark_model,​
    temperature=0.1,​
    max_tokens=64,​
    messages=[​
    {​
    "role": "user",​
    "content": [​
    {"type": "text", "text": prompt},​
    {​
    "type": "image_url",​
    "image_url": {​
    "url": "data:image/jpeg;base64," + image_to_base64(image)}​
    }​
    ]​
    }​
    ])​
    return response.choices[0].message.content​
    ​
    def image_to_base64(pil_image):​
    buffered = BytesIO()​
    pil_image.save(buffered, format="JPEG")​
    img_str = base64.b64encode(buffered.getvalue())​
    return img_str.decode()​
    ​
  1. 配置 OpenSearch 和 TOS 连接。​
  • 连接 OpenSearch 实例,请注意填写以下信息:​
  • port:设置为 OpenSearch 实例的访问端口。​
  • username 和 password: 设置为 OpenSearch 实例的访问用户名和访问密码。​
  • ​
    from opensearchpy import OpenSearch​
    ​
    host = 'opensearch-o-***.escloud.volces.com'​
    port = '9200'​
    auth = ('username', 'password')​
    index = 'imagesearch'​
    client = OpenSearch(​
    hosts=[{'host': host, 'port': port}],​
    http_compress=True,​
    http_auth=auth,​
    use_ssl=True,​
    verify_certs=False,​
    ssl_assert_hostname=False,​
    ssl_show_warn=False,​
    )​
    ​
  • 建立 TOS 连接,请注意填写以下信息:​
  • tos_path:设置为期望存储图片文件的 TOS Bucket 路径。​
  • tos_region:设置为 TOS Bucket 所在的区域,如 cn-beijing。​
  • ​
    import tos​
    ​
    accessKey = 'AK**'​
    secretKey = '****'​
    tos_path = 'tos://doc-test/picture/image_temp'​
    tos_region = 'cn-beijing'​
    tos_endpoint = 'https://tos-' + tos_region + '.volces.com'​
    tos_client = tos.TosClientV2(accessKey, secretKey, tos_endpoint, tos_region)​
    ​
  1. 上传图片。​
  • 将图片上传到 TOS Bucket,并生成图片向量和图片描述存储到 OpenSearch。​
  • ​
    import gradio as gr​
    ​
    def upload_images(file_objs, ark_prompt: str = None, progress=gr.Progress()):​
    result = []​
    for file_obj in progress.tqdm(file_objs, desc="Uploading files"):​
    file_name = file_obj.name​
    base_file_name = os.path.basename(file_name)​
    tos_file_path = upload_to_tos(file_obj)​
    upload_to_opensearch(index, tos_file_path, file_name, ark_prompt)​
    result.append([base_file_name, "Done"])​
    return result​
    ​
    def upload_to_tos(file):​
    from urllib.parse import urlparse​
    bucket_name = urlparse(tos_path).netloc​
    prefix = urlparse(tos_path).path[1:]​
    object_key = os.path.join(prefix,os.path.basename(file))​
    tos_client.put_object_from_file(bucket_name, object_key, file)​
    return os.path.join(tos_path, os.path.basename(file))​
    ​
    def upload_to_opensearch(index, tos_file_path, local_file_path, ark_prompt):​
    base_name = os.path.basename(tos_file_path)​
    document = {​
    "id": base_name,​
    "vector_source_image": tos_file_path,​
    "description": gen_image_description(None, prompt=ark_prompt, image_path=local_file_path)​
    }​
    print(document['description'])​
    pipeline_id = 'image_remote_embedding'​
    client.index(index=index, body=document, id=base_name, params={"pipeline": pipeline_id})​
    ​
  1. 搜索图片。​
  • ​
    from concurrent.futures import ThreadPoolExecutor​
    ​
    def search_images(img: Image.Image, description=None, percent=None, size=None):​
    result = query_opensearch(img, description, percent, size)​
    image_list = []​
    data_list = []​
    for item in result:​
    image_list.append(item["_source"]["vector_source_image"])​
    data_list.append(​
    [item["_id"], item["_score"], item["_source"]["vector_source_image"], item["_source"]["description"]])​
    return data_list, download_from_tos(image_list)​
    ​
    def query_opensearch(img: Image.Image, description=None, percent=None, size=None):​
    if not size:​
    size = 20​
    bool_queries = {"must": [], "should": []}​
    bool_queries["must"].append({​
    "remote_neural": {​
    "vector": {​
    "query_text": "data:image," + image_to_base64(img),​
    "k": size * 10,​
    "boost": 100​
    }​
    }​
    })​
    if description and percent and percent > 0:​
    bool_queries["must"].append({​
    "remote_neural": {​
    "vector": {​
    "query_text": description,​
    "k": size * 10,​
    "boost": percent​
    }​
    }​
    })​
    bool_queries["should"].append({​
    "dis_max": {​
    "queries": [​
    {​
    "match": {​
    "description": {​
    "query": description,​
    "boost": percent/10​
    }​
    }​
    }​
    ]​
    }​
    })​
    search_query = {​
    "_source": {​
    "exclude": ["vector"]​
    },​
    "size": size,​
    "query": {​
    "function_score": {​
    "query": {​
    "bool": bool_queries​
    }​
    }​
    }​
    }​
    ​
    search_pipeline_id = 'image_remote_embedding_search_pipeline'​
    result = client.search(index=index, body=search_query, params={"search_pipeline": search_pipeline_id})​
    return result["hits"]["hits"]​
    ​
    def download_from_tos(keys):​
    with ThreadPoolExecutor() as executor:​
    images = list(executor.map(get_image_from_tos, keys))​
    return images​
    ​
    def get_image_from_tos(url):​
    from urllib.parse import urlparse​
    bucket_name = urlparse(url).netloc​
    prefix = urlparse(url).path[1:]​
    object_stream = tos_client.get_object(bucket_name, prefix)​
    return Image.open(object_stream)​
    ​
  1. 创建搜索应用界面。​
  • ​
    import gradio as gr​
    ​
    with gr.Blocks(analytics_enabled=False) as demo:​
    local_storage = gr.State([])​
    gr.Markdown("# CloudSearch Image Search")​
    with gr.Tab("Upload Files"):​
    with gr.Column():​
    with gr.Row():​
    with gr.Column():​
    gr.Markdown("# 上传图像文件")​
    file_input = gr.File(file_count="multiple", label="支持 png,jpeg,jpg,webp 类型 ", file_types=['image'])​
    radio_input = gr.Radio([False, True], label="是否开启图像增强,开启后会调用大模型生成图像描述", value=True)​
    prompt = gr.Textbox(lines=1, label="Prompt", value='提取图中商品的关键词,直接输出结果')​
    upload_button = gr.Button("Uploads")​
    result = gr.Dataframe(​
    headers=["File", "Status"],​
    datatype=["str", "str"],​
    label="Upload Result",​
    )​
    upload_button.click(upload_images, inputs=[file_input, prompt], outputs=[result])​
    ​
    with gr.Tab("Search Image"):​
    with gr.Column():​
    with gr.Row():​
    image_input = gr.Image(type="pil", sources=["upload"], height=450, width=450)​
    with gr.Column():​
    gr.Markdown("图片描述内容用来提供混合搜索。可以调用大模型生成图片描述,也可以手动填写内容,如果描述内容为空,则只进行图片搜索")​
    prompt = gr.Textbox(lines=1, label="Prompt", value='提取图中商品的关键词,直接输出结果')​
    generate_des_btn = gr.Button(value="生成图片描述")​
    description_input = gr.Textbox(lines=1, label="描述", value='')​
    temp_slider = gr.Slider(0, 100, value=1, step=0.1, interactive=True, label="文字描述搜索占比")​
    generate_des_btn.click(gen_image_description,inputs=[image_input, prompt],outputs=[description_input])​
    size = gr.Number(minimum=10, maximum=1000, step=10, value=10)​
    image_button = gr.Button("Search Image")​
    data_output = gr.Dataframe(​
    headers=["Id", "Score", "ImageUrl","Description"],​
    datatype=["str", "number", "str", "str"],​
    label="Image List",​
    )​
    image_output = gr.Gallery(show_label=False, elem_id="gallery", columns=[4], object_fit="contain", height="auto")​
    ​
    image_button.click(search_images, inputs=[image_input, description_input, temp_slider, size], outputs=[data_output, image_output])​
    ​
    demo.launch(server_name="0.0.0.0", server_port=7860)​
    ​
效果验证​
上传图片数据​
启动图文搜索应用后,在浏览器中打开 http://0.0.0.0:7860/, 从页面上传图片。​
​Image​​
图文混合搜图​
启动图文搜索应用后,在浏览器中打开 http://0.0.0.0:7860/,从页面进行图片检索。​
​Image​​
​
最近更新时间:2026.03.03 16:19:33
这个页面对您有帮助吗?
有用
有用
无用
无用