---
title: Fluoroscope
canonical_url: "https://www.modelscope.cn/models/smau0441/Fluoroscope"
md_url: "https://www.modelscope.cn/models/smau0441/Fluoroscope.md"
repository: smau0441/Fluoroscope
chinese_name: "电商场景中用户意图的智能“透视仪”"
last_updated: 2025-02-03
license: "Apache License 2.0"
pipeline_tag: image-text-to-text
tasks:
  - image-text-to-text
base_model:
  - Qwen/Qwen2-VL-7B-Instruct
base_model_relation: finetune
parameters: 24.9B
tensor_type:
  - BF16
library_name:
  - pytorch
  - safetensors
frameworks:
  - PyTorch
language:
  - zh
downloads: 280
stars: 8
---

# Fluoroscope

> Fluoroscope - smau0441 在 ModelScope 开源的模型。显存占用 |模式|显存占用（GiM）| |----|----| |LoRA|32.62| |LoRA-Plus|35.99| |推理|21.41| 1. 微调 通过微调的方式，能够在少量数据上训练出一个针对特定数据集的模型。完成这一步，可以学习以下知识；数据集的制作、模型环境的配置、LoRA 微调、数据集优化、模型超参数优化等知识。 1.1 配置环境 建议使用 miniconda 创建虚拟环境，可参考…

smau0441/Fluoroscope 是 ModelScope 魔搭社区上的 24.9B 参数image-text-to-text模型，采用 Apache License 2.0 许可，基于 Qwen/Qwen2-VL-7B-Instruct 构建。

- **Repository**: smau0441/Fluoroscope
- **License**: Apache License 2.0
- **Tasks**: image-text-to-text
- **Parameters**: 24.9B
- **Base model**: Qwen/Qwen2-VL-7B-Instruct
- **Downloads**: 280
- **Stars**: 8
- **Last updated**: 2025-02-03

Source: https://www.modelscope.cn/models/smau0441/Fluoroscope

---

# 0. 前言
## 文件格式
```bash
Qwen2-VL-7B-Instruct/   # 初始权重
README.md               # readme
app.py                  # gradio服务
datasets/               # 数据集
export.sh               # 合并数据集
fine-tune-lora-plus.sh  # 优化后的微调脚本
fine-tune-lora.sh       # lora 微调脚本
infer-lora-plus.sh      # 优化后的推理脚本
infer-lora.sh           # lora 推理脚本
lora/                   # lora 微调权重
lora-plus/              # 优化后的微调权重
requirements.txt        # 环境依赖
```
## 显存占用
|模式|显存占用（GiM）|
|----|----|
|LoRA|32.62| 
|LoRA-Plus|35.99|
|推理|21.41|
# 1. 微调
通过微调的方式，能够在少量数据上训练出一个针对特定数据集的模型。完成这一步，可以学习以下知识；数据集的制作、模型环境的配置、LoRA 微调、数据集优化、模型超参数优化等知识。
## 1.1 配置环境
建议使用 miniconda 创建虚拟环境，可参考 https://docs.anaconda.com/miniconda/ 。

### 1.1.1 安装 ms-swift
参考官方 Github https://github.com/modelscope/ms-swift ，从源码编译。同时运行 ms-swift/requirements/install_all.sh ，安装额外的依赖。

### 1.1.2 安装 flash-attn
参考官方 Github https://github.com/Dao-AILab/flash-attention ，建议从源码编译。

### 1.1.3 requirements.txt 安装
```bash
pip install -r requirements.txt
```
以下是一份完整的 requirements.txt ，包含了 ms-swift 和 flash-attn 的所有依赖。
<details>
<summary>requirements.txt</summary>
```
absl-py==2.1.0
accelerate==1.3.0
addict==2.4.0
aiofiles==23.2.1
aiohappyeyeballs==2.4.4
aiohttp==3.11.11
aiohttp-cors==0.7.0
aiosignal==1.3.2
airportsdata==20241001
aliyun-python-sdk-core==2.16.0
aliyun-python-sdk-kms==2.16.5
altair==5.5.0
annotated-types==0.7.0
antlr4-python3-runtime==4.9.3
anyio==4.8.0
appdirs==1.4.4
argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
arrow==1.3.0
arxiv==2.1.3
astor==0.8.1
asttokens==3.0.0
async-lru==2.0.4
async-timeout==4.0.3
attrdict==2.0.1
attrs==24.3.0
audioread==3.0.1
auto_gptq==0.7.1
autoawq==0.2.8
av==14.0.1
babel==2.16.0
beautifulsoup4==4.12.3
binpacking==1.5.2
bitsandbytes==0.45.0
blake3==1.0.2
bleach==6.2.0
blinker==1.9.0
braceexpand==0.1.7
Brotli==1.1.0
cachetools==5.5.0
certifi==2024.12.14
cffi==1.17.1
charset-normalizer==3.4.1
click==8.1.8
cloudpickle==3.1.1
colorama==0.4.6
coloredlogs==15.0.1
colorful==0.5.6
comm==0.2.2
compressed-tensors==0.8.1
contourpy==1.3.1
cpm-kernels==1.0.11
crcmod==1.7
cryptography==44.0.0
cycler==0.12.1
dacite==1.8.1
dataclasses-json==0.6.7
datasets==3.0.1
debugpy==1.8.12
decorator==5.1.1
decord==0.6.0
deepspeed==0.14.5
defusedxml==0.7.1
depyf==0.18.0
dill==0.3.8
diskcache==5.6.3
distlib==0.3.9
distro==1.9.0
docstring_parser==0.16
duckduckgo_search==5.3.1b1
editdistance==0.8.1
einops==0.8.0
et_xmlfile==2.0.0
eval_type_backport==0.2.2
evalscope==0.10.0
evaluate==0.4.3
exceptiongroup==1.2.2
executing==2.1.0
fastapi==0.115.6
fastjsonschema==2.21.1
feedparser==6.0.11
ffmpy==0.5.0
filelock==3.16.1
fire==0.7.0
flash_attn==2.7.3
fonttools==4.55.3
fqdn==1.5.1
frozenlist==1.5.0
fsspec==2024.6.1
func_timeout==4.3.5
future==1.0.0
fuzzywuzzy==0.18.0
gekko==1.2.1
gguf==0.10.0
gitdb==4.0.12
GitPython==3.1.44
google-api-core==2.24.0
google-auth==2.37.0
googleapis-common-protos==1.66.0
gradio==5.12.0
gradio_client==1.5.4
greenlet==3.1.1
griffe==0.49.0
grpcio==1.69.0
h11==0.14.0
h2==4.1.0
h5py==3.12.1
hjson==3.1.0
hpack==4.0.0
httpcore==1.0.7
httptools==0.6.4
httpx==0.28.1
httpx-sse==0.4.0
huggingface-hub==0.27.1
human-eval==1.0.3
humanfriendly==10.0
hyperframe==6.0.1
icecream==2.1.4
idna==3.10
imageio==2.37.0
immutabledict==4.2.1
importlib_metadata==8.6.1
iniconfig==2.0.0
interegular==0.3.3
ipykernel==6.29.5
ipython==8.31.0
ipywidgets==8.1.5
isoduration==20.11.0
jedi==0.19.2
jieba==0.42.1
Jinja2==3.1.5
jiter==0.8.2
jmespath==0.10.0
joblib==1.4.2
json5==0.10.0
jsonlines==4.0.0
jsonpatch==1.33
jsonpointer==3.0.0
jsonschema==4.23.0
jsonschema-specifications==2024.10.1
jupyter==1.1.1
jupyter-console==6.6.3
jupyter-events==0.11.0
jupyter-lsp==2.2.5
jupyter_client==8.6.3
jupyter_core==5.7.2
jupyter_server==2.15.0
jupyter_server_terminals==0.5.3
jupyterlab==4.3.4
jupyterlab_pygments==0.3.0
jupyterlab_server==2.27.3
jupyterlab_widgets==3.0.13
kiwisolver==1.4.8
lagent==0.2.4
langchain==0.3.15
langchain-community==0.3.15
langchain-core==0.3.31
langchain-openai==0.3.1
langchain-text-splitters==0.3.5
langsmith==0.2.11
lark==1.2.2
lazy_loader==0.4
Levenshtein==0.26.1
librosa==0.10.2.post1
linkify-it-py==2.0.3
-e git+https://github.com/hiyouga/LLaMA-Factory.git@0f45982bac6b65533a94054ea5f792cb0f9e5a1f#egg=llamafactory
llvmlite==0.44.0
lm-format-enforcer==0.10.9
lmdeploy==0.7.0
lxml==5.3.0
Markdown==3.7
markdown-it-py==3.0.0
MarkupSafe==2.1.5
marshmallow==3.25.1
matplotlib==3.10.0
matplotlib-inline==0.1.7
mdit-py-plugins==0.4.2
mdurl==0.1.2
memray==1.15.0
mistral_common==1.5.1
mistune==3.1.0
mmengine==0.10.6
mmengine-lite==0.10.6
modelscope==1.22.3
mpmath==1.3.0
ms-opencompass==0.1.5
-e git+https://github.com/modelscope/ms-swift.git@c34b1288425999a664fb053648fd639ccaa3e03c#egg=ms_swift
ms-vlmeval==0.0.12
msgpack==1.1.0
msgspec==0.19.0
mteb==1.19.4
multidict==6.1.0
multiprocess==0.70.16
mypy-extensions==1.0.0
narwhals==1.23.0
nbclient==0.10.2
nbconvert==7.16.5
nbformat==5.10.4
nest-asyncio==1.6.0
networkx==3.4.2
ninja==1.11.1.3
nltk==3.9.1
notebook==7.3.2
notebook_shim==0.2.4
numba==0.61.0
numpy==1.26.4
nvidia-cublas-cu12==12.4.5.8
nvidia-cuda-cupti-cu12==12.4.127
nvidia-cuda-nvrtc-cu12==12.4.127
nvidia-cuda-runtime-cu12==12.4.127
nvidia-cudnn-cu12==9.1.0.70
nvidia-cufft-cu12==11.2.1.3
nvidia-curand-cu12==10.3.5.147
nvidia-cusolver-cu12==11.6.1.9
nvidia-cusparse-cu12==12.3.1.170
nvidia-ml-py==12.560.30
nvidia-nccl-cu12==2.21.5
nvidia-nvjitlink-cu12==12.4.127
nvidia-nvtx-cu12==12.4.127
omegaconf==2.3.0
openai==1.59.9
OpenCC==1.1.9
opencensus==0.11.4
opencensus-context==0.1.3
opencv-python==4.11.0.86
opencv-python-headless==4.11.0.86
openpyxl==3.1.5
optimum==1.23.3
orjson==3.10.15
oss2==2.19.1
outlines==0.1.11
outlines_core==0.1.26
overrides==7.7.0
packaging==24.2
pandas==2.2.3
pandocfilters==1.5.1
parso==0.8.4
partial-json-parser==0.2.1.1.post5
peft==0.12.0
pexpect==4.9.0
phx-class-registry==4.1.0
pillow==10.4.0
platformdirs==4.3.6
plotly==5.24.1
pluggy==1.5.0
ply==3.11
polars==1.20.0
pooch==1.8.2
portalocker==3.1.1
prettytable==3.12.0
prometheus-fastapi-instrumentator==7.0.2
prometheus_client==0.21.1
prompt_toolkit==3.0.50
propcache==0.2.1
proto-plus==1.25.0
protobuf==5.29.3
psutil==6.1.1
ptyprocess==0.7.0
pure_eval==0.2.3
py-cpuinfo==9.0.0
py-spy==0.4.0
pyarrow==19.0.0
pyasn1==0.6.1
pyasn1_modules==0.4.1
pyav==14.0.1
pybind11==2.13.6
pycountry==24.6.1
pycparser==2.22
pycryptodome==3.21.0
pydantic==2.10.5
pydantic-settings==2.7.1
pydantic_core==2.27.2
pydeck==0.9.1
pydub==0.25.1
Pygments==2.19.1
Pympler==1.1
pyparsing==3.2.1
pypinyin==0.53.0
pysbd==0.3.4
pytest==8.3.4
python-dateutil==2.9.0.post0
python-dotenv==1.0.1
python-json-logger==3.2.1
python-Levenshtein==0.26.1
python-multipart==0.0.20
pytrec-eval-terrier==0.5.6
pytz==2024.2
PyYAML==6.0.2
pyzmq==26.2.0
qwen-vl-utils==0.0.8
ragas==0.2.9
rank-bm25==0.2.2
RapidFuzz==3.11.0
ray==2.40.0
referencing==0.36.1
regex==2024.11.6
requests==2.32.3
requests-toolbelt==1.0.0
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
rich==13.9.4
rouge==1.0.1
rouge-chinese==1.0.3
rouge_score==0.1.2
rpds-py==0.22.3
rsa==4.9
ruff==0.9.2
sacrebleu==2.5.1
safehttpx==0.1.6
safetensors==0.5.2
scikit-image==0.25.0
scikit-learn==1.6.1
scipy==1.15.1
seaborn==0.13.2
semantic-version==2.10.0
Send2Trash==1.8.3
sentence-transformers==3.3.1
sentencepiece==0.2.0
sgmllib3k==1.0.0
shellingham==1.5.4
shtab==1.7.1
simple-ddl-parser==1.7.1
simplejson==3.19.3
six==1.17.0
smart-open==7.1.0
smmap==5.0.2
sniffio==1.3.1
socksio==1.0.0
sortedcontainers==2.4.0
soundfile==0.13.0
soupsieve==2.6
soxr==0.5.0.post1
SQLAlchemy==2.0.37
sse-starlette==2.2.1
stack-data==0.6.3
starlette==0.41.3
streamlit==1.41.1
sty==1.0.6
sympy==1.13.1
tabulate==0.9.0
tenacity==9.0.0
tensorboard==2.18.0
tensorboard-data-server==0.7.2
termcolor==2.5.0
terminado==0.18.1
textual==1.0.0
threadpoolctl==3.5.0
tifffile==2025.1.10
tiktoken==0.7.0
timeout-decorator==0.5.0
timm==1.0.14
tinycss2==1.4.0
tokenizers==0.20.3
toml==0.10.2
tomli==2.2.1
tomlkit==0.13.2
torch==2.5.1
torchvision==0.20.1
tornado==6.4.2
tqdm==4.67.1
traitlets==5.14.3
transformers==4.46.1
transformers-stream-generator==0.0.5
triton==3.1.0
trl==0.11.4
typeguard==4.4.1
typer==0.15.1
types-python-dateutil==2.9.0.20241206
typing-inspect==0.9.0
typing_extensions==4.12.2
tyro==0.8.14
tzdata==2024.2
uc-micro-py==1.0.3
unicorn==2.1.1
uri-template==1.3.0
urllib3==2.3.0
uvicorn==0.34.0
uvloop==0.21.0
validators==0.34.0
virtualenv==20.29.1
vllm==0.6.6.post1
watchdog==6.0.0
watchfiles==1.0.4
wcwidth==0.2.13
webcolors==24.11.1
webdataset==0.2.100
webencodings==0.5.1
websocket-client==1.8.0
websockets==14.2
Werkzeug==3.1.3
widgetsnbextension==4.0.13
wrapt==1.17.2
xformers==0.0.28.post3
xgrammar==0.1.10
XlsxWriter==3.2.0
xtuner==0.1.23
xxhash==3.5.0
yapf==0.43.0
yarl==1.18.3
zipp==3.21.0
zstandard==0.23.0
```
</details>

## 1.2 数据集获取
使用的数据集已经上传 modelscope，参考 https://www.modelscope.cn/datasets/smau0441/www2025-train/files 。
### 1.2.1 数据来源

该数据集来自于**WWW2025 多模态对话系统意图识别挑战赛**，网址：https://tianchi.aliyun.com/competition/entrance/532277/information 。数据集中包括 1000 个标记的对话样本用于训练。

### 1.2.2 数据介绍

该数据集全部为文本结合图像的分类任务，包含 2 大类：图片场景分类以及多轮对话意图分类。

#### 1.2.2.1 图片场景分类
图片场景分类的具体场景为用户向客服发了一张图片，需要判断这张图片属于电商场景的哪个类型。具体类型标签以及对应的描述如下：

| label | 描述 |
| ---- | ---- |
| 商品分类选项 | 商品颜色、规格选项 |
| 商品头图 | 商品首页大图 |
| 商品详情页截图 | 可能出现在商品详情页各个部分的截图 |
| 下单过程中出现异常（显示购买失败浮窗） | 下单过程中出现异常（显示购买失败浮窗）的截图 |
| 订单详情页面 | 呈现出完整订单信息的订单页面 |
| 支付页面 | 包含支付方式选择、支付成功的页面 |
| 评论区截图页面 | 在淘宝 APP 内或其他 APP 内的评论区截图 |
| 物流页面 - 物流列表页面 | 呈现出两个以上物流信息的页面 |
| 物流页面 - 物流跟踪页面 | 呈现出物流运输路径的页面 |
| 物流页面 - 物流异常页面 | 包含物流异常信息的页面 |
| 退款页面 | 含有退款信息的页面 |
| 退货页面 | 含有退货信息的页面 |
| 换货页面 | 含有换货信息的页面 |
| 购物车页面 | 淘宝购物车页面的列表图或金额计算图 |
| 店铺页面 | 店铺首页截图 |
| 活动页面 | 活动截图 |
| 优惠券领取页面 | 店铺首页或活动页中领取优惠券的截图 |
| 账单/账户页面 | 包含交易明细列表、资产列表、卡券红包列表等 |
| 投诉举报页面 | 投诉或举报页面 |
| 实物拍摄(含售后) | 用户用相机实拍的照片，包括用户售后的照片（损坏、缺失、与描述不符），或者其他用相机实拍的图片 |
| 外部 APP 截图 | 各种非淘宝、菜鸟 APP 的截图，包括京东、拼多多、短信、手机系统截图 |
| 平台介入页面 | 平台客服介入处理的截图 |
| 其他类别图片 | 拿不准的其他图片 |

#### 1.2.2.2 对话意图分类
对话意图分类的场景为通过用户与客服的对话历史以及当前用户的问题判断用户的意图，对话历史中包含至少一张用户发的图片，可能有助于意图的判断。意图标签以及对应的描述如下：

| label | 描述 |
| ---- | ---- |
| 反馈密封性不好 | 买家反馈商品密封性差会漏 |
| 是否好用 | 买家咨询商品是否好用 |
| 是否会生锈 | 咨询商品是否会生锈 |
| 排水方式 | （适用产品：洗衣机、热水器）咨询商品排水方式 |
| 包装区别 | 咨询商品不同包装的区别 |
| 发货数量 | 咨询商品发货数量 |
| 反馈用后症状 | 买家反馈用完后引起的人体反应 |
| 商品材质 | 咨询商品具体材质，配件材质，填充物 |
| 功效功能 | 咨询商品功效功能 |
| 是否易褪色 | 咨询商品是否易褪色 |
| 适用季节 | 咨询商品适用季节 |
| 能否调光 | 咨询商品光源/灯光/光线/亮度是否可调 |
| 版本款型区别 | 咨询两个版本/型号/款式/类型/套装/组合等区别，不包括商品数量/重量/尺寸类区别 |
| 单品推荐 | 消费者咨询能否帮助推荐一下某类/某个商品，非 sku 级别 |
| 用法用量 | 咨询商品使用方法/步骤/顺序，包括但不限于用量，使用时间，使用部位 |
| 控制方式 | 咨询商品如何控制，能否可以通过手机/电脑控制 |
| 上市时间 | 咨询商品的上市时间 |
| 商品规格 | 咨询商品的数量、重量、含量、容量 |
| 信号情况 | 咨询手机使用的信号是否良好，以及信号不佳如何处理 |
| 养护方法 | 咨询商品的养护方法 |
| 套装推荐 | 消费者希望能推荐一些套装 |
| 何时上货 | 咨询补货/上货时间 |
| 气泡 | 咨询贴膜如何避免产生气泡及除气泡方法 |

## 1.3 Qwen2-VL-7B-Instruct 权重获取
获取模型权重可参考魔搭官方仓库 https://www.modelscope.cn/models/qwen/Qwen2-7B-Instruct/files 。

## 1.4 制作用于微调的数据集的配置文件
首先需要将原始的 train.json 数据做成 swift 能够读取的数据格式。make_ms_dataset.py 与 train.json 同级，下面代码是 make_ms_dataset.py 的内容，该脚本将 1000 个数据打乱之后，划分成 900 个训练数据和 100 个测试数据，并给图像增加相对路径，确保能够正确读取图像。
```python
import os
import json
import random

with open("train.json", "r") as file:
    root = json.load(file)

random.shuffle(root)

train_root = root[:900]
val_root = root[900:]

def save_data(root, name):
    new_root = []
    for item in root:
        # print(item)
        new_item = {}
        new_item['id'] = item['id']
        new_item['query'] = item['instruction'] 
        new_item['response'] = item['output']
        
        images = []
        for image in item["image"]:
            images.append(os.path.join("datasets/images", image))
        
        new_item["images"] = images
        new_root.append(new_item)

    if os.path.exists("ms_split/"+name+".jsonl"):
        os.remove("ms_split/"+name+".jsonl")
        print('remove:'+"ms_split/"+name+".jsonl")

    print(name,' 数据集长度：',len(new_root))
    
    with open("ms_split/"+name+".jsonl", "w") as file:
        json.dump(new_root, file, indent=4, ensure_ascii=False)
        print("--add image path success--")
    return new_root
    
train_data = save_data(train_root,'train')
val_data = save_data(val_root,'val')

all_data = train_data + val_data
all_path = "ms_split/all.jsonl"

if os.path.exists(all_path):
    os.remove(all_path)
    print('remove:'+all_path)

print('all',' 数据集长度：',len(all_data))
with open(all_path, "w") as file:
    json.dump(all_data, file, indent=4, ensure_ascii=False)
    print("--add image path success--")
```

## 1.5 Qwen2-VL-7B-Instruct LoRA 微调实验
### 微调脚本
对于 7B 的模型，需要极致缩小 MAX_PINXELS 的参数，才能够勉强将模型跑起来。
```bash
# 256 * 28 * 28 
TOKENIZERS_PARALLELIS=true  MNPROC_PER_NODE=1 CUDA_VISIBLE_DEVICES=0 MAX_PIXELS=200704 \
swift sft \
    --model Qwen2-VL-7B-Instruct \
    --check_model false \
    --train_type lora \
    --attn_impl flash_attn \
    --dataset datasets/ms_split/train.jsonl \
    --val_dataset datasets/ms_split/val.jsonl \
    --torch_dtype bfloat16 \
    --num_train_epochs 2 \
    --per_device_train_batch_size 1 \
    --per_device_eval_batch_size 1 \
    --learning_rate 1e-4 \
    --lora_rank 8 \
    --lora_alpha 32 \
    --target_modules all-linear \
    --gradient_accumulation_steps 16 \
    --eval_steps 25 \
    --save_steps 25 \
    --save_total_limit 5 \
    --logging_steps 5 \
    --max_length 2048 \
    --system 'You are a helpful assistant.' \
    --warmup_ratio 0.05 \
    --dataloader_num_workers 4 \
    --model_author GLLM \
    --model_name Qwen2-VL-7B-Instruct-Fluoroscope \
    --output_dir output/lora \
    --gradient_checkpointing false 
```

### 微调结果
验证集上，eval_token_acc 达到 91%。
```json
{
    "eval_loss": 0.2504124,
    "eval_token_acc": 0.91836735,
    "eval_runtime": 45.996,
    "eval_samples_per_second": 2.174,
    "eval_steps_per_second": 2.174,
    "epoch": 1.99111111,
    "global_step/max_steps": "112/112",
    "percentage": "100.00%",
    "elapsed_time": "1h 2m 14s",
    "remaining_time": "0s"
}
```
#### 训练损失
![训练损失](lora/images/train_loss.png)
#### 验证损失
![验证损失](lora/images/eval_loss.png)
#### 验证精度
![验证精度](lora/images/eval_token_acc.png)

# 2. prompt改造
## 2.1 原始词条
### 意图识别
```json
[
    {
        "id": "2104a80d17282572952783243d0afd",
        "instruction": "
            你是一个电商客服专家，请根据用户与客服的多轮对话判断用户的意图分类标签。\n
            <用户与客服的对话 START>\n
            用户: <http>\n
            客服: 您具体想要了解什么问题呢？\n
            用户: <image>\n
            客服: 亲爱的顾客，我们的智能助手还在成长中，目前还无法识别图片内容，麻烦您用文字简要说明遇到的问题，非常感谢。\n
            用户: 亲爱的:这款商品是全部采用金属材质的吗？\n
            客服: \n
            <用户与客服的对话 END>\n
            请直接只输出分类标签结果，不需要其他多余的话。以下是可以参考的分类标签为：
            [\"反馈密封性不好\",
            \"是否好用\",
            \"是否会生锈\",
            \"排水方式\",
            \"包装区别\",
            \"发货数量\",
            \"反馈用后症状\",
            \"商品材质\",
            \"功效功能\",
            \"是否易褪色\",
            \"适用季节\",
            \"能否调光\",
            \"版本款型区别\",
            \"单品推荐\",
            \"用法用量\",
            \"控制方式\",
            \"上市时间\",
            \"商品规格\",
            \"信号情况\",
            \"养护方法\",
            \"套装推荐\",
            \"何时上货\",
            \"气泡\"]\n",
        "input": "",
        "output": "商品材质",
        "image": [
            "2104a80d17282572952783243d0afd-0.jpg"
        ]
    },
]
```
### 场景分类
```json
[
    {
        "id": "2104a80d17282572952783243d0afd",
        "instruction": "
            Picture 1: <image>\n
            你是一个电商领域识图专家,可以理解消费者上传的软件截图或实物拍摄图。现在,请你对消费者上传的图片进行分类。你只需要回答图片分类结果,不需要其他多余的话。
            以下是可以参考的分类标签,分类标签:
            [\"实物拍摄(含售后)\",
            \"商品分类选项\",
            \"商品头图\"
            \"商品详情页截图\",
            \"下单过程中出现异常（显示购买失败浮窗）\",
            \"订单详情页面\",
            \"支付页面\",
            \"消费者与客服聊天页面\",
            \"评论区截图页面\",
            \"物流页面-物流列表页面\",
            \"物流页面-物流跟踪页面\",
            \"物流页面-物流异常页面\",
            \"退款页面\",
            \"退货页面\",
            \"换货页面\",
            \"购物车页面\",
            \"店铺页面\",
            \"活动页面\",
            \"优惠券领取页面\",
            \"账单/账户页面\",
            \"个人信息页面\",
            \"投诉举报页面\",
            \"平台介入页面\",
            \"外部APP截图\",
            \"其他类别图片\"]。",
        "input": "",
        "output": "商品材质",
        "image": [
            "2104a80d17282572952783243d0afd-0.jpg"
        ]
    },
]
```
## 2.1 问题分析：
1. 词条缺少解释，部分词条难以区分（从微调结果分析得知）；
2. prompt结构性不强，背景目标逻辑模糊，没有强调。

## 2.2 优化prompt
针对词条缺少解释，在官方的解释基础上进行完善，缓解词条之间难以区分的问题，增强模型对逻辑和重点的理解。\
优化后的prompt为：
### 意图识别
```json
'''
作为一名资深电商客服专家，你的任务是根据用户与客服之间的多轮对话，精准判断用户的意图，并从给定的分类标签中选择与之匹配的标签。

### 分类标签及详细说明
1. **反馈密封性不好**：买家反馈商品密封性差，存在渗漏问题。
2. **是否好用**：买家就商品的使用体验、实用性等方面进行询问。
3. **是否会生锈**：针对商品是否会生锈这一特性展开咨询。
4. **排水方式**：适用于洗衣机、热水器等产品，询问其排水的具体方式。
5. **包装区别**：对不同商品包装之间的差异进行咨询。
6. **发货数量**：咨询下单后商品的发货数量。
7. **反馈用后症状**：买家反馈使用商品后，身体出现的反应情况。
8. **商品材质**：涉及商品主体、配件以及填充物等具体材质的询问。
9. **功效功能**：咨询商品所具备的功效和功能。
10. **是否易褪色**：询问商品在使用过程中是否容易出现褪色现象。
11. **适用季节**：针对商品适合在哪些季节使用进行咨询。
12. **能否调光**：咨询商品的光源、灯光、光线或亮度是否可以调节。
13. **版本款型区别**：询问不同版本、型号、款式、类型、套装、组合之间的区别，不包含商品数量、重量、尺寸等方面的差异。
14. **单品推荐**：消费者请求推荐某类或某个商品，并非针对具体SKU（库存保有单位）。
15. **用法用量**：咨询商品的使用方法、步骤、顺序，包括但不限于用量、使用时间、使用部位等。
16. **控制方式**：询问商品的控制方法，例如能否通过手机、电脑等设备进行控制。
17. **上市时间**：咨询商品的上市日期。
18. **商品规格**：咨询商品的数量、重量、含量、容量等规格参数。
19. **信号情况**：针对手机使用时的信号状况进行咨询，以及信号不佳时的解决办法。
20. **养护方法**：询问商品的日常保养、维护方法。
21. **套装推荐**：消费者希望获得关于套装产品的推荐。
22. **何时上货**：咨询商品的补货时间或重新上架时间。
23. **气泡**：咨询贴膜过程中避免产生气泡以及去除气泡的方法。

### 任务要求
请严格依据对话内容，仅输出分类标签结果，不需要任何多余的解释或说明。

### 用户与客服的对话
<用户与客服的对话 START>
...
<用户与客服的对话 END>
'''
```

### 场景分类
```json
'''
作为一名资深电商客服专家，你的任务是解读消费者上传的软件截图或实物拍摄图。请对消费者上传的这张图片进行分类。

### 分类标签及详细说明
1. **实物拍摄(含售后)**：用户通过相机实拍的照片，涵盖售后相关照片（如商品损坏、缺失、与描述不符等情况）以及其他相机实拍图。
2. **商品分类选项**：展示商品颜色、规格等选项的图片。
3. **商品头图**：商品首页所展示的大图。
4. **商品详情页截图**：截取商品详情页任意部分的图片。
5. **下单过程中出现异常（显示购买失败浮窗）**：下单时出现异常且显示购买失败浮窗的截图。
6. **订单详情页面**：完整呈现订单信息的页面截图。
7. **支付页面**：包含支付方式选择或支付成功等信息的页面截图。
8. **消费者与客服聊天页面**：消费者与客服沟通的聊天页面截图。
9. **评论区截图页面**：淘宝 APP 或其他 APP 内评论区的截图。
10. **物流页面 - 物流列表页面**：呈现两条及以上物流信息的页面截图。
11. **物流页面 - 物流跟踪页面**：展示物流运输路径的页面截图。
12. **物流页面 - 物流异常页面**：包含物流异常信息的页面截图。
13. **退款页面**：含有退款相关信息的页面截图。
14. **退货页面**：含有退货相关信息的页面截图。
15. **换货页面**：含有换货相关信息的页面截图。
16. **购物车页面**：淘宝购物车的列表图或金额计算图。
17. **店铺页面**：店铺首页的截图。
18. **活动页面**：各类活动的截图。
19. **优惠券领取页面**：店铺首页或活动页中领取优惠券的相关截图。
20. **账单/账户页面**：包含交易明细列表、资产列表、卡券红包列表等信息的页面截图。
21. **个人信息页面**：展示个人信息的页面截图。
22. **投诉举报页面**：投诉或举报相关页面的截图。
23. **平台介入页面**：平台客服介入处理的页面截图。
24. **外部 APP 截图**：除淘宝、菜鸟 APP 之外的各类 APP 截图，例如京东、拼多多，以及短信、手机系统截图等。
25. **其他类别图片**：难以明确归类的其他图片。 

### 任务要求
仅输出分类标签结果，不需要任何多余的解释或说明。
'''
```

## 2.3 改进数据集代码
```python
import os
import json
import random
import re
from tqdm import tqdm


new_template = '''
作为一名资深电商客服专家，你的任务是根据用户与客服之间的多轮对话，精准判断用户的意图，并从给定的分类标签中选择与之匹配的标签。

### 分类标签及详细说明
1. **反馈密封性不好**：买家反馈商品密封性差，存在渗漏问题。
2. **是否好用**：买家就商品的使用体验、实用性等方面进行询问。
3. **是否会生锈**：针对商品是否会生锈这一特性展开咨询。
4. **排水方式**：适用于洗衣机、热水器等产品，询问其排水的具体方式。
5. **包装区别**：对不同商品包装之间的差异进行咨询。
6. **发货数量**：咨询下单后商品的发货数量。
7. **反馈用后症状**：买家反馈使用商品后，身体出现的反应情况。
8. **商品材质**：涉及商品主体、配件以及填充物等具体材质的询问。
9. **功效功能**：咨询商品所具备的功效和功能。
10. **是否易褪色**：询问商品在使用过程中是否容易出现褪色现象。
11. **适用季节**：针对商品适合在哪些季节使用进行咨询。
12. **能否调光**：咨询商品的光源、灯光、光线或亮度是否可以调节。
13. **版本款型区别**：询问不同版本、型号、款式、类型、套装、组合之间的区别，不包含商品数量、重量、尺寸等方面的差异。
14. **单品推荐**：消费者请求推荐某类或某个商品，并非针对具体SKU（库存保有单位）。
15. **用法用量**：咨询商品的使用方法、步骤、顺序，包括但不限于用量、使用时间、使用部位等。
16. **控制方式**：询问商品的控制方法，例如能否通过手机、电脑等设备进行控制。
17. **上市时间**：咨询商品的上市日期。
18. **商品规格**：咨询商品的数量、重量、含量、容量等规格参数。
19. **信号情况**：针对手机使用时的信号状况进行咨询，以及信号不佳时的解决办法。
20. **养护方法**：询问商品的日常保养、维护方法。
21. **套装推荐**：消费者希望获得关于套装产品的推荐。
22. **何时上货**：咨询商品的补货时间或重新上架时间。
23. **气泡**：咨询贴膜过程中避免产生气泡以及去除气泡的方法。

### 任务要求
请严格依据对话内容，仅输出分类标签结果，不需要任何多余的解释或说明。

### 用户与客服的对话
<用户与客服的对话 START>
'''

template_image_task = '''
作为一名资深电商客服专家，你的任务是解读消费者上传的软件截图或实物拍摄图。请对消费者上传的这张图片进行分类。

### 分类标签及详细说明
1. **实物拍摄(含售后)**：用户通过相机实拍的照片，涵盖售后相关照片（如商品损坏、缺失、与描述不符等情况）以及其他相机实拍图。
2. **商品分类选项**：展示商品颜色、规格等选项的图片。
3. **商品头图**：商品首页所展示的大图。
4. **商品详情页截图**：截取商品详情页任意部分的图片。
5. **下单过程中出现异常（显示购买失败浮窗）**：下单时出现异常且显示购买失败浮窗的截图。
6. **订单详情页面**：完整呈现订单信息的页面截图。
7. **支付页面**：包含支付方式选择或支付成功等信息的页面截图。
8. **消费者与客服聊天页面**：消费者与客服沟通的聊天页面截图。
9. **评论区截图页面**：淘宝 APP 或其他 APP 内评论区的截图。
10. **物流页面 - 物流列表页面**：呈现两条及以上物流信息的页面截图。
11. **物流页面 - 物流跟踪页面**：展示物流运输路径的页面截图。
12. **物流页面 - 物流异常页面**：包含物流异常信息的页面截图。
13. **退款页面**：含有退款相关信息的页面截图。
14. **退货页面**：含有退货相关信息的页面截图。
15. **换货页面**：含有换货相关信息的页面截图。
16. **购物车页面**：淘宝购物车的列表图或金额计算图。
17. **店铺页面**：店铺首页的截图。
18. **活动页面**：各类活动的截图。
19. **优惠券领取页面**：店铺首页或活动页中领取优惠券的相关截图。
20. **账单/账户页面**：包含交易明细列表、资产列表、卡券红包列表等信息的页面截图。
21. **个人信息页面**：展示个人信息的页面截图。
22. **投诉举报页面**：投诉或举报相关页面的截图。
23. **平台介入页面**：平台客服介入处理的页面截图。
24. **外部 APP 截图**：除淘宝、菜鸟 APP 之外的各类 APP 截图，例如京东、拼多多，以及短信、手机系统截图等。
25. **其他类别图片**：难以明确归类的其他图片。 

### 任务要求
仅输出分类标签结果，不需要任何多余的解释或说明。

'''
end_words = '''
<用户与客服的对话 END>'''

def extract_content(text):
    pattern = r'<用户与客服的对话 START>(.*?)<用户与客服的对话 END>'
    match = re.search(pattern, text, re.DOTALL)
    if match:
        return match.group(1)
    return None

def extract_text_before(text):
    end_index = text.rfind('你是一个电商领域识图专家')
    if end_index!= -1:
        return text[:end_index]
    return None

with open("train.json", "r") as file:
    root = json.load(file)

random.shuffle(root)

train_root = root[:900]
val_root = root[900:]

def save_data(root, name):
    new_root = []
    for item in tqdm(root):
        # print(item)
        new_item = {}
        new_item['id'] = item['id']
        
        new_item['response'] = item['output']
        
        images = []
        for image in item["image"]:
            images.append(os.path.join("datasets/images", image))
        
        query = item["instruction"]
        if query.startswith("Picture"):
            # 场景分类
            result = extract_text_before(item["instruction"])
            
            item["instruction"] = result + template_image_task
        else:
            # 意图识别
            result = extract_content(item["instruction"])
            
            item["instruction"] = new_template + result + end_words
            
        new_item['query'] = item["instruction"]
        
        new_item["images"] = images
        new_root.append(new_item)
    
    path = "new_ms_split/"+name+".jsonl"

    if os.path.exists(path):
        os.remove(path)
        print('remove:'+path)

    print(name,' 数据集长度：',len(new_root))
    
    with open(path, "w") as file:
        json.dump(new_root, file, indent=4, ensure_ascii=False)
        print("--add image path success--")
    return new_root
    
train_data = save_data(train_root,'train')
val_data = save_data(val_root,'val')

all_data = train_data + val_data
all_path = "new_ms_split/all.jsonl"

if os.path.exists(all_path):
    os.remove(all_path)
    print('remove:'+all_path)

print('all',' 数据集长度：',len(all_data))
with open(all_path, "w") as file:
    json.dump(all_data, file, indent=4, ensure_ascii=False)
    print("--add image path success--")
```

# 3. 使用改进后的数据集微调
## 3.1 微调脚本
```bash
# 256 * 28 * 28 
TOKENIZERS_PARALLELIS=true  MNPROC_PER_NODE=1 CUDA_VISIBLE_DEVICES=0 MAX_PIXELS=200704 \
swift sft \
    --model sources/Qwen2-VL-7B-Instruct \
    --check_model false \
    --train_type lora \
    --attn_impl flash_attn \
    --dataset datasets/new_ms_split/train.jsonl \
    --val_dataset datasets/new_ms_split/val.jsonl \
    --torch_dtype bfloat16 \
    --num_train_epochs 2 \
    --per_device_train_batch_size 1 \
    --per_device_eval_batch_size 1 \
    --learning_rate 1e-4 \
    --lora_rank 8 \
    --lora_alpha 32 \
    --target_modules all-linear \
    --gradient_accumulation_steps 16 \
    --eval_steps 25 \
    --save_steps 25 \
    --save_total_limit 5 \
    --logging_steps 5 \
    --max_length 2048 \
    --system 'You are a helpful assistant.' \
    --warmup_ratio 0.05 \
    --dataloader_num_workers 4 \
    --model_author GLLM \
    --model_name Qwen2-VL-7B-Instruct-Fluoroscope-Plus \
    --output_dir output/lora-plus \
    --gradient_checkpointing false 
```
## 3.2 微调结果
验证集上，eval_token_acc 达到 95%，提升了4%。
```json
{
    "eval_loss": 0.17871645,
    "eval_token_acc": 0.95143488,
    "eval_runtime": 676.9039,
    "eval_samples_per_second": 0.148,
    "eval_steps_per_second": 0.148,
    "epoch": 1.99111111,
    "global_step/max_steps": "112/112",
    "percentage": "100.00%",
    "elapsed_time": "4h 51m 40s",
    "remaining_time": "0s"
}
```
#### 训练损失
![训练损失](lora-plus/images/train_loss.png)
#### 验证损失
![验证损失](lora-plus/images/eval_loss.png)
#### 验证精度
![验证精度](lora-plus/images/eval_token_acc.png)

# 4. 推理
## 4.1 合并模型
合并原始模型和 LoRA 模型。
```bash
swift export \
    --adapters output/lora/v0-20250127-182003/checkpoint-112 \
    --merge_lora true
```

## 4.2 推理脚本
可以使用 bash 脚本推理，唯一缺点就是扩展性不够：
```bash
TOKENIZERS_PARALLELIS=true  MNPROC_PER_NODE=1 CUDA_VISIBLE_DEVICES=0 MAX_PIXELS=200704 \
swift infer \
    --model lora/best-checkpoint-merged \
    --infer_backend pt \
    --attn_impl flash_attn \
    --val_dataset datasets/ms_split/all.jsonl \
    --gpu_memory_utilization 0.9 \
    --tensor_parallel_size 1 \
    --max_model_len 32768 \
    --max_new_tokens 2048 \
    --limit_mm_per_prompt '{"image": 5, "video": 2}'
```

推荐使用 python 脚本推理，扩展性更好：
```python
from modelscope import Qwen2VLForConditionalGeneration, AutoTokenizer, AutoProcessor
from qwen_vl_utils import process_vision_info, smart_resize
import json
import os
import pandas as pd
import copy
from datetime import datetime
import torch
from tqdm import tqdm

model_path = "lora/best-checkpoint-merged" # lora

model = Qwen2VLForConditionalGeneration.from_pretrained(model_path,
                                                        torch_dtype=torch.bfloat16,
                                                        attn_implementation="flash_attention_2",
                                                        device_map="auto")

processor = AutoProcessor.from_pretrained(model_path, max_pixels = 256 * 28 * 28)


MAX_SEQ_LEN = 32000

input_file = "datasets/train.json"

false_sample_list = []

with open(input_file) as f:
    lines = json.load(f)
    for line in tqdm(lines):
        cur_train_data = dict()

        if line["instruction"].startswith("<image>"):
            raise
        if line["instruction"].endswith("<image>"):
            raise
        
        content_list = []
        for index, content in enumerate(line["instruction"].split("<image>")):
            content_list.append({"type":"text", "text":content})
            if index != len(line["instruction"].split("<image>")) - 1:
                content_list.append({"type":"image", "image": 'datasets/images'+line["image"][index]})
        messages = [{"role":"user", "content":content_list}]
        
        while True:
            # Preparation for inference
            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")

            if len(inputs.input_ids[0]) > MAX_SEQ_LEN:
                messages[0]['content'] = messages[0]['content'][1:]
                continue
            else:
                break
            
        # Inference: Generation of the output
        generated_ids = model.generate(**inputs, max_new_tokens=128)
        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
        )[0]
        
        print(line['output'] == output_text)
        if line['output'] != output_text:
            false_sample_list.append(line)

with open("false_sample.json", "w") as file:
    json.dump(false_sample_list, file, indent=4, ensure_ascii=False)
    print("--all images infer success--")
```

## 4.2 推理结果
对所有的训练数据进行推理，统计错误数和错误率。

|模型类型|错误数|错误率|
| ---- | ---- | ---- |
|原始模型|529 个|52.9%|
|LoRA 模型|130 个|13.0%|
|LoRA-Plus 模型| 90个| 9%|

# 5. gradio 服务
## 部署代码
```python
import gradio as gr
from modelscope import Qwen2VLForConditionalGeneration, AutoTokenizer, AutoProcessor
from qwen_vl_utils import process_vision_info, smart_resize
import json
import os
import pandas as pd
import copy
from datetime import datetime
import torch
from tqdm import tqdm
import os
import json
import random
import re
from tqdm import tqdm

    
class Model:
    def __init__(self):
        model_path = os.getcwd() + "/lora-plus/best-checkpoint-merged"

        self.model = Qwen2VLForConditionalGeneration.from_pretrained(model_path,
                                                                torch_dtype=torch.bfloat16,
                                                                attn_implementation="flash_attention_2",
                                                                device_map="auto")

        self.processor = AutoProcessor.from_pretrained(model_path, max_pixels = 256 * 28 * 28)

    def preprocess(self, input):
        new_template = '''
        作为一名资深电商客服专家，你的任务是根据用户与客服之间的多轮对话，精准判断用户的意图，并从给定的分类标签中选择与之匹配的标签。

        ### 分类标签及详细说明
        1. **反馈密封性不好**：买家反馈商品密封性差，存在渗漏问题。
        2. **是否好用**：买家就商品的使用体验、实用性等方面进行询问。
        3. **是否会生锈**：针对商品是否会生锈这一特性展开咨询。
        4. **排水方式**：适用于洗衣机、热水器等产品，询问其排水的具体方式。
        5. **包装区别**：对不同商品包装之间的差异进行咨询。
        6. **发货数量**：咨询下单后商品的发货数量。
        7. **反馈用后症状**：买家反馈使用商品后，身体出现的反应情况。
        8. **商品材质**：涉及商品主体、配件以及填充物等具体材质的询问。
        9. **功效功能**：咨询商品所具备的功效和功能。
        10. **是否易褪色**：询问商品在使用过程中是否容易出现褪色现象。
        11. **适用季节**：针对商品适合在哪些季节使用进行咨询。
        12. **能否调光**：咨询商品的光源、灯光、光线或亮度是否可以调节。
        13. **版本款型区别**：询问不同版本、型号、款式、类型、套装、组合之间的区别，不包含商品数量、重量、尺寸等方面的差异。
        14. **单品推荐**：消费者请求推荐某类或某个商品，并非针对具体SKU（库存保有单位）。
        15. **用法用量**：咨询商品的使用方法、步骤、顺序，包括但不限于用量、使用时间、使用部位等。
        16. **控制方式**：询问商品的控制方法，例如能否通过手机、电脑等设备进行控制。
        17. **上市时间**：咨询商品的上市日期。
        18. **商品规格**：咨询商品的数量、重量、含量、容量等规格参数。
        19. **信号情况**：针对手机使用时的信号状况进行咨询，以及信号不佳时的解决办法。
        20. **养护方法**：询问商品的日常保养、维护方法。
        21. **套装推荐**：消费者希望获得关于套装产品的推荐。
        22. **何时上货**：咨询商品的补货时间或重新上架时间。
        23. **气泡**：咨询贴膜过程中避免产生气泡以及去除气泡的方法。

        ### 任务要求
        请严格依据对话内容，仅输出分类标签结果，不需要任何多余的解释或说明。

        ### 用户与客服的对话
        <用户与客服的对话 START>
        '''
        template_image_task = '''
        作为一名资深电商客服专家，你的任务是解读消费者上传的软件截图或实物拍摄图。请对消费者上传的这张图片进行分类。

        ### 分类标签及详细说明
        1. **实物拍摄(含售后)**：用户通过相机实拍的照片，涵盖售后相关照片（如商品损坏、缺失、与描述不符等情况）以及其他相机实拍图。
        2. **商品分类选项**：展示商品颜色、规格等选项的图片。
        3. **商品头图**：商品首页所展示的大图。
        4. **商品详情页截图**：截取商品详情页任意部分的图片。
        5. **下单过程中出现异常（显示购买失败浮窗）**：下单时出现异常且显示购买失败浮窗的截图。
        6. **订单详情页面**：完整呈现订单信息的页面截图。
        7. **支付页面**：包含支付方式选择或支付成功等信息的页面截图。
        8. **消费者与客服聊天页面**：消费者与客服沟通的聊天页面截图。
        9. **评论区截图页面**：淘宝 APP 或其他 APP 内评论区的截图。
        10. **物流页面 - 物流列表页面**：呈现两条及以上物流信息的页面截图。
        11. **物流页面 - 物流跟踪页面**：展示物流运输路径的页面截图。
        12. **物流页面 - 物流异常页面**：包含物流异常信息的页面截图。
        13. **退款页面**：含有退款相关信息的页面截图。
        14. **退货页面**：含有退货相关信息的页面截图。
        15. **换货页面**：含有换货相关信息的页面截图。
        16. **购物车页面**：淘宝购物车的列表图或金额计算图。
        17. **店铺页面**：店铺首页的截图。
        18. **活动页面**：各类活动的截图。
        19. **优惠券领取页面**：店铺首页或活动页中领取优惠券的相关截图。
        20. **账单/账户页面**：包含交易明细列表、资产列表、卡券红包列表等信息的页面截图。
        21. **个人信息页面**：展示个人信息的页面截图。
        22. **投诉举报页面**：投诉或举报相关页面的截图。
        23. **平台介入页面**：平台客服介入处理的页面截图。
        24. **外部 APP 截图**：除淘宝、菜鸟 APP 之外的各类 APP 截图，例如京东、拼多多，以及短信、手机系统截图等。
        25. **其他类别图片**：难以明确归类的其他图片。 

        ### 任务要求
        仅输出分类标签结果，不需要任何多余的解释或说明。

        '''
        end_words = '''
        <用户与客服的对话 END>'''
        
        new_item = {}
                
        query = input["text"]
        if query == '':
            # 场景分类
            new_item["instruction"] =  "Picture 1: <image>\n" + template_image_task
            new_item['type'] = '场景分类'
        else:
            # 意图识别
            new_item['instruction'] = new_template + query + end_words
            new_item['type'] = '意图识别'
        
        new_item["image"] =  [input["image"]]
        
        return new_item
            
    def infer(self, input):
        MAX_SEQ_LEN = 32000
        
        line = self.preprocess(input)

        if line["instruction"].startswith("<image>"):
            raise
        if line["instruction"].endswith("<image>"):
            raise
        
        content_list = []
        for index, content in enumerate(line["instruction"].split("<image>")):
            content_list.append({"type":"text", "text":content})
            if index != len(line["instruction"].split("<image>")) - 1:
                content_list.append({"type":"image", "image": line["image"][index]})
        messages = [{"role":"user", "content":content_list}]
        
        while True:
            # Preparation for inference
            text =self.processor.apply_chat_template(
                messages, tokenize=False, add_generation_prompt=True
            )
            image_inputs, video_inputs = process_vision_info(messages)
            inputs = self.processor(
                text=[text],
                images=image_inputs,
                videos=video_inputs,
                padding=True,
                return_tensors="pt",
            )
            inputs = inputs.to("cuda")

            if len(inputs.input_ids[0]) > MAX_SEQ_LEN:
                messages[0]['content'] = messages[0]['content'][1:]
                continue
            else:
                break
            
        # Inference: Generation of the output
        generated_ids = self.model.generate(**inputs, max_new_tokens=128)
        generated_ids_trimmed = [
            out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
        ]
        output_text = self.processor.batch_decode(
            generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
        )[0]
        
        return '任务类型：' + line['type'] + '； 分类结果：' + output_text

model = Model()

def process_input(image, text):
    if image is not None:
        image_path = image.name if hasattr(image, 'name') else str(image)
    else:
        image_path = ''
    input = {
        "image": image_path,
        "text": text
    }
    if image_path == '':
        return '保证图像不为空输入。'
    
    result = model.infer(input)
    return result

def clear_inputs():
    return None, ""

with gr.Blocks() as demo:
    gr.Markdown('<div style="text-align: center; font-weight: bold; font-size: 32px;">电商场景中用户意图的智能“透视仪”</div>')

    # 上传图片框
    image_input = gr.Image(type="filepath", label="上传图片")
    # 文字输入框
    text_input = gr.Textbox(label="输入文字", placeholder="请输入一些文字...")

    with gr.Row():
        # 提交按钮
        submit_button = gr.Button("提交")
        # 清除按钮
        clear_button = gr.Button("清除")

    output = gr.Textbox(label="输出结果")

    submit_button.click(
        fn=process_input,
        inputs=[image_input, text_input],
        outputs=output
    )

    clear_button.click(
        fn=clear_inputs,
        inputs=[],
        outputs=[image_input, text_input]
    )

demo.launch(share=True)
```

# 6. 总结
## 数据集地址
https://www.modelscope.cn/datasets/smau0441/www2025-train
## 模型地址
https://www.modelscope.cn/models/smau0441/Fluoroscope
## 服务地址
https://www.modelscope.cn/studios/smau0441/Fluoroscope
