news 2026/8/8 7:41:30

【Bug已解决】Kosmos2.5: index error on long ocr input 解决方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Bug已解决】Kosmos2.5: index error on long ocr input 解决方案

【Bug已解决】Kosmos2.5: index error on long ocr input 解决方案

一、现象长什么样

Kosmos2.5 是一个面向 OCR/文档理解的多模态模型,输入通常是"一张文档图 + 一段提示文本"。当文档较长(高分辨率扫描、多行密集文字)时,generateforward抛索引错误:

# 现象 A:图像 patch 索引越界 IndexError: index 1024 is out of bounds for dimension 0 with size 1024 File ".../models/kosmos2_5/modeling_kosmos2_5.py", line 210, in forward img_feat = image_features[image_token_indices] # 现象 B:截断后位置错位 RuntimeError: index -1 is out of bounds for dimension 0 with size 0 # 长输入被截断到 max_length,但 image_token 的占位索引还指着被截掉的位置 # 现象 C:batch 内长短不一拼 padding 后越界 ValueError: too many indices for tensor of dimension 1 # padding 把序列补齐到统一长度,但 image_token_indices 仍是原始未 padding 的下标

最典型的触发:一张高分辨率文档图被切成很多 patch(比如 1024 个),加上 OCR 提示文本后总长度超过max_length=2048,截断逻辑只截了文本侧,却忘了同步修正图像 token 的索引 → 索引越界。

二、背景

Kosmos2.5 的输入构造流程是:

  1. 图像经 vision encoder 切成若干 patch 特征(image_features,长度 = patch 数 N)。
  2. 文本里用特殊<image>token 占位, tokenizer 后这些占位被展开成 N 个 image token,分布在序列的不同位置。
  3. forward 时,模型根据image_token_indices(这些 image token 在序列里的下标)去image_features里取对应特征,拼回序列。

这套机制依赖一个不变式:image_token_indices里的最大值 < N(patch 总数),且截断/ padding 后这些索引必须同步更新。当长 OCR 输入触发截断(为了塞进max_length)或 padding(为了 batch),这个不变式被打破,就出现上面的索引错误。

三、根因

根因有三类:

  1. 截断只截断文本,不修正 image_token_indicesprocessormax_length超限时直接裁掉文本 token 尾部,但 image token 的下标是相对"裁剪前序列"算的。裁剪后序列变短,那些指向被裁区域的 image token 下标变成非法(指向越界或负位置)。

  2. padding 后索引未平移/未 mask。 batch 推理时短样本被 pad 到最长。padding 在序列前面或后面插入了 dummy token,但image_token_indices仍是原始下标,没有随 padding 偏移 → 在 padding 之后的位置取 image_features 时错位。

  3. patch 数 N 与 image token 数不一致。 长文档切的 patch 数超过image_features实际长度(例如 image encoder 有自己内部的max_patches限制,超出的 patch 被丢弃),但 tokenizer 展开的 image token 数仍是按"未限制"算的 → 下标越界。

四、最小可运行复现

下面用纯 Python 模拟"截断文本后 image_token_indices 越界"的逻辑:

from typing import List def build_image_token_indices(seq_len: int, n_image_tokens: int) -> List[int]: """模拟:在序列末尾均匀放置 n_image_tokens 个 image token 的下标。""" step = max(1, seq_len // n_image_tokens) return list(range(0, seq_len, step))[:n_image_tokens] def truncate(seq_len: int, max_len: int) -> int: return min(seq_len, max_len) # 正常短输入 seq_len = 500 n_img = 100 idx = build_image_token_indices(seq_len, n_img) print("短输入 max idx:", max(idx), "patch 数 N:", n_img) # 合法 # 长输入触发截断 max_len = 300 new_len = truncate(seq_len, max_len) new_idx = build_image_token_indices(seq_len, n_img) # 索引仍按旧 seq_len 算! print("截断后序列长:", new_len, "但 image idx max:", max(new_idx)) assert max(new_idx) >= new_len, "复现成功:截断后 image token 索引越界" # 修正版:截断时同步裁剪 image_token_indices def truncate_with_indices(seq_len, max_len, idx): return [i for i in idx if i < max_len] fixed = truncate_with_indices(seq_len, max_len, idx) print("修正后 image idx:", fixed, "max:", max(fixed) if fixed else None) assert all(i < new_len for i in fixed), "修正失败"

运行后,原new_idx的最大值(~499)超过了截断后的序列长度(300),触发越界;修正函数把越界索引裁掉,恢复不变式。

五、解决方案(第一层:最小直接修复)

最快的止血:在调用 processor / 截断前,手动清洗 image_token_indices,使其始终落在有效范围内

import torch def sanitize_image_token_indices(image_token_indices, seq_len, image_features_len): """第一层修复:保证索引在 [0, seq_len) 且 < image_features_len。""" valid = [] for i in image_token_indices: if 0 <= i < seq_len and i < image_features_len: valid.append(i) # 若全部越界(极端长输入),退化为均匀取样 image_features if not valid and image_features_len > 0: step = max(1, image_features_len // seq_len) if seq_len else 1 valid = list(range(0, image_features_len, max(step, 1)))[:seq_len] return valid # 使用示意:在构造模型输入后、forward 前 inputs = processor(images=doc_image, text=prompt, return_tensors="pt", truncation=True, max_length=2048) seq_len = inputs["input_ids"].shape[1] image_token_indices = (inputs["input_ids"][0] == processor.image_token_id).nonzero().flatten().tolist() # 取出 image_features(来自 vision encoder) valid_idx = sanitize_image_token_indices(image_token_indices, seq_len, image_features.shape[0]) # 用合法索引重建(或传给模型一个已清洗的 indices 参数) assert max(valid_idx) < image_features.shape[0], "仍有越界,检查 image_features 长度"

第一层让用户立刻消除IndexError,长 OCR 文档也能跑。

六、解决方案(第二层:结构性改进)

把"索引与序列同步"做成KosmosIndexSync,在 processor 和模型之间统一维护不变式:

from dataclasses import dataclass from typing import List @dataclass class KosmosIndexSync: """维护 image_token_indices 与(截断后)序列长度、image_features 长度的一致。""" max_patches: int = 1024 def sync_after_truncation(self, indices: List[int], new_seq_len: int) -> List[int]: kept = [i for i in indices if 0 <= i < new_seq_len] # 同时保证不超过 image_features 实际容量 kept = [i for i in kept if i < self.max_patches] # 若因截断丢失过多 image token,从 image_features 均匀补回 if len(kept) < max(1, len(indices) // 2) and self.max_patches > 0: step = max(1, self.max_patches // max(new_seq_len, 1)) kept = list(range(0, self.max_patches, step))[:new_seq_len] return kept def sync_after_padding(self, indices: List[int], pad_left: int) -> List[int]: # padding 在左侧插入 dummy 时,所有索引右移 pad_left return [i + pad_left for i in indices] # 使用 sync = KosmosIndexSync(max_patches=1024) inputs = processor(images=doc_image, text=prompt, return_tensors="pt", truncation=True, max_length=2048, padding="max_length") seq_len = inputs["input_ids"].shape[1] raw_idx = (inputs["input_ids"][0] == processor.image_token_id).nonzero().flatten().tolist() valid = sync.sync_after_truncation(raw_idx, seq_len) if inputs.get("attention_mask") is not None: pad_left = int((inputs["attention_mask"][0] == 0).sum().item()) # 左 padding 数量 valid = sync.sync_after_padding(valid, pad_left)

KosmosIndexSync把"截断同步 + 左 padding 平移 + 容量上限"三件事集中处理,保证image_token_indices永远落在合法区间。

七、解决方案(第三层:断言 / CI 守护)

用 pytest 固化"长输入不产生越界索引"的契约:

import pytest def test_indices_within_bounds_after_truncation(): from index_sync import KosmosIndexSync sync = KosmosIndexSync(max_patches=1024) # 模拟长 OCR:1024 个 image token,序列被截到 300 indices = list(range(0, 10000, 10))[:1024] new_len = 300 valid = sync.sync_after_truncation(indices, new_len) assert all(0 <= i < new_len for i in valid), "截断后索引仍越界" assert all(i < 1024 for i in valid), "索引超过 image_features 容量" def test_padding_shifts_indices(): from index_sync import KosmosIndexSync sync = KosmosIndexSync() idx = [5, 10, 15] shifted = sync.sync_after_padding(idx, pad_left=4) assert shifted == [9, 14, 19], "左 padding 后索引应整体右移" def test_no_indexerror_on_long_ocr(): # 端到端:长文档不应抛 IndexError import torch from unittest.mock import MagicMock image_features = torch.randn(1024, 64, 64) # N=1024 patch indices = list(range(0, 10000, 10))[:1024] new_len = 300 valid = [i for i in indices if i < new_len and i < image_features.shape[0]] # 取特征不应越界 feats = image_features[valid] assert feats.shape[0] == len(valid)

CI 跑pytest tests/test_kosmos2_5_long_ocr.py,以后只要截断/padding 逻辑又忘了同步索引,测试立刻红灯。

八、排查清单

当 Kosmos2.5 在长 OCR 输入上报索引错误,按顺序查:

  1. IndexError: index X is out of bounds for image_featuresimage_token_indices越界,先用sanitize_image_token_indices清洗。
  2. index -1 is out of bounds→ 截断把 image token 全裁掉了,需要同步裁剪或均匀补回。
  3. batch 推理报too many indices→ padding 后索引未平移,用sync_after_padding右移。
  4. 确认image_features实际 patch 数 N,与 tokenizer 展开的 image token 数是否一致;不一致要限制max_patches
  5. 长期方案:把索引同步逻辑收进 processor(返回已清洗的 indices),而不是让模型侧去猜。

九、小结

"Kosmos2.5: index error on long ocr input" 的根因是:图像 token 的下标(image_token_indices)在长输入触发截断/padding 后未同步更新,破坏了'下标 < patch 数 & < 序列长'的不变式,于是索引越界。

  • 第一层:forward 前用手动sanitize_image_token_indices清洗越界索引,长文档立即能跑。
  • 第二层:用KosmosIndexSync统一处理"截断裁剪 + 左 padding 平移 + patch 容量上限",结构性保证索引合法。
  • 第三层:pytest 断言"截断后索引在界内、padding 后正确平移、长 OCR 端到端不越界",防止回归。

记住:多模态模型里,跨模态的索引(image token ↔ image_features)必须在每次序列变换(截断/padding)后同步修正;这个不变式一旦破坏,就是索引错误。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/8 7:40:09

C++ std::string 底层实现深度解析:SSO、COW 与容量增长策略

一、痛点引入&#xff1a;为什么字符串值得单独写一篇&#xff1f; 很多 C 新手把 std::string 当成"高级的 char 数组"&#xff0c;或者干脆当成 Java 的 String 来用。直到某天遇到这些诡异问题&#xff1a; #include <string> #include <iostream>i…

作者头像 李华
网站建设 2026/8/8 7:39:02

理财网站建设方案书:如何打造高转化率且值得信赖的在线财富管理平台

在这个人人都在谈论搞钱的时代,大家对财富的渴望似乎比以往任何时候都要强烈。但与此同时,大家对“被割韭菜”的恐惧也变得如影随形。这就给了很多想要进入理财行业的朋友一个巨大的机会,也带来了一个巨大的挑战:如何让一个网站不仅仅是一堆代码的堆砌,而是真正成为用户信…

作者头像 李华
网站建设 2026/8/8 7:37:59

SpringBoot运动服装电商系统架构设计与实践

1. 项目背景与核心价值运动服装电商系统是当前互联网零售领域的热门方向&#xff0c;随着健康生活方式的普及&#xff0c;2023年全球运动服装市场规模已突破4000亿美元。这个基于SpringBoot的运动服装销售系统&#xff08;项目编号14203&#xff09;正是瞄准了这一快速增长的市…

作者头像 李华
网站建设 2026/8/8 7:36:58

AI视频生成工具PixVerse Live本地部署与API集成全流程指南

这次我们来看一个名为 PixVerse Live 的项目。根据其发布信息&#xff0c;这很可能是一个即将上线的、专注于实时或动态内容生成的AI工具或平台。从“直播倒计时”的表述来看&#xff0c;它可能是一个集成了图像、视频或数字人生成能力的在线服务或本地部署方案&#xff0c;旨在…

作者头像 李华
网站建设 2026/8/8 7:36:19

MySQL2PG v2.0.0:高效MySQL到PostgreSQL迁移工具解析

1. MySQL2PG v2.0.0 项目概述MySQL2PG v2.0.0 是一款专为数据库迁移场景设计的开源工具&#xff0c;它能够高效、准确地将MySQL数据库结构和数据迁移到PostgreSQL环境。这个版本在原有功能基础上进行了全面重构&#xff0c;解决了数据类型转换、语法差异、约束处理等核心痛点问…

作者头像 李华
网站建设 2026/8/8 7:34:15

中医馆理疗机器人选型指南:从技术参数到 ROI 测算的完整分析

一、背景&#xff1a;中医馆的人力成本困境中医馆行业正面临结构性的人力成本压力。一个熟练的理疗技师&#xff0c;综合用工成本&#xff08;月薪社保提成&#xff09;超过1万元/月。培养周期1-3年&#xff0c;流失率居高不下。核心矛盾&#xff1a;培养周期长、流失率高、服务…

作者头像 李华