# 実施手順

本課題の実施手順について説明します．

## 提出データ

* 推論結果のcsv（フォーマットは後述）
* csvを生成するソースコード（Notebook）

## 提供データ

* trainディレクトリ: 学習用画像（ラベルごとにディレクトリが分けられている）
* test: 推論用画像
* Sample_submission.csv: 提出ファイルの例

詳細は「Data」タブをご覧ください．
注意事項として，学習データには似たような画像が多数含まれています．なの学習データを分離して検証用データを作った場合，検証データと学習用データが類似してしまう場合があり，検証用データでの正解率が実際よりも大きくなることがあります．

## 具体的な手順について

## 1. ノートブックの作成

Codeタブで「+ New Notebook」をクリックすることで，Kaggle内にNotebook（Python実行環境）を作成することができます．

## 2. GPU利用の設定

GPUを利用できるようにします（利用時間に制限がありますのでご注意ください）．以下の手順を行います．  
    画面上部の「Settings」→「Accelerator」→「GPU P100」

## 3. 学習データの読み込み

コンテスト内でNotebookを作成すると最初のコードセルに提供データのパスを表示するコードが生成されます．
それを実行することで，ファイルパス（ファイルの置かれている場所）が分かります．
その情報を元に以下のように記述することで学習用画像のパス情報を読み込むことができます（下記ではラベルごとの画像枚数を表示します）．

```
import pathlib

dir_orig = pathlib.Path("/kaggle/input/dxi-dl-2025/train")
paper = list(dir_orig.glob('paper/*.png'))
rock = list(dir_orig.glob('rock/*.png'))
scissors = list(dir_orig.glob('scissors/*.png'))
print(len(paper), len(rock), len(scissors))
```

ラベルごとの画像の一部を確認したい場合，下記のように書きます．

```
import matplotlib.pyplot as plt
print("Paper")
plt.figure(figsize=(15, 10))
for i in range(12):
    ax = plt.subplot(3, 4, i + 1)
    img = plt.imread(paper[i])
    plt.imshow(img)
plt.show()
print("Rock")
plt.figure(figsize=(15, 10))
for i in range(12):
    ax = plt.subplot(3, 4, i + 1)
    img = plt.imread(rock[i])
    plt.imshow(img)
plt.show()
print("Scissors")
plt.figure(figsize=(15, 10))
for i in range(12):
    ax = plt.subplot(3, 4, i + 1)
    img = plt.imread(scissors[i])
    plt.imshow(img)
plt.show()
```

## 4. 検証用データの分離

学習用データの一部を検証用データとして分離します．
ここでは，split-foldersライブラリを使用して分離作業を行います．
Kaggle実行環境では`/kaggle/temp/`というディレクトリをファイルの一時的な保管場所として利用できますので，そこに分離したファイルを保存することにします．

```
!pip install split-folders
!rm -rf janken
import splitfolders

splitfolders.ratio(
    dir_orig,
    output = "/kaggle/temp/janken",
    seed = 1234,
    ratio = (0.9, 0.1, 0.0),
    move = False
)
```

## 5. 画像データ読み込みの設定

ImageDataGeneratorを使用して，学習用と検証用の画像データを読み込むことにします．画像サイズを150x150，バッチサイズを32に設定します．
ここではデータ拡張は入っていませんが，必要なら入れてください（ただし，zoom等の設定値を見直す必要がありそうです）．

```
from keras.src.legacy.preprocessing.image import ImageDataGenerator

# parameters
img_size = 150
batch_size = 32

# Train data
train_dir = "/kaggle/temp/janken/train"
train_image_generator_aug = ImageDataGenerator(
    rescale=1./255,
)
train_data_gen_aug = train_image_generator_aug.flow_from_directory(
    train_dir,
    target_size=(img_size, img_size),
    batch_size=batch_size,
    class_mode="categorical"
)
print(train_data_gen_aug.class_indices)

# Validation data
validation_dir = "/kaggle/temp/janken/val"
validation_image_generator = ImageDataGenerator(rescale=1./255)
val_data_gen = validation_image_generator.flow_from_directory(
    batch_size=batch_size,
    directory=validation_dir,
    shuffle=False,
    target_size=(img_size, img_size),
    class_mode="categorical")
```

## 6. モデルの設計

画像分類のためのモデルを設計します．
ここではシンプルなCNNを使います．（適宜より良いものに変更してください）

```
from keras import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import GlobalAveragePooling2D, Dense
from keras.optimizers import Adam

model = Sequential([
    Conv2D(32, (3,3), activation='relu', input_shape=(img_size, img_size, 3)),
    MaxPooling2D(2, 2),
    Conv2D(64, (3,3), activation='relu'),
    GlobalAveragePooling2D(),
    Dense(1024, activation='relu'),
    Dense(3, activation='softmax')
])
model.compile(optimizer=Adam(learning_rate=0.0005), loss='categorical_crossentropy', metrics=['accuracy'])
```

## 7. モデルの学習

学習用データを用いて作成したモデルを学習します．
検証用データとして，上記で読み込んだ検証用データのImageDataGeneratorを指定します．

```
history = model.fit(
    train_data_gen_aug,
    epochs=10,
    validation_data=val_data_gen,
)
```

上記注意事項の通り，検証用データでの性能は実際の性能よりも良くなっていると思われるため，得られる値を信頼しないほうが良いです．

## 8. テストデータの読み込み

テストデータを読み込みます．
ここではImageDataGeneratorを使用します．
テストデータは`unknown`というディレクトリに格納されていますが，実際のラベル名はunknownではなく未設定です．
`class_mode=None`とすることでラベル情報が無視されます．
また，ファイルの順番を変えないように`shuffle=False`と設定します．

```
submission_dir = "/kaggle/input/dxi-dl-2025/test/"
submission_image_generator = ImageDataGenerator(rescale=1./255)
submission_data_gen = submission_image_generator.flow_from_directory(
    batch_size=1,
    directory=submission_dir,
    shuffle=False,
    target_size=(img_size, img_size),
    class_mode=None)
```

## 8. テストデータを使った推論

テスト用データを使って，推論を行います．
以下のようなコードにより推論を行うことができます．

```
class_names=["paper", "rock", "scissors"]
preds = model.predict(submission_data_gen, verbose=1)
pred_indices = np.argmax(preds, axis=1)
predicted_labels = [class_names[idx] for idx in pred_indices]
```

## 9. 提出用csvファイルの生成

提出用csvにはサンプルの`'id'`と分類結果（`'label'`）の予想結果の二つの列が必要です．
`id`は画像ファイルから拡張子を取り除くことで取得できます．
すべての画像に対してidと分類結果（ラベル）のペアを作ったら，それらをソートします（これは不要な処理ですが念の為）．
その後，`id`と`label`を列として持つデータフレームを作成し，それをcsvファイルとして保存します．
以下のようなコードによりsubmission.csvというcsvファイルを作ることができます．

```
submission_data = []
for filename, label in zip(submission_data_gen.filenames, predicted_labels):
    # ファイル名から数字だけを取り出して id とする
    base_name = os.path.basename(filename)  # "1.jpg"
    image_id = int(os.path.splitext(base_name)[0])
    submission_data.append([image_id, label])

# image_id でソート (1,2,3... の順序)
submission_data.sort(key=lambda x: x[0])

# pandas DataFrame 経由で CSV 出力
df = pd.DataFrame(submission_data, columns=["id", "label"])
df.to_csv("submission.csv", index=False)
```

## 10. 開発したコードの保存と全実行

ノートブックを保存し，すべてのコードを実行しなおします．
右上の「Save Version」を押し，VERSION TYPEを「Save & Run All (Commit)」にした後，「Save」をクリックします．
左下に実行の進捗が表示されますので，実行完了まで待ち，Successfulと表示されることを確認してください．
（Failedと表示される場合は，正しく実行できていません）

## 11. Kaggleへの提出とスコア（正解率）の取得

開発したノートブックを提出し，スコア（正解率）を得ます．
まず10.で完了したノートをクリックし，Notebookを開きます（もしくは上部の「Code」→「Your Work」から選択）．
開いた「Notebook」において，「Output」タブを選択したのち「Submit to competition」ボタンをクリックし，次の画面でもう一回「Submit」ボタンをクリックしてください．
スコアが計算されますので，それを確認してください．

## 12. 性能改良

合格条件（正解率0.7以上）を満たすまで性能改善を行なってください．

改善例：
- モデルの改良（より複雑なCNNを設計する，転移学習を使用する等）
- データ拡張（学習データに対してデータ拡張を適用する；手の画像が大きいので，zoomやshiftの値を大きくしすぎるとダメそうなので，値を調整する）
- epoch数や学習率などを変更する