A primeira resolução que vimos

A cena descrita na página [Sobre](/pt/about/) é este modelo. A IA que resolvia sudoku foi preenchendo as respostas uma a uma, na horizontal, a partir da primeira casa. Parecia uma criança pequena que já sabia as respostas.

Dados de treinamento feitos a partir da folha de respostas

Os primeiros dados de treinamento foram feitos com o dataset_sudoku.py. O motor gera o quebra-cabeça junto com a solução, e escrever o dígito correto na primeira casa vazia do tabuleiro (na horizontal, a partir do canto superior esquerdo) é salvo como um problema. Essa casa é preenchida, passa-se para a próxima casa vazia, e isso se repete até o tabuleiro ficar cheio.

300 tabuleiros fáceis, médios e difíceis geraram 13.497 problemas, que foram usados para treinar o Qwen3-VL-2B-Instruct com LoRA (lora_stoku).

O prompt também incluía a lista de jogadas possíveis. Mas a resposta correta era sempre a primeira casa vazia. Mesmo que tanto o 5 quanto o 9 pudessem entrar em (0,3), se a folha de respostas dissesse 9, o 9 era a resposta. Por que era 9 não aparecia em lugar nenhum dos dados.

A ordem em que um quebra-cabeça difícil foi preenchido. As casas vermelhas não podiam ser determinadas só olhando o tabuleiro naquele momento
A ordem em que um quebra-cabeça difícil foi preenchido. As casas vermelhas não podiam ser determinadas só olhando o tabuleiro naquele momento

A casa podia ser resolvida naquele momento?

Verificamos de novo as casas de resposta dos 13.497 problemas usando apenas o tabuleiro daquele momento.

  • Casas com um único dígito candidato: 9.502 (70%)
  • Casas que eram o único lugar para aquele dígito dentro da linha, coluna ou caixa: 2.127 (16%)
  • Casas que nenhum dos dois métodos conseguia definir e que exigiam a folha de respostas: 1.868 (14%)

As casas que exigiam a folha de respostas eram cerca de 21% no difícil, 12% no médio e 5% no fácil. Uma pessoa pularia essas casas e preencheria primeiro as certas. O modelo aprendeu a escrever no mesmo lugar o dígito da folha de respostas.

Se a casa de resposta podia ser determinada naquele momento, por dificuldade
Se a casa de resposta podia ser determinada naquele momento, por dificuldade

O código real desta etapa

Esta é a função que gera os primeiros dados de treinamento. empties[0] é a primeira casa vazia contada a partir do canto superior esquerdo, e o valor vem de engine.solution.

dataset_sudoku.pypython · 42 linhas
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

O próximo post é "2. Ela não conseguiu aprender por tentativa e erro".