はじめに
Qwen3-VL-30B-A3B-Thinkingに令和7年度秋期試験の応用情報技術者試験の午前問題を全問解かせてみた。具体的には応用情報技術者試験の午前問題の設問1つ1つを画像化し、それを1つずつQwen3-VL-30B-A3B-Thinkingのプロンプトに指定して、解かせてどの程度の正答率が得られるかを検証してみた。
検証環境
検証環境は以下の通り。マルチGPU環境で実施。
| 項目 (Item) | 内容 (Content) |
|---|---|
| OS | Kubuntu 24.04.03 |
| CPU | Ryzen 5 7600 |
| メモリ (Memory) | DDR5-5600 64GB |
| マザーボード (Motherboard) | MSI MAG B650 TOMAHAWK WIFI |
| GPU1 | ZOTAC GAMING GeForce RTX 5060 Ti 16GB Twin Edge |
| GPU2 | MSI GeForce RTX 3060 VENTUS 2X 12G OC |
| GPUドライババージョン (GPU Driver Version) | 580.82.09 |
$ nvidia-smi
Mon Nov 3 12:31:33 2025
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.82.09 Driver Version: 580.82.09 CUDA Version: 13.0 |
+-----------------------------------------+------------------------+----------------------+
| 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 5060 Ti Off | 00000000:01:00.0 On | N/A |
| 0% 34C P3 13W / 180W | 1324MiB / 16311MiB | 1% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
| 1 NVIDIA GeForce RTX 3060 Off | 00000000:06:00.0 Off | N/A |
| 0% 37C P8 13W / 170W | 15MiB / 12288MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
VLMの実行にはllama-swapを使用。
Qwen3-VL-30B-A3B-ThinkingはUnlothのGGUFを使用。量子化はUD-Q4_K_XLを選択。
llama-swapのconfig.yamlのQwen3-VL-30B-A3B-Thinkingに関する起動パラメータは以下の通り。
サンプリングパラメータはQwen3-VL-Thinkinの推奨パラメータを設定している。
"Qwen3-VL-30B-A3B-Thinking-GGUF":
cmd: |
${latest-llama}
--model /app/models/Qwen3-VL-30B-A3B-Thinking-GGUF/Qwen3-VL-30B-A3B-Thinking-UD-Q4_K_XL.gguf
--mmproj /app/models/Qwen3-VL-30B-A3B-Thinking-GGUF/mmproj-F16.gguf
--ctx-size 16384
--cache-type-k q8_0
--cache-type-v q8_0
--n-gpu-layers 1000
--top-p 0.95
--top-k 20
--temp 0.6
--repeat-penalty 1.0
--presence_penalty 0
--jinja
--flash-attn on
env:
- "CUDA_VISIBLE_DEVICES=0,1"
ttl: 600
事前準備
応用情報技術者試験の午前問題を1問ずつ、画像化&連番で保存しておく。
プロンプト
プロンプトは以下の通り。解答(ア、イ、ウ、エ)のみを出力するように指示。
あなたは情報処理応用技術者試験の午前問題を解答する専門家です。
【役割】
- 情報処理技術者試験の専門知識を持つアシスタント
- 問題文と選択肢を正確に分析する能力を持つ
- 四択問題の正答を高い精度で選択できる
【指示】
1. 画像内の問題文と4つの選択肢を注意深く読んでください
2. 各選択肢の内容を比較検討し、最も適切な解答を選択してください
3. 解答は「ア」「イ」「ウ」「エ」のいずれか1文字のみで出力してください
【出力形式】
- ア/イ/ウ/エのいずれか
- 例: 「ア」や「イ」など、解答の文字だけを出力
【注意事項】
- 出力には解答の文字以外のテキストは一切含めないでください
- 「答えは」「解答は」などの前置きは不要です
- 確信度や説明は出力に含めないでください
ソースコード
画像化しておいた問題を順次読み込んで、Base64エンコードした上で、Qwen3-VLに渡す。
最後に正解数や正解率を出力するようにした。
from pydantic import BaseModel, Field
from typing import List
import base64
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
import os
from dotenv import load_dotenv
load_dotenv()
# 正解リスト
CORRECT_ANSWERS = [
"エ", "イ", "イ", "ウ", "イ", "イ", "ア", "エ", "イ", "ア", "イ", "ウ", "ア", "イ", "エ", "イ", "ウ", "イ", "エ", "ア",
"ア", "ウ", "エ", "ウ", "ウ", "イ", "エ", "ア", "イ", "ウ", "ア", "ア", "イ", "エ", "エ", "イ", "ウ", "ア", "エ", "ア",
"ア", "ア", "ウ", "イ", "ア", "エ", "イ", "イ", "エ", "エ", "ア", "エ", "ウ", "ア", "ア", "ア", "イ", "エ", "ウ", "イ",
"ア", "エ", "イ", "ウ", "イ", "ウ", "ウ", "イ", "ア", "イ", "イ", "イ", "ウ", "エ", "ウ", "エ", "イ", "エ", "ア", "ア"
]
def get_image_paths(directory: str) -> List[str]:
"""指定ディレクトリ内の画像ファイルパスを取得(名前順)"""
valid_extensions = (".jpg", ".jpeg", ".png", ".webp", ".bmp")
image_files = [
os.path.join(directory, filename)
for filename in os.listdir(directory)
if filename.lower().endswith(valid_extensions)
]
return sorted(image_files)
def encode_image_to_base64(image_path: str) -> str:
"""画像ファイルをBase64エンコード"""
with open(image_path, "rb") as image_file:
encoded_image = base64.b64encode(image_file.read()).decode("utf-8")
return f"data:image/jpeg;base64,{encoded_image}"
PROMPT = """
あなたは情報処理応用技術者試験の午前問題を解答する専門家です。
【役割】
- 情報処理技術者試験の専門知識を持つアシスタント
- 問題文と選択肢を正確に分析する能力を持つ
- 四択問題の正答を高い精度で選択できる
【指示】
1. 画像内の問題文と4つの選択肢を注意深く読んでください
2. 各選択肢の内容を比較検討し、最も適切な解答を選択してください
3. 解答は「ア」「イ」「ウ」「エ」のいずれか1文字のみで出力してください
【出力形式】
- ア/イ/ウ/エのいずれか
- 例: 「ア」や「イ」など、解答の文字だけを出力
【注意事項】
- 出力には解答の文字以外のテキストは一切含めないでください
- 「答えは」「解答は」などの前置きは不要です
- 確信度や説明は出力に含めないでください
"""
def analyze_exam_question(model_config: dict, image_path: str) -> str:
"""試験問題を分析して解答を取得"""
model = ChatOpenAI(**model_config)
prompt_template = ChatPromptTemplate.from_messages([
("human", [
{"type": "image_url", "image_url": {"url": "{image_url}"}},
{"type": "text", "text": PROMPT}
])
])
# 画像をbase64エンコード
image_url = encode_image_to_base64(image_path)
analysis_chain = prompt_template | model
result = analysis_chain.invoke({"image_url": image_url})
return result.content
def calculate_score(predicted_answers: List[str], correct_answers: List[str]) -> dict:
"""予測結果のスコアを計算"""
correct_count = sum(1 for pred, correct in zip(predicted_answers, correct_answers) if pred == correct)
total_questions = len(predicted_answers)
accuracy = correct_count / total_questions * 100 if total_questions > 0 else 0
return {
"correct_count": correct_count,
"total_questions": total_questions,
"accuracy": accuracy
}
def display_results(score: dict):
"""結果を表示"""
print("\n" + "="*50)
print(f"正解数: {score['correct_count']}/{score['total_questions']}")
print(f"正答率: {score['accuracy']:.2f}%")
print("="*50)
def process_exam_questions():
"""試験問題の処理を実行"""
# 接続先LLM設定。OpenAI互換APIを持つAIであれば以下の設定値を変更するだで動作する
model_config = {
"model": "Qwen3-VL-30B-A3B-Thinking-GGUF",
"base_url": os.getenv("LOCAL_LLM_BASE_URL"),
"api_key": os.getenv("LOCAL_LLM_API_KEY"),
}
# 問題一覧取得
image_paths = get_image_paths("questions")
predicted_answers = []
print("問題番号,LLM解答,正解")
for index, image_path in enumerate(image_paths):
# LLMに回答させる
answer = analyze_exam_question(model_config, image_path)
predicted_answers.append(answer)
print(f"{index+1},{answer},{CORRECT_ANSWERS[index]}")
# スコア計算と表示
score = calculate_score(predicted_answers, CORRECT_ANSWERS[:len(predicted_answers)])
display_results(score)
if __name__ == "__main__":
process_exam_questions()
実行結果
なんと93.75%という驚異の正解率を記録....
正解数: 75/80
正答率: 93.75%
| 設問 | LLM解答 | 正解 | 備考 |
|---|---|---|---|
| 1 | エ | エ | 一致 |
| 2 | イ | イ | 一致 |
| 3 | ア | イ | 不一致 |
| 4 | ウ | ウ | 一致 |
| 5 | イ | イ | 一致 |
| 6 | イ | イ | 一致 |
| 7 | ア | ア | 一致 |
| 8 | エ | エ | 一致 |
| 9 | イ | イ | 一致 |
| 10 | ア | ア | 一致 |
| 11 | イ | イ | 一致 |
| 12 | ウ | ウ | 一致 |
| 13 | ア | ア | 一致 |
| 14 | イ | イ | 一致 |
| 15 | エ | エ | 一致 |
| 16 | イ | イ | 一致 |
| 17 | ウ | ウ | 一致 |
| 18 | イ | イ | 一致 |
| 19 | エ | エ | 一致 |
| 20 | ア | ア | 一致 |
| 21 | イ | ア | 不一致 |
| 22 | ウ | ウ | 一致 |
| 23 | エ | エ | 一致 |
| 24 | ウ | ウ | 一致 |
| 25 | ウ | ウ | 一致 |
| 26 | イ | イ | 一致 |
| 27 | エ | エ | 一致 |
| 28 | ア | ア | 一致 |
| 29 | イ | イ | 一致 |
| 30 | ウ | ウ | 一致 |
| 31 | ア | ア | 一致 |
| 32 | ア | ア | 一致 |
| 33 | イ | イ | 一致 |
| 34 | エ | エ | 一致 |
| 35 | エ | エ | 一致 |
| 36 | イ | イ | 一致 |
| 37 | ウ | ウ | 一致 |
| 38 | ア | ア | 一致 |
| 39 | エ | エ | 一致 |
| 40 | ア | ア | 一致 |
| 41 | ア | ア | 一致 |
| 42 | ア | ア | 一致 |
| 43 | ウ | ウ | 一致 |
| 44 | イ | イ | 一致 |
| 45 | ア | ア | 一致 |
| 46 | エ | エ | 一致 |
| 47 | イ | イ | 一致 |
| 48 | イ | イ | 一致 |
| 49 | エ | エ | 一致 |
| 50 | エ | エ | 一致 |
| 51 | ア | ア | 一致 |
| 52 | エ | エ | 一致 |
| 53 | ア | ウ | 不一致 |
| 54 | ア | ア | 一致 |
| 55 | ア | ア | 一致 |
| 56 | ア | ア | 一致 |
| 57 | イ | イ | 一致 |
| 58 | エ | エ | 一致 |
| 59 | ウ | ウ | 一致 |
| 60 | イ | イ | 一致 |
| 61 | ア | ア | 一致 |
| 62 | エ | エ | 一致 |
| 63 | イ | イ | 一致 |
| 64 | ウ | ウ | 一致 |
| 65 | イ | イ | 一致 |
| 66 | ウ | ウ | 一致 |
| 67 | ウ | ウ | 一致 |
| 68 | イ | イ | 一致 |
| 69 | ア | ア | 一致 |
| 70 | ア | イ | 不一致 |
| 71 | イ | イ | 一致 |
| 72 | イ | イ | 一致 |
| 73 | ウ | ウ | 一致 |
| 74 | エ | エ | 一致 |
| 75 | ウ | ウ | 一致 |
| 76 | エ | エ | 一致 |
| 77 | イ | イ | 一致 |
| 78 | エ | エ | 一致 |
| 79 | ア | ア | 一致 |
| 80 | エ | ア | 不一致 |
まとめ
今回はQwen3-VL-30B-A3B-Thinkingに令和7年度秋期試験の応用情報技術者試験の午前問題を全問解かせてみた。
80問中75問正解するという驚異の正解率であり、応用情報技術者試験の午前問題の範囲ならローカル環境で動作させやすいQwen3-VL-30B-A3B-Thinkingでも十分な知識を持っていることが今回の検証によりわかった。
