最初に見た解き方
[紹介](/jp/about/)に書いた場面がこのモデルです。数独を解くAIが、最初のマスから横へ順々に答えを埋めていきました。答えをすでに知っている幼い子どものようでした。
正解表で作った学習データ
最初の学習データは dataset_sudoku.py で作りました。エンジンがパズルと正解を一緒に作り、盤の中で最も前にある空きマス(左上から横の順)に正解の数字を書いたものを1問として保存します。そのマスを埋めたら次の空きマスに進み、盤が埋まるまで繰り返します。
やさしい・ふつう・むずかしいの300局から13,497問ができ、Qwen3-VL-2B-Instruct に LoRA で学習させました(lora_stoku)。
プロンプトには打てる手のリストも入れました。しかし正解はいつも最初の空きマスでした。(0,3) に5と9のどちらも入れられても、正解表が9なら9が正解です。なぜ9なのかは、データのどこにもありません。

その瞬間に解けるマスだったのか
13,497問の正解マスを、その瞬間の盤だけを見て改めて調べました。
- 候補の数字が一つしかないマス: 9,502個(70%)
- 行・列・ボックスの中でその数字が入れる場所が一つしかないマス: 2,127個(16%)
- 二つの方法では決められず、正解表が必要なマス: 1,868個(14%)
正解表が必要なマスは、むずかしいで21%、ふつうで12%、やさしいで5%ほどでした。人ならこうしたマスは飛ばして、確実なマスから埋めます。モデルは同じ場所に正解表の数字を書くように学びました。

この段階の実際のコード
最初の学習データを作る関数です。empties[0] が左上から数えた最初の空きマスで、値は engine.solution から取ります。
dataset_sudoku.py
def generate_game_samples(
engine: SudokuEngine,
difficulty: str,
game_id: int,
image_dir: Path,
) -> list[dict]:
engine.new_game(difficulty)
board = [row[:] for row in engine.puzzle]
given_mask = [[engine.is_given(r, c) for c in range(9)] for r in range(9)]
samples: list[dict] = []
step = 0
while True:
empties = [(r, c) for r in range(9) for c in range(9) if board[r][c] == 0]
if not empties:
break
r, c = empties[0]
value = engine.solution[r][c]
img = board_to_image(board, given_mask)
img_name = f"g{game_id:05d}_s{step:02d}.png"
img_path = image_dir / img_name
img.save(img_path)
answer = json.dumps({"row": r, "col": c, "value": value}, ensure_ascii=False)
prompt = build_sudoku_prompt(board, given_mask)
samples.append(
{
"id": f"g{game_id:05d}_s{step:02d}",
"image_path": str(img_path.resolve()),
"prompt": prompt,
"answer": answer,
"difficulty": difficulty,
}
)
board[r][c] = value
step += 1
return samples次の記事は「2. 試行錯誤では学べなかった」です。
COMMENTS · CHAPTER 01
答えを知っている子どものように解くAI コメント 0
まだコメントはありません。気になった点や補足があれば残してください。
コメントを登録するには、Googleで始めてニックネームとメールアドレスを登録してください。
Googleで始める