Conv2DのFilters数(32 vs 64 vs 128)を変えると 精度はどう変わる?【CIFAR-10実験】

投稿日:2026年3月5日木曜日 最終更新日:

CIFAR-10 CNN Conv2D Google Colab Keras 画像分類

X f B! P L
Conv2DのFilters数(32 vs 64 vs 128)を変えると 精度はどう変わる?【CIFAR-10実験】 アイキャッチ画像

KerasでCNNを書くとき、Conv2Dのfilters数をいくつにするか迷ったことはありますか?

「よくある記事が32か64を使っているから」「なんとなく64にしている」という方も多いと思います。

では実際、32・64・128で精度はどれだけ変わるのでしょうか?そして学習時間は?

今回はGoogle ColabとCIFAR-10を使い、filters数だけを変えた3パターンを実験で比較しました。

この記事を読むとわかること:

  • filters=32・64・128でval_accuracyはどう変わるか
  • 学習時間(秒)とパラメータ数の違い
  • 精度と計算コストのトレードオフを踏まえた選び方

filtersとは?増やすと何が起きるか

Conv2Dのfilters(フィルタ数)は、その層が検出できる特徴パターンの数です。

filters=32なら「32種類の特徴」を同時に検出し、filters=64なら「64種類」を検出します。多いほど複雑なパターンを学習できますが、パラメータ数と計算コストは比例して増加します。

直感的にはこのように考えると整理しやすいです。

  • filters=32:エッジ・シンプルなテクスチャ程度なら十分
  • filters=64:中程度の複雑さのパターンに対応
  • filters=128:細かい特徴・複雑なパターンも検出可能

では実際に数値で確認してみましょう。

※特徴マップの可視化で実際に何が検出されているか確認したい方 → 中間層の特徴マップを可視化してみた|Keras × CNN × MNIST

実験設定:3パターンのモデルコード

使用環境はGoogle Colab(GPU:T4)、データセットはCIFAR-10(32×32×3のカラー画像、10クラス)です。

filters数以外の条件(層数・Optimizer・エポック数・バッチサイズ)は全て同一にして、filters数の影響だけを取り出します。学習時間はtime.time()で計測しました。

実験コード

環境準備(最初に一度だけ実行)

# ── 環境準備(最初に一度だけ実行)──────────────────────
!apt-get -y install fonts-ipafont-gothic
!rm -rf /root/.cache/matplotlib
!pip install -q japanize_matplotlib
print("環境準備完了")
実行結果をクリックして内容を開く
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
  fonts-ipafont-mincho
The following NEW packages will be installed:
  fonts-ipafont-gothic fonts-ipafont-mincho
0 upgraded, 2 newly installed, 0 to remove and 37 not upgraded.
Need to get 8,237 kB of archives.
After this operation, 28.7 MB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu jammy/universe amd64 fonts-ipafont-gothic all 00303-21ubuntu1 [3,513 kB]
Get:2 http://archive.ubuntu.com/ubuntu jammy/universe amd64 fonts-ipafont-mincho all 00303-21ubuntu1 [4,724 kB]
Fetched 8,237 kB in 2s (4,623 kB/s)
Selecting previously unselected package fonts-ipafont-gothic.
(Reading database ... 121852 files and directories currently installed.)
Preparing to unpack .../fonts-ipafont-gothic_00303-21ubuntu1_all.deb ...
Unpacking fonts-ipafont-gothic (00303-21ubuntu1) ...
Selecting previously unselected package fonts-ipafont-mincho.
Preparing to unpack .../fonts-ipafont-mincho_00303-21ubuntu1_all.deb ...
Unpacking fonts-ipafont-mincho (00303-21ubuntu1) ...
Setting up fonts-ipafont-mincho (00303-21ubuntu1) ...
update-alternatives: using /usr/share/fonts/opentype/ipafont-mincho/ipam.ttf to provide /usr/share/fonts/truetype/fonts-japanese-mincho.ttf (fonts-japanese-mincho.ttf) in auto mode
Setting up fonts-ipafont-gothic (00303-21ubuntu1) ...
update-alternatives: using /usr/share/fonts/opentype/ipafont-gothic/ipag.ttf to provide /usr/share/fonts/truetype/fonts-japanese-gothic.ttf (fonts-japanese-gothic.ttf) in auto mode
Processing triggers for fontconfig (2.13.1-4.2ubuntu5) ...
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.1/4.1 MB 31.0 MB/s eta 0:00:00
  Preparing metadata (setup.py) ... done
  Building wheel for japanize_matplotlib (setup.py) ... done
環境準備完了

import・データ準備・共通関数

import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
import japanize_matplotlib
import time

# CIFAR-10データの読み込み・前処理
(x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data()
x_train = x_train.astype('float32') / 255.0
x_test  = x_test.astype('float32')  / 255.0

# モデル構築関数(filtersを引数で切り替え)
def build_cnn(filters, name):
    return keras.Sequential([
        keras.layers.Input(shape=(32, 32, 3)),
        keras.layers.Conv2D(filters,      (3, 3), activation='relu', padding='same'),
        keras.layers.MaxPooling2D((2, 2)),
        keras.layers.Conv2D(filters * 2, (3, 3), activation='relu', padding='same'),
        keras.layers.MaxPooling2D((2, 2)),
        keras.layers.Flatten(),
        keras.layers.Dense(128, activation='relu'),
        keras.layers.Dense(10,  activation='softmax')
    ], name=name)

# 共通コンパイル&学習関数(学習時間も計測)
def compile_and_fit(model):
    model.compile(
        optimizer='adam',
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )
    start = time.time()
    history = model.fit(
        x_train, y_train,
        epochs=30,
        batch_size=64,
        validation_split=0.2,
        verbose=1
    )
    elapsed = time.time() - start
    return history, elapsed
実行結果をクリックして内容を開く
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170498071/170498071 ━━━━━━━━━━━━━━━━━━━━ 4s 0us/step

3パターンの学習実行

print("\n=== Pattern A:filters=32 ===")
model_A = build_cnn(32,  'A_filters32')
history_A, time_A = compile_and_fit(model_A)
print(f"学習時間:{time_A:.1f}秒 パラメータ数:{model_A.count_params():,}")

print("\n=== Pattern B:filters=64 ===")
model_B = build_cnn(64,  'B_filters64')
history_B, time_B = compile_and_fit(model_B)
print(f"学習時間:{time_B:.1f}秒 パラメータ数:{model_B.count_params():,}")

print("\n=== Pattern C:filters=128 ===")
model_C = build_cnn(128, 'C_filters128')
history_C, time_C = compile_and_fit(model_C)
print(f"学習時間:{time_C:.1f}秒 パラメータ数:{model_C.count_params():,}")

# テストデータで最終精度を評価
test_results = {
    'A:filters=32':  model_A.evaluate(x_test, y_test, verbose=0),
    'B:filters=64':  model_B.evaluate(x_test, y_test, verbose=0),
    'C:filters=128': model_C.evaluate(x_test, y_test, verbose=0),
}
実行結果をクリックして内容を開く
=== Pattern A:filters=32 ===
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 16s 12ms/step - accuracy: 0.3625 - loss: 1.7537 - val_accuracy: 0.5511 - val_loss: 1.2897
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.5850 - loss: 1.1766 - val_accuracy: 0.6335 - val_loss: 1.0524
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.6487 - loss: 1.0045 - val_accuracy: 0.6507 - val_loss: 1.0107
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.6932 - loss: 0.8887 - val_accuracy: 0.6583 - val_loss: 0.9884
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.7162 - loss: 0.8132 - val_accuracy: 0.6740 - val_loss: 0.9524
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.7485 - loss: 0.7218 - val_accuracy: 0.6980 - val_loss: 0.9009
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.7695 - loss: 0.6715 - val_accuracy: 0.6712 - val_loss: 0.9868
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.7873 - loss: 0.6083 - val_accuracy: 0.6960 - val_loss: 0.9115
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.8138 - loss: 0.5399 - val_accuracy: 0.6925 - val_loss: 0.9418
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.8305 - loss: 0.4865 - val_accuracy: 0.6976 - val_loss: 0.9703
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.8525 - loss: 0.4212 - val_accuracy: 0.6984 - val_loss: 1.0167
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.8712 - loss: 0.3754 - val_accuracy: 0.6931 - val_loss: 1.0332
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.8863 - loss: 0.3267 - val_accuracy: 0.6876 - val_loss: 1.1452
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.9073 - loss: 0.2805 - val_accuracy: 0.6877 - val_loss: 1.2071
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.9169 - loss: 0.2407 - val_accuracy: 0.6874 - val_loss: 1.2856
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.9322 - loss: 0.1992 - val_accuracy: 0.6879 - val_loss: 1.3737
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.9410 - loss: 0.1747 - val_accuracy: 0.6850 - val_loss: 1.4368
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.9494 - loss: 0.1513 - val_accuracy: 0.6843 - val_loss: 1.5439
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.9610 - loss: 0.1223 - val_accuracy: 0.6741 - val_loss: 1.7076
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.9629 - loss: 0.1131 - val_accuracy: 0.6837 - val_loss: 1.7454
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.9649 - loss: 0.1046 - val_accuracy: 0.6813 - val_loss: 1.8495
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.9692 - loss: 0.0897 - val_accuracy: 0.6708 - val_loss: 1.9698
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.9668 - loss: 0.0940 - val_accuracy: 0.6768 - val_loss: 2.0167
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.9767 - loss: 0.0708 - val_accuracy: 0.6716 - val_loss: 2.1593
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.9788 - loss: 0.0664 - val_accuracy: 0.6704 - val_loss: 2.3555
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.9714 - loss: 0.0868 - val_accuracy: 0.6649 - val_loss: 2.2778
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.9781 - loss: 0.0709 - val_accuracy: 0.6710 - val_loss: 2.3457
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - accuracy: 0.9799 - loss: 0.0596 - val_accuracy: 0.6769 - val_loss: 2.4372
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 5s 4ms/step - accuracy: 0.9782 - loss: 0.0635 - val_accuracy: 0.6720 - val_loss: 2.4973
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - accuracy: 0.9836 - loss: 0.0487 - val_accuracy: 0.6775 - val_loss: 2.5907
学習時間:103.6秒 パラメータ数:545,098

=== Pattern B:filters=64 ===
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 8s 9ms/step - accuracy: 0.3862 - loss: 1.6852 - val_accuracy: 0.5938 - val_loss: 1.1673
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.6184 - loss: 1.0908 - val_accuracy: 0.6383 - val_loss: 1.0413
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.6817 - loss: 0.9116 - val_accuracy: 0.6779 - val_loss: 0.9395
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.7183 - loss: 0.8030 - val_accuracy: 0.6903 - val_loss: 0.9098
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.7531 - loss: 0.7030 - val_accuracy: 0.6893 - val_loss: 0.9225
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.7854 - loss: 0.6167 - val_accuracy: 0.7042 - val_loss: 0.8753
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.8111 - loss: 0.5454 - val_accuracy: 0.7002 - val_loss: 0.9164
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.8383 - loss: 0.4693 - val_accuracy: 0.7085 - val_loss: 0.8816
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.8660 - loss: 0.3888 - val_accuracy: 0.7191 - val_loss: 0.9419
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.8885 - loss: 0.3244 - val_accuracy: 0.7155 - val_loss: 1.0077
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9102 - loss: 0.2634 - val_accuracy: 0.7003 - val_loss: 1.1001
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.9230 - loss: 0.2238 - val_accuracy: 0.7041 - val_loss: 1.1758
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9444 - loss: 0.1695 - val_accuracy: 0.7016 - val_loss: 1.2449
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9571 - loss: 0.1348 - val_accuracy: 0.7043 - val_loss: 1.3737
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.9633 - loss: 0.1136 - val_accuracy: 0.6952 - val_loss: 1.5601
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9632 - loss: 0.1103 - val_accuracy: 0.6918 - val_loss: 1.5530
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9679 - loss: 0.0962 - val_accuracy: 0.7021 - val_loss: 1.6425
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.9768 - loss: 0.0693 - val_accuracy: 0.6998 - val_loss: 1.7488
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9770 - loss: 0.0710 - val_accuracy: 0.6998 - val_loss: 1.8728
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9761 - loss: 0.0712 - val_accuracy: 0.6936 - val_loss: 1.9332
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.9793 - loss: 0.0593 - val_accuracy: 0.6957 - val_loss: 1.9568
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9795 - loss: 0.0589 - val_accuracy: 0.6873 - val_loss: 2.1381
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9767 - loss: 0.0705 - val_accuracy: 0.6933 - val_loss: 2.1083
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.9761 - loss: 0.0682 - val_accuracy: 0.6873 - val_loss: 2.2367
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9833 - loss: 0.0510 - val_accuracy: 0.6954 - val_loss: 2.3435
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9809 - loss: 0.0529 - val_accuracy: 0.6898 - val_loss: 2.3137
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9833 - loss: 0.0515 - val_accuracy: 0.6896 - val_loss: 2.2306
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 6ms/step - accuracy: 0.9792 - loss: 0.0596 - val_accuracy: 0.6908 - val_loss: 2.6236
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 6s 10ms/step - accuracy: 0.9775 - loss: 0.0680 - val_accuracy: 0.6929 - val_loss: 2.4238
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 4s 7ms/step - accuracy: 0.9858 - loss: 0.0414 - val_accuracy: 0.6917 - val_loss: 2.6809
学習時間:129.3秒 パラメータ数:1,125,642

=== Pattern C:filters=128 ===
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 12s 14ms/step - accuracy: 0.3838 - loss: 1.6947 - val_accuracy: 0.5943 - val_loss: 1.1492
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.6297 - loss: 1.0574 - val_accuracy: 0.6610 - val_loss: 0.9736
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.6973 - loss: 0.8735 - val_accuracy: 0.6782 - val_loss: 0.9280
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.7350 - loss: 0.7565 - val_accuracy: 0.6952 - val_loss: 0.8848
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.7762 - loss: 0.6437 - val_accuracy: 0.7054 - val_loss: 0.8593
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 9s 14ms/step - accuracy: 0.8135 - loss: 0.5388 - val_accuracy: 0.7107 - val_loss: 0.8953
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 8s 11ms/step - accuracy: 0.8447 - loss: 0.4484 - val_accuracy: 0.7250 - val_loss: 0.8993
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.8769 - loss: 0.3591 - val_accuracy: 0.7106 - val_loss: 1.0159
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9021 - loss: 0.2846 - val_accuracy: 0.7016 - val_loss: 1.0348
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9246 - loss: 0.2213 - val_accuracy: 0.7145 - val_loss: 1.1100
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9445 - loss: 0.1675 - val_accuracy: 0.7009 - val_loss: 1.2857
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9494 - loss: 0.1451 - val_accuracy: 0.7088 - val_loss: 1.3051
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 8s 12ms/step - accuracy: 0.9630 - loss: 0.1096 - val_accuracy: 0.6985 - val_loss: 1.4761
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 8s 13ms/step - accuracy: 0.9701 - loss: 0.0912 - val_accuracy: 0.7029 - val_loss: 1.6217
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9726 - loss: 0.0842 - val_accuracy: 0.7097 - val_loss: 1.6592
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9759 - loss: 0.0716 - val_accuracy: 0.7007 - val_loss: 1.6959
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9745 - loss: 0.0750 - val_accuracy: 0.7036 - val_loss: 1.8456
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9782 - loss: 0.0644 - val_accuracy: 0.7007 - val_loss: 1.9264
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9778 - loss: 0.0625 - val_accuracy: 0.7023 - val_loss: 1.8913
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9800 - loss: 0.0573 - val_accuracy: 0.6957 - val_loss: 2.0243
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9759 - loss: 0.0698 - val_accuracy: 0.6911 - val_loss: 2.0259
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9815 - loss: 0.0579 - val_accuracy: 0.7027 - val_loss: 2.2861
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9832 - loss: 0.0484 - val_accuracy: 0.6973 - val_loss: 2.1983
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9801 - loss: 0.0578 - val_accuracy: 0.6868 - val_loss: 2.3369
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9823 - loss: 0.0511 - val_accuracy: 0.6954 - val_loss: 2.4092
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9802 - loss: 0.0576 - val_accuracy: 0.6994 - val_loss: 2.5139
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9849 - loss: 0.0493 - val_accuracy: 0.7012 - val_loss: 2.5416
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9873 - loss: 0.0375 - val_accuracy: 0.6995 - val_loss: 2.4627
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9850 - loss: 0.0458 - val_accuracy: 0.6915 - val_loss: 2.5602
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 7s 11ms/step - accuracy: 0.9859 - loss: 0.0453 - val_accuracy: 0.7005 - val_loss: 2.6231
学習時間:217.9秒 パラメータ数:2,397,322

学習曲線グラフ + 最終結果サマリー

histories = {
    'A:filters=32':  history_A,
    'B:filters=64':  history_B,
    'C:filters=128': history_C,
}

# ── val_accuracy / val_loss 比較グラフ ───────────────
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
for label, h in histories.items():
    axes[0].plot(h.history['val_accuracy'], label=label)
    axes[1].plot(h.history['val_loss'],     label=label)
axes[0].set_title('val_accuracy の比較(全30エポック)')
axes[1].set_title('val_loss の比較(全30エポック)')
for ax in axes:
    ax.set_xlabel('Epoch'); ax.legend(); ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('filters_comparison.png', dpi=150)
plt.show()

# ── 最終結果サマリー ─────────────────────────────────
key_order  = ['A:filters=32', 'B:filters=64', 'C:filters=128']
time_list  = {'A:filters=32': time_A, 'B:filters=64': time_B, 'C:filters=128': time_C}
params_list = {
    'A:filters=32':  model_A.count_params(),
    'B:filters=64':  model_B.count_params(),
    'C:filters=128': model_C.count_params(),
}

print("\n===== 最終結果サマリー =====")
print(f"{'Pattern':>16} | {'Val Acc':>8} | {'Test Acc':>9} | {'Time(s)':>8} | {'Params':>10}")
print("-" * 65)
for key in key_order:
    val_acc  = histories[key].history['val_accuracy'][-1]
    test_loss, test_acc = test_results[key]
    elapsed  = time_list[key]
    params   = params_list[key]
    print(f"{key:>16} | {val_acc:>8.4f} | {test_acc:>9.4f} | {elapsed:>8.1f} | {params:>10,}")
print("-" * 65)

最終結果サマリー

===== 最終結果サマリー =====
         Pattern |  Val Acc |  Test Acc |  Time(s) |     Params
-----------------------------------------------------------------
    A:filters=32 |   0.6775 |    0.6707 |    103.6 |    545,098
    B:filters=64 |   0.6917 |    0.6823 |    129.3 |  1,125,642
   C:filters=128 |   0.7005 |    0.6969 |    217.9 |  2,397,322
-----------------------------------------------------------------

実験結果①:精度の比較

val_accuracy の比較(全30エポック)

精度グラフ

Conv2DのFilters数(32 vs 64 vs 128)を変えた場合の精度グラフ

損失グラフ

Conv2DのFilters数(32 vs 64 vs 128)を変えた場合の損失グラフ

グラフから読み取れること:

  • filters=128(パターンC)が最も高い最終精度に到達しましたが、差は思ったより小さい結果でした。
  • filters=32(パターンA)は序盤の立ち上がりが最も速く、精度への到達効率が良いことが見て取れます。
  • val_lossを見ると、filters数が多いモデルほどvalidationとのギャップがやや大きくなる傾向が見られ、過学習の兆候が示唆されます。
パターン最終 val_accuracy最終 test_accuracy収束の速さ過学習の傾向
A:filters=3267.75%67.07%速い少ない
B:filters=6469.17%68.23%普通中程度
C:filters=12870.05%69.69%やや遅いやや多い

実験結果②:学習時間とパラメータ数の比較

精度だけ見ると「filters=128が最強」に見えますが、コストを加えると話が変わります。

パターンTest Accuracy学習時間(秒)パラメータ数精度/コスト比
A:filters=3267.07%103.6秒545,098◎ 最良
B:filters=6468.23%129.3秒1,125,642○ バランス
C:filters=12869.69%217.9秒2,397,322△ 高コスト

学習時間・パラメータ数を見ると、filters=32からfilters=128へ大きく増加することが確認できます。

精度の改善幅と比べたとき、コストの増加が精度向上を上回る場合があるという「トレードオフ」がこの表から読み取れます。

Colabの無料枠(GPU利用時間に制限あり)で実験する場合は特に、このコスト感覚が重要になります。

結論:filters数はどう選ぶべきか

実験結果をもとに、状況別の結論をまとめます。

「まず動かしてみたい・プロトタイプを作りたい」→ filters=32から始める
学習が速く、結果をすぐに確認できます。精度に不満が出てから大きくすれば十分です。

「バランスよく使いたい・迷ったらこれ」→ filters=64
多くの教材やサンプルコードが64を使っているのは理由があります。精度と速度のバランスが良く、CIFAR-10程度の難易度なら十分な表現力があります。

「精度を追求したい・学習時間を気にしない」→ filters=128以上を検討
ただしCIFAR-10レベルでは精度改善幅は限定的です。より複雑なデータセットや深いモデルほど効果が出やすくなります。

また、よくある設計パターンとして「Conv2D(32) → Conv2D(64)」のように層を深くするにつれてfilters数を増やす方法があります。最初の層でシンプルな特徴(エッジ等)を検出し、深い層で複雑な特徴を検出するという考え方です。

補足:filters数の増やし方のパターン

今回の実験では全層で同じfilters数を使いましたが、実際によく見るアーキテクチャでは層が深くなるほどfilters数を増やす設計が一般的です。

例:32 → 64 → 128(徐々に増やす)

これは最初の層では「エッジ・色」などシンプルな特徴を少ないfiltersで検出し、深い層ほど「形・テクスチャの組み合わせ」など複雑な特徴を多くのfiltersで表現するという考え方に基づいています。

全層を128にするより、32→64→128と徐々に増やす方がパラメータ数を抑えながら表現力を確保できます。

まとめ

今回はKerasとCIFAR-10を使い、Conv2Dのfilters数を32・64・128の3パターンで比較しました。

  • プロトタイプ・速さ優先なら → filters=32
  • バランス重視・迷ったら → filters=64
  • 精度追求・コスト許容できるなら → filters=128以上

「大きければ良い」ではなく、精度とコストのトレードオフを意識して選ぶことが重要です。

関連記事もあわせてどうぞ: