Qwen2-VL:发票数据提取、视频聊天和使用 PDF 的多模态 RAG 的实践指南

概述

随着人工智能技术的迅猛发展,多模态模型在各类应用场景中展现出强大的潜力和广泛的适用性。Qwen2-VL 作为最新一代的多模态大模型,融合了视觉与语言处理能力,旨在提升复杂任务的执行效率和准确性。本指南聚焦于 Qwen2-VL 在三个关键领域的实践应用:发票数据提取、视频聊天以及基于 PDF 文档的多模态检索增强生成(RAG, Retrieval-Augmented Generation)。

多模态 RAG 的重要性

传统的生成模型主要依赖于文本数据,而多模态 RAG 则通过结合视觉信息,实现对复杂数据的更深入理解和处理。这一方法不仅提高了生成内容的相关性和准确性,还扩展了模型在实际应用中的适用范围。Qwen2-VL 通过整合图像、视频和文本数据,能够在多种场景下提供智能化的解决方案,满足企业和个人用户日益增长的需求。

发票数据提取

在财务和会计领域,发票作为重要的交易凭证,其数据的准确提取和处理对于企业的运营至关重要。传统的方法往往依赖人工录入,效率低下且易出错。Qwen2-VL 利用其强大的视觉识别和自然语言理解能力,能够自动识别发票中的关键信息,如金额、日期和供应商信息,实现高效、准确的数据提取,大幅提升工作效率并降低人为错误的风险。

视频聊天

随着远程办公和在线交流的普及,视频聊天已成为日常工作和社交的重要工具。Qwen2-VL 在视频聊天应用中,通过结合视觉和语言模型,实现智能化的实时翻译、情感分析和内容摘要等功能。此举不仅提升了沟通的便捷性和效果,还为用户提供了更加个性化和高效的交流体验。

基于 PDF 的多模态 RAG

PDF 作为一种广泛使用的文档格式,涵盖了文本、图表和图像等多种信息形式。Qwen2-VL 通过解析和理解 PDF 文档中的多模态内容,能够实现智能检索和生成。例如,在科研、法律和教育等领域,用户可以通过自然语言查询,快速获取相关信息,并生成简洁明了的总结报告。这不仅提高了信息获取的效率,还促进了知识的传播和应用。

Qwen2-VL 架构

下面这张图就是Qwen2-VL的架构图。

到目前为止,已知的是 Qwen2-VL 使用带有 Vision Transformer 的 Qwen2-LM — 能够处理图像和视频。此外,Qwen2-VL 还推出了新颖的多模态旋转位置嵌入 s( M-ROPE )。这是 ROPE 嵌入的一种变体,它将位置嵌入分解为多个部分 。

Qwen2-VL 支持多种语言,包括大多数欧洲语言、日语、韩语、中文和阿拉伯语。更多细节参考官方文档。

项目 1:将发票数据提取为 JSON 格式

在这个小型项目中,我们将从下面的发票中提取财务和个人信息 — JSON 格式:

首先,安装必要的库:

pip install git+https://github.com/huggingface/transformers accelerate
pip install qwen-vl-utils

接下来,我们下载我们的文件:

import urllib.request# 发票图片地址
url = "<http://cwb.stdusfc.edu.cn/images/2015/cw112701.png>"
# 下载发票
file_name = url.split('/')[-1]
urllib.request.urlretrieve(url, file_name)
print(f"Downloaded file name: {file_name}")
# Downloaded file name: cw112701.png

然后,我们将安装 Qwen2-VL-7B-Instruct

from transformers import Qwen2VLForConditionalGeneration, AutoTokenizer, AutoProcessor
from qwen_vl_utils import process_vision_info
import jsonmodel_name = "Qwen/Qwen2-VL-7B-Instruct"
model = Qwen2VLForConditionalGeneration.from_pretrained(model_name,torch_dtype="auto",device_map="auto"
)
processor = AutoProcessor.from_pretrained(model_name
)

在模型下载并放入内存后,我们可以发送我们的请求。一些额外提示:

  1. 至少使用原始图像尺寸 :确保至少使用图像的原始尺寸以获得最佳效果(resized_height & resized_width arguments参数)
  2. 较大的尺寸 : 在质量较差的图像中,尺寸稍大可以提高准确性,但会增加 VRAM 的使用量。相应地调整:

我们将使用 Qwen2-VL 的聊天模板,并提示如下:


"检索项目内容,金额,付款单位,时间,发票代码,发票号码。响应必须是 JSON 格式"
messages = [{"role": "user","content": [{"type": "image","image": file_name,"resized_height": 696,"resized_width": 943,},{"type": "text","text": "检索项目内容,金额,付款单位,时间,发票代码,发票号码。响应必须是 JSON 格式"}]}
]text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True
)
image_inputs, video_inputs = process_vision_info(messages)
inputs = processor(text=[text],images=image_inputs,videos=video_inputs,padding=True,return_tensors="pt",
)
inputs = inputs.to("cuda")
generated_ids = model.generate(**inputs, max_new_tokens=512)
generated_ids_trimmed = [out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
]
output_text = processor.batch_decode(generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=True)
output_text

我们得到以下输出:

['```json\\n{\\n  "项目内容": [\\n    "钢尺 30 x11.5",\\n    "美工刀 60 x2",\\n    "腊线 30 x14"\\n  ],\\n  "金额": [\\n    "345.00",\\n    "200.00",\\n    "420.00"\\n  ],\\n  "付款单位": "石家庄铁道大学四方学院",\\n  "时间": "2015年5月11日",\\n  "发票代码": "113001464131",\\n  "发票号码": "09404611"\\n}\\n```']

你可以使用以下代码来修复潜在错误并设置模型的 JSON 输出格式:

json_string = output_text[0]
json_string = json_string.strip("[]'")
json_string = json_string.replace("```json\\n", "").replace("\\n```", "")
json_string = json_string.replace("'", "")
try:formatted_json = json.loads(json_string)print(json.dumps(formatted_json, indent=3, ensure_ascii=False))
except json.JSONDecodeError as e:print("Not valid JSON format:", e)

通过将结果与上述发票进行比较,我们注意到:

  • 该模型的输出准确率非常高 — 它准确地提取了所有相关信息!
  • 尽管图像质量很差并且表格中嵌入了数据!
  • 较小的 Qwen2-VL 在这里表现良好,但对于更复杂的图像或手写文本,你可能需要更大的模型,例如 Qwen2-VL-72B .

项目 2:通过视频聊天

Qwen2-VL 还可以提取信息并与视频交互。

在这个项目中,我们将使用一个简短的 B站 视频 —《这一段毫无表演痕迹 堪称经典》 :

## 下载B站视频
pip install yt-dlp

按如下方式下载:

import yt_dlp
import osdef download_bilibili_video(url, download_path='downloads', fmt='100047+30280', cookiefile=None):# 创建下载目录(如果不存在)if not os.path.exists(download_path):os.makedirs(download_path)ydl_opts = {'outtmpl': os.path.join(download_path, '%(title)s.%(ext)s'),'format': fmt,  # 指定视频和音频的格式ID'noplaylist': True,'merge_output_format': 'mp4',  # 合并为mp4格式}if cookiefile:ydl_opts['cookiefile'] = cookiefilewith yt_dlp.YoutubeDL(ydl_opts) as ydl:ydl.download([url])video_url = '<https://www.bilibili.com/video/BV1us41eBERp>'  # 替换为你要下载的视频URL
download_directory = './downloads'  # 替换为你希望保存视频的目录# 如果需要使用Cookies进行认证,取消下行注释并提供Cookies文件路径# cookies_path = 'path_to_cookies.txt'# download_bilibili_video(video_url, download_directory, fmt='100047+30280', cookiefile=cookies_path)# 如果不需要认证,使用以下行
download_bilibili_video(video_url, download_directory, fmt='100046+30280')file_name = './downloads/这一段毫无表演痕迹  堪称经典.mp4'

我们将再次使用Qwen2-VL-7B,因为它的资源密集度较低。

from transformers import Qwen2VLForConditionalGeneration, AutoTokenizer, AutoProcessormodel_name = "Qwen/Qwen2-VL-7B-Instruct"model = Qwen2VLForConditionalGeneration.from_pretrained(model_name,torch_dtype="auto",##attn_implementation="flash_attention_2", #use flash-attention2 if your gpu card supports it (Free Colab's T4 does not support it)device_map="auto",
)
processor = AutoProcessor.from_pretrained(model_name
)

我们定义函数 chat_with_video ,它接受调整后的视频尺寸、每秒帧数和我们将向 Qwen 询问的文本消息:

def chat_with_video(file_name, query, video_width, video_height, fps=1.0):messages = [{"role": "user","content": [{"type": "video","video": file_name,"max_pixels": video_width * video_height,"fps": 1.0,},{"type": "text", "text": query},],}]text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)image_inputs, video_inputs = process_vision_info(messages)inputs = processor(text=[text],images=image_inputs,videos=video_inputs,padding=True,return_tensors="pt",)inputs = inputs.to("cuda")generated_ids = model.generate(**inputs, max_new_tokens=150)generated_ids_trimmed = [out_ids[len(in_ids) :] for in_ids, out_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)return output_text

让我们问一下模型:


output_text = chat_with_video(file_name, "这个视频展示了什么?", 360, 360,fps=0.5)
>>['这个视频展示了两个人在餐馆里吃饭的场景。其中一个人穿着蓝色衣服,戴着帽子,另一个人穿着棕色外套。他们用筷子夹着食物,喝着汤,看起来非常享受。']

还有另一个问题:

output_text = chat_with_video(file_name, "谁付的钱?", 360, 360,fps=0.5)
>>['根据视频内容,最后是穿棕色大衣的男子付了钱。']

耗费gpu资源情况:

令人惊讶的是,该模型准确地回答了这两个问题!

一些额外的说明:

  • 增加视频的高度、宽度和帧速率 (fps) 通常会提高准确性,但需要更多的 GPU VRAM。
  • Qwen2-VL 可以处理超过 20 分钟的视频,但是GPU资源需求很大。
  • 从我的实验来看,Qwen2-VL-7B 在准确性和资源需求 (GPU VRAM) 之间提供了最佳平衡。

项目 3:多模态 RAG

在本项目中,我们将 Qwen2-VL 与另一个模型 ColPali 相结合,以对 PDF 执行 RAG。ColPali 是一个文档检索模型,包含一个 PaliGemma-3B 模型(也是 VLM)和一个 Gemma-2B。ColPali 的作用是执行文档检索部分并创建一个多向量文档存储:

  • 在我们的例子中,流程如下:
  • 将每个 PDF 页面转换为图像。
  • 将图像推送到 ColPali 中,以存储每个页面的多向量表示。
  • 向 ColPali 提交文本查询以检索相关图像。
  • 将文本查询和相关图像提交给 Qwen2-VL 以获取答案。

我们将使用 Byaldi 库创建图像向量存储。Byaldi 加载 ColPali(以及使用 API 的类似模型)。我们还将使用 pdf2image 将 PDF 转换为图像:

让我们从安装必要的库开始:

#pip install --upgrade byaldi
pip install byaldi==0.0.5
pip install -q git+https://github.com/huggingface/transformers.git qwen-vl-utils pdf2image
## pdf2image 必要的工具
!sudo apt-get install -y poppler-utils

我们将为此项目下载一个 1 页的 PDF — 一个用于节省 VRAM 的小文件。

import urllib.request# We will use this pdf:
url = "<http://ep.ycwb.com/epaper/ycwb/resfile/2020-01-28/A08/ycwb20200128A08.pdf>"
# Download the file
pdf_filepath = url.split('/')[-1]
urllib.request.urlretrieve(url, pdf_filepath)
print(f"Downloaded file name: {pdf_filepath}")

由于模型处理的是图像,而不是 PDF 文件,因此我们将每个页面转换为图像。如果要在 Jupyter/Colab 中可视化图像,请运行以下代码:

from PIL import Image as PILImage
from pdf2image import convert_from_path
from IPython.display import displayimages = convert_from_path(pdf_filepath)
for page_number, page in enumerate(images):resized_image = page.resize((600, 800), PILImage.Resampling.LANCZOS)print(f"Page {page_number + 1}:")display(resized_image)

以下是我们 PDF 中的一张图片:

接下来,我们加载 ColPali 并构建我们的索引存储:

from transformers import Qwen2VLForConditionalGeneration, AutoTokenizer, AutoProcessor
from qwen_vl_utils import process_vision_info
import torchvlm_name = "Qwen/Qwen2-VL-7B-Instruct"
model = Qwen2VLForConditionalGeneration.from_pretrained(vlm_name,torch_dtype="auto",device_map="auto")
processor = AutoProcessor.from_pretrained(vlm_name)

该函数extract_answer_from_pdf执行以下操作:

  1. 给定一个文本查询,我们要求 Colpali 检索最相关的图像 (k=1)。该图像表示一个 PDF 页面。
  2. 给定文本查询和相关图像,我们要求 Qwen-VL-7B 执行图像识别并提供文本查询的答案:
  3. 该函数返回答案 (output_text)、包含答案的页码以及相关的图像/页面
def extract_answer_from_pdf(text_query):results = RAG.search(text_query, k=1)print(results)image_index = results[0]["page_num"] - 1messages = [{"role": "user","content": [{"type": "image","image": images[image_index], ## 包含检索到的 pdf 页面作为图像"resized_height": 527,"resized_width": 522,},{"type": "text", "text": text_query},],}]text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)image_inputs, video_inputs = process_vision_info(messages)inputs = processor(text=[text],images=image_inputs,videos=video_inputs,padding=True,return_tensors="pt",)inputs = inputs.to(device)generated_ids = model.generate(**inputs, max_new_tokens=50)## 从答案中删除提示generated_ids_trimmed = [out_ids[len(in_ids) :] for in_ids, out_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)return output_text, results[0].page_num , images[image_index]

让我们问问我们的模型:

text_query = "这篇报道的时间是什么?"
output_text, page_number, image =  extract_answer_from_pdf(text_query)print("\\n\\n")
print(output_text)>>> 
['2020-01-28']

模型是正确的!报道的时间是2020-01-28。让我们再问一个问题:

text_query = "科比的直升机坠机地点在哪?"
output_text, page_number, image =  extract_answer_from_pdf(text_query)print("\\n\\n")
print(output_text)>>>
['T事故发生在美国加利福尼亚州卡拉巴萨斯市,在洛杉矶以西大约 30 公里.']
  1. 我们可以使用多个 PDF 吗?

是的!只需将多个 PDF 放在访问的RAG.index()文件夹中即可。

  1. 我们可以检索多张图像吗?

是的。在这种情况下,我们只检索了最相关的图像 (k=1)。你可以通过设置 k=2 来检索更多图像,然后将两张图像都传递给 Qwen 进行处理。

chat_template = [{"role": "user","content": [{"type": "image","image": image[0],},{"type": "image","image": image[1],}{"type": "text", "text": text_query},],}
]

但是,添加更多 PDF 或检索多个页面需要更多的资源。

结束语

本文探讨了 Qwen2-VL 在图像、视频和文档检索任务中的应用。

对于更复杂的情况,你可以选择模型的更大版本或量化版本——这些版本的大小更小,质量损失最小。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/60529.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

探索Python的HTTP利器:Requests库的神秘面纱

文章目录 **探索Python的HTTP利器&#xff1a;Requests库的神秘面纱**一、背景&#xff1a;为何选择Requests库&#xff1f;二、Requests库是什么&#xff1f;三、如何安装Requests库&#xff1f;四、Requests库的五个简单函数使用方法1. GET请求2. POST请求3. PUT请求4. DELET…

信号保存和信号处理

目录 信号保存中重要的概念 内核中信号的保存 对sigset_t操作的函数 对block&#xff0c;pendding&#xff0c;handler三张表的操作 sigpromask ​编辑 sigpending 是否有sighandler函数呢&#xff1f; 案例 信号处理 操作系统是如何运行的&#xff1f; 硬件中断 …

基于HTTP编写ping操作

基于HTTP编写ping操作 前言 在上一集我们就完成了创建MockServer的任务&#xff0c;那么我们就可以正式开始进行网络的通讯&#xff0c;那么我们今天就来基于HTTP来做一个客户端ping服务端的请求&#xff0c;服务端返回pong的响应。 需求分析 基于HTTP&#xff0c;实现ping…

机器学习 贝叶斯公式

这是条件概率的计算公式 &#x1d443;(&#x1d434;|&#x1d435;)&#x1d443;(B|A)&#x1d443;(&#x1d434;)/&#x1d443;(&#x1d435;) 全概率公式 &#x1d443;(&#x1d435;)&#x1d443;(&#x1d435;|&#x1d434;)&#x1d443;(&#x1d434;)&am…

【ACM出版】第四届信号处理与通信技术国际学术会议(SPCT 2024)

第四届信号处理与通信技术国际学术会议&#xff08;SPCT 2024&#xff09; 2024 4th International Conference on Signal Processing and Communication Technology 2024年12月27-29日 中国深圳 www.icspct.com 重要信息 三轮征稿时间&#xff1a;2024年11月30日23:5…

【工具插件类教学】在 Unity 中使用 iTextSharp 实现 PDF 文件生成与导出

目录 一、准备工作 1. 安装 iTextSharp 2. 准备资源文件 二、创建 ExportPDFTool 脚本 1、初始化 PDF 文件,设置字体 2、添加标题、内容、表格和图片 三、使用工具类生成 PDF 四、源码地址 在 Unity 项目中,我们有时会需要生成带有文本、表格和图片的 PDF 文件,以便…

Java 责任链模式 减少 if else 实战案例

一、场景介绍 假设有这么一个朝廷&#xff0c;它有 县-->府-->省-->朝廷&#xff0c;四级行政机构。 这四级行政机构的关系如下表&#xff1a; 1、县-->府-->省-->朝廷&#xff1a;有些地方有完整的四级行政机构。 2、县-->府-->朝廷&#xff1a;直…

vue项目使用eslint+prettier管理项目格式化

代码格式化、规范化说明 使用eslintprettier进行格式化&#xff0c;vscode中需要安装插件ESLint、Prettier - Code formatter&#xff0c;且格式化程序选择为后者&#xff08;vue文件、js文件要分别设置&#xff09; 对于eslint规则&#xff0c;在格式化时不会全部自动调整&…

易考八股文之Elasticsearch合集

1、为什么要使用 Elasticsearch? 系统中的数据&#xff0c; 随着业务的发展&#xff0c; 时间的推移&#xff0c; 将会非常多&#xff0c;而业务中往往采用模糊查询进行数据的 搜索&#xff0c;而模糊查询会导致查询引擎放弃索引&#xff0c; 导致系统查询数据时都是全表扫描&…

Python 使用链式赋值

n n_ max(round(n * gd), 1) if n > 1 else n # depth gainPython 使用链式赋值将同一个值同时赋给多个变量。这意味着 n 和 n_ 会同时接收相同的结果值。我们可以将这行代码逐步拆解&#xff0c;以便理解传值的顺序和方法&#xff1a; 逐步解析 先计算右侧表达式的值&a…

Leetcode 整数转罗马数字

这段代码的算法思想是基于罗马数字的减法规则&#xff0c;将整数转换为罗马数字的字符串表示。下面是详细的解释&#xff1a; 算法步骤&#xff1a; 定义数值和符号对应关系&#xff1a;代码中定义了两个数组&#xff1a;values 和 symbols。values 数组包含了罗马数字的数值&…

web——sqliabs靶场——第六关——报错注入和布尔盲注

这一关还是使用报错注入和布尔盲注 一. 判断是否有sql注入 二. 判断注入的类型 是双引号的注入类型。 3.报错注入的检测 可以使用sql报错注入 4.查看库名 5. 查看表名 6.查看字段名 7. 查具体字段的内容 结束 布尔盲注 结束

Spring Cloud Eureka 服务注册与发现

Spring Cloud Eureka 服务注册与发现 一、Eureka基础知识概述1.Eureka两个核心组件2.Eureka 服务注册与发现 二、Eureka单机搭建三、Eureka集群搭建四、心跳续约五、Eureka自我保护机制 一、Eureka基础知识概述 1.Eureka两个核心组件 Eureka Server &#xff1a;服务注册中心…

PowerShell 的执行策略限制了脚本的运行

问题 . : 无法加载文件 C:\Users\pumpkin84514\Documents\WindowsPowerShell\profile.ps1&#xff0c;因为在此系统上禁止运行脚本。有关详细信 息&#xff0c;请参阅 https:/go.microsoft.com/fwlink/?LinkID135170 中的 about_Execution_Policies。 所在位置 行:1 字符: 3 …

计算机网络(3)网络拓扑和IP地址,MAC地址,端口地址详解

ok网络拓扑以及令人困惑的IP地址&#xff0c;MAC地址和端口地址来了&#xff01;&#xff01;&#xff01; 网络拓扑是指计算机网络中各个节点&#xff08;如计算机、路由器、交换机等&#xff09;以及它们之间连接的布局或结构。&#xff08;人话翻译过来也就是网络拓扑是计算…

Git - Think in Git

记录一些使用Git时的一些想法 区的概念 当 clone 仓库代码到本地后四个区相同 当编辑代码后&#xff0c;工作区 与其余三个区不同 当使用 add 将修改的代码暂存后&#xff0c;索引区与 工作区 相同 当使用 commit 将修改的代码提交后&#xff0c;仓库区 与 索引区 和 工作区 相…

CAN通讯演示(U90-M24DR)

概述 CAN通讯一般用的不多&#xff0c;相比于Modbus通讯不是特别常见&#xff0c;但也会用到&#xff0c;下面介绍一下CAN通讯&#xff0c;主要用U90军用PLC演示一下具体的数据传输过程。想更具体的了解的话&#xff0c;可以自行上网学习&#xff0c;此处大致介绍演示。…

时序论文19|ICML24 : 一篇很好的时序模型轻量化文章,用1k参数进行长时预测

论文标题&#xff1a;SparseTSF: Modeling Long-term Time Series Forecasting with 1k Parameters 论文链接&#xff1a;https://arxiv.org/pdf/2402.01533 代码链接&#xff1a;https://github.com/lss-1138/SparseTSF 前言 最近读论文发现时间序列研究中&#xff0c;模型…

(动画版)排序算法 -希尔排序

文章目录 1. 希尔排序&#xff08;Shellsort&#xff09;1.1 简介1.2 希尔排序的步骤1.3 希尔排序的C实现1.4 时间复杂度1.5 空间复杂度1.6 希尔排序动画 1. 希尔排序&#xff08;Shellsort&#xff09; 1.1 简介 希尔排序&#xff08;Shells Sort&#xff09;&#xff0c;又…

Python学习从0到1 day26 第三阶段 Spark ④ 数据输出

半山腰太挤了&#xff0c;你该去山顶看看 —— 24.11.10 一、输出为python对象 1.collect算子 功能: 将RDD各个分区内的数据&#xff0c;统一收集到Driver中&#xff0c;形成一个List对象 语法&#xff1a; rdd.collect() 返回值是一个list列表 示例&#xff1a; from …