Qwen2-VLでYoutube動画を解釈+サイズ比較
video-to-textができるモデルを探していて、「Qwen2-VLがいいよ」という話を聞いたのでお試しした時の備忘録
Qwenはメインとする中国語以外にもヨーロッパの主要な言語、日本語、韓国語、アラビア語、ベトナム語に対応しているらしい
今回は2Bと7Bの使用GPU量と生成結果を簡単に比較
ライブラリのインストール
まずは必要ライブラリをインストール
transformersは最新にしておかないとqwenがエラー吐くらしい
git+https://github.com/huggingface/transformers
pip install qwen-vl-utils
pip install yt-dlp
pip install flash-attn --no-build-isolation
pip install accelerate
追加で各自の環境に合うPytorchをインストール
コード
指定のyoutubeの動画をダウンロード
from yt_dlp import YoutubeDL
YOUTUBE_URL = "https://www.youtube.com/watch?v=wQ93J_HOD-c&t=480s&ab_channel=SUSURUTV."
# 最高の画質と音質で動画をダウンロード
ydl_opts = {
"format": "best",
"outtmpl":"../data/youtube.mp4"
}
with YoutubeDL(ydl_opts) as ydl:
ydl.download([YOUTUBE_URL])
今回入力する動画は京都一乗寺にある二郎系ラーメンの名店「池田屋」の動画 (京都池田屋で調べると新選組の方が引っかかるでおなじみ)
以下のコードで推論させます
モデル読み込みして入力の読み込み+処理、推論させるいつもの流れ
from transformers import Qwen2VLForConditionalGeneration, AutoTokenizer, AutoProcessor
from qwen_vl_utils import process_vision_info
import torch
# モデルは好きな方を選択
MODEL_NAME = "Qwen/Qwen2-VL-7B-Instruct"
MODEL_NAME = "Qwen/Qwen2-VL-2B-Instruct"
model = Qwen2VLForConditionalGeneration.from_pretrained(
MODEL_NAME,
torch_dtype=torch.bfloat16,
device_map="auto",
attn_implementation="flash_attention_2",
)
processor = AutoProcessor.from_pretrained(MODEL_NAME)
# 動画を入力する場合
messages = [
{
"role": "user",
"content": [
{
"type": "video",
"video": "../data/youtube.mp4",
"max_pixels": 1280 * 780,
"fps": 0.1,
},
{"type": "text", "text": "Summarize the video."},
],
}
]
# 画像を入力する場合
# messages = [
# {
# "role": "user",
# "content": [
# {
# "type": "image",
# "image": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg",
# },
# {"type": "text", "text": "Describe this image."},
# ],
# }
# ]
# 入力の前処理
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=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
)
print(output_text)
とりあえず動画の要約を2Bにさせる
推論結果 (2B) 「Summarize the video.」
The video shows a young man standing outside a building with a yellow sign above it. He is wearing a blue and yellow scarf and a white shirt. He then walks inside a restaurant and sits at a table with other people. He is eating a large bowl of noodles with chopsticks. The video ends with the young man standing outside again.
(日本語訳)
ビデオには、黄色い看板が掲げられた建物の外に立つ若い男が映っている。彼は青と黄色のスカーフを巻き、白いシャツを着ている。その後、彼はレストランの中に入り、他の人々と一緒にテーブルに座る。彼は箸で大盛りの麺を食べている。ビデオは、青年が再び外に立っているところで終わる。
大盛の麺であることを言及できているのは凄くないか...?
動画内で食べてるラーメンを当てさせたいのでプロンプトを変更
txt:推論結果 (2B) 「この動画で食べられているラーメンは何ラーメンですか?」
The video shows a young man eating ramen in a restaurant. He is wearing a white shirt and a blue and yellow scarf. He is holding chopsticks and eating the noodles. The restaurant has a sign that reads "SUSURU TV" and a menu with different sizes of ramen. The video also shows the young man standing on a street with a sign that reads "池田屋" and a sign that reads "SUSURU TV.
(日本語訳)
ビデオにはレストランでラーメンを食べる若い男性が映っている。彼は白いシャツに青と黄色のスカーフを巻いている。箸を持ってラーメンを食べている。店には「SUSURU TV」と書かれた看板と、さまざまなサイズのラーメンが書かれたメニューがある。ビデオには、「池田屋」と書かれた看板と「SUSURU TV」と書かれた看板のある通りに立っている青年も映っている。
日本語プロンプトで聞いたら日本語で答えてほしい...
しかも結果は要約のまま...
とはいえ「池田屋」や「SUSURU TV」みたいな文字認識もちゃんとしてくれてるのは凄い
次に7B
推論結果 (7B) 「Summarize the video.」
The video shows a man eating a large bowl of noodles while standing outside in the rain. He then sits down and continues eating the noodles
(日本語訳)
ビデオには、雨の中、屋外で大盛りの麺を食べる男の姿が映っている。その後、彼は座って麺を食べ続ける
「この動画で食べられているラーメンは何ラーメンですか?」
The video shows a man eating a bowl of noodles with chopsticks. The caption mentions "ラーメン" which translates to "ramen" in English. Therefore, the man is eating ramen noodles.
(日本語訳)ビデオには、箸でラーメンを食べる男性の姿が映っている。キャプションには「ラーメン」と書かれている。したがって、この男性はラーメンを食べていることになる。
雨の中ラーメン食べてることにされてる...
2Bよりも賢いように見えて的外れなことを言っている印象
プロンプトには回答できているが、生成文が英語になってしまうのは不思議
一応画像入力に対して日本語で質問すると日本語で返してくれるので、動画の場合固有の問題なのかもしれない
「日本語でお願いします。」を追加して再度推論
推論結果 (7B)「この動画について説明してください。日本語でお願いします。」
この動画は、雨の中を歩く男性がラーメン屋でラーメンを食べるシーンを収めたもので、日本語で解説されています。男性はラーメンを食べながら、周囲の景色や店舗の情報などを紹介しています。また、ラーメンの種類や価格、店舗の特徴なども紹介されています。最後には、ラーメンを食べ終えた男性が、満足の表情でカメラに向かって話しています。
今度はちゃんと日本語で返してくれたが、それでも数回に一回は英語が出力される
また、2Bでは全く日本語を返してくれなかったので割愛
QwenのVideo推論は要約に最適化とQAにかなり最適化されているのか、英語なら結構いい精度が出そうだなと実感
また、二郎みたいな日本固有のものに関しては学習セットに含まれていない可能性が高いので、やはり国産VLM (特にVideo2Textができる)が欲しい所
GPU使用量
24GBあれば7Bモデルでyoutube動画を低解像度にするなどの処理をしなくてよくなるのは良い
2Bモデルも12GBのGPUで推論ができそうなのは非常によい。
推論時間は7Bモデルで42s, 2Bモデルで20sほど
# 7B モデル読み込み時
nvidia-smi
Fri Dec 13 19:25:43 2024
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 555.42.02 Driver Version: 555.42.02 CUDA Version: 12.5 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA GeForce RTX 3090 Off | 00000000:01:00.0 Off | N/A |
| 0% 45C P8 30W / 350W | 16475MiB / 24576MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
# 7B 9分動画推論時
nvidia-smi
Fri Dec 13 19:26:57 2024
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 555.42.02 Driver Version: 555.42.02 CUDA Version: 12.5 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA GeForce RTX 3090 Off | 00000000:01:00.0 Off | N/A |
| 31% 51C P0 121W / 350W | 21447MiB / 24576MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
# 2B モデル読み込み時
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 555.42.02 Driver Version: 555.42.02 CUDA Version: 12.5 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA GeForce RTX 3090 Off | 00000000:01:00.0 Off | N/A |
| 0% 53C P0 121W / 350W | 4911MiB / 24576MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
# 2B 9分動画推論時
| NVIDIA-SMI 555.42.02 Driver Version: 555.42.02 CUDA Version: 12.5 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA GeForce RTX 3090 Off | 00000000:01:00.0 Off | N/A |
| 0% 40C P8 31W / 350W | 9843MiB / 24576MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
Discussion