MixUpのalpha値を変えると効果はどう変わる?(α=0.1 vs 0.2 vs 0.4 vs 1.0)【Keras×CIFAR-10実験】

投稿日:2026年7月31日金曜日 最終更新日:

CIFAR-10 CNN Data Augmentation Google Colab Keras MixUp

X f B! L
MixUpのalpha値を変えると効果はどう変わる?(α=0.1 vs 0.2 vs 0.4 vs 1.0)【Keras×CIFAR-10実験】 アイキャッチ画像

以前の記事「MixUpで精度は上がる?」ではMixUpのあり・なしを比較しましたが、MixUpの強さを決めるalphaパラメータ自体を変えた検証はまだ行っていませんでした。今回はalpha=0(なし)・0.1・0.2・0.4・1.0の5段階で、test_accuracyに加えて予測の自信度(confidence)過学習ギャップまで踏み込んで比較します。

📘 この記事でわかること
  • MixUpのalpha値がBeta分布の形状をどう変えるか
  • alphaがtest_accuracyに与える影響
  • alphaが予測の自信度(過信の抑制)に与える影響
  • tf.random.betaが存在しない問題への対処法

MixUpとalphaの役割

MixUpは2枚の画像とラベルを、Beta分布からサンプリングした混合比 λ で線形補間する手法です。

\[ \tilde{x} = \lambda x_i + (1-\lambda) x_j, \quad \tilde{y} = \lambda y_i + (1-\lambda) y_j, \quad \lambda \sim \text{Beta}(\alpha, \alpha) \]

alphaの値によって、Beta(α, α)分布の形状は大きく変わります。

alphaλの分布の傾向
0(MixUpなし)λ=1固定(混合しない)
0.10か1付近に偏る(ほとんど混合しない、たまに強く混合)
0.2〜0.40.1よりは中間値も出やすいが、依然として両端寄り
1.0Uniform(0,1)と一致。あらゆる混合比が均等に出現

実験コード

ベースラインは本シリーズ標準構成(Conv2D×2・GAP・Dropout=0.2・Adam lr=0.001・batch_size=64・30エポック)です。

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

!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 53 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,797 kB/s)
Selecting previously unselected package fonts-ipafont-gothic.
(Reading database ... 122403 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 33.4 MB/s eta 0:00:00
  Preparing metadata (setup.py) ... done
  Building wheel for japanize_matplotlib (setup.py) ... done
環境準備完了

② import・データ準備・MixUpパイプライン・モデル構築関数

import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import matplotlib.pyplot as plt
import japanize_matplotlib

SEED = 42
tf.random.set_seed(SEED)
np.random.seed(SEED)

NUM_CLASSES = 10
EPOCHS = 30
BATCH_SIZE = 64

# データ読み込み・正規化
(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
y_train = keras.utils.to_categorical(y_train, NUM_CLASSES).astype("float32")
y_test_onehot = keras.utils.to_categorical(y_test, NUM_CLASSES).astype("float32")

# 検証用データを先に切り分ける(MixUpは訓練データにのみ適用)
val_size = int(len(x_train) * 0.2)
x_val, y_val = x_train[:val_size], y_train[:val_size]
x_tr, y_tr = x_train[val_size:], y_train[val_size:]

def sample_beta(alpha):
    """
    tf.random.betaは存在しないため、np.random.betaをtf.py_functionでラップして使う。
    tf.py_functionが渡すのはTensorなので、.numpy()で明示的にPythonの値へ変換する必要がある。
    """
    def _sample(a):
        a_val = float(a.numpy())
        return np.float32(np.random.beta(a_val, a_val))
    lam = tf.py_function(_sample, [alpha], tf.float32)
    lam.set_shape([])
    return lam

def make_mixup_dataset(x, y, alpha, batch_size):
    ds = tf.data.Dataset.from_tensor_slices((x, y))
    ds = ds.shuffle(buffer_size=10000, seed=SEED)
    ds = ds.batch(batch_size, drop_remainder=True)

    def mixup_batch(images, labels):
        if alpha <= 0.0:
            return images, labels
        lam = sample_beta(tf.constant(alpha, dtype=tf.float32))
        shuffled_idx = tf.random.shuffle(tf.range(tf.shape(images)[0]))
        images2 = tf.gather(images, shuffled_idx)
        labels2 = tf.gather(labels, shuffled_idx)
        mixed_images = lam * images + (1 - lam) * images2
        mixed_labels = lam * labels + (1 - lam) * labels2
        return mixed_images, mixed_labels

    ds = ds.map(mixup_batch, num_parallel_calls=tf.data.AUTOTUNE)
    ds = ds.prefetch(tf.data.AUTOTUNE)
    return ds

def build_model():
    inputs = keras.Input(shape=(32, 32, 3))
    x = layers.Conv2D(32, 3, padding="same", activation="relu")(inputs)
    x = layers.MaxPooling2D()(x)
    x = layers.Conv2D(64, 3, padding="same", activation="relu")(x)
    x = layers.MaxPooling2D()(x)
    x = layers.GlobalAveragePooling2D()(x)
    x = layers.Dropout(0.2)(x)
    outputs = layers.Dense(10, activation="softmax")(x)

    model = keras.Model(inputs, outputs)
    model.compile(
        optimizer=keras.optimizers.Adam(learning_rate=0.001),
        loss="categorical_crossentropy",
        metrics=["accuracy"],
    )
    return model
実行結果をクリックして内容を開く
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170498071/170498071 ━━━━━━━━━━━━━━━━━━━━ 1977s 12us/step

③ alpha=0, 0.1, 0.2, 0.4, 1.0 で学習実行

alphas = [0.0, 0.1, 0.2, 0.4, 1.0]  # 0.0はMixUpなし(ベースライン)
results = []
histories = {}

for alpha in alphas:
    print(f"\n=== alpha={alpha} ===")
    tf.random.set_seed(SEED)
    model = build_model()
    train_ds = make_mixup_dataset(x_tr, y_tr, alpha, BATCH_SIZE)
    history = model.fit(
        train_ds,
        validation_data=(x_val, y_val),
        epochs=EPOCHS,
        verbose=0,
    )

    test_score = model.evaluate(x_test, y_test_onehot, verbose=0)
    # 過学習チェック用:訓練データを「混合前の元ラベル」で評価
    train_score = model.evaluate(x_tr, y_tr, verbose=0)
    probs = model.predict(x_test, verbose=0)
    mean_confidence = float(np.mean(np.max(probs, axis=1)))

    results.append({
        "alpha": alpha,
        "test_accuracy": test_score[1],
        "test_loss": test_score[0],
        "train_accuracy_true": train_score[1],
        "overfit_gap": train_score[1] - test_score[1],
        "mean_confidence": mean_confidence,
    })
    histories[alpha] = history
    print(f"test_accuracy: {test_score[1]:.4f}  mean_confidence: {mean_confidence:.4f}")
実行結果をクリックして内容を開く
=== alpha=0.0 ===
test_accuracy: 0.5740  mean_confidence: 0.4724

=== alpha=0.1 ===
test_accuracy: 0.5650  mean_confidence: 0.4568

=== alpha=0.2 ===
test_accuracy: 0.5524  mean_confidence: 0.4423

=== alpha=0.4 ===
test_accuracy: 0.5536  mean_confidence: 0.4315

=== alpha=1.0 ===
test_accuracy: 0.5381  mean_confidence: 0.4091

④ 結果集計とグラフ化

import pandas as pd

df = pd.DataFrame(results).sort_values("alpha")
print(df.to_string(index=False))

fig, axes = plt.subplots(1, 2, figsize=(12, 5))

axes[0].plot(df["alpha"], df["test_accuracy"], marker="o")
axes[0].set_xlabel("alpha")
axes[0].set_ylabel("test_accuracy")
axes[0].set_title("alphaとtest_accuracy")
axes[0].grid(True, alpha=0.3)

axes[1].plot(df["alpha"], df["mean_confidence"], marker="o", color="orange", label="mean_confidence")
axes[1].plot(df["alpha"], df["overfit_gap"], marker="s", color="green", label="overfit_gap")
axes[1].set_xlabel("alpha")
axes[1].legend()
axes[1].set_title("alphaと予測confidence/過学習ギャップ")
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig("mixup_alpha_summary.png", dpi=150)
plt.show()

# 学習曲線比較
plt.figure(figsize=(8, 5))
for alpha in alphas:
    plt.plot(histories[alpha].history["val_accuracy"], label=f"alpha={alpha}")
plt.xlabel("epoch")
plt.ylabel("val_accuracy")
plt.title("alpha別のval_accuracy推移")
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("mixup_alpha_curves.png", dpi=150)
plt.show()
実行結果をクリックして内容を開く
 alpha  test_accuracy  test_loss  train_accuracy_true  overfit_gap  mean_confidence
   0.0         0.5740   1.241558             0.575825     0.001825         0.472379
   0.1         0.5650   1.264177             0.568175     0.003175         0.456821
   0.2         0.5524   1.295729             0.556900     0.004500         0.442280
   0.4         0.5536   1.312612             0.556750     0.003150         0.431516
   1.0         0.5381   1.353315             0.539425     0.001325         0.409138

実験結果

alpha別サマリー

alphatest_accuracytrain_accuracy(元ラベル)過学習ギャップmean_confidence
0(なし)57.40%57.58%0.18pt0.4724
0.156.50%56.82%0.32pt0.4568
0.255.24%55.69%0.45pt0.4423
0.455.36%55.68%0.32pt0.4315
1.053.81%53.94%0.13pt0.4091

alpha=0(なし)を基準にした差分は次の通りです。

alphatest_accuracy差分mean_confidence差分
0.1−0.90pt−0.0156
0.2−2.16pt−0.0301
0.4−2.04pt−0.0409
1.0−3.59pt−0.0632

alphaとtest_accuracy/confidence・過学習ギャップ

alphaとtest_accuracy/confidence・過学習ギャップ

学習曲線(val_accuracy推移)

学習曲線
⚠️ ハマりポイント:tf.random.betaは存在しない/tf.py_functionにはTensorが渡る/to_categoricalはfloat64を返すことがある
Beta分布からのサンプリングにはnp.random.betaを使う必要があります。TensorFlowの乱数APIにはtf.random.normaltf.random.uniformはありますが、tf.random.betaは存在しません。

また、tf.py_functionでラップした関数にはPythonの数値ではなくTensorが渡されるため、np.random.betaにそのまま渡すとエラーになります。a.numpy()で明示的にPythonの値へ変換してから渡してください。

さらに、keras.utils.to_categorical()の戻り値は環境によってfloat64になることがあります(Keras 3系ではdtype引数自体も受け付けません)。一方Beta分布からサンプリングしたlamtf.py_functionでfloat32に固定しているため、そのまま掛け算するとdtype不一致でエラーになります。.astype("float32")で明示的にキャストしてください(alpha=0.0のときはif分岐でMixUp処理自体がスキップされるため気づきにくく、alpha>0の設定で初めてエラーが表面化します)。

考察

① alphaとtest_accuracyの関係

今回の設定では、alphaを上げるほどtest_accuracyはほぼ一貫して低下しました(alpha=0.2のみ0.4よりわずかに低い程度で、大きくは0→1.0にかけて単調減少)。alpha=1.0では57.40%→53.81%と−3.59ptの低下です。これはMixUpが「効かなかった」というより、本シリーズ標準構成(Conv2D×2・GAP・Dropout=0.2・30エポック)というシンプルなベースラインに対して、MixUpの難易度が過大だった可能性が高いと考えられます。過去の学習で確認している通り、30エポックという学習回数はAugmentation系の手法が効果を発揮するには不十分な場合があり、MixUpによって作られた「存在しない中間的なサンプル」を学習しきる前に学習が終わってしまったと解釈できます。

② alphaと予測の自信度(confidence)

一方でmean_confidenceはalpha=0の0.4724からalpha=1.0の0.4091まで完全に単調減少しました。alphaを上げるほどモデルが「言い切らなくなる」という、MixUpの理論通りの効果がノイズなくきれいに表れています。精度は下がっているにもかかわらず、この指標だけは素直にalphaに反応しており、精度とconfidenceは必ずしも同じ方向に動くとは限らない好例になっています。

③ alphaと過学習ギャップ

過学習ギャップ(train_accuracy_true − test_accuracy)は、alpha=0ですでに0.18ptと極めて小さく、alphaを上げても0.13〜0.45ptの範囲で大きくは変化しませんでした。これは、そもそも本シリーズの標準構成(Conv2D×2・GAP・Dropout=0.2)自体が軽量で、30エポックでは過学習がほとんど起きていないためだと考えられます。MixUpの正則化効果は「過学習が起きているモデル」に対してこそ意味を持つため、今回のように土台がすでに過学習していない設定では、精度低下という副作用だけが目立つ結果になったと言えます。

実務での推奨

状況推奨理由
過学習を抑えたい/モデルの過信を減らしたい過学習ギャップが実際に大きいモデル(より深い・パラメータ数の多いネット)で試す今回のようにベースライン自体が過学習していない場合、MixUpは精度低下という副作用だけが表れやすいため
短いエポック数で精度を出したいMixUpは使わない、または弱いalpha(0.1程度)にとどめる30エポック程度ではMixUpが作る難しいサンプルを学習しきれず、精度が素直に下がる結果となったため
alphaの初期値に迷ったときまずalpha=0.2程度から試すMixUp原論文でもCIFAR系データではalpha=0.1〜0.4程度が一般的に使われるため
まとめ
  • test_accuracyはalpha=0の57.40%からalpha=1.0の53.81%まで、ほぼ一貫して−3.59pt低下した。今回のシンプルなベースライン(Conv2D×2・GAP・Dropout=0.2・30エポック)ではMixUpは精度を下げる方向に働いた
  • mean_confidenceは0.4724から0.4091まで完全に単調減少し、「alphaを上げるほどモデルが言い切らなくなる」というMixUpの理論通りの効果は明確に確認できた
  • 過学習ギャップはalpha=0の時点で0.18ptとすでに小さく、MixUpを加えても0.13〜0.45ptの範囲でほぼ変化しなかった。そもそも過学習していないベースラインにMixUpを使っても、精度低下という副作用だけが目立つ結果になった
  • MixUpの効果を正しく評価するには、ある程度過学習が起きるモデル・エポック数で検証する必要がある

関連記事