第2章

ニューラルネットワークで衣服の画像を分類する

Fashion-MNISTを使って畳み込みニューラルネットワークを学習し、未知の画像の分類と誤分類の分析を行います。

この章で学ぶこと

  • 画像を数値の配列として扱う
  • 学習用・検証用・テスト用データの役割を区別する
  • 畳み込みニューラルネットワーク(CNN)を構築する
  • 正解率だけでなく、誤分類された画像と確信度を確認する
  • 画像認識モデルの限界と、安全な利用方法を考える

作るもの

28×28ピクセルの衣服画像を、Tシャツ、ズボン、スニーカーなど10種類に分類するモデルを作ります。データにはFashion-MNISTを使います。60,000枚が学習用、10,000枚がテスト用として用意されており、各画像はグレースケールです。TensorFlowのFashion-MNISTチュートリアル

28×28の画像 → CNN → 10クラスそれぞれの確率 → 最も高いクラス

この章では、画像の一部に共通して現れる模様や形を学習しやすい**畳み込みニューラルネットワーク(CNN)**を使います。

1. 実行環境を準備する

前章とは別の作業用フォルダーと仮想環境を作ると、依存関係を分けて管理できます。Windows PowerShellでは次のように実行します。

mkdir ml-images
cd ml-images
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install tensorflow matplotlib

macOS・Linuxでは、仮想環境を有効にする行を次のように変更します。

source .venv/bin/activate

TensorFlowが対応するPythonやOSの条件は更新されるため、インストールできない場合はTensorFlow公式のインストール手順を確認してください。GPUの設定は環境によって異なりますが、この演習はCPUでも実行できます。

2. データを読み込んで確認する

完成コードをダウンロードし、作業用フォルダーへ保存します。

完成コード:fashion_mnist_cnn.pyをダウンロード

データはKerasから読み込みます。初回だけインターネットからダウンロードされ、以後はローカルのキャッシュが使われます。

(train_images, train_labels), (test_images, test_labels) = (
    tf.keras.datasets.fashion_mnist.load_data()
)

読み込まれる配列の意味は次の通りです。

配列 内容 形状
train_images 学習に使う画像 (60000, 28, 28)
train_labels 学習画像の正解ラベル (60000,)
test_images 最後の評価だけに使う画像 (10000, 28, 28)
test_labels テスト画像の正解ラベル (10000,)

ラベルは0〜9の整数で、それぞれ次の種類を表します。

ラベル 種類 ラベル 種類
0 T-shirt/top 5 Sandal
1 Trouser 6 Shirt
2 Pullover 7 Sneaker
3 Dress 8 Bag
4 Coat 9 Ankle boot

学習用データの一部は validation_split=0.1 によって検証用になります。検証用データは学習中の変化を確認するため、テスト用データは完成したモデルを最後に評価するために使います。

3. 画素を前処理する

各画素は0〜255の整数です。0〜1の小数へ変換し、CNNが受け取るチャンネル方向の次元を追加します。

train_images = train_images.astype("float32") / 255.0
test_images = test_images.astype("float32") / 255.0
train_images = train_images[..., np.newaxis]
test_images = test_images[..., np.newaxis]

これで画像1枚の形状は (28, 28, 1) になります。最後の 1 はグレースケールの1チャンネルを表します。カラー画像の場合は一般に赤・緑・青の3チャンネルです。

前処理の方法は学習用とテスト用でそろえます。ただし、テスト画像や正解ラベルを学習に混ぜてはいけません。

4. CNNを組み立てる

完成コードでは、次のモデルを作ります。

model = tf.keras.Sequential([
    tf.keras.layers.Input(shape=(28, 28, 1)),
    tf.keras.layers.Conv2D(32, 3, activation="relu"),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Conv2D(64, 3, activation="relu"),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(64, activation="relu"),
    tf.keras.layers.Dropout(0.3),
    tf.keras.layers.Dense(10, activation="softmax"),
])
層 役割
Conv2D 小さなフィルターを動かし、輪郭や模様などの特徴を取り出す
MaxPooling2D 特徴を残しながら縦横のサイズを小さくする
Flatten 2次元の特徴を1列へ並べる
Dense 取り出した特徴を組み合わせて分類する
Dropout 学習中に一部の出力を無効にし、学習データへの過度な適合を抑える
softmax 10クラスの出力を、合計が1になる確率として表す

正解ラベルが整数なので、損失関数には sparse_categorical_crossentropy を使います。最適化手法はAdam、確認する指標は正解率です。

model.compile(
    optimizer="adam",
    loss="sparse_categorical_crossentropy",
    metrics=["accuracy"],
)

5. 学習して評価する

プログラムを実行します。

python fashion_mnist_cnn.py

学習は5エポック行います。1エポックは、学習用データ全体を一巡することです。

model.fit(
    train_images,
    train_labels,
    epochs=5,
    batch_size=64,
    validation_split=0.1,
)

実行中は accuracy と val_accuracy が表示されます。前者は実際に重みの更新へ使う画像、後者は分けておいた検証画像に対する正解率です。学習側だけが上がり、検証側が下がり始める場合は、学習データへ過度に適合する過学習が疑われます。

学習後は、最後まで取っておいたテストデータで評価します。

test_loss, test_accuracy = model.evaluate(
    test_images, test_labels, verbose=0
)

正解率は全画像のうち正しく分類した割合です。毎回の実行環境によって結果は多少変わります。値だけを目標にせず、学習用とは別のデータで測定していることを確認してください。

6. 間違い方を観察する

完成コードは、誤分類した画像を最初の12枚まで fashion_mnist_errors.png に保存します。各画像には正解、予測、予測したクラスの確信度が表示されます。

probabilities = model.predict(test_images, verbose=0)
predicted_labels = probabilities.argmax(axis=1)
wrong_indices = np.flatnonzero(predicted_labels != test_labels)[:12]

画像を開き、次を観察してください。

  1. Shirt、T-shirt/top、Pullover など形が似たクラスで間違えていないか
  2. 人間にも判別しにくい画像ではないか
  3. 間違っているのに確信度が高い例がないか
  4. 28×28ピクセルやグレースケールという条件で失われた情報は何か

softmaxの出力が90%でも、「現実世界で90%正しい」と保証されるわけではありません。確信度の調整には別の検証が必要です。

プログラムは学習済みモデルも fashion_mnist_model.keras に保存します。これはモデルの構造と重みを再利用するためのファイルです。出所が不明な学習済みモデルは、信頼できないプログラムと同様に扱い、むやみに読み込まないでください。

現実の画像へ使う前に

Fashion-MNISTは、中央に配置された28×28のグレースケール画像だけで構成されています。スマートフォンで撮影したカラー写真は、背景、角度、明るさ、縮尺が大きく異なるため、このモデルへそのまま入力してもうまく分類できません。

現実の課題では、次の点も検討します。

  • 実際の利用環境を反映した学習・評価データを用意する
  • 人や地域などのグループごとに性能差がないか調べる
  • 誤分類したときの影響を考え、人が確認する仕組みを設ける
  • 画像の著作権、個人情報、撮影された人の同意を確認する
  • モデルが未学習の種類を無理に10クラスのどれかへ分類する問題を扱う

医療、本人確認、安全に関わる判断へ、この演習モデルを使用してはいけません。

トラブル対処

症状 確認すること
TensorFlowをインストールできない Python・OS・CPUが対応条件を満たすか公式手順で確認する
初回のデータ取得に失敗する インターネット接続、プロキシ、空き容量を確認して再実行する
実行に時間がかかる epochs=1 で流れを確認してから増やす。ほかの重い処理を終了する
メモリ不足になる batch_size を32や16へ下げる
val_accuracy が改善しない 前処理、ラベル、学習回数を確認する。層を増やす前に誤分類を見る

やってみよう

  1. epochs を1、3、10に変え、学習・検証・テストの正解率を表にしてください。
  2. Dropout(0.3) を外した場合、過学習の様子がどう変わるか比較してください。
  3. クラスごとの正解数と誤分類数を数え、苦手な種類を調べてください。
  4. 自分で撮影した服の写真を使うには、どのような前処理と追加データが必要か設計してください。

精度を上げる変更をしたときも、テストデータを何度も見ながら設定を選ぶと、テストデータへ間接的に適合します。比較には検証データを使い、最後の評価だけをテストデータで行いましょう。