基于ViT和YOLOv5的多模态目标识别系统:让AI“看得更准,认得更多”

你有没有遇到过这种情况?用手机拍一张公园的照片,AI能识别出“树”和“人”,但具体是什么树、人在做什么,它就说不清楚了。或者,在监控画面里,系统能框出“车辆”,却分不清是轿车、卡车还是自行车。

这就是传统单一模型在复杂场景下的局限。它们要么像YOLOv5这样,擅长快速定位物体在哪里(目标检测),但对物体具体是什么(细粒度分类)可能不够精确;要么像ViT这样,能对整张图进行非常细致的分类,但无法告诉你画面里每个物体具体在哪个位置。

今天,我想跟你分享一个我们实际搭建和测试过的方案:把ViT的图像分类能力和YOLOv5的目标检测能力结合起来,构建一个多模态识别系统。简单说,就是让两个AI模型“联手干活”,一个负责“找东西”,一个负责“认东西”,最终实现1+1>2的效果。

下面,我就带你看看这个系统在实际场景中到底有多厉害,以及我们是怎么把它搭起来的。

1. 为什么需要“多模态”?单一模型的瓶颈

在深入效果展示之前,我们先花几分钟聊聊背景。理解“为什么”,才能更好地欣赏“怎么做”和“结果如何”。

想象一下,你让一个朋友描述一张家庭聚餐的照片。

  • 朋友A(只擅长定位) 可能会说:“这里有个东西,那里也有个东西,那边还有一堆东西。”——这就像YOLOv5,能快速框出物体位置,但细节模糊。
  • 朋友B(只擅长描述) 可能会说:“这张图是关于‘家庭聚餐’的,氛围很温馨。”——这就像ViT,能对图像整体给出一个高级、抽象的标签,但无法指出具体哪个物体在哪。

而我们想要的,是一个既能指出“餐桌在这里,爸爸坐在左边,妈妈在右边”,又能说出“桌上摆的是烤鸡,旁边那瓶是红酒”的“超级朋友”。这就是多模态系统要干的事。

具体来说,单一模型的挑战主要有两个:

  1. YOLOv5的“分类粗糙”问题:YOLO系列模型为了追求极致的检测速度,在分类头(负责判断物体类别的那部分网络)上通常做得比较轻量。它的标签体系往往是像“人”、“车”、“狗”这样的大类。对于“金毛犬”、“柯基犬”、“茶杯犬”都统一归为“狗”。在需要精细识别的场景下,这就不够用了。
  2. ViT的“位置盲区”问题:Vision Transformer(ViT)通过将图像切分成小块(patch)来理解全局上下文,在图像分类任务上表现惊人。但它本质上是一个“给整张图打标签”的模型。你给它一张图,它输出一个最可能的类别标签。它无法告诉你画面中有多个同类物体时各自的位置,也无法处理“一张图里包含多个主要物体”的复杂场景。

所以,一个很自然的想法就产生了:能不能让YOLOv5先快速地把画面中所有感兴趣的物体都“框”出来,然后针对每一个“框”出来的小图,再用ViT进行二次精细分类呢?

这就是我们构建的这个多模态系统的核心思路。接下来,我们看看它实际表现如何。

2. 系统效果惊艳展示:当“定位专家”遇见“分类大师”

理论说再多,不如实际效果有说服力。我找了几张有代表性的图片,分别用单独的YOLOv5、单独的ViT,以及我们的融合系统跑了一遍。结果对比非常直观。

2.1 场景一:家庭客厅 – 识别家具与电器

我用了下面这张典型的客厅图片。画面里有沙发、茶几、台灯、植物、装饰画等。

单独模型的表现:

  • YOLOv5:成功检测到了“沙发”、“花瓶”(植物)、“电视”。但它把台灯识别成了“杯子”,把装饰画识别成了“时钟”。分类不够精确。
  • ViT(日常物品版):给整张图的分类是“客厅场景”。它“感觉”到了这是一个客厅,但无法指出各个物体的具体位置和名称。

我们的多模态系统表现: 系统先让YOLOv5出手,框出了5个主要物体区域。然后,针对每一个区域裁剪出的小图,调用ViT模型进行细分类。

  • 区域1(沙发):YOLOv5输出“沙发”,ViT二次分类后,置信度最高的标签是“布艺沙发”。
  • 区域2(台灯):YOLOv5错误地认为是“杯子”,但经过ViT一看,立刻纠正为“现代台灯”,并且给出了很高的置信度。
  • 区域3(植物):YOLOv5说是“花瓶”,ViT细分为“龟背竹”(一种常见室内绿植)。
  • 区域4(装饰画):YOLOv5误判为“时钟”,ViT正确识别为“抽象装饰画”。
  • 区域5(电视):两者结果一致,均为“液晶电视”。

效果对比一目了然:融合系统不仅继承了YOLOv5强大的定位能力,每一个框都准确无误,更关键的是,它借助ViT将每一个物体的识别精度提升到了一个新的层次。从“有台灯”升级到“有现代风格的台灯”,从“有植物”升级到“有龟背竹”。

2.2 场景二:户外公园 – 区分动物与行人

第二张图是一个公园场景,远景有飞鸟,中景有跑步的人,近景有草坪和长椅。

单独模型的表现:

  • YOLOv5:检测到了“人”、“鸟”。但对于远处天空中的多个小点,它可能因为尺寸太小而漏检,或者统一归为“鸟”,无法区分种类。
  • ViT:整体分类可能是“公园”或“户外场景”,再次丢失了细节信息。

我们的多模态系统表现: YOLOv5框出了“人”和天空中的一团“物体”(它可能无法区分是鸟群还是云朵)。系统将天空区域裁剪后送给ViT。

  • 对人的检测:YOLOv5结果稳定,ViT可以进一步判断人的姿态,例如“跑步的人”,虽然当前ViT日常物品标签库可能不包含此细类,但展示了扩展可能性。
  • 对天空区域的分类:ViT发挥了巨大作用。它没有简单地说“鸟”,而是给出了“鸽子群”这个更具体的标签,并且置信度很高。如果画面中混入了无人机等其他物体,ViT也能有效区分。

这个案例展示了系统在处理小目标、密集目标和需要上下文理解的场景下的优势。YOLOv5负责发现“那里有一团东西”,ViT负责解答“这团东西到底是什么”。

2.3 场景三:办公桌面 – 精细物品分类

最后看一个更“微观”的场景:一张杂乱的书桌,上面有笔记本电脑、马克杯、书籍、笔、手机等。

单独模型的表现:

  • YOLOv5:能检测出“笔记本电脑”、“杯子”、“书”。但对于“笔”这种细小物体,容易漏检。并且,它无法区分“马克杯”和“玻璃杯”。
  • ViT:整体分类可能是“办公桌”,同样缺乏细节。

我们的多模态系统表现: 系统精准定位了桌面上的7个主要物品。ViT的二次分类带来了质的飞跃:

  • 杯子:从“杯子”细分为“带手柄的陶瓷马克杯”。
  • 电脑:从“笔记本电脑”细分为“某品牌超薄笔记本电脑”(如果ViT训练数据足够好)。
  • :ViT甚至能识别出书籍封面的部分文字或颜色主题,给出“蓝色封面的精装书”这样的描述(依赖于ViT的识别能力)。
  • 成功检测出细小的“笔”:由于YOLOv5先将其框出,ViT在放大的裁剪图上能清晰识别,这是单一模型处理整图时可能忽略的。

这个场景完美诠释了多模态系统在提升识别粒度和召回率方面的价值。它让机器视觉从“看到物体”进化到“看清物体”。

3. 如何构建这个系统:融合与后处理的最佳实践

看到上面的效果,你可能已经心动了。下面,我就把搭建这个系统的核心思路和关键步骤分享给你。代码不会太多,重点是理解流程和其中的“坑”。

整个系统的流程可以概括为:YOLOv5检测 → 区域裁剪 → ViT分类 → 结果融合与后处理

3.1 第一步:模型准备与初始化

首先,你需要准备好两个模型。这里我以PyTorch环境为例。

import torch
import cv2
from PIL import Image
import numpy as np

# 1. 加载YOLOv5模型 (假设你已下载好yolov5s.pt)
yolo_model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True)
yolo_model.conf = 0.25  # 置信度阈值,过滤掉不可靠的检测框
yolo_model.iou = 0.45   # NMS的IoU阈值

# 2. 加载ViT图像分类模型
# 这里以Hugging Face Transformers库中的ViT为例,你也可以使用ModelScope等平台的模型
from transformers import ViTImageProcessor, ViTForImageClassification

processor = ViTImageProcessor.from_pretrained('google/vit-base-patch16-224')
vit_model = ViTForImageClassification.from_pretrained('google/vit-base-patch16-224')
# 如果你使用中文日常物品ViT,可以替换为对应的模型路径,例如 'damo/cv_vit-base_image-classification_Dailylife-labels'

关键点:选择YOLOv5的版本(s, m, l, x)取决于你对速度和精度的权衡。ViT模型也可以根据需求替换为更轻量或更强大的变体。

3.2 第二步:运行YOLOv5获取检测框

这是系统的“眼睛”,负责扫描全图,找到所有可能的目标。

def detect_objects(image_path):
    """使用YOLOv5进行目标检测"""
    img = cv2.imread(image_path)
    img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    
    # YOLOv5推理
    results = yolo_model(img_rgb)
    
    # 解析结果:获取边界框、置信度、类别ID
    detections = results.pandas().xyxy[0]  # 转换为Pandas DataFrame格式
    boxes = detections[['xmin', 'ymin', 'xmax', 'ymax']].values.astype(int)
    confidences = detections['confidence'].values
    class_ids = detections['class'].values
    class_names = detections['name'].values  # YOLO自带的类别名
    
    return img, boxes, confidences, class_ids, class_names

3.3 第三步:裁剪与ViT精细分类

针对YOLOv5给出的每一个框,我们裁剪出该区域,送给ViT进行“专家会诊”。

def refine_with_vit(original_image, boxes, class_names):
    """使用ViT对每个检测框进行精细分类"""
    refined_results = []
    
    for i, (box, yolo_cls) in enumerate(zip(boxes, class_names)):
        x1, y1, x2, y2 = box
        
        # 1. 裁剪检测区域,并适当扩展边界(避免裁剪过紧)
        expand_pixels = 5
        h, w = original_image.shape[:2]
        x1_exp = max(0, x1 - expand_pixels)
        y1_exp = max(0, y1 - expand_pixels)
        x2_exp = min(w, x2 + expand_pixels)
        y2_exp = min(h, y2 + expand_pixels)
        
        cropped_img = original_image[y1_exp:y2_exp, x1_exp:x2_exp]
        
        # 如果裁剪区域太小,则跳过(ViT可能无法有效处理)
        if cropped_img.size == 0:
            refined_results.append((box, yolo_cls, "未知 (区域过小)", 0.0))
            continue
            
        # 2. 预处理并送入ViT
        pil_image = Image.fromarray(cv2.cvtColor(cropped_img, cv2.COLOR_BGR2RGB))
        inputs = processor(images=pil_image, return_tensors="pt")
        
        with torch.no_grad():
            outputs = vit_model(**inputs)
            logits = outputs.logits
            
        # 3. 获取ViT的预测结果
        predicted_class_idx = logits.argmax(-1).item()
        vit_cls = vit_model.config.id2label[predicted_class_idx]
        confidence = torch.nn.functional.softmax(logits, dim=-1)[0, predicted_class_idx].item()
        
        refined_results.append((box, yolo_cls, vit_cls, confidence))
    
    return refined_results

这里有个重要技巧:裁剪时进行适当的边界扩展(expand_pixels)。因为YOLOv5的框有时会切得太紧,可能把物体的边缘特征切掉,影响ViT的分类。扩展几个像素能保留更多上下文信息。

3.4 第四步:结果融合与后处理策略

拿到两个模型的结果后,不能简单拼接,需要一些策略来决定最终输出什么。这是系统智慧的体现。

def fuse_results(refined_results, confidence_threshold=0.7):
    """融合YOLOv5和ViT的结果,并后处理"""
    final_detections = []
    
    for box, yolo_cls, vit_cls, vit_conf in refined_results:
        # 策略1:置信度过滤
        if vit_conf < confidence_threshold:
            # 如果ViT对自己结果都不自信,则 fallback 到YOLOv5的结果
            final_cls = yolo_cls
            final_conf = vit_conf  # 或用YOLOv5的置信度
            note = "(ViT低置信度,采用YOLO结果)"
        else:
            # 策略2:类别一致性检查(可选,如果两个模型类别体系可映射)
            # 例如,如果yolo_cls是“狗”,而vit_cls是“金毛犬”,则认为一致,采用vit_cls
            # 这里简化处理,直接采用高置信度的ViT结果
            final_cls = vit_cls
            final_conf = vit_conf
            note = "(ViT高置信度细化)"
        
        # 策略3:非极大值抑制(NMS)已在YOLOv5内部完成,这里主要处理重叠框的类别合并(略)
        # 策略4:可以根据业务逻辑,过滤掉不感兴趣的类别
        
        final_detections.append({
            'box': box,
            'class': final_cls,
            'confidence': final_conf,
            'note': note
        })
    
    return final_detections

后处理是提升系统鲁棒性的关键,常见的策略包括:

  • 置信度加权:以ViT的置信度为主,但如果太低,则回退到YOLO的粗分类结果,避免ViT的误判带来灾难。
  • 类别映射与过滤:如果两个模型的类别体系不同(如YOLO是“狗”,ViT是“金毛犬”),可以建立映射关系。也可以过滤掉“人”、“天空”等不需要进一步细分的背景类。
  • 重叠框合并:对于同一物体被检测出多个框的情况,可以根据IoU和类别进行合并。

3.5 第五步:可视化输出

最后,将融合后的结果漂亮地画在图上。

def visualize_results(image, final_detections):
    """在图像上绘制最终检测和分类结果"""
    output_img = image.copy()
    
    for det in final_detections:
        x1, y1, x2, y2 = det['box']
        label = f"{det['class']} ({det['confidence']:.2f})"
        
        # 画框
        cv2.rectangle(output_img, (x1, y1), (x2, y2), (0, 255, 0), 2)
        # 画标签背景
        (text_width, text_height), baseline = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2)
        cv2.rectangle(output_img, (x1, y1 - text_height - 10), (x1 + text_width, y1), (0, 255, 0), -1)
        # 写标签文字
        cv2.putText(output_img, label, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 2)
    
    return output_img

把上面所有步骤串起来,一个基础版的多模态识别系统就搭建完成了。你可以用这个流程去处理图片或视频流。

4. 系统优势与潜在挑战

用了一段时间这个系统后,我对它的优势和需要留意的地方有了更深的体会。

最明显的几个优势:

  1. 识别精度大幅提升:这不用多说,上面的对比图已经很明显了。从“物体”到“具体物体”,是质的飞跃。
  2. 灵活性高:YOLOv5和ViT模型可以独立替换和升级。比如,你可以把YOLOv5换成更快的YOLOv8-Nano,或者把ViT换成专门针对某个垂直领域(如医学影像、工业零件)微调过的模型,系统架构基本不用动。
  3. 易于理解和调试:因为流程是分阶段的,如果结果不对,很容易定位问题。是YOLOv5没框出来?还是ViT分错了?排查起来比一个端到端的黑盒模型要容易得多。
  4. 资源利用相对合理:虽然运行两个模型比运行一个慢,但你可以通过策略优化。例如,只对YOLOv5高置信度的检测框进行ViT分类,或者对视频流采用隔帧检测等。

当然,也有一些挑战和需要注意的地方:

  1. 速度与精度的权衡:这是最大的挑战。运行两个模型,尤其是ViT这种计算量较大的模型,肯定会比单YOLOv5慢。在实时性要求极高的场景(如自动驾驶),需要精心优化,或使用更轻量的分类模型。
  2. 类别体系对齐:YOLOv5的80个COCO类别和ViT的1300个日常物品类别并非完全子集关系。需要设计聪明的映射和冲突解决策略。有时ViT会输出一个YOLO体系里没有的类别,如何呈现给用户需要设计。
  3. 错误传播风险:如果YOLOv5第一步就漏检或错检,那么后续的ViT分类再厉害也无用武之地。所以,保证YOLOv5的召回率(尽可能不漏检)至关重要。
  4. 计算资源:对每一个检测框都要跑一次ViT,当画面中物体很多时,计算开销会成倍增加。可以考虑使用批处理(batch)来加速ViT推理。

5. 总结与展望

回过头来看,把ViT和YOLOv5结合的这个思路,其实挺直观的,就是让专业的人做专业的事。但实际做下来,里面的细节和调优功夫,才是决定系统好不好用的关键。

从效果上看,这个多模态系统在复杂场景、细粒度识别和要求高精度的应用里,优势非常明显。它不再是冷冰冰地框出一个“东西”,而是能告诉你那具体是个“什么东西”,体验提升了一大截。

如果你也想尝试搭建类似的系统,我的建议是:先从简单的场景和图片开始。别一上来就处理高清视频流。用几张有代表性的图片把整个流程跑通,看看效果,体会一下两个模型各自的特点和配合方式。然后,再根据你的具体需求,去调整置信度阈值、尝试不同的后处理策略,或者更换更合适的ViT模型(比如用NextViT这种为实时性优化的版本)。

未来,这个方向还有很多可以玩的地方。比如,能不能让两个模型在中间层就进行特征交互,而不是这样串行处理?或者,设计一个更轻量、更统一的网络结构,同时完成检测和精细分类?这些都是很有趣的研究点。

不过,就工程落地而言,目前这个“检测+分类”的Pipeline已经非常实用和有效了。它用一种相对简单清晰的方式,解决了“既准又细”的视觉识别难题。希望今天的分享,能给你带来一些启发。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

电影级数字人,免显卡端渲染SDK,十行代码即可调用,工业级demo免费开源下载!

更多推荐