Residual接続(スキップ接続)あり vs なし を比較|深いネットが学習できる理由を実験で確認【Keras×CIFAR-10】

投稿日:2026年5月15日金曜日 最終更新日:

CIFAR-10 CNN Google Colab Keras Residual接続 ResNet 画像分類

X f B! P L
Residual接続(スキップ接続)あり vs なし を比較|深いネットが学習できる理由を実験で確認【Keras×CIFAR-10】 アイキャッチ画像

「ネットワークを深くすれば精度が上がるはず」——実際に試すと、ある深さを超えたあたりから精度が頭打ちになったり、むしろ下がったりした経験はありませんか?

その原因の一つが勾配消失問題です。ResNetが2015年に提案したResidual接続(スキップ接続)は、その問題を解決し、100層を超えるネットワークの学習を可能にした革命的なアイデアです。

今回はその仕組みをKerasで自前実装し、CIFAR-10を使ってResidual接続あり vs なしを直接比較します。

関連記事:Global Average Pooling vs Flatten|CNNの最終層、どっちが精度・速度で有利か?【Keras実験】

📘 この記事でわかること
  • Residual接続(スキップ接続)の仕組みと役割
  • なぜ深いネットワークで勾配消失が起きるのか
  • Kerasでスキップ接続を実装する具体的なコード
  • Residual接続あり・なしで精度と学習安定性がどう変わるか

Residual接続とは何か

通常のCNNでは、入力 x が複数の層を通り変換されて出力 F(x) になります。Residual接続では、この変換に加えて入力 x を直接出力に足すという操作を行います。

\[ \text{出力} = F(x) + x \]

この「+ x」の部分がスキップ接続(ショートカット接続)です。

なぜこれが効くのか

深いネットワークで学習がうまくいかない理由の一つは、誤差逆伝播の際に勾配が層を遡るたびに小さくなっていく勾配消失です。スキップ接続があると、勾配が + x のパスを通ってそのまま浅い層まで届くため、勾配消失が起きにくくなります。

また、もしブロック内の変換 F(x) がゼロに近くなっても、スキップ接続によって x がそのまま伝わるため、「恒等写像(identity mapping)」を簡単に学習できるという利点もあります。ブロックは差分 F(x) = 出力 - x を学習すればよいので、残差学習(Residual learning)とも呼ばれます。

チャンネル数が異なる場合の1×1 Conv

スキップ接続で x を足すには、xF(x) の形(チャンネル数・空間サイズ)が一致している必要があります。チャンネル数が変わる場合は 1×1 Conv でチャンネル数を揃えます。

⚠️ ハマりポイント:形が合わないとエラーになる
スキップ接続で x + F(x) の加算を行う際、チャンネル数や空間サイズが一致していないと ValueError が発生します。
チャンネル数が変わるときは 1×1 Conv(kernel_size=(1,1))でショートカット側を変換してください。padding='same' の指定も忘れずに。

実験コード

使用環境はGoogle Colab(GPU:T4)、データセットはCIFAR-10です。Residual接続の有無以外の条件は全て同一にします。

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

# ── 環境準備(最初に一度だけ実行)──────────────────────
!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 51 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 0s (18.3 MB/s)
Selecting previously unselected package fonts-ipafont-gothic.
(Reading database ... 122412 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 51.5 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
from tensorflow.keras import layers
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
print("データ準備完了")
実行結果をクリックして内容を開く
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170498071/170498071 ━━━━━━━━━━━━━━━━━━━━ 4s 0us/step
データ準備完了

Residual Blockの実装

まず今回の実験のキーとなる、Residual BlockとPlain Blockの関数を定義します。

def residual_block(x, filters, use_skip=True):
    """
    Residual Block(use_skip=True: スキップ接続あり, False: なし)
    チャンネル数が変わる場合は 1x1 Conv でショートカット側を変換する。
    """
    shortcut = x

    # メインパス:Conv → BN → ReLU → Conv → BN
    h = layers.Conv2D(filters, (3, 3), padding='same')(x)
    h = layers.BatchNormalization()(h)
    h = layers.Activation('relu')(h)
    h = layers.Conv2D(filters, (3, 3), padding='same')(h)
    h = layers.BatchNormalization()(h)

    if use_skip:
        # チャンネル数が異なる場合は 1x1 Conv で揃える
        if shortcut.shape[-1] != filters:
            shortcut = layers.Conv2D(filters, (1, 1), padding='same')(shortcut)
            shortcut = layers.BatchNormalization()(shortcut)
        h = layers.Add()([h, shortcut])  # ← スキップ接続

    h = layers.Activation('relu')(h)
    return h

モデル構築関数

def build_model(use_skip, name):
    """
    Residual接続あり(use_skip=True) / なし(use_skip=False) のCNNを構築
    ネットワーク深さ・チャンネル数はどちらも同一
    """
    inputs = keras.Input(shape=(32, 32, 3))

    # ── 入力Conv ───────────────────────────────────────
    x = layers.Conv2D(64, (3, 3), padding='same')(inputs)
    x = layers.BatchNormalization()(x)
    x = layers.Activation('relu')(x)

    # ── ブロック群(深さ6ブロック) ─────────────────────
    # Stage 1: 64ch × 2ブロック
    x = residual_block(x, 64,  use_skip=use_skip)
    x = residual_block(x, 64,  use_skip=use_skip)
    x = layers.MaxPooling2D((2, 2))(x)  # 32→16

    # Stage 2: 128ch × 2ブロック
    x = residual_block(x, 128, use_skip=use_skip)
    x = residual_block(x, 128, use_skip=use_skip)
    x = layers.MaxPooling2D((2, 2))(x)  # 16→8

    # Stage 3: 256ch × 2ブロック
    x = residual_block(x, 256, use_skip=use_skip)
    x = residual_block(x, 256, use_skip=use_skip)

    # ── 出力 ───────────────────────────────────────────
    x = layers.GlobalAveragePooling2D()(x)
    x = layers.Dense(128, activation='relu')(x)
    x = layers.Dropout(0.3)(x)
    outputs = layers.Dense(10, activation='softmax')(x)

    return keras.Model(inputs, outputs, 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)
    return history, time.time() - start

2パターンの学習実行

configs = [
    (True,  'A_with_skip'),    # Residual接続あり
    (False, 'B_without_skip'), # Residual接続なし
]

histories, times, scores, params = {}, {}, {}, {}

for use_skip, name in configs:
    print(f"\n=== {name} ===")
    model = build_model(use_skip, name)
    model.summary()
    h, t = compile_and_fit(model)
    s = model.evaluate(x_test, y_test, verbose=0)
    label = name.split('_', 1)[1]  # 'with_skip' / 'without_skip'
    histories[label] = h
    times[label]     = t
    scores[label]    = s
    params[label]    = model.count_params()
    print(f"学習時間:{t:.1f}秒 パラメータ数:{model.count_params():,} test_accuracy:{s[1]:.4f}")
実行結果をクリックして内容を開く
=== A_with_skip ===
Model: "A_with_skip"
┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
┃ Layer (type)        ┃ Output Shape      ┃    Param # ┃ Connected to      ┃
┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│ input_layer         │ (None, 32, 32, 3) │          0 │ -                 │
│ (InputLayer)        │                   │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d (Conv2D)     │ (None, 32, 32,    │      1,792 │ input_layer[0][0] │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ batch_normalization │ (None, 32, 32,    │        256 │ conv2d[0][0]      │
│ (BatchNormalizatio… │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ activation          │ (None, 32, 32,    │          0 │ batch_normalizat… │
│ (Activation)        │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_1 (Conv2D)   │ (None, 32, 32,    │     36,928 │ activation[0][0]  │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ batch_normalizatio… │ (None, 32, 32,    │        256 │ conv2d_1[0][0]    │
│ (BatchNormalizatio… │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ activation_1        │ (None, 32, 32,    │          0 │ batch_normalizat… │
│ (Activation)        │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_2 (Conv2D)   │ (None, 32, 32,    │     36,928 │ activation_1[0][… │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ batch_normalizatio… │ (None, 32, 32,    │        256 │ conv2d_2[0][0]    │
│ (BatchNormalizatio… │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ add (Add)           │ (None, 32, 32,    │          0 │ batch_normalizat… │
│                     │ 64)               │            │ activation[0][0]  │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ activation_2        │ (None, 32, 32,    │          0 │ add[0][0]         │
│ (Activation)        │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_3 (Conv2D)   │ (None, 32, 32,    │     36,928 │ activation_2[0][… │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ batch_normalizatio… │ (None, 32, 32,    │        256 │ conv2d_3[0][0]    │
│ (BatchNormalizatio… │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ activation_3        │ (None, 32, 32,    │          0 │ batch_normalizat… │
│ (Activation)        │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_4 (Conv2D)   │ (None, 32, 32,    │     36,928 │ activation_3[0][… │
│                     │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ batch_normalizatio… │ (None, 32, 32,    │        256 │ conv2d_4[0][0]    │
│ (BatchNormalizatio… │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ add_1 (Add)         │ (None, 32, 32,    │          0 │ batch_normalizat… │
│                     │ 64)               │            │ activation_2[0][… │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ activation_4        │ (None, 32, 32,    │          0 │ add_1[0][0]       │
│ (Activation)        │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ max_pooling2d       │ (None, 16, 16,    │          0 │ activation_4[0][… │
│ (MaxPooling2D)      │ 64)               │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_5 (Conv2D)   │ (None, 16, 16,    │     73,856 │ max_pooling2d[0]… │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ batch_normalizatio… │ (None, 16, 16,    │        512 │ conv2d_5[0][0]    │
│ (BatchNormalizatio… │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ activation_5        │ (None, 16, 16,    │          0 │ batch_normalizat… │
│ (Activation)        │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_6 (Conv2D)   │ (None, 16, 16,    │    147,584 │ activation_5[0][… │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_7 (Conv2D)   │ (None, 16, 16,    │      8,320 │ max_pooling2d[0]… │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ batch_normalizatio… │ (None, 16, 16,    │        512 │ conv2d_6[0][0]    │
│ (BatchNormalizatio… │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ batch_normalizatio… │ (None, 16, 16,    │        512 │ conv2d_7[0][0]    │
│ (BatchNormalizatio… │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ add_2 (Add)         │ (None, 16, 16,    │          0 │ batch_normalizat… │
│                     │ 128)              │            │ batch_normalizat… │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ activation_6        │ (None, 16, 16,    │          0 │ add_2[0][0]       │
│ (Activation)        │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_8 (Conv2D)   │ (None, 16, 16,    │    147,584 │ activation_6[0][… │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ batch_normalizatio… │ (None, 16, 16,    │        512 │ conv2d_8[0][0]    │
│ (BatchNormalizatio… │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ activation_7        │ (None, 16, 16,    │          0 │ batch_normalizat… │
│ (Activation)        │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_9 (Conv2D)   │ (None, 16, 16,    │    147,584 │ activation_7[0][… │
│                     │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ batch_normalizatio… │ (None, 16, 16,    │        512 │ conv2d_9[0][0]    │
│ (BatchNormalizatio… │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ add_3 (Add)         │ (None, 16, 16,    │          0 │ batch_normalizat… │
│                     │ 128)              │            │ activation_6[0][… │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ activation_8        │ (None, 16, 16,    │          0 │ add_3[0][0]       │
│ (Activation)        │ 128)              │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ max_pooling2d_1     │ (None, 8, 8, 128) │          0 │ activation_8[0][… │
│ (MaxPooling2D)      │                   │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_10 (Conv2D)  │ (None, 8, 8, 256) │    295,168 │ max_pooling2d_1[… │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ batch_normalizatio… │ (None, 8, 8, 256) │      1,024 │ conv2d_10[0][0]   │
│ (BatchNormalizatio… │                   │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ activation_9        │ (None, 8, 8, 256) │          0 │ batch_normalizat… │
│ (Activation)        │                   │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_11 (Conv2D)  │ (None, 8, 8, 256) │    590,080 │ activation_9[0][… │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_12 (Conv2D)  │ (None, 8, 8, 256) │     33,024 │ max_pooling2d_1[… │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ batch_normalizatio… │ (None, 8, 8, 256) │      1,024 │ conv2d_11[0][0]   │
│ (BatchNormalizatio… │                   │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ batch_normalizatio… │ (None, 8, 8, 256) │      1,024 │ conv2d_12[0][0]   │
│ (BatchNormalizatio… │                   │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ add_4 (Add)         │ (None, 8, 8, 256) │          0 │ batch_normalizat… │
│                     │                   │            │ batch_normalizat… │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ activation_10       │ (None, 8, 8, 256) │          0 │ add_4[0][0]       │
│ (Activation)        │                   │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_13 (Conv2D)  │ (None, 8, 8, 256) │    590,080 │ activation_10[0]… │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ batch_normalizatio… │ (None, 8, 8, 256) │      1,024 │ conv2d_13[0][0]   │
│ (BatchNormalizatio… │                   │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ activation_11       │ (None, 8, 8, 256) │          0 │ batch_normalizat… │
│ (Activation)        │                   │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d_14 (Conv2D)  │ (None, 8, 8, 256) │    590,080 │ activation_11[0]… │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ batch_normalizatio… │ (None, 8, 8, 256) │      1,024 │ conv2d_14[0][0]   │
│ (BatchNormalizatio… │                   │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ add_5 (Add)         │ (None, 8, 8, 256) │          0 │ batch_normalizat… │
│                     │                   │            │ activation_10[0]… │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ activation_12       │ (None, 8, 8, 256) │          0 │ add_5[0][0]       │
│ (Activation)        │                   │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ global_average_poo… │ (None, 256)       │          0 │ activation_12[0]… │
│ (GlobalAveragePool… │                   │            │                   │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dense (Dense)       │ (None, 128)       │     32,896 │ global_average_p… │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dropout (Dropout)   │ (None, 128)       │          0 │ dense[0][0]       │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ dense_1 (Dense)     │ (None, 10)        │      1,290 │ dropout[0][0]     │
└─────────────────────┴───────────────────┴────────────┴───────────────────┘
 Total params: 2,816,010 (10.74 MB)
 Trainable params: 2,811,530 (10.73 MB)
 Non-trainable params: 4,480 (17.50 KB)
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 49s 50ms/step - accuracy: 0.5160 - loss: 1.3457 - val_accuracy: 0.5150 - val_loss: 1.5214
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.6890 - loss: 0.8981 - val_accuracy: 0.6007 - val_loss: 1.1967
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 29s 47ms/step - accuracy: 0.7606 - loss: 0.7040 - val_accuracy: 0.7005 - val_loss: 0.8658
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 31s 49ms/step - accuracy: 0.8031 - loss: 0.5905 - val_accuracy: 0.6942 - val_loss: 0.9124
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 31s 49ms/step - accuracy: 0.8293 - loss: 0.5000 - val_accuracy: 0.7288 - val_loss: 0.7699
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.8596 - loss: 0.4218 - val_accuracy: 0.7234 - val_loss: 0.8523
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.8796 - loss: 0.3555 - val_accuracy: 0.7862 - val_loss: 0.7377
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 49ms/step - accuracy: 0.8990 - loss: 0.3006 - val_accuracy: 0.7825 - val_loss: 0.7059
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9155 - loss: 0.2504 - val_accuracy: 0.8262 - val_loss: 0.5775
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9284 - loss: 0.2121 - val_accuracy: 0.7382 - val_loss: 1.0013
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9421 - loss: 0.1740 - val_accuracy: 0.8003 - val_loss: 0.7194
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9484 - loss: 0.1537 - val_accuracy: 0.8023 - val_loss: 0.6419
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9582 - loss: 0.1225 - val_accuracy: 0.8135 - val_loss: 0.8706
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 31s 49ms/step - accuracy: 0.9635 - loss: 0.1106 - val_accuracy: 0.6922 - val_loss: 1.8044
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 31s 50ms/step - accuracy: 0.9685 - loss: 0.0953 - val_accuracy: 0.8439 - val_loss: 0.5770
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9713 - loss: 0.0848 - val_accuracy: 0.8024 - val_loss: 0.8008
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9742 - loss: 0.0770 - val_accuracy: 0.8184 - val_loss: 0.8351
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9758 - loss: 0.0722 - val_accuracy: 0.8097 - val_loss: 0.8360
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9792 - loss: 0.0638 - val_accuracy: 0.8382 - val_loss: 0.6745
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9789 - loss: 0.0634 - val_accuracy: 0.8113 - val_loss: 0.9820
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9815 - loss: 0.0556 - val_accuracy: 0.7907 - val_loss: 1.0682
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9827 - loss: 0.0540 - val_accuracy: 0.8081 - val_loss: 1.0396
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9836 - loss: 0.0517 - val_accuracy: 0.8305 - val_loss: 0.8231
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9861 - loss: 0.0437 - val_accuracy: 0.8401 - val_loss: 0.8829
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9838 - loss: 0.0498 - val_accuracy: 0.8092 - val_loss: 0.9178
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 49ms/step - accuracy: 0.9851 - loss: 0.0461 - val_accuracy: 0.8286 - val_loss: 0.8215
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9877 - loss: 0.0366 - val_accuracy: 0.8336 - val_loss: 0.7747
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9881 - loss: 0.0382 - val_accuracy: 0.8447 - val_loss: 0.8085
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9874 - loss: 0.0390 - val_accuracy: 0.8266 - val_loss: 0.9674
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9875 - loss: 0.0392 - val_accuracy: 0.8075 - val_loss: 1.1337
学習時間:926.0秒 パラメータ数:2,816,010 test_accuracy:0.7963

=== B_without_skip ===
Model: "B_without_skip"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ input_layer_1 (InputLayer)      │ (None, 32, 32, 3)      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_15 (Conv2D)              │ (None, 32, 32, 64)     │         1,792 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization_15          │ (None, 32, 32, 64)     │           256 │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_13 (Activation)      │ (None, 32, 32, 64)     │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_16 (Conv2D)              │ (None, 32, 32, 64)     │        36,928 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization_16          │ (None, 32, 32, 64)     │           256 │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_14 (Activation)      │ (None, 32, 32, 64)     │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_17 (Conv2D)              │ (None, 32, 32, 64)     │        36,928 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization_17          │ (None, 32, 32, 64)     │           256 │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_15 (Activation)      │ (None, 32, 32, 64)     │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_18 (Conv2D)              │ (None, 32, 32, 64)     │        36,928 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization_18          │ (None, 32, 32, 64)     │           256 │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_16 (Activation)      │ (None, 32, 32, 64)     │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_19 (Conv2D)              │ (None, 32, 32, 64)     │        36,928 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization_19          │ (None, 32, 32, 64)     │           256 │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_17 (Activation)      │ (None, 32, 32, 64)     │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d_2 (MaxPooling2D)  │ (None, 16, 16, 64)     │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_20 (Conv2D)              │ (None, 16, 16, 128)    │        73,856 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization_20          │ (None, 16, 16, 128)    │           512 │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_18 (Activation)      │ (None, 16, 16, 128)    │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_21 (Conv2D)              │ (None, 16, 16, 128)    │       147,584 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization_21          │ (None, 16, 16, 128)    │           512 │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_19 (Activation)      │ (None, 16, 16, 128)    │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_22 (Conv2D)              │ (None, 16, 16, 128)    │       147,584 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization_22          │ (None, 16, 16, 128)    │           512 │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_20 (Activation)      │ (None, 16, 16, 128)    │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_23 (Conv2D)              │ (None, 16, 16, 128)    │       147,584 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization_23          │ (None, 16, 16, 128)    │           512 │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_21 (Activation)      │ (None, 16, 16, 128)    │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ max_pooling2d_3 (MaxPooling2D)  │ (None, 8, 8, 128)      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_24 (Conv2D)              │ (None, 8, 8, 256)      │       295,168 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization_24          │ (None, 8, 8, 256)      │         1,024 │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_22 (Activation)      │ (None, 8, 8, 256)      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_25 (Conv2D)              │ (None, 8, 8, 256)      │       590,080 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization_25          │ (None, 8, 8, 256)      │         1,024 │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_23 (Activation)      │ (None, 8, 8, 256)      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_26 (Conv2D)              │ (None, 8, 8, 256)      │       590,080 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization_26          │ (None, 8, 8, 256)      │         1,024 │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_24 (Activation)      │ (None, 8, 8, 256)      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ conv2d_27 (Conv2D)              │ (None, 8, 8, 256)      │       590,080 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ batch_normalization_27          │ (None, 8, 8, 256)      │         1,024 │
│ (BatchNormalization)            │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ activation_25 (Activation)      │ (None, 8, 8, 256)      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ global_average_pooling2d_1      │ (None, 256)            │             0 │
│ (GlobalAveragePooling2D)        │                        │               │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_2 (Dense)                 │ (None, 128)            │        32,896 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout_1 (Dropout)             │ (None, 128)            │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_3 (Dense)                 │ (None, 10)             │         1,290 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 2,773,130 (10.58 MB)
 Trainable params: 2,769,418 (10.56 MB)
 Non-trainable params: 3,712 (14.50 KB)
Epoch 1/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 46s 52ms/step - accuracy: 0.4753 - loss: 1.4362 - val_accuracy: 0.4110 - val_loss: 2.3687
Epoch 2/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 29s 46ms/step - accuracy: 0.6430 - loss: 1.0066 - val_accuracy: 0.3818 - val_loss: 2.7285
Epoch 3/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.7151 - loss: 0.8230 - val_accuracy: 0.6055 - val_loss: 1.3240
Epoch 4/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 46ms/step - accuracy: 0.7627 - loss: 0.6940 - val_accuracy: 0.7251 - val_loss: 0.8020
Epoch 5/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 29s 46ms/step - accuracy: 0.7966 - loss: 0.5976 - val_accuracy: 0.7088 - val_loss: 0.8812
Epoch 6/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.8232 - loss: 0.5229 - val_accuracy: 0.7462 - val_loss: 0.8431
Epoch 7/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.8439 - loss: 0.4621 - val_accuracy: 0.7405 - val_loss: 0.8004
Epoch 8/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.8636 - loss: 0.4040 - val_accuracy: 0.7942 - val_loss: 0.6351
Epoch 9/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.8832 - loss: 0.3500 - val_accuracy: 0.8115 - val_loss: 0.5778
Epoch 10/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.8949 - loss: 0.3104 - val_accuracy: 0.7932 - val_loss: 0.6827
Epoch 11/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 29s 46ms/step - accuracy: 0.9091 - loss: 0.2691 - val_accuracy: 0.8004 - val_loss: 0.6670
Epoch 12/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.9184 - loss: 0.2388 - val_accuracy: 0.8074 - val_loss: 0.6502
Epoch 13/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.9301 - loss: 0.2064 - val_accuracy: 0.8020 - val_loss: 0.7048
Epoch 14/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 41s 46ms/step - accuracy: 0.9400 - loss: 0.1822 - val_accuracy: 0.7523 - val_loss: 0.9711
Epoch 15/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 29s 46ms/step - accuracy: 0.9470 - loss: 0.1552 - val_accuracy: 0.8081 - val_loss: 0.7556
Epoch 16/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.9524 - loss: 0.1409 - val_accuracy: 0.7672 - val_loss: 1.0236
Epoch 17/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 29s 46ms/step - accuracy: 0.9579 - loss: 0.1229 - val_accuracy: 0.8438 - val_loss: 0.5993
Epoch 18/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 29s 46ms/step - accuracy: 0.9622 - loss: 0.1121 - val_accuracy: 0.7967 - val_loss: 0.8734
Epoch 19/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.9667 - loss: 0.0989 - val_accuracy: 0.7816 - val_loss: 1.0158
Epoch 20/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.9680 - loss: 0.0954 - val_accuracy: 0.7685 - val_loss: 1.1077
Epoch 21/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.9732 - loss: 0.0822 - val_accuracy: 0.8008 - val_loss: 0.9681
Epoch 22/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.9749 - loss: 0.0755 - val_accuracy: 0.8264 - val_loss: 0.7816
Epoch 23/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.9748 - loss: 0.0735 - val_accuracy: 0.7982 - val_loss: 0.9215
Epoch 24/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.9776 - loss: 0.0661 - val_accuracy: 0.8000 - val_loss: 0.9783
Epoch 25/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.9785 - loss: 0.0644 - val_accuracy: 0.8077 - val_loss: 1.0973
Epoch 26/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.9785 - loss: 0.0651 - val_accuracy: 0.8391 - val_loss: 0.7431
Epoch 27/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.9807 - loss: 0.0592 - val_accuracy: 0.7971 - val_loss: 1.1145
Epoch 28/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 30s 48ms/step - accuracy: 0.9832 - loss: 0.0510 - val_accuracy: 0.8310 - val_loss: 0.8199
Epoch 29/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 29s 46ms/step - accuracy: 0.9830 - loss: 0.0536 - val_accuracy: 0.8454 - val_loss: 0.7514
Epoch 30/30
625/625 ━━━━━━━━━━━━━━━━━━━━ 28s 45ms/step - accuracy: 0.9842 - loss: 0.0469 - val_accuracy: 0.8354 - val_loss: 0.8374
学習時間:885.8秒 パラメータ数:2,773,130 test_accuracy:0.8304

グラフ+サマリー

# ── 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('residual_comparison.png', dpi=150)
plt.show()

# ── train_loss vs val_loss(過学習・収束の安定性)────────
fig2, axes2 = plt.subplots(2, 1, figsize=(7, 10))
for i, (label, h) in enumerate(histories.items()):
    axes2[i].plot(h.history['loss'],     label='train_loss')
    axes2[i].plot(h.history['val_loss'], label='val_loss')
    axes2[i].set_title(label)
    axes2[i].set_xlabel('Epoch'); axes2[i].legend(); axes2[i].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('residual_overfit.png', dpi=150)
plt.show()

print("\n===== 最終結果サマリー =====")
print(f"{'Pattern':>14} | {'Val Acc':>8} | {'Test Acc':>9} | {'Time(s)':>8} | {'Params':>12}")
print("-" * 62)
for label in ['with_skip', 'without_skip']:
    val_acc  = histories[label].history['val_accuracy'][-1]
    test_acc = scores[label][1]
    t        = times[label]
    p        = params[label]
    print(f"{label:>14} | {val_acc:>8.4f} | {test_acc:>9.4f} | {t:>8.1f} | {p:>12,}")
print("-" * 62)

最終結果サマリー

===== 最終結果サマリー =====
       Pattern |  Val Acc |  Test Acc |  Time(s) |       Params
--------------------------------------------------------------
     with_skip |   0.8075 |    0.7963 |    926.0 |    2,816,010
  without_skip |   0.8354 |    0.8304 |    885.8 |    2,773,130
--------------------------------------------------------------

実験結果

精度グラフ

精度グラフ

損失グラフ

損失グラフ

with_skip

with_skip

without_skip

without_skip
パターン 最終 val_accuracy 最終 test_accuracy パラメータ数 学習時間
A:Residual接続あり 80.75% 79.63% 2,816,010 926.0秒
B:Residual接続なし 83.54% 83.04% 2,773,130 885.8秒

考察

① 「スキップ接続あり」が負けた——なぜか?

今回の実験では、直感に反してResidual接続なし(B)の方がtest_accuracyで約3.4pt高いという結果になりました。「スキップ接続=精度が上がる」という単純な話ではないことが、この実験からよく分かります。

最大の原因は過学習の深刻さです。スキップありモデル(A)のtrain_accuracyは終盤で98.75%に達しているのに対し、val_accuracyは80.75%止まりで、乖離幅が約18ptあります。一方、スキップなしモデル(B)の乖離幅は約15ptで、Aより小さく抑えられています。

スキップ接続は勾配の流れを改善し、学習を速める効果があります。ところがそれが今回は「訓練データへの素早い過剰適合」として裏目に出ました。Aはearly epochから急激にtrain_lossを下げますが、val_lossは10エポック前後から上昇傾向に転じ、30エポック終盤では1.1を超えています。

② スキップなしのval_lossが序盤に暴れた理由

スキップなし(B)はEpoch 1〜2でval_lossが2.37→2.73と一時的に大きく跳ね上がっています。これはスキップ接続がない分、深い層への勾配伝達が不安定で学習の立ち上がりが遅いことを示しています。ただしEpoch 3以降は安定して収束しており、最終的にはAを上回る汎化性能を発揮しました。

③ 今回の設定が「スキップありに不利」だった

Residual接続が本来の効果を発揮するのは、BatchNorm・Data Augmentation・適切な正則化が整った条件下です。今回の実験はData Augmentationなし・Dropout 0.3のみという比較的シンプルな設定です。スキップ接続で学習が速くなったぶん、過学習の抑制が追いつかなかったと考えられます。

実際のResNetはData Augmentation(ランダムクロップ・水平反転など)を前提とした設計になっており、それが過学習を防ぐ重要な役割を担っています。

④ スキップ接続の効果を引き出すには

今回の結果を踏まえると、Residual接続を活かすためには以下のような追加の工夫が必要です。

  • Data Augmentation(ランダムクロップ・水平反転)で訓練データの多様性を上げる
  • EarlyStoppingでval_lossが上昇し始めたタイミングで学習を止める
  • 学習率スケジューリング(ReduceLROnPlateau など)で後半の過学習を抑える
  • Dropout率の引き上げ(0.3 → 0.5 など)

⑤ ResNetとの実装上の違い

本家のResNet-50はBottleneck Block(1×1→3×3→1×1の3層構成)を使っており、Data Augmentationも標準で組み込まれています。今回は2層構成のBasic Blockのみで実験したため、本来の性能を引き出せていない部分があります。Bottleneck Blockとの比較はまた別の記事で検証予定です。

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

まとめ
  • Residual接続(スキップ接続)は、入力 x をブロック出力に直接足す F(x) + x という構造で、勾配を浅い層まで届けやすくする
  • 今回の実験では、スキップ接続ありの方が学習は速かったが過学習も深刻で、test_accuracyはスキップなしに負けた(79.63% vs 83.04%)
  • スキップ接続は「学習を速くする道具」であり、過学習への対策(Data Augmentation・EarlyStopping・学習率調整)をセットで使わないと逆効果になることがある
  • Kerasでは layers.Add() でスキップ接続を実装し、チャンネル数が変わる箇所は 1×1 Conv でショートカット側を変換する
  • ResNetが強いのはスキップ接続単体の力ではなく、Data Augmentationや学習率スケジューリングとの組み合わせによるもの