Abstraction and Reasoning Corpus (ARC) チャレンジ 2024 入門ガイド

## はじめに

Abstraction and Reasoning Corpus (ARC) チャレンジは、人工知能の抽象化と推論能力を測るための興味深いコンペティションです。このガイドでは、ARCチャレンジ2024のデータセットを可視化し、基本的な分析を行う方法を学びます。

## 必要なライブラリのインポート

まずは、必要なライブラリをインポートします。これらのライブラリは、データの処理や可視化に使用します。

```python
# 基本的なデータ処理とファイル操作のためのライブラリ
import os
import gc
import json
import random

# データ分析と可視化のためのライブラリ
import numpy as np 
import pandas as pd
import seaborn as sns
from scipy import stats
from pathlib import Path
from collections import Counter

# Jupyter環境での表示のためのライブラリ
from IPython.display import HTML
import time

# コンソール出力の色付けのためのライブラリ
from colorama import Style, Fore

# グラフ描画のためのライブラリ
import matplotlib.pyplot as plt
from matplotlib import colors

# Matplotlibのインライン表示設定
%matplotlib inline
```

各ライブラリの役割を理解することで、以降のコードがどのような目的で書かれているかが分かりやすくなります。

## データファイルの確認

次に、使用するデータファイルを確認します。

```python
# 入力ディレクトリ内のファイルを表示
!ls ../input/*
```

このコマンドを実行すると、使用可能なデータファイルの一覧が表示されます。

## ファイルパスの設定

データファイルのパスを設定します。これにより、後続の処理でファイルを簡単に参照できるようになります。

```python
# トレーニングデータのパス
train_question_path = '../input/arc-prize-2024/arc-agi_training_challenges.json'
train_answer_path = '../input/arc-prize-2024/arc-agi_training_solutions.json'

# 評価データのパス
eval_question_path = '../input/arc-prize-2024/arc-agi_evaluation_challenges.json'
eval_answer_path = '../input/arc-prize-2024/arc-agi_evaluation_solutions.json'

# テストデータとサンプル提出ファイルのパス
test_path = '../input/arc-prize-2024/arc-agi_test_challenges.json'
sample_path = '../input/arc-prize-2024/sample_submission.json'
```

##  カラーマップの設定

ARCチャレンジでは、グリッドの各セルに0から9までの数字が割り当てられています。これらの数字を視覚的に区別しやすくするため、カラーマップを設定します。

```python
# カラーマップの定義
cmap = colors.ListedColormap(
    ['#000000', '#0074D9','#FF4136','#2ECC40','#FFDC00',
     '#AAAAAA', '#F012BE', '#FF851B', '#7FDBFF', '#870C25'])

# 数値の正規化（0-9の範囲に）
norm = colors.Normalize(vmin=0, vmax=9)

# 色名のリスト（デバッグ用）
color_list = ["black", "blue", "red", "green", "yellow", "gray", "magenta", "orange", "sky", "brown"]

# カラーマップの可視化
plt.figure(figsize=(5, 2), dpi=200)
plt.imshow([list(range(10))], cmap=cmap, norm=norm)
plt.xticks(list(range(10)))
plt.yticks([])
plt.show()
```

このコードを実行すると、0から9までの数字に対応する色のバーが表示されます。これにより、後続の可視化で使用される色の意味を理解しやすくなります。

##  グリッド分析用の関数

グリッドの行と列ごとに最も頻出する色を特定するための関数を定義します。

```python
def y_most_common_colors(a):
    # 各行で最も頻出する色を返す
    return [Counter(x).most_common(1) for x in a]

def x_most_common_colors(a):
    # 各列で最も頻出する色を返す
    return [Counter([x[i] for x in a]).most_common(1) for i in range(len(a[0]))]
```

これらの関数は、グリッドのパターンを分析する際に役立ちます。

## 背景色予測関数

タスクの背景色を予測するための関数群を定義します。

```python
def get_background_candidates(a, n=10):
    # グリッド内で最も頻出するn個の色を候補として返す
    a = [x for r in a for x in r]
    return [x for x,y in Counter(a).most_common(n) if len(a) < 6 or y > 1]

def is_same_output_color(a, c):
    # すべての出力グリッドが同じ背景色を持つかチェック
    return all([c in x for x in a])

def is_same_input_color(a, b, i):
    # 入力と出力のグリッドが同じ背景色を持つかチェック
    return all([y[i] in x for x,y in zip(a,b)])

def predict_background(task, solution = None):
    # 背景色を予測する主要な関数
    train_in = [get_background_candidates(t["input"], 2) for t in task["train"]]
    train_out = [get_background_candidates(t["output"],3) for t in task["train"]]
    test = [get_background_candidates(t["input"],2) for t in task["test"]]
    
    if solution:
        solution = [get_background_candidates(t,3) for t in solution]

    pred = None
    kind = None
    
    # 様々な条件をチェックして背景色を予測
    if is_same_output_color(train_in + train_out + test, test[0][0]):
        color = test[0][0]
        pred = [color for x in test]
        kind = color_list[color]
    elif any([is_same_output_color(train_out, c) for c in train_out[0]]):
        for color in train_out[0]:
            if is_same_output_color(train_out, color):
                pred = [color for x in test]
                kind = color_list[color]
    elif is_same_input_color(train_out, train_in, 0):
        pred = [x[0] for x in test]
        if is_same_output_color(train_in + test, test[0][0]):
            color = test[0][0]
            kind = color_list[color]
        else:
            kind = 'Input'
    else:
        kind = '?'
    
    # 予測が正しいかチェック（解答がある場合）
    correct = None
    conclusion = ''
    if solution and pred:
        correct = all(p in s for s,p in zip(solution, pred))
        conclusion = '?' if pred is None else ('' if correct else 'Wrong!')

    return pred, kind, conclusion
```

この関数群は、タスクの入力と出力のグリッドを分析し、背景色のパターンを見つけ出します。

## サイズ予測関数

タスクのグリッドサイズを予測するための関数群を定義します。

```python
def is_same_size(a):
    # すべての要素が同じサイズかチェック
    return all([x==a[0] for x in a])

def is_same_ratio(a,b):
    # 2つのリストの要素間の比率が一定かチェック
    return all([x/y==a[0]/b[0] for x,y in zip(a,b)])

def is_same_diff(a,b):
    # 2つのリストの要素間の差が一定かチェック
    return all([x-y==a[0]-b[0] for x,y in zip(a,b)])

def predict_dimension(name, train_in, train_out, test, solution):
    # グリッドの特定の次元（幅または高さ）を予測
    pred = None
    kind = None
    if is_same_ratio(train_in, train_out):
        ratio = train_out[0]/train_in[0]
        pred = [int(x*ratio) for x in test]
        ratio = int(ratio) if int(ratio)==ratio else f'{ratio:.2f}'
        ratio = ratio if ratio != 1 else ""
        kind = f'{ratio}{name}'
    elif is_same_diff(train_in, train_out):
        diff = train_out[0]-train_in[0]
        pred = [x+diff for x in test]
        kind = f'{name}{"+" if diff>0 else ""}{diff if diff != 0 else ""}'
    elif is_same_size(train_out):
        size = train_out[0]
        pred = [size for x in test]
        kind = f'{size}'
    else:
        kind = '?'
    correct = None
    if solution and pred:
        correct = all(s==p for s,p in zip(solution, pred))
    return pred, kind, correct

def predict_size(task, solution = None):
    # タスクのグリッドサイズを予測する主要な関数
    m = len(task["train"])
    n = len(task["test"])
    x_train_in = [len(t["input"][0]) for t in task["train"]]
    y_train_in = [len(t["input"]) for t in task["train"]]
    x_train_out = [len(t["output"][0]) for t in task["train"]]
    y_train_out = [len(t["output"]) for t in task["train"]]
    x_test = [len(t["input"][0]) for t in task["test"]]
    y_test = [len(t["input"]) for t in task["test"]]
    
    x_solution = None
    y_solution = None
    if solution:
        x_solution = [len(t[0]) for t in solution]
        y_solution = [len(t) for t in solution]

    x_pred, x_kind, x_correct = predict_dimension('X', x_train_in, x_train_out, x_test, x_solution)
    y_pred, y_kind, y_correct = predict_dimension('Y', y_train_in, y_train_out, y_test, y_solution)
        
    formula = f'{x_kind}\u00D7{y_kind}'
    conclusion = ''
    if solution:
        conclusion = 'Crop?' if x_pred is None or y_pred is None else ('' if x_correct and y_correct else 'Wrong!')
        
    return x_pred, y_pred, formula, conclusion
```

これらの関数は、入力と出力のグリッドサイズを分析し、テストデータのグリッドサイズを予測します。

## タスク可視化関数

タスクのグリッドを可視化するための関数を定義します。

```python
def plot_task(task, solution = None):
    # タスクのグリッドを可視化する関数
    m = len(task["train"])
    n = m + len(task["test"])
    c = 3 if solution else 2
    fig, axs = plt.subplots(c, n, figsize=(4*n,4*c), dpi=200)
    plt.subplots_adjust(wspace=0, hspace=0)
    
    # トレーニングデータの可視化
    for i, t in enumerate(task["train"]):
        t_in, t_out = np.array(t["input"]), np.array(t["output"])
        axs[0][i].imshow(t_in, cmap=cmap, norm=norm)
        axs[0][i].set_title(f'Train-{i} in')
        axs[0][i].set_yticks(list(range(t_in.shape[0])))
        axs[0][i].set_xticks(list(range(t_in.shape[1])))
        axs[1][i].imshow(t_out, cmap=cmap, norm=norm)
        axs[1][i].set_title(f'Train-{i} out')
        axs[1][i].set_yticks(list(range(t_out.shape[0])))
        axs[1][i].set_xticks(list(range(t_out.shape[1])))
    
    # テストデータの可視化
    for i, t in enumerate(task["test"]):
        t_in = np.array(t["input"])
        axs[0][m+i].imshow(t_in, cmap=cmap, norm=norm)
        axs[0][m+i].set_title(f'Test-{i} in')
        axs[0][m+i].set_yticks(list(range(t_in.shape[0])))
        axs[0][m+i].set_xticks(list(range(t_in.shape[1])))
        axs[1][m+i].axis('off')
    
    # 解答の可視化（ある場合）
    if solution:
        for i in range(m):
            axs[2][i].axis('off')
        for i, t in enumerate(solution):
            t_in = np.array(t)
            axs[2][m+i].imshow(t_in, cmap=cmap, norm=norm)
            axs[2][m+i].set_title(f'Test-{i} out')
            axs[2][m+i].set_yticks(list(range(t_in.shape[0])))
            axs[2][m+i].set_xticks(list(range(t_in.shape[1])))
    
    plt.tight_layout()
    plt.show()  
```

この関数は、タスクの入力、出力、そして解答（ある場合）のグリッドを視覚的に表示します。

## データ読み込み関数

JSONファイルからデータを読み込むための関数を定義します。

```python
def load_file(path):
    # JSONファイルを読み込み、キーとバリューのリストを返す関数
    with open(path,'r') as f:
        data = json.load(f)
        return list(data.keys()), list(data.values())
```

この関数は、指定されたパスのJSONファイルを読み込み、そのキー（タスク名）とバリュー（タスクの内容）をリストとして返します。

## タスク読み込みと可視化関数

タスクを読み込み、分析し、可視化するための総合的な関数を定義します。

```python
def load_and_plot_tasks(path_question, path_answer, category):
    # タスクを読み込み、分析し、可視化する関数
    
    # 問題と解答（ある場合）を読み込む
    name_question, task_question = load_file(path_question)
    if path_answer:
        name_answer, task_answer = load_file(path_answer)
    else:
        name_answer, task_answer = name_question, None
    
    # 読み込んだデータの情報を表示
    print(len(name_question), len(task_question), len(task_answer) if task_answer else None, (name_answer==name_question))
    
    # 各タスクに対して処理を行う
    for t in range(len(name_question[:10])):
        solution = task_answer[t] if task_answer else None
        
        # サイズと背景色の予測
        x_pred, y_pred, xy_formula, xy_conclusion = predict_size(task_question[t], solution)
        pred, color, conclusion = predict_background(task_question[t], solution)
        
        # タスク情報の表示
        display(pd.DataFrame(data={
            'Task Name': name_question[t],
            'Category': category,
        }, index=[t]))
        
        # 予測結果の表示
        display(pd.DataFrame(data={
            'Prediction': [
                xy_formula + (': '+', '.join([f'{x}x{y}' for x,y in zip(x_pred,y_pred)]) if x_pred and y_pred else ''),
                color+((': ' + ', '.join([color_list[c] for c in pred])) if color == 'Input' else '')
            ],
            'Correct?': [xy_conclusion, conclusion]
        }, index=['Size','Background']))
        
        # タスクの可視化
        plot_task(task_question[t], solution)
```

この関数は、以下の手順で動作します：

1. 問題と解答（ある場合）のデータを読み込みます。
2. 各タスクに対して、サイズと背景色の予測を行います。
3. タスクの情報と予測結果を表形式で表示します。
4. タスクのグリッドを可視化します。

## トレーニングデータの分析

トレーニングデータを読み込み、分析し、可視化します。

```python
# トレーニングデータの読み込みと分析
load_and_plot_tasks(train_question_path, train_answer_path, category='training')
```

このコードを実行すると、トレーニングデータの各タスクに対して以下の情報が表示されます：
- タスク名と種類（トレーニング）
- 予測されたグリッドサイズとその正誤
- 予測された背景色とその正誤
- タスクのグリッド（入力、出力、解答）の可視化

## 評価データの分析

評価データを読み込み、分析し、可視化します。

```python
# 評価データの読み込みと分析
load_and_plot_tasks(eval_question_path, eval_answer_path, category='evaluation')
```

このコードは、評価データに対しても同様の分析と可視化を行います。

## テストデータの分析

最後に、テストデータを読み込み、分析し、可視化します。

```python
# テストデータの読み込みと分析
load_and_plot_tasks(test_path, None, category='test')
```

テストデータには解答が含まれていないため、予測の正誤は表示されません。

## まとめ

このノートブックでは、ARCチャレンジ2024のデータセットを分析するための基本的なツールを提供しました。主な機能は以下の通りです：

1. データの読み込みと整理
2. グリッドサイズの予測
3. 背景色の予測
4. タスクの可視化

これらの分析を通じて、ARCタスクの特徴やパターンを理解することができます。この理解は、より高度な解法やアルゴリズムの開発につながる可能性があります。

ここで紹介した手法は基本的なものですが、ARCチャレンジの複雑さを考えると、より洗練された手法や機械学習アルゴリズムの適用が必要になるでしょう。

このノートブックを出発点として、独自のアイデアを追加し、ARCチャレンジに挑戦してみてください。Good luck!

## ノートブック

https://www.kaggle.com/code/makimakiai/arc24-visualization-size-predictor