基于 Hologres 构建智能驾驶图像高性能分析系统

随着人工智能技术的深入发展,企业对数据的利用已不再局限于传统的结构化数据分析。越来越多的行业开始依赖多模态数据进行智能决策,涵盖商品推荐、驾驶行为分析、金融风控、教育个性化等多个场景。这些场景普遍具备一个共同特征:数据形态多样、分析需求复杂、检索方式多元。Hologres 4.0的整体架构围绕"多模态分析检索 all-in-one"设计,实现"一份数据、一份计算、多模分析"的一站式目标,一条SQL即可完成从数据接入、AI加工到多模查询的全流程。

智能驾驶场景中,可以看到采集的车机各种信号数据,以大宽表的形式存储在数据库中。这些信号数据通常会包含结构化数据(车辆状态、车机版本等)、半结构化数据(车机信号)、非结构化数据(轨迹照片等)。在业务应用的时候,要进行点查、OLAP分析、全文检索、向量检索、混合检索等多种场景。

传统架构往往依赖多个独立引擎协同工作,导致系统复杂、成本高昂、数据不一致等问题频发。AI时代的应用需要在一个统一平台上完成OLAP分析、点查服务、全文检索、向量搜索以及AI推理等多种能力的融合使用。本文以自动驾驶图像数据集为例,介绍Hologres如何在传统的结构化分析的基础上,完成文搜图、图搜图等多模态分析场景。

基于Hologres构建自动驾驶图像高性能分析系统

在自动驾驶系统中,车辆图片分析是环境感知模块的核心场景之一,主要用于实时解析车辆内外部摄像头捕捉的视觉信息,以实现对周围环境的精准理解与决策。本文以BDD自动驾驶数据集为例模拟自动驾驶场景,模拟真实驾驶场景,采集10万张包括驾驶区域、地理、环境、天气多样性等数据的图片,通过对图片的分析与检索,进行了行驶轨迹分析、环境感知优化及行人车辆识别精度提升等全栈技术验证,提升系统对复杂交通场景的适应能力、安全性和用户体验。

基于Hologres构建自动驾驶图像高性能分析系统包含的主要能力如下:

  • 非结构化数据(Object Table):支持通过表的形式读取OSS中非结构化数据(PDF、IMAGE、PPT等)。
  • AI Function:在Hologres中可以用标准SQL的方式调用Function,自动调用内置大模型,完成AI服务建设场景。
    • 数据加工:提供Embed、Chunk算子,可以对非结构化数据加工成结构化数据存储,无需使用外部算法就能自动Embed。
    • 数据检索和分析:提供ai_gen、ai_summarize等算子,使用SQL就能对数据进行推理、问题总结以及翻译等能力。
  • Dynamic Table介绍:支持增量刷新模式对非结构化数据自动加工,每次只计算增量的数据有效减少重复计算,降低资源利用率。
  • 向量检索:支持标准SQL的向量检索,用于非结构化数据的相似度搜索、场景识别等,在同一个查询中可以自由地实现向量和标量的检索。
  • 全文检索:通过倒排索引、分词等机制实现对非结构化数据的高效检索,支持关键词匹配、短语检索等丰富的检索方式,实现更加灵活的检索。

方案优势

通过如上核心能力,在Hologres中对图片检索的核心优势如下:

  • 完整的AI数据处理流程:涵盖从数据Embed、Chunk、增量加工和检索/分析的全流程,与使用大数据系统一样轻松构建AI应用。
  • 标准SQL加工图片数据 :无需使用专用开发语言,纯SQL就能完成图片数据提取、加工。
  • 一个平台支持跨模态检索:支持以文搜图、以图搜图,语义理解突破关键词局限,在Hologres就能实现跨模态检索。
  • 检索更精准、灵活和智能:可以轻松构建"关键词+语义+多模态"的混合检索链路,覆盖从精准搜索到意图理解的全场景需求。还能结合AI Function实现对用户意图的深度理解,语义关联和上下文推理,实现更智能的检索能力。
  • 数据不出库,安全性更高:不需要将数据导出到外部系统,与Hologres的多种安全能力无缝集成,并高效保护数据安全。

方案流程

本次方案的流程如下:

  1. 数据集准备。

将图片数据上传至OSS存储。

  1. 图片加工。

使用Object Table读取图片的元数据信息,然后创建增量刷新的Dynamic Table对数据进行Embed,并为Dynamic Table构建向量索引,以便后续检索能够充分利用索引的功能。

  1. 使用ai_embed算子将自然语言的问题进行Embedding,然后使用向量检索输出Top N的结果。

准备工作

  1. 数据准备

本文使用ModelScope公开的BDD100K 自动驾驶图像数据集中val.zip文件,模拟多个车辆真是行驶数据。

  1. 环境准备
  • 购买Hologres V4.0及以上版本实例并创建数据库。
  • 购买AI资源。

本文以large-96core-512GB-384GB、1个节点为例。

  • 模型部署。本次方案使用的模型以及分配的资源为:
类别 示例值
模型名称 image_embed
模型类别 clip-ViT-B-32
模型作用描述 图片Embedding
单副本CPU(Core) 7
单副本内存 30 GB
单副本GPU 1卡(96 GB)
资源副本数 1

**说明:**上述模型的资源均为默认分配的资源。

操作步骤

1.下载图片数据并导入至OSS。

  • 下载BDD100K 自动驾驶图像数据集中的文件。
  • 登录OSS管理控制台,创建Bucket并将已下载的文件上传至该Bucket路径下。上传操作详情,请参见简单上传。

**说明:**文件夹名称请使用小写。

  1. 账号授权。

a. 登录RAM控制台,创建阿里云RAM角色并授予OSS的相关权限。 推荐授予AliyunOSSReadOnlyAccess权限。

b.为上述阿里云RAM角色添加登录和Hologres的访问权限。

  • 阿里云账号(主账号)

修改RAM角色的信任策略。重点需更新如下参数:

json 复制代码
{
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Effect": "Allow",
      "Principal": {
        "RAM": [
          "acs:ram::1866xxxx:root"
        ],
        "Service": [
          "hologres.aliyuncs.com"
        ]
      }
    }
  ],
  "Version": "1"
}
  • RAM用户(子账号)
  1. 为RAM用户授权。
  • 权限管理 > 权限策略 页面,单击创建权限策略 ,并选择脚本编辑模式创建权限策略。具体操作,请参见创建自定义权限策略

Hologres可通过该策略判断当前RAM用户是否具备创建对应RAM角色的权限。权限策略内容如下。

json 复制代码
{
  "Version": "1",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "hologram:GrantAssumeRole",
      "Resource": "<arn账号>"
    }
  ]
}
  • 身份管理 > 用户 页面,单击目标RAM用户操作 列中的添加权限,为RAM用户(子账号)授予上述步骤已创建的权限策略。具体操作,请参见为RAM用户授权。
  1. 为已创建的RAM角色授权。

修改RAM角色的信任策略。重点需更新如下参数:

json 复制代码
{
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Effect": "Allow",
      "Principal": {
        "RAM": [
          "acs:ram::1866xxxx:root"
        ],
        "Service": [
          "hologres.aliyuncs.com"
        ]
      }
    }
  ],
  "Version": "1"
}
  1. 对图片进行Embedding。

创建Object Table和Dynamic Table读取图片元数据,并对图片加工Embedding。因为流程较长,Hologres直接将过程封装成附录:存储过程。该存储过程包括的能力如下:

  • 创建一张Object Table,用于读取图片的元数据。
  • 创建一张增量刷新的Dynamic Table结果表,用于存储加工后的数据,并设置向量索引。该Dynamic Table未设置自动刷新,需要手动刷新。
  • Dynamic Table的刷新过程中会使用ai_embed对图片进行Embedding。

该存储过程的使用如下:

sql 复制代码
--存储过程,创建Object Table和Dynamic Table,使用dt对图片进行Embedding
CALL create_image_table_from_oss(
    oss_path => 'oss://xxxx/bdd100k/val/images',
    oss_endpoint => 'oss-cn-hangzhou-internal.aliyuncs.com',
    oss_role_arn => 'acs:ram::1xxxx:role/xxxx',
    image_table => 'public.dt_image_bdd100k',
    embedding_model =>'image_embed'
);
  1. 刷新结果表。

通过上述步骤创建的Object Table和Dynamic Table都需要手动刷新,才能完成数据加工。该步骤已被封装为附录:存储过程,该存储过程包括的能力如下:

  • 刷新一次Object Table获取图片元数据。
  • 刷新一次Dynamic Table,进行图片的Embedding加工。

该存储过程的使用如下:

sql 复制代码
--刷新Dynamic Table,将图片Embedding
CALL refresh_image_table(
    image_table => 'public.dt_image_bdd100k'
);
  1. 图片检索。

图片数据处理过后可以使用向量检索和AI Function进行检索。

以文搜图:

如果使用clip-ViT-B-32模型进行以文搜图,问题请使用英文。如果是中文问题,请换成LLM模型。以文搜图的示例SQL如下:

sql 复制代码
--以文搜图
SELECT
    object_uri,
    approx_cosine_distance (embedding_vector, ai_embed ('image_embed', 'a red car in the rain')) AS score
FROM
    public.dt_image_bdd100k
ORDER BY
    score DESC
LIMIT 1;

                            object_uri                         |  score   
---------------------------------------------------------------+-------
 oss://****/bd****k/val/images/b836b14a-fb13****.jpg| 0.322337151
(5 rows)

在OSS中找到第一个结果的图片如下:

以图搜图:

以图搜图的示例SQL如下:

sql 复制代码
--以图搜图
SELECT
    object_uri,
    approx_cosine_distance (embedding_vector, ai_embed ('image_embed', to_file ('oss://xxxx/val/images/b9b53753-91a5d5f8.jpg', 'oss-cn-hangzhou-internal.aliyuncs.com', 'acs:ram::18xxx:role/xxx'))) AS score
FROM
    public.dt_image_bdd100k
WHERE
    object_uri <> 'oss://hm-**-hangzhou/bd****k/val/images/b9b53753-91a5****.jpg' --排除自身
ORDER BY
    score DESC
LIMIT 1;

                          object_uri                           |  score   
---------------------------------------------------------------+------
 oss://****/bd****k/val/images/c0e9b7c4-cd8b****.jpg | 0.918008327

在OSS中找到召回的图片,并做对比,结果如下:

附录:存储过程

  • 创建Object Table和Dynamic Table
sql 复制代码
-- Query查询结果默认限制200行,如需更多数据请修改limit,最多展示10000行或20M。
CREATE OR REPLACE PROCEDURE create_image_table_from_oss(
    oss_path TEXT,
    oss_endpoint TEXT,
    oss_role_arn TEXT,
    image_table TEXT,
    embedding_model TEXT DEFAULT NULL,
    overwrite BOOLEAN DEFAULT FALSE
)
AS $$
DECLARE
    image_schema_name TEXT;
    image_table_name TEXT;
    obj_table_name TEXT;
    full_image_table_ident TEXT;
    full_obj_ident TEXT;
    embed_expr TEXT;
    create_sql TEXT;
    embedding_dims INT;
BEGIN
    -- 1. 拆 schema name + table name
    IF position('.' in image_table) > 0 THEN
        image_schema_name := split_part(image_table, '.', 1);
        image_table_name  := split_part(image_table, '.', 2);
    ELSE
        image_schema_name := 'public';
        image_table_name  := image_table;
    END IF;

    obj_table_name := image_table_name || '_obj_table';

    full_image_table_ident := format('%I.%I', image_schema_name, image_table_name);
    full_obj_ident    := format('%I.%I', image_schema_name, obj_table_name);
    
    -- 2. 如果需要覆盖,先删表和索引
    IF overwrite THEN
        DECLARE
            dyn_table_exists BOOLEAN;
            rec RECORD;
        BEGIN
            -- 检查 dynamic table 是否存在
            SELECT EXISTS (
                SELECT 1
                FROM pg_class c
                JOIN pg_namespace n ON n.oid = c.relnamespace
                WHERE c.relname = image_table_name
                AND n.nspname = image_schema_name
            )
            INTO dyn_table_exists;

            IF dyn_table_exists THEN
                -- 2.1 关闭动态表自动刷新
                -- RAISE NOTICE 'Disabling auto refresh for %', full_image_table_ident;
                -- EXECUTE format('ALTER TABLE IF EXISTS %s SET (auto_refresh_enable=false)', full_image_table_ident);

                -- 2.2 查找 RUNNING 刷新任务并取消
                FOR rec IN
                    EXECUTE format(
                        $f$
                        SELECT query_job_id
                            FROM hologres.hg_dynamic_table_refresh_log(%L)
                            WHERE status = 'RUNNING';
                        $f$,
                        image_table
                    )
                LOOP
                    RAISE NOTICE 'Found running refresh job: %', rec.query_job_id;
                    IF hologres.hg_internal_cancel_query_job(rec.query_job_id::bigint) THEN
                        RAISE NOTICE 'Cancel job % succeeded.', rec.query_job_id;
                    ELSE
                        RAISE WARNING 'Cancel job % failed.', rec.query_job_id;
                    END IF;
                END LOOP;

                -- 2.3 删除 Dynamic Table
                EXECUTE format('DROP TABLE IF EXISTS %s;', full_image_table_ident);
            ELSE
                RAISE NOTICE 'Dynamic table % does not exist, skip cancel job and drop.', full_image_table_ident;
            END IF;

            -- 2.4 无论如何,Object Table 都要删除
            EXECUTE format('DROP OBJECT TABLE IF EXISTS %s;', full_obj_ident);
        END;
    END IF;

    -- 3. 创建 Object Table
    RAISE NOTICE 'Create object table: %', obj_table_name;
    EXECUTE format(
        $f$
        CREATE OBJECT TABLE %s
        WITH (
            path = %L,
            oss_endpoint = %L,
            role_arn = %L
        );
        $f$,
        full_obj_ident,
        oss_path,
        oss_endpoint,
        oss_role_arn
    );

    COMMIT;

    -- 4. 刷新 Object Table
    RAISE NOTICE 'Refresh object table: %', obj_table_name;
    EXECUTE format('REFRESH OBJECT TABLE %s;', full_obj_ident);

    COMMIT;

    -- 5. embedding 模型选择
    IF embedding_model IS NULL OR length(trim(embedding_model)) = 0 THEN
        embed_expr := 'ai_embed(file)';

        EXECUTE 'SELECT array_length(ai_embed(''dummy''), 1)'
        INTO embedding_dims;
    ELSE
        embed_expr := format('ai_embed(%L, file)', embedding_model);

        EXECUTE format(
            'SELECT array_length(ai_embed(%L, ''dummy''), 1)',
            embedding_model
        )
        INTO embedding_dims;
    END IF;

    RAISE NOTICE 'embedding dimension is: %', embedding_dims;

    -- 6. 创建 RAG 输出动态表
    RAISE NOTICE 'create dynamic table: %', image_table_name;
    EXECUTE format(
        $f$
        CREATE DYNAMIC TABLE %s(
            CHECK(array_ndims(embedding_vector) = 1 AND array_length(embedding_vector, 1) = %s)
        )
        WITH (
            vectors = '{
                "embedding_vector": {
                    "algorithm": "HGraph",
                    "distance_method": "Cosine",
                    "builder_params": {
                    "base_quantization_type": "sq8_uniform",
                    "max_degree": 64,
                    "ef_construction": 400,
                    "precise_quantization_type": "fp32",
                    "use_reorder": true
                    }
                }
            }',
            auto_refresh_mode = 'incremental',
            freshness = '5 minutes',
            auto_refresh_enable = 'false'
        ) AS
        SELECT
            object_uri,
            etag,
            %s AS embedding_vector
        FROM %s;
        $f$,
        full_image_table_ident,
        embedding_dims,
        embed_expr,
        obj_table_name
    );

    COMMIT;

    RAISE NOTICE '';
    RAISE NOTICE 'Create image table success: %', image_table;
    RAISE NOTICE '    Vector index is: %.embedding_vector', image_table;
END;
$$ LANGUAGE plpgsql;
  • Object Table和Dynamic Table
sql 复制代码
CREATE OR REPLACE PROCEDURE refresh_image_table(
    image_table TEXT
)
AS $$
DECLARE
    image_schema_name TEXT;
    image_table_name   TEXT;
    obj_table_name TEXT;
    full_image_table_ident TEXT;
    full_obj_ident    TEXT;
BEGIN
    -- 1. 解析 schema 和表名
    IF position('.' in image_table) > 0 THEN
        image_schema_name := split_part(image_table, '.', 1);
        image_table_name  := split_part(image_table, '.', 2);
    ELSE
        image_schema_name := 'public';
        image_table_name  := image_table;
    END IF;

    obj_table_name := image_table_name || '_obj_table';

    full_image_table_ident := format('%I.%I', image_schema_name, image_table_name);
    full_obj_ident    := format('%I.%I', image_schema_name, obj_table_name);

    -- 2. 刷新 Object Table
    RAISE NOTICE 'Refreshing Object Table: %', obj_table_name;
    EXECUTE format('REFRESH OBJECT TABLE %s;', full_obj_ident);

    -- 3. 刷新 Dynamic Table
    RAISE NOTICE 'Refreshing Dynamic Table: %', image_table_name;
    EXECUTE format('REFRESH TABLE %s;', full_image_table_ident);

    RAISE NOTICE 'Refresh image table complete: %', image_table;
END;
$$ LANGUAGE plpgsql;
  • 删除存储过程
sql 复制代码
CREATE OR REPLACE PROCEDURE drop_image_table(
    image_table TEXT
)
AS $$
DECLARE
    image_schema_name TEXT;
    image_table_name   TEXT;
    obj_table_name TEXT;
    full_image_table_ident TEXT;
    full_obj_ident    TEXT;
    rec RECORD;
BEGIN
    -- 1. 解析 schema 和表名
    IF position('.' in image_table) > 0 THEN
        image_schema_name := split_part(image_table, '.', 1);
        image_table_name   := split_part(image_table, '.', 2);
    ELSE
        image_schema_name := 'public';
        image_table_name   := image_table;
    END IF;

    obj_table_name := image_table_name || '_obj_table';

    full_image_table_ident := format('%I.%I', image_schema_name, image_table_name);
    full_obj_ident    := format('%I.%I', image_schema_name, obj_table_name);

    -- 2. 删除表
    -- 2.1 关闭动态表自动刷新
    -- RAISE NOTICE 'Disabling auto refresh for %', full_image_table_ident;
    -- EXECUTE format('ALTER TABLE IF EXISTS %s SET (auto_refresh_enable=false)', full_image_table_ident);

    -- 2.2 查找 RUNNING 刷新任务并取消
    FOR rec IN
        EXECUTE format(
            $f$
            SELECT query_job_id
                FROM hologres.hg_dynamic_table_refresh_log(%L)
                WHERE status = 'RUNNING';
            $f$,
            image_table
        )
    LOOP
        RAISE NOTICE 'Found running refresh job: %', rec.query_job_id;
        IF hologres.hg_internal_cancel_query_job(rec.query_job_id::bigint) THEN
            RAISE NOTICE 'Cancel job % succeeded.', rec.query_job_id;
        ELSE
            RAISE WARNING 'Cancel job % failed.', rec.query_job_id;
        END IF;
    END LOOP;

    -- 2.3 删除 Dynamic Table
    RAISE NOTICE 'Dropping Dynamic Table: %', image_table_name;
    EXECUTE format('DROP TABLE IF EXISTS %s;', full_image_table_ident);

    -- 2.4 删除 Object Table
    RAISE NOTICE 'Dropping Object Table: %', obj_table_name;
    EXECUTE format('DROP OBJECT TABLE IF EXISTS %s;', full_obj_ident);

    RAISE NOTICE 'Drop image table complete: %', image_table;
END;
$$ LANGUAGE plpgsql;

如果想体验更多操作流程,欢迎到阿里云官网领取Hologres免费试用,开通Hologres4.0,并按照操作文档实践。

help.aliyun.com/zh/hologres...

相关推荐
咚咚王者4 小时前
人工智能之数据分析 numpy:第五章 索引与切片
人工智能·数据分析·numpy
java1234_小锋5 小时前
[免费]基于python的Flask+Vue医疗疾病数据分析大屏可视化系统(机器学习随机森林算法+requests)【论文+源码+SQL脚本】
python·机器学习·数据分析·flask·疾病数据分析
谅望者5 小时前
数据分析笔记10:数据容器
笔记·数据挖掘·数据分析
谅望者6 小时前
数据分析笔记05:区间估计
笔记·数据挖掘·数据分析
Dev7z7 小时前
基于图像处理与数据分析的智能答题卡识别与阅卷系统设计与实现
图像处理·人工智能·数据分析
谅望者21 小时前
数据分析笔记06:假设检验
笔记·数据挖掘·数据分析
源码之家1 天前
机器学习:基于大数据二手房房价预测与分析系统 可视化 线性回归预测算法 Django框架 链家网站 二手房 计算机毕业设计✅
大数据·算法·机器学习·数据分析·spark·线性回归·推荐算法
可观测性用观测云1 天前
利用CMDB数据实现指标业务维度的动态扩展
数据分析
咚咚王者1 天前
人工智能之数据分析 numpy:第一章 学习链路
人工智能·数据分析·numpy