Download code/ask_cli.py from Duoia/duovlm-40m-v1: direct link, hf CLI and curl.
- Browser
- Download file 3.9 kB
-
https://huggingface.co/Duoia/duovlm-40m-v1/resolve/main/code/ask_cli.py
- Command line
-
hf download hf://Duoia/duovlm-40m-v1/code/ask_cli.py
-
curl -L -o ask_cli.py https://huggingface.co/Duoia/duovlm-40m-v1/resolve/main/code/ask_cli.py
3.9 kB
| #!/home/devcontainers/tinyllm/.venv/bin/python | |
| """DuoVLM-40M 命令行:给一张图 + 一个问题 → 回答。 | |
| python ask_cli.py --image photo.jpg --question "How many people are in the image?" | |
| python ask_cli.py --image photo.jpg # 不给问题 = 描述这张图 | |
| python ask_cli.py --image photo.jpg --blind # 并排跑一遍"不看图"对照 | |
| python ask_cli.py --dir ./photos --question "What is happening?" | |
| python ask_cli.py --text "The history of the city begins in" --continue-n 60 | |
| 提示:40M 小模型擅长物体/场景识别、计数、是-否、简短描述;不擅长精细颜色、 | |
| 读文字(OCR)、长句推理;长输出必须带 no_repeat_ngram=3(默认已开)。 | |
| """ | |
| import argparse | |
| import json | |
| import sys | |
| from pathlib import Path | |
| sys.path.insert(0, str(Path(__file__).resolve().parent)) | |
| from duovlm_infer import DESC_Q, DuoVLMInfer # noqa: E402 | |
| def main() -> None: | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--image", action="append", default=[]) | |
| ap.add_argument("--dir", default="") | |
| ap.add_argument("--question", default="") | |
| ap.add_argument("--questions", default="", help="同一张图连问多个,用 | 分隔") | |
| ap.add_argument("--text", default="", help="纯文本续写种子(不用图)") | |
| ap.add_argument("--continue-n", type=int, default=40) | |
| ap.add_argument("--blind", action="store_true") | |
| ap.add_argument("--no-image", action="store_true") | |
| ap.add_argument("--max-new", type=int, default=12) | |
| ap.add_argument("--ngram", type=int, default=3) | |
| ap.add_argument("--rep-penalty", type=float, default=1.0) | |
| ap.add_argument("--temperature", type=float, default=0.0) | |
| ap.add_argument("--top-k", type=int, default=0) | |
| ap.add_argument("--no-loop-break", action="store_true") | |
| ap.add_argument("--weights", default="") | |
| ap.add_argument("--json", action="store_true", help="输出 JSON(给脚本/服务用)") | |
| a = ap.parse_args() | |
| imgs = list(a.image) | |
| if a.dir: | |
| imgs += [str(p) for p in sorted(Path(a.dir).glob("*.jpg"))[:20]] | |
| if not imgs and not a.text and not a.no_image: | |
| print("需要 --image / --dir(或 --text / --no-image 走无图模式)", file=sys.stderr) | |
| raise SystemExit(2) | |
| qs = [x.strip() for x in a.questions.split("|") if x.strip()] or [a.question] | |
| kw = dict(ngram=a.ngram, rep_penalty=a.rep_penalty, temperature=a.temperature, | |
| top_k=a.top_k, loop_break=not a.no_loop_break) | |
| vlm = DuoVLMInfer(weights=a.weights or None, verbose=not a.json) | |
| if not a.json: | |
| print(vlm.param_report()) | |
| print("-" * 88, flush=True) | |
| rows = [] | |
| if a.text: | |
| for seed in [x for x in a.text.split("|") if x]: | |
| r = vlm.continue_text(seed, max_new=a.continue_n, **kw) | |
| r["seed"] = seed | |
| rows.append(r) | |
| if not a.json: | |
| print(f"种子: {seed}\n续写: {r['answer']}\n ({r['ms']}ms)\n", flush=True) | |
| else: | |
| for p in imgs or [None]: | |
| for i, q in enumerate(qs): | |
| r = vlm.ask(p, q, no_image=a.no_image, max_new=a.max_new, **kw) | |
| r.update({"image": p, "question": q or DESC_Q}) | |
| if a.blind and p: | |
| b = vlm.ask(p, q, blind=True, max_new=a.max_new, **kw) | |
| r["blind_answer"] = b["answer"] | |
| rows.append(r) | |
| if not a.json: | |
| tag = "" if i else Path(p).name if p else "(无图)" | |
| print(f"{tag:30s} Q: {r['question']}") | |
| print(f"{'':30s} [{r['source']}] {r['ms']}ms 答: {r['answer']}") | |
| if "blind_answer" in r: | |
| print(f"{'':30s} 不看图: {r['blind_answer']}") | |
| if a.json: | |
| print(json.dumps(rows, ensure_ascii=False, indent=1)) | |
| if __name__ == "__main__": | |
| main() | |