week21_project_design
最后四周,我们一起实战一个基于 OCR 与 Qwen-VL 的文档多模态问答系统。给这个系统起个名字叫 DocuMind-VL。
代码已开源:From0to1-MLLM-StudyLog
目前 我们所说的 OCR 领域现在已经不只是图片转文字了,它更像是一个完整的文档理解 pipline。
传统的 OCR 流程一般是:
#mermaid-svg-hBiAzh8skT6blQUM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-hBiAzh8skT6blQUM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hBiAzh8skT6blQUM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hBiAzh8skT6blQUM .error-icon{fill:#552222;}#mermaid-svg-hBiAzh8skT6blQUM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hBiAzh8skT6blQUM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hBiAzh8skT6blQUM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hBiAzh8skT6blQUM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hBiAzh8skT6blQUM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hBiAzh8skT6blQUM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hBiAzh8skT6blQUM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hBiAzh8skT6blQUM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hBiAzh8skT6blQUM .marker.cross{stroke:#333333;}#mermaid-svg-hBiAzh8skT6blQUM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hBiAzh8skT6blQUM p{margin:0;}#mermaid-svg-hBiAzh8skT6blQUM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hBiAzh8skT6blQUM .cluster-label text{fill:#333;}#mermaid-svg-hBiAzh8skT6blQUM .cluster-label span{color:#333;}#mermaid-svg-hBiAzh8skT6blQUM .cluster-label span p{background-color:transparent;}#mermaid-svg-hBiAzh8skT6blQUM .label text,#mermaid-svg-hBiAzh8skT6blQUM span{fill:#333;color:#333;}#mermaid-svg-hBiAzh8skT6blQUM .node rect,#mermaid-svg-hBiAzh8skT6blQUM .node circle,#mermaid-svg-hBiAzh8skT6blQUM .node ellipse,#mermaid-svg-hBiAzh8skT6blQUM .node polygon,#mermaid-svg-hBiAzh8skT6blQUM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hBiAzh8skT6blQUM .rough-node .label text,#mermaid-svg-hBiAzh8skT6blQUM .node .label text,#mermaid-svg-hBiAzh8skT6blQUM .image-shape .label,#mermaid-svg-hBiAzh8skT6blQUM .icon-shape .label{text-anchor:middle;}#mermaid-svg-hBiAzh8skT6blQUM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hBiAzh8skT6blQUM .rough-node .label,#mermaid-svg-hBiAzh8skT6blQUM .node .label,#mermaid-svg-hBiAzh8skT6blQUM .image-shape .label,#mermaid-svg-hBiAzh8skT6blQUM .icon-shape .label{text-align:center;}#mermaid-svg-hBiAzh8skT6blQUM .node.clickable{cursor:pointer;}#mermaid-svg-hBiAzh8skT6blQUM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hBiAzh8skT6blQUM .arrowheadPath{fill:#333333;}#mermaid-svg-hBiAzh8skT6blQUM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hBiAzh8skT6blQUM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hBiAzh8skT6blQUM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hBiAzh8skT6blQUM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hBiAzh8skT6blQUM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hBiAzh8skT6blQUM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hBiAzh8skT6blQUM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hBiAzh8skT6blQUM .cluster text{fill:#333;}#mermaid-svg-hBiAzh8skT6blQUM .cluster span{color:#333;}#mermaid-svg-hBiAzh8skT6blQUM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-hBiAzh8skT6blQUM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hBiAzh8skT6blQUM rect.text{fill:none;stroke-width:0;}#mermaid-svg-hBiAzh8skT6blQUM .icon-shape,#mermaid-svg-hBiAzh8skT6blQUM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hBiAzh8skT6blQUM .icon-shape p,#mermaid-svg-hBiAzh8skT6blQUM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hBiAzh8skT6blQUM .icon-shape .label rect,#mermaid-svg-hBiAzh8skT6blQUM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hBiAzh8skT6blQUM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hBiAzh8skT6blQUM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hBiAzh8skT6blQUM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 图片
文字
现代 OCR:
#mermaid-svg-kwQJis5mqP92IkqV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kwQJis5mqP92IkqV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kwQJis5mqP92IkqV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kwQJis5mqP92IkqV .error-icon{fill:#552222;}#mermaid-svg-kwQJis5mqP92IkqV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kwQJis5mqP92IkqV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kwQJis5mqP92IkqV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kwQJis5mqP92IkqV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kwQJis5mqP92IkqV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kwQJis5mqP92IkqV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kwQJis5mqP92IkqV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kwQJis5mqP92IkqV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kwQJis5mqP92IkqV .marker.cross{stroke:#333333;}#mermaid-svg-kwQJis5mqP92IkqV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kwQJis5mqP92IkqV p{margin:0;}#mermaid-svg-kwQJis5mqP92IkqV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kwQJis5mqP92IkqV .cluster-label text{fill:#333;}#mermaid-svg-kwQJis5mqP92IkqV .cluster-label span{color:#333;}#mermaid-svg-kwQJis5mqP92IkqV .cluster-label span p{background-color:transparent;}#mermaid-svg-kwQJis5mqP92IkqV .label text,#mermaid-svg-kwQJis5mqP92IkqV span{fill:#333;color:#333;}#mermaid-svg-kwQJis5mqP92IkqV .node rect,#mermaid-svg-kwQJis5mqP92IkqV .node circle,#mermaid-svg-kwQJis5mqP92IkqV .node ellipse,#mermaid-svg-kwQJis5mqP92IkqV .node polygon,#mermaid-svg-kwQJis5mqP92IkqV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kwQJis5mqP92IkqV .rough-node .label text,#mermaid-svg-kwQJis5mqP92IkqV .node .label text,#mermaid-svg-kwQJis5mqP92IkqV .image-shape .label,#mermaid-svg-kwQJis5mqP92IkqV .icon-shape .label{text-anchor:middle;}#mermaid-svg-kwQJis5mqP92IkqV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kwQJis5mqP92IkqV .rough-node .label,#mermaid-svg-kwQJis5mqP92IkqV .node .label,#mermaid-svg-kwQJis5mqP92IkqV .image-shape .label,#mermaid-svg-kwQJis5mqP92IkqV .icon-shape .label{text-align:center;}#mermaid-svg-kwQJis5mqP92IkqV .node.clickable{cursor:pointer;}#mermaid-svg-kwQJis5mqP92IkqV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kwQJis5mqP92IkqV .arrowheadPath{fill:#333333;}#mermaid-svg-kwQJis5mqP92IkqV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kwQJis5mqP92IkqV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kwQJis5mqP92IkqV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kwQJis5mqP92IkqV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kwQJis5mqP92IkqV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kwQJis5mqP92IkqV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kwQJis5mqP92IkqV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kwQJis5mqP92IkqV .cluster text{fill:#333;}#mermaid-svg-kwQJis5mqP92IkqV .cluster span{color:#333;}#mermaid-svg-kwQJis5mqP92IkqV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kwQJis5mqP92IkqV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kwQJis5mqP92IkqV rect.text{fill:none;stroke-width:0;}#mermaid-svg-kwQJis5mqP92IkqV .icon-shape,#mermaid-svg-kwQJis5mqP92IkqV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kwQJis5mqP92IkqV .icon-shape p,#mermaid-svg-kwQJis5mqP92IkqV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kwQJis5mqP92IkqV .icon-shape .label rect,#mermaid-svg-kwQJis5mqP92IkqV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kwQJis5mqP92IkqV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kwQJis5mqP92IkqV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kwQJis5mqP92IkqV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 图片/PDF
页面方向判断
图像矫正
文档布局分析
文字检测
文字识别
表格识别
公式识别
图表理解
信息抽取
结构化数据
JSON / Markdown / Excel
1. 文档方向分类模型(Document Orientation Classification)
为什么要有一个文本方向分类模型?用户输入的扫描件很可能是正着、横着、倒着的。不一定每次文字都是正向排布,而文本方向分类模型需要判断图中的文字方向。
例如:rotation = 90° 判断完后,再通过旋转将文字转正。
代表模型:PP-LCNet_x1_0_doc_ori
2. 文本检测模型(Text Detection)
这是 OCR 最核心的第一个模型。它找出图片中文字在哪里。也就是输入一张图,输出一些文字的区域。
text
[
{
box:[
[10,20],
[200,20],
[200,60],
[10,60]
]
}
]
常见模型: PP-OCRv5_server_det、PP-OCRv5_mobile_det
mobile_det 顾名思义模型更小,以 mobilenet 为骨干网络来设计的。
而 server 版本是参数量更大的模型,一般跑在服务器上,推理速度较慢,但是准确率较高。
3. 文本识别模型(Text Recognition)
在检测到文本位置之后,我们还需要知道这个位置的文本内容是什么。
裁剪出这部分图片,交给文本识别模型。文本识别模型会输出裁剪下来的图像中包含的文字内容。
代表模型:PP-OCRv5_server_rec、PP-OCRv5_mobile_rec
它支持很多中语言:中文、英文、日文等多语言。
检测模型决定文字找得准不准。识别模型决定字看得准不准。两个需要配合使用。
Document AI
1. 文档布局分析模型(Layout Detection)
这是进入 Document AI 的关键。
如果只进行文本检测和识别,我们不知道哪部分是标题,哪部分是表格,哪里是段落,哪里是公式。
在做文档分析的时候会很被动,无法形成结构化的结果。
Layout 模型本质上也是一个检测模型,但它会输出大块的区域信息,比文本检测的细粒度更粗,但是它区分出了文本的性质。
text
[
{
type:"title",
box:[]
},
{
type:"table",
box:[]
},
{
type:"paragraph",
box:[]
}
]
代表模型:PP-DocLayoutV2
它可以识别:标题、段落、图片、表格、公式、图表、页眉页脚等等。
2. 表格识别模型(Table Recognition)
这是商业 OCR 很重要的一块。模型会把图片或 pdf 表格恢复成:
json
{
"姓名":"张三",
"年龄":20
}
一般应用于财报、发票、Excel截图、PDF表格等。
3. 公式识别模型(Formula Recognition)
顾名思义,专门用来处理数学公式。
假如有一张图片:

模型输出 LaTeX 标准公式:
text
x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}
4. 图表解析模型(Chart Parsing)
输入一些柱状图、折线图等等。
模型输出:
json
[
{
"name":"xxx图例",
"value":100
}
]
5. 印章识别模型(Seal Recognition)
印章识别在国内场景非常重要。例如一些合同公章、企业印章等等。他的特点是文字区域一般是弧形排列。
总结一下
文档结构化流程一般如下:
#mermaid-svg-icj4ZuSCOWBIyIOV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-icj4ZuSCOWBIyIOV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-icj4ZuSCOWBIyIOV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-icj4ZuSCOWBIyIOV .error-icon{fill:#552222;}#mermaid-svg-icj4ZuSCOWBIyIOV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-icj4ZuSCOWBIyIOV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-icj4ZuSCOWBIyIOV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-icj4ZuSCOWBIyIOV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-icj4ZuSCOWBIyIOV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-icj4ZuSCOWBIyIOV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-icj4ZuSCOWBIyIOV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-icj4ZuSCOWBIyIOV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-icj4ZuSCOWBIyIOV .marker.cross{stroke:#333333;}#mermaid-svg-icj4ZuSCOWBIyIOV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-icj4ZuSCOWBIyIOV p{margin:0;}#mermaid-svg-icj4ZuSCOWBIyIOV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-icj4ZuSCOWBIyIOV .cluster-label text{fill:#333;}#mermaid-svg-icj4ZuSCOWBIyIOV .cluster-label span{color:#333;}#mermaid-svg-icj4ZuSCOWBIyIOV .cluster-label span p{background-color:transparent;}#mermaid-svg-icj4ZuSCOWBIyIOV .label text,#mermaid-svg-icj4ZuSCOWBIyIOV span{fill:#333;color:#333;}#mermaid-svg-icj4ZuSCOWBIyIOV .node rect,#mermaid-svg-icj4ZuSCOWBIyIOV .node circle,#mermaid-svg-icj4ZuSCOWBIyIOV .node ellipse,#mermaid-svg-icj4ZuSCOWBIyIOV .node polygon,#mermaid-svg-icj4ZuSCOWBIyIOV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-icj4ZuSCOWBIyIOV .rough-node .label text,#mermaid-svg-icj4ZuSCOWBIyIOV .node .label text,#mermaid-svg-icj4ZuSCOWBIyIOV .image-shape .label,#mermaid-svg-icj4ZuSCOWBIyIOV .icon-shape .label{text-anchor:middle;}#mermaid-svg-icj4ZuSCOWBIyIOV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-icj4ZuSCOWBIyIOV .rough-node .label,#mermaid-svg-icj4ZuSCOWBIyIOV .node .label,#mermaid-svg-icj4ZuSCOWBIyIOV .image-shape .label,#mermaid-svg-icj4ZuSCOWBIyIOV .icon-shape .label{text-align:center;}#mermaid-svg-icj4ZuSCOWBIyIOV .node.clickable{cursor:pointer;}#mermaid-svg-icj4ZuSCOWBIyIOV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-icj4ZuSCOWBIyIOV .arrowheadPath{fill:#333333;}#mermaid-svg-icj4ZuSCOWBIyIOV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-icj4ZuSCOWBIyIOV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-icj4ZuSCOWBIyIOV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-icj4ZuSCOWBIyIOV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-icj4ZuSCOWBIyIOV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-icj4ZuSCOWBIyIOV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-icj4ZuSCOWBIyIOV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-icj4ZuSCOWBIyIOV .cluster text{fill:#333;}#mermaid-svg-icj4ZuSCOWBIyIOV .cluster span{color:#333;}#mermaid-svg-icj4ZuSCOWBIyIOV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-icj4ZuSCOWBIyIOV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-icj4ZuSCOWBIyIOV rect.text{fill:none;stroke-width:0;}#mermaid-svg-icj4ZuSCOWBIyIOV .icon-shape,#mermaid-svg-icj4ZuSCOWBIyIOV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-icj4ZuSCOWBIyIOV .icon-shape p,#mermaid-svg-icj4ZuSCOWBIyIOV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-icj4ZuSCOWBIyIOV .icon-shape .label rect,#mermaid-svg-icj4ZuSCOWBIyIOV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-icj4ZuSCOWBIyIOV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-icj4ZuSCOWBIyIOV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-icj4ZuSCOWBIyIOV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 文档图片/PDF
文档预处理
旋转 / 去噪 / 矫正
Layout Analysis
版面分析
文本区域
非文本区域
Text Detection
Text Recognition
表格 / 公式 / 图表专项解析
结构化输出
JSON / Markdown
VLM(视觉语言模型)
这是最新方向。例如:PaddleOCR-VL、GPT-4V、Qwen-VL、InternVL。
它不像传统 OCR pipeline 那样结构复杂,串联多个模型。很暴力直接使用图片进行理解。输入图片和问题,直接输出答案。
比如有一个增长曲线表格,可以直接问:2025收入比2023增长多少?
传统 OCR 只能识别出文字,增长多少需要自己计算,VLM 会通过理解图片和问题的语义,直接回答增长xx%。
有些 VLM 内部也不只有一个大模型比如,PaddleOCR-VL:
它会通过使用传统 OCR 进行一个预处理,先提取一部分信息,再通过 VLM 理解。流程大概是这样:
#mermaid-svg-jmE5ZkyaD0u8VQLj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jmE5ZkyaD0u8VQLj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jmE5ZkyaD0u8VQLj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jmE5ZkyaD0u8VQLj .error-icon{fill:#552222;}#mermaid-svg-jmE5ZkyaD0u8VQLj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jmE5ZkyaD0u8VQLj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jmE5ZkyaD0u8VQLj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jmE5ZkyaD0u8VQLj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jmE5ZkyaD0u8VQLj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jmE5ZkyaD0u8VQLj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jmE5ZkyaD0u8VQLj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jmE5ZkyaD0u8VQLj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jmE5ZkyaD0u8VQLj .marker.cross{stroke:#333333;}#mermaid-svg-jmE5ZkyaD0u8VQLj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jmE5ZkyaD0u8VQLj p{margin:0;}#mermaid-svg-jmE5ZkyaD0u8VQLj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jmE5ZkyaD0u8VQLj .cluster-label text{fill:#333;}#mermaid-svg-jmE5ZkyaD0u8VQLj .cluster-label span{color:#333;}#mermaid-svg-jmE5ZkyaD0u8VQLj .cluster-label span p{background-color:transparent;}#mermaid-svg-jmE5ZkyaD0u8VQLj .label text,#mermaid-svg-jmE5ZkyaD0u8VQLj span{fill:#333;color:#333;}#mermaid-svg-jmE5ZkyaD0u8VQLj .node rect,#mermaid-svg-jmE5ZkyaD0u8VQLj .node circle,#mermaid-svg-jmE5ZkyaD0u8VQLj .node ellipse,#mermaid-svg-jmE5ZkyaD0u8VQLj .node polygon,#mermaid-svg-jmE5ZkyaD0u8VQLj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jmE5ZkyaD0u8VQLj .rough-node .label text,#mermaid-svg-jmE5ZkyaD0u8VQLj .node .label text,#mermaid-svg-jmE5ZkyaD0u8VQLj .image-shape .label,#mermaid-svg-jmE5ZkyaD0u8VQLj .icon-shape .label{text-anchor:middle;}#mermaid-svg-jmE5ZkyaD0u8VQLj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jmE5ZkyaD0u8VQLj .rough-node .label,#mermaid-svg-jmE5ZkyaD0u8VQLj .node .label,#mermaid-svg-jmE5ZkyaD0u8VQLj .image-shape .label,#mermaid-svg-jmE5ZkyaD0u8VQLj .icon-shape .label{text-align:center;}#mermaid-svg-jmE5ZkyaD0u8VQLj .node.clickable{cursor:pointer;}#mermaid-svg-jmE5ZkyaD0u8VQLj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jmE5ZkyaD0u8VQLj .arrowheadPath{fill:#333333;}#mermaid-svg-jmE5ZkyaD0u8VQLj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jmE5ZkyaD0u8VQLj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jmE5ZkyaD0u8VQLj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jmE5ZkyaD0u8VQLj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jmE5ZkyaD0u8VQLj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jmE5ZkyaD0u8VQLj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jmE5ZkyaD0u8VQLj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jmE5ZkyaD0u8VQLj .cluster text{fill:#333;}#mermaid-svg-jmE5ZkyaD0u8VQLj .cluster span{color:#333;}#mermaid-svg-jmE5ZkyaD0u8VQLj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jmE5ZkyaD0u8VQLj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jmE5ZkyaD0u8VQLj rect.text{fill:none;stroke-width:0;}#mermaid-svg-jmE5ZkyaD0u8VQLj .icon-shape,#mermaid-svg-jmE5ZkyaD0u8VQLj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jmE5ZkyaD0u8VQLj .icon-shape p,#mermaid-svg-jmE5ZkyaD0u8VQLj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jmE5ZkyaD0u8VQLj .icon-shape .label rect,#mermaid-svg-jmE5ZkyaD0u8VQLj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jmE5ZkyaD0u8VQLj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jmE5ZkyaD0u8VQLj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jmE5ZkyaD0u8VQLj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Document Image
Layout Analysis
Text / Table 区域
图片 / 公式 / 图表区域
OCR
VLM
Structured Output
JSON / Markdown
这样既能减少整张图片直接塞进 VLM 后产生的大量视觉 token,也能更精准的让 VLM 理解关键信息。
整理一下:
#mermaid-svg-jUUzytnhuBnsMdtq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jUUzytnhuBnsMdtq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jUUzytnhuBnsMdtq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jUUzytnhuBnsMdtq .error-icon{fill:#552222;}#mermaid-svg-jUUzytnhuBnsMdtq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jUUzytnhuBnsMdtq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jUUzytnhuBnsMdtq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jUUzytnhuBnsMdtq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jUUzytnhuBnsMdtq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jUUzytnhuBnsMdtq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jUUzytnhuBnsMdtq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jUUzytnhuBnsMdtq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jUUzytnhuBnsMdtq .marker.cross{stroke:#333333;}#mermaid-svg-jUUzytnhuBnsMdtq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jUUzytnhuBnsMdtq p{margin:0;}#mermaid-svg-jUUzytnhuBnsMdtq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jUUzytnhuBnsMdtq .cluster-label text{fill:#333;}#mermaid-svg-jUUzytnhuBnsMdtq .cluster-label span{color:#333;}#mermaid-svg-jUUzytnhuBnsMdtq .cluster-label span p{background-color:transparent;}#mermaid-svg-jUUzytnhuBnsMdtq .label text,#mermaid-svg-jUUzytnhuBnsMdtq span{fill:#333;color:#333;}#mermaid-svg-jUUzytnhuBnsMdtq .node rect,#mermaid-svg-jUUzytnhuBnsMdtq .node circle,#mermaid-svg-jUUzytnhuBnsMdtq .node ellipse,#mermaid-svg-jUUzytnhuBnsMdtq .node polygon,#mermaid-svg-jUUzytnhuBnsMdtq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jUUzytnhuBnsMdtq .rough-node .label text,#mermaid-svg-jUUzytnhuBnsMdtq .node .label text,#mermaid-svg-jUUzytnhuBnsMdtq .image-shape .label,#mermaid-svg-jUUzytnhuBnsMdtq .icon-shape .label{text-anchor:middle;}#mermaid-svg-jUUzytnhuBnsMdtq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jUUzytnhuBnsMdtq .rough-node .label,#mermaid-svg-jUUzytnhuBnsMdtq .node .label,#mermaid-svg-jUUzytnhuBnsMdtq .image-shape .label,#mermaid-svg-jUUzytnhuBnsMdtq .icon-shape .label{text-align:center;}#mermaid-svg-jUUzytnhuBnsMdtq .node.clickable{cursor:pointer;}#mermaid-svg-jUUzytnhuBnsMdtq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jUUzytnhuBnsMdtq .arrowheadPath{fill:#333333;}#mermaid-svg-jUUzytnhuBnsMdtq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jUUzytnhuBnsMdtq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jUUzytnhuBnsMdtq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jUUzytnhuBnsMdtq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jUUzytnhuBnsMdtq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jUUzytnhuBnsMdtq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jUUzytnhuBnsMdtq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jUUzytnhuBnsMdtq .cluster text{fill:#333;}#mermaid-svg-jUUzytnhuBnsMdtq .cluster span{color:#333;}#mermaid-svg-jUUzytnhuBnsMdtq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jUUzytnhuBnsMdtq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jUUzytnhuBnsMdtq rect.text{fill:none;stroke-width:0;}#mermaid-svg-jUUzytnhuBnsMdtq .icon-shape,#mermaid-svg-jUUzytnhuBnsMdtq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jUUzytnhuBnsMdtq .icon-shape p,#mermaid-svg-jUUzytnhuBnsMdtq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jUUzytnhuBnsMdtq .icon-shape .label rect,#mermaid-svg-jUUzytnhuBnsMdtq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jUUzytnhuBnsMdtq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jUUzytnhuBnsMdtq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jUUzytnhuBnsMdtq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} OCR 世界
基础 OCR
det
rec
文档 OCR
Layout
Table
Formula
Chart
Document AI
VLM
应用场景
1、什么时候用传统 ocr?
场景:大批量身份证、票据、快速文字提取等。
要求:快、准、便宜
2、什么时候用 VLM?
场景:PDF理解、合同分析、年报分析、论文解析、企业知识库等。
例如:上传一个100页年报.pdf
问:找出所有关于净利润下降原因的章节。
基于 PaddleOCR 和 Qwen3-VL-8B-Instruct 的文档理解实战
1、安装
shell
pip install -r week21_project_design/requirements.txt
2、文档结构化
python
from paddleocr import PPStructureV3
input_file = "docs/notes/BLIP.pdf"
output_path = Path("week21_project_design/output")
pipeline = PPStructureV3(
engine="transformers",
lang="en",
use_table_recognition=False,
use_formula_recognition=False,
)
output = pipeline.predict(input=input_file)
markdown_list = []
markdown_images = []
for res in output:
md_info = res.markdown
markdown_list.append(md_info)
markdown_images.append(md_info.get("markdown_images", {}))
markdown_texts = pipeline.concatenate_markdown_pages(markdown_list)
print(f"markdown result type={type(markdown_texts).__name__}")
if not isinstance(markdown_texts, str):
markdown_texts = markdown_texts["markdown_texts"]
if not isinstance(markdown_texts, str):
raise TypeError(f"Expected markdown text to be str, got {type(markdown_texts).__name__}")
mkd_file_path = output_path / f"{Path(input_file).stem}.md"
mkd_file_path.parent.mkdir(parents=True, exist_ok=True)
with open(mkd_file_path, "w", encoding="utf-8") as f:
f.write(markdown_texts)
for item in markdown_images:
if item:
for path, image in item.items():
file_path = output_path / path
file_path.parent.mkdir(parents=True, exist_ok=True)
image.save(file_path)
3、 VLM 文档理解
python
import argparse
import re
from dataclasses import dataclass
from pathlib import Path
from urllib.parse import unquote
import torch
from transformers import AutoProcessor
try:
from transformers import Qwen3VLForConditionalGeneration as Qwen3VLModel
except ImportError:
from transformers import AutoModelForImageTextToText as Qwen3VLModel
ROOT = Path(__file__).resolve().parents[1]
DEFAULT_MODEL_PATH = ROOT / "models" / "Qwen3-VL-8B-Instruct"
DEFAULT_INPUT_PATH = ROOT / "docs" / "notes" / "BLIP.pdf"
DEFAULT_MARKDOWN_PATH = Path(__file__).resolve().parent / "output" / "BLIP.md"
DEFAULT_QUESTION = "根据这篇文章,总结文章创新点"
MARKDOWN_IMAGE_RE = re.compile(r"!\[[^\]]*\]\(([^)\s]+)(?:\s+['\"][^'\"]*['\"])?\)")
HTML_IMAGE_RE = re.compile(r"<img[^>]+src=[\"']([^\"']+)[\"'][^>]*>", re.IGNORECASE)
@dataclass
class DocumentInput:
text: str
images: list[Path]
def resolve_image_path(raw_path: str, base_dir: Path) -> Path | None:
raw_path = unquote(raw_path.strip().strip("<>"))
if not raw_path or raw_path.startswith(("http://", "https://", "data:")):
return None
path = Path(raw_path)
if not path.is_absolute():
path = base_dir / path
path = path.resolve()
return path if path.exists() else None
def extract_markdown_images(text: str, base_dir: Path, max_images: int | None) -> list[Path]:
image_paths = []
seen = set()
raw_paths = MARKDOWN_IMAGE_RE.findall(text) + HTML_IMAGE_RE.findall(text)
for raw_path in raw_paths:
image_path = resolve_image_path(raw_path, base_dir)
if image_path is None or image_path in seen:
continue
seen.add(image_path)
image_paths.append(image_path)
if max_images is not None and len(image_paths) >= max_images:
break
return image_paths
def parse_markdown(
path: Path,
max_chars: int | None = None,
max_images: int | None = None,
) -> DocumentInput:
"""Read markdown text and collect local images referenced by markdown/html tags."""
text = path.read_text(encoding="utf-8")
images = extract_markdown_images(text, path.parent, max_images)
text = re.sub(r"<div[^>]*>.*?</div>", "\n", text, flags=re.DOTALL | re.IGNORECASE)
text = re.sub(r"!\[[^\]]*\]\([^)]+\)", "\n", text)
text = re.sub(r"<img[^>]*>", "\n", text, flags=re.IGNORECASE)
text = re.sub(r"\n{3,}", "\n\n", text).strip()
if max_chars is not None and len(text) > max_chars:
text = text[:max_chars]
return DocumentInput(text=text, images=images)
def parse_pdf(path: Path, max_chars: int | None = None) -> DocumentInput:
try:
import pypdfium2 as pdfium
except ImportError as exc:
raise RuntimeError(
"PDF input requires pypdfium2. Install it with: pip install pypdfium2"
) from exc
pdf = pdfium.PdfDocument(path)
pages = []
for index in range(len(pdf)):
page = pdf[index]
textpage = page.get_textpage()
pages.append(textpage.get_text_range())
textpage.close()
page.close()
text = "\n\n".join(pages)
text = re.sub(r"\n{3,}", "\n\n", text).strip()
if max_chars is not None and len(text) > max_chars:
text = text[:max_chars]
return DocumentInput(text=text, images=[])
def parse_document(
path: Path,
max_chars: int | None = None,
max_images: int | None = None,
) -> DocumentInput:
suffix = path.suffix.lower()
if suffix in {".md", ".markdown", ".txt"}:
return parse_markdown(path, max_chars, max_images)
if suffix == ".pdf":
return parse_pdf(path, max_chars)
raise ValueError(f"Unsupported input file type: {suffix}. Use markdown, txt, or pdf.")
def build_messages(document: DocumentInput, question: str) -> list[dict]:
image_note = ""
if document.images:
image_note = f"\n\n文档中同时附带了 {len(document.images)} 张按原文顺序抽取的图片,请结合这些图片理解图表、模型结构和实验结果。"
content = [
{
"type": "text",
"text": (
"你是一名多模态论文阅读助手。请只根据给定文章内容作答,"
"优先提炼方法层面的创新点,并用中文分点总结。"
f"{image_note}\n\n文章文本:\n{document.text}"
),
}
]
for image_path in document.images:
content.append({"type": "image", "image": str(image_path)})
content.append({"type": "text", "text": f"问题:{question}"})
return [{"role": "user", "content": content}]
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Use local Qwen3-VL-8B-Instruct to summarize innovations in a paper."
)
parser.add_argument("--model-path", type=Path, default=DEFAULT_MODEL_PATH)
parser.add_argument(
"--input",
type=Path,
default=DEFAULT_INPUT_PATH,
help="Input paper file. Supports markdown, txt, and pdf.",
)
parser.add_argument(
"--markdown",
type=Path,
default=None,
help="Backward-compatible alias for --input.",
)
parser.add_argument("--question", default=DEFAULT_QUESTION)
parser.add_argument("--max-new-tokens", type=int, default=768)
parser.add_argument(
"--max-input-chars",
type=int,
default=None,
help="Optionally truncate the parsed document before inference.",
)
parser.add_argument(
"--max-images",
type=int,
default=8,
help="Maximum number of local images to include from markdown input. Use 0 to disable.",
)
parser.add_argument(
"--device-map",
default="auto",
help='Passed to from_pretrained; use "cpu" to force CPU inference.',
)
return parser.parse_args()
def main() -> None:
args = parse_args()
model_path = args.model_path.resolve()
input_path = (args.markdown or args.input).resolve()
max_images = None if args.max_images < 0 else args.max_images
document = parse_document(input_path, args.max_input_chars, max_images)
messages = build_messages(document, args.question)
processor = AutoProcessor.from_pretrained(model_path, trust_remote_code=True)
model = Qwen3VLModel.from_pretrained(
model_path,
dtype="auto",
device_map=args.device_map,
trust_remote_code=True,
)
model.eval()
inputs = processor.apply_chat_template(
messages,
tokenize=True,
add_generation_prompt=True,
return_dict=True,
return_tensors="pt",
)
inputs = inputs.to(model.device)
with torch.inference_mode():
generated_ids = model.generate(
**inputs,
max_new_tokens=args.max_new_tokens,
do_sample=False,
)
generated_ids_trimmed = [
output_ids[len(input_ids) :]
for input_ids, output_ids in zip(inputs.input_ids, generated_ids)
]
output_text = processor.batch_decode(
generated_ids_trimmed,
skip_special_tokens=True,
clean_up_tokenization_spaces=False,
)
print(output_text[0].strip())
if __name__ == "__main__":
main()
对比结果
只传 pdf 时
模型输出结果比较抽象
text
根据文章内容,BLIP 的方法层面创新点可总结为以下几点:
1. **提出多模态混合编码器-解码器(Multimodal Mixture of Encoder-Decoder, MED)架构**
- 该架构可灵活切换为三种模式:
(1)单模态编码器(用于图像-文本对比学习);
(2)图像引导文本编码器(用于图像-文本匹配);
(3)图像引导文本解码器(用于图像条件语言建模)。
- 通过共享参数(除自注意力层外)实现多任务联合预训练,提升效率并增强泛化能力。
2. **提出"生成+过滤"(Captioning and Filtering, CapFilt)数据自举方法**
- 利用预训练 MED 模型微调出两个模块:
- **Captioner**:生成图像的合成描述(合成文本);
- **Filter**:通过 ITM 任务过滤掉噪声文本(包括原始网络文本和合成文本)。
- 通过"生成-过滤"循环提升数据质量,有效利用噪声网络数据,而非简单依赖人工标注数据。
3. **设计统一的预训练目标组合**
- 联合优化三个任务:
(1)图像-文本对比学习(ITC);
(2)图像-文本匹配(ITM);
(3)图像条件语言建模(LM)。
- 该组合支持理解型任务(如检索)和生成型任务(如图像描述),实现统一预训练框架。
4. **引入多样性驱动的合成文本生成策略**
- 使用 **Nucleus Sampling**(而非 Beam Search)生成合成描述,以提高文本多样性,避免生成"安全但无信息"的文本,从而提升模型学习效果。
5. **参数共享与解耦设计优化训练效率与性能**
- 文本编码器与解码器共享除自注意力层外的参数,减少模型规模并提升训练效率;
- 实验表明共享自注意力层会降低性能,因编码与解码任务存在冲突。
6. **支持零样本迁移至视频语言任务**
- 模型在图像语言任务上训练后,可直接零样本迁移至视频检索与视频问答任务,验证其强大的泛化能力。
文档结构化后
模型根据文档结构化后的 markdown 文本和图片,模型看了图之后输出更为具体:
text
1. **提出"多模态混合编码器-解码器"(MED)模型架构**:
* 该架构设计为一个统一的模型,能够根据任务需求灵活切换为三种模式:**仅编码器模式**(用于图像和文本的独立编码)、**图像引导的文本编码器模式**(用于图像-文本对的联合表示学习)和**图像引导的文本解码器模式**(用于图像条件下的文本生成)。
* 通过共享大部分参数(仅自注意力层不同),该架构实现了高效的多任务联合预训练,同时兼顾了理解型任务(如检索)和生成型任务(如图像描述)的需求,解决了现有模型在单一架构上难以同时精通两类任务的局限。
2. **提出"生成与过滤"(CapFilt)数据自举方法**:
* 该方法旨在有效利用海量但噪声严重的网络图像-文本对。其核心思想是:**先用一个预训练好的 MED 模型作为"生成器"(Captioner),为网络图像生成合成描述(Ts);再用同一个模型作为"过滤器"(Filter),根据图像-文本匹配度(ITM)来筛选并剔除噪声文本(包括原始网络文本 Tw 和合成文本 Ts)**。
* 通过这种"生成-过滤"的协同机制,CapFilt 能够构建一个更高质量、更丰富的训练数据集,从而显著提升下游任务性能。实验表明,生成的合成文本多样性越高,效果越好。
3. **引入"图像引导的文本解码器"作为生成任务的核心组件**:
* 与传统的掩码语言建模(MLM)不同,BLIP 在生成任务中采用**图像引导的文本解码器**,其核心是使用**因果自注意力**机制,以图像特征为条件,自回归地生成连贯的文本描述。
* 这种设计使得模型在图像描述等生成任务上具备更强的泛化能力,能够将视觉信息有效转化为自然语言。
4. **在预训练中联合优化三种任务目标**:
* BLIP 在预训练阶段同时优化三个目标:**图像-文本对比学习(ITC)**、**图像-文本匹配(ITM)** 和**图像条件语言建模(LM)**。
* 这种多任务联合学习策略,使得模型能够从不同角度学习视觉与语言的对齐关系,从而在理解与生成任务上都取得优异表现。
5. **在模型架构中引入"图像引导的文本编码器"**:
* 该编码器在标准文本编码器的基础上,插入了**跨注意力层**,使其能够将图像特征注入到文本编码过程中,从而学习到更细粒度的视觉-语言对齐表示,这对于图像-文本检索等理解任务至关重要。
6. **在数据处理中引入"核采样"(Nucleus Sampling)生成合成文本**:
* 为了提高合成文本的多样性,CapFilt 使用核采样(Nucleus Sampling)而非确定性的束搜索(Beam Search)来生成合成描述。
* 实验表明,尽管核采样生成的文本可能更"嘈杂",但其带来的多样性能为模型提供更丰富的信息,从而在下游任务中取得更好的性能。
我正在连载一个从零到一的多模态大模型学习笔记。如果你对多模态大模型感兴趣,或者也在准备往大模型方向转,可以点赞/Fork我的仓库: From0to1-MLLM-StudyLog
也可评论区留言交流,后面我会继续把每周的学习记录、踩坑经验陆续更新到仓库和这里。
抱团:关注"1个算法工程师" 后台私信"mllm",加入mllm交流学习群。