项目文件夹

文件
wehub-resource-sync 542cfa195c
CI / Frontend build (push) Failing after 9m6s
CI / Plugin validate (push) Failing after 9m27s
CI / Python lint (push) Failing after 16m1s
CI / Tests (push) Successful in 18m0s
Deploy / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:27 +08:00

144 行
4.9 KiB
Python

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
#!/usr/bin/env python3
"""Push the mixed-llmvit universal adapter to HuggingFace."""
import os
import shutil
import tempfile
from pathlib import Path
from huggingface_hub import create_repo, upload_folder
TOKEN = os.environ["HF_TOKEN"]
USER = "Chrisyichuan"
REPO_ID = f"{USER}/qwen3vl-4b-wiki-screenshot-universal-lora"
SRC = Path(
"/scratch/users/zwcolin/cxr_embeds/sft_output/qwen3vl_mixed_llmvit_v2/checkpoint-6503"
)
KEEP_FILES = {
"adapter_config.json",
"adapter_model.safetensors",
"added_tokens.json",
"chat_template.jinja",
"merges.txt",
"preprocessor_config.json",
"special_tokens_map.json",
"tokenizer.json",
"tokenizer_config.json",
"training_args.bin",
"trainer_state.json",
"video_preprocessor_config.json",
"vocab.json",
}
README = f"""---
license: apache-2.0
library_name: peft
base_model: Qwen/Qwen3-VL-4B-Instruct
tags:
- peft
- lora
- qwen3-vl
- screenshot-qa
- compressed-images
- multi-compression
pipeline_tag: image-text-to-text
---
# Qwen3-VL-4B Wikipedia Screenshot QA — Universal Compression LoRA
A **single** LoRA adapter for [Qwen3-VL-4B-Instruct](https://huggingface.co/Qwen/Qwen3-VL-4B-Instruct) trained on images compressed at **four different levels simultaneously** (2x / 3x / 5x / 9x), so one adapter handles any of them at deployment.
## Performance (GPT-4.1 LLM-judge, 500 test examples)
| Compression | This universal adapter | Compression-specific adapter | Base Qwen (no SFT) |
|---|---|---|---|
| Uncompressed (0x) | — | — | 0.958 |
| **2x** | **0.940** | 0.948 | 0.904 |
| **3x** | **0.892** | 0.894 | 0.826 |
| **5x** | **0.692** | 0.730 | 0.554 |
| **9x** | **0.302** | 0.378 | 0.180 |
Trade-off: the universal adapter is **0.2–7.6 LLM-judge points** below each specialized adapter. At 3x it is essentially tied. Gap widens at 9x where compression-specific ViT tuning helps most.
## Training config
- Method: **LoRA r=256, α=256, target=all (LLM + ViT)** — `freeze_vision_tower: false`
- Base model: `Qwen/Qwen3-VL-4B-Instruct`
- Checkpoint: step `6503` (1 epoch, ~5h on 4× H100)
- Framework: [LLaMA-Factory](https://github.com/hiyouga/LLaMA-Factory) (fork), DeepSpeed ZeRO-2, bf16
- Training data: 4 × 104k Wikipedia screenshot QA pairs, each compressed at 2x/3x/5x/9x → 416k mixed examples
- lr = 1e-5, cosine schedule, warmup_ratio 0.03, effective batch size 64
## Why train a universal adapter?
- **One LoRA to ship** instead of four.
- Compression-agnostic ViT: training sees the same text at multiple blur levels in the same run, regularizing the visual encoder toward blur-invariant features.
- At deployment, the caller just sends whatever compression suits their token budget — no routing logic.
## Usage
```python
from transformers import AutoProcessor, Qwen3VLForConditionalGeneration
from peft import PeftModel
import torch
base = "Qwen/Qwen3-VL-4B-Instruct"
adapter = "{USER}/qwen3vl-4b-wiki-screenshot-universal-lora"
model = Qwen3VLForConditionalGeneration.from_pretrained(base, torch_dtype=torch.bfloat16).cuda()
model = PeftModel.from_pretrained(model, adapter).merge_and_unload()
processor = AutoProcessor.from_pretrained(base)
# PIL image compressed at 2x / 3x / 5x / 9x — all work
messages = [{{"role": "user", "content": [
{{"type": "image", "image": your_compressed_image}},
{{"type": "text", "text": your_question}},
]}}]
# ... standard Qwen3-VL inference
```
## Data compression reference
Images were downscaled with PIL LANCZOS by `1/sqrt(N)` per dimension, so pixel count = `1/N` of original:
- 2x → 50% pixels
- 3x → 33% pixels
- 5x → 20% pixels
- 9x → 11% pixels
## Alternatives
If you need maximum accuracy at a known fixed compression, use the specialized adapters:
- [qwen3vl-4b-wiki-screenshot-2x-lora](https://huggingface.co/{USER}/qwen3vl-4b-wiki-screenshot-2x-lora)
- [qwen3vl-4b-wiki-screenshot-3x-lora](https://huggingface.co/{USER}/qwen3vl-4b-wiki-screenshot-3x-lora)
- [qwen3vl-4b-wiki-screenshot-5x-lora](https://huggingface.co/{USER}/qwen3vl-4b-wiki-screenshot-5x-lora)
- [qwen3vl-4b-wiki-screenshot-9x-lora](https://huggingface.co/{USER}/qwen3vl-4b-wiki-screenshot-9x-lora)
"""
def main():
print(f"=== {REPO_ID} ===")
print(f" src: {SRC}")
with tempfile.TemporaryDirectory() as tmp:
tmp = Path(tmp)
for name in KEEP_FILES:
p = SRC / name
if p.exists():
shutil.copy2(p, tmp / name)
print(f" + {name}")
(tmp / "README.md").write_text(README)
print(" + README.md")
create_repo(REPO_ID, token=TOKEN, exist_ok=True, private=False)
upload_folder(
folder_path=str(tmp),
repo_id=REPO_ID,
token=TOKEN,
commit_message="Upload universal LoRA adapter (LLM+ViT on 2x/3x/5x/9x mixed, step 6503)",
)
print(f" uploaded → https://huggingface.co/{REPO_ID}")
if __name__ == "__main__":
main()