はじめに
こんにちは、SHOUです。
「画像認識を始めたいけど、何から手をつければいいのか分からない…」そんな悩みを持つ方も多いのではないでしょうか。
この記事では、画像認識の代表的な手法である 畳み込みニューラルネットワーク(CNN) と、既に学習済みの強力なモデルを再利用する 事前学習(転移学習) を組み合わせて、手軽に高精度な画像分類を行う方法を紹介します。
Python と Keras を使って実際に動かしながら学べる内容になっており、初めて画像分類に挑戦する方にもおすすめです。
CNNとは何か?
CNN(Convolutional Neural Network) は、画像や映像のような空間的な情報を効率よく処理できる、ディープラーニングでよく使われるモデルです。
画像分類や物体検出といったタスクにおいて高い性能を発揮するCNNは、次のような層で構成されています:
- 畳み込み層:画像からエッジや形状などの特徴を抽出
- プーリング層:特徴量を圧縮し、計算効率を向上
- 全結合層:抽出した特徴を使って分類や予測を行う
これらの層が組み合わさることで、画像内のパターンを認識することが可能になります。
事前学習(転移学習)とは?
事前学習とは、すでに大規模なデータで学習されたモデルを活用し、自分の課題に合わせて再利用する方法です。これにより、ゼロから学習を行うよりも効率よく、かつ高精度なモデルを作成することができます。
主なメリット:
- 大量のデータを用意しなくても良い
- 学習時間を大幅に短縮できる
- 精度の高いモデルを素早く構築できる
代表的な事前学習済みモデルには、以下のようなものがあります:
- VGG16 / VGG19:シンプルで理解しやすい構造
- ResNet50:深いネットワーク構造でも学習が安定
- InceptionV3:計算効率と精度のバランスが良い
- MobileNetV2:軽量でモバイルデバイスでも動作可能
今回は、数字(0〜9)の画像分類というタスクに対して、軽量で高性能な MobileNetV2 を利用してみます。
Kerasで事前学習済みモデルを使った画像分類
ここからは実際に、KerasとGoogle Colabを使って、事前学習済みの MobileNetV2 を用いた画像分類の実装を行います。
分類対象は、手書きの数字画像(0〜9)です。分類には自作の画像データを使いますが、枚数が少なくても高精度を狙えるのが転移学習の強みです。
まずは、Google DriveをColabに接続して、画像データを読み込めるようにしましょう。
事前準備:Google DriveをColabにマウント
まず、Google Drive を Colab に接続します👇from google.colab import drive drive.mount('/content/drive')すると、認証リンクが出るので、指示に従って認証してください。
マウントが完了すると、Drive のファイルが /content/drive/MyDrive/ 以下に見えるようになります。
転移学習
ここでは、Kerasに内蔵されているMobileNetV2という事前学習済みのモデルを使って、手書き数字の分類を行います。 MobileNetV2は、軽量で高速に動作するため、Colabやモバイル環境での使用にも適しています。
ただし、MobileNetV2は元々、一般的な物体画像(犬・猫・車など)を対象に学習されており、入力画像サイズも96×96以上のRGB画像を想定しています。 そのため、手書き数字画像(28×28・白黒)を分類に使うには、以下のような前処理が必要になります。
- 画像サイズの拡大:MNISTの28×28画像を96×96にリサイズ
- RGB化:グレースケール画像を3チャンネルに変換
- 白黒反転:MNISTは「黒地に白文字」ですが、事前学習済みモデルは通常「白地に黒い物体」を想定しているため、明暗を反転
- データ拡張:回転や拡大・平行移動を加えて、少ない画像でも学習効果を高める
こうした前処理をまとめて行うために、Kerasの ImageDataGenerator
を使います。以下のコードで、データの読み込みと前処理を行い、学習用・検証用に分割します。
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.preprocessing.image import ImageDataGenerator
# MobileNetV2は最低96x96の入力が必要 → 画像サイズを拡大
IMG_SIZE = 96
BATCH_SIZE = 32
# 白黒反転 + 正規化 + 拡張
datagen = ImageDataGenerator(
rescale=1./255,
preprocessing_function=lambda x: 1.0 - x, # 白地黒文字に対応
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.1,
zoom_range=0.1,
validation_split=0.2
)
train_generator = datagen.flow_from_directory(
'/content/drive/MyDrive/Colab/digits',
target_size=(IMG_SIZE, IMG_SIZE),
color_mode='rgb', # MobileNetV2はRGBを期待
batch_size=BATCH_SIZE,
class_mode='sparse',
subset='training'
)
val_generator = datagen.flow_from_directory(
'/content/drive/MyDrive/Colab/digits',
target_size=(IMG_SIZE, IMG_SIZE),
color_mode='rgb',
batch_size=BATCH_SIZE,
class_mode='sparse',
subset='validation'
)
モデルの構築
続いて、事前学習済みの MobileNetV2 をベースにした分類モデルを構築します。
- base_model:MobileNetV2の特徴抽出部のみを利用します。分類部分(出力層)は除外します(
include_top=False
)。 - trainable=False:ベースモデルの重みは固定(凍結)して使います。これは、学習済みの知識を活かす「転移学習」の基本戦略です。
- 分類ヘッド:MobileNetV2の出力を受け取り、手書き数字(0〜9)を分類する小さなネットワーク(全結合層)を追加します。
モデル全体は以下のような構成になります:
- MobileNetV2(特徴抽出)
- GlobalAveragePooling(特徴マップを1次元に変換)
- Dense層(隠れ層)+Dropoutで過学習を防止
- 最終的な10クラスの出力層(softmax)
モデルが完成したら、model.fit()
を使って学習を開始します。
このとき、画像データは train_generator
と val_generator
から読み込まれ、1エポックごとにパラメータが更新されていきます。
# モデル構築(ベースモデル + 分類ヘッド)
base_model = keras.applications.MobileNetV2(
input_shape=(IMG_SIZE, IMG_SIZE, 3),
include_top=False,
weights='imagenet'
)
base_model.trainable = False # 転移学習の基本:特徴抽出器は凍結
# 新しい分類ヘッドを追加
model = keras.Sequential([
base_model,
keras.layers.GlobalAveragePooling2D(),
keras.layers.Dense(128, activation='relu'),
keras.layers.Dropout(0.3),
keras.layers.Dense(10, activation='softmax')
])
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# 学習
history = model.fit(train_generator, epochs=20, validation_data=val_generator)
学習結果
学習の途中では、訓練精度(accuracy)と検証精度(val_accuracy)がログとして表示されます。訓練精度だけでなく、検証精度も重要です。検証精度が高いほど、未知のデータに対しても正しく分類できる可能性が高くなります。
学習中のログは以下のようになります:
Found 80 images belonging to 10 classes. Found 20 images belonging to 10 classes. Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/mobilenet_v2/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_96_no_top.h5 9406464/9406464 ━━━━━━━━━━━━━━━━━━━━ 0s 0us/step /usr/local/lib/python3.11/dist-packages/keras/src/trainers/data_adapters/py_dataset_adapter.py:121: UserWarning: Your `PyDataset` class should call `super().__init__(**kwargs)` in its constructor. `**kwargs` can include `workers`, `use_multiprocessing`, `max_queue_size`. Do not pass these arguments to `fit()`, as they will be ignored. self._warn_if_super_not_called() Epoch 1/20 3/3 ━━━━━━━━━━━━━━━━━━━━ 19s 6s/step - accuracy: 0.1120 - loss: 2.7394 - val_accuracy: 0.3000 - val_loss: 1.8545 Epoch 2/20 3/3 ━━━━━━━━━━━━━━━━━━━━ 1s 444ms/step - accuracy: 0.3615 - loss: 1.9677 - val_accuracy: 0.5000 - val_loss: 1.5880 Epoch 3/20 3/3 ━━━━━━━━━━━━━━━━━━━━ 1s 362ms/step - accuracy: 0.5229 - loss: 1.4203 - val_accuracy: 0.7000 - val_loss: 1.2281 ・・・ Epoch 15/20 3/3 ━━━━━━━━━━━━━━━━━━━━ 1s 354ms/step - accuracy: 0.8927 - loss: 0.3191 - val_accuracy: 0.9000 - val_loss: 0.3266 Epoch 16/20 3/3 ━━━━━━━━━━━━━━━━━━━━ 2s 829ms/step - accuracy: 0.9510 - loss: 0.1885 - val_accuracy: 0.9000 - val_loss: 0.6225 Epoch 17/20 3/3 ━━━━━━━━━━━━━━━━━━━━ 2s 436ms/step - accuracy: 0.8656 - loss: 0.3272 - val_accuracy: 0.9500 - val_loss: 0.3617 Epoch 18/20 3/3 ━━━━━━━━━━━━━━━━━━━━ 1s 332ms/step - accuracy: 0.9617 - loss: 0.1494 - val_accuracy: 0.7500 - val_loss: 0.5225 Epoch 19/20 3/3 ━━━━━━━━━━━━━━━━━━━━ 2s 399ms/step - accuracy: 0.9182 - loss: 0.1808 - val_accuracy: 0.9000 - val_loss: 0.4033 Epoch 20/20 3/3 ━━━━━━━━━━━━━━━━━━━━ 1s 438ms/step - accuracy: 1.0000 - loss: 0.1406 - val_accuracy: 0.8500 - val_loss: 0.3530
このように、エポックが進むにつれて精度が向上していく様子が確認できます。
画像の分類(推論)
学習が終わったら、モデルを使って新しい画像を分類してみましょう。 ここでは、自分で用意した手書き数字の画像を使って、モデルがどの数字だと判断するかを試します。
ポイント:
- 画像サイズ:MobileNetV2に合わせて、96×96にリサイズ
- RGB:カラー画像(3チャンネル)として読み込み
- 白黒反転:MNISTとの整合性を保つために必要
- 前処理:
preprocess_input()
でMobileNetV2向けの標準化を実施
推論は、model.predict()
を使って行い、np.argmax()
で最もスコアの高いクラス(0~9のどれか)を取り出します。
from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
import numpy as np
# 画像パス
img_path = '/content/drive/MyDrive/Colab/handwritten_digit.png'
# MobileNetV2用の入力に合わせてサイズとカラーを指定
img = image.load_img(img_path, target_size=(96, 96), color_mode='rgb')
# 配列に変換
img_array = image.img_to_array(img)
# 白黒反転(白地に黒文字 → 黒地に白文字)
img_array = 255.0 - img_array
# バッチ次元を追加 + 前処理
img_array = np.expand_dims(img_array, axis=0)
img_array = preprocess_input(img_array)
# 予測
prediction = model.predict(img_array)
predicted_label = np.argmax(prediction)
print('予測ラベル:', predicted_label)
実行結果は以下です:
1/1 ━━━━━━━━━━━━━━━━━━━━ 1s 1s/step 予測ラベル: 1
今回、実際の推論結果では「4」と書かれた画像が「1」と誤認識されていました。
実践でのポイント
より高い精度を目指す場合、以下の点にも注目してみてください:
- データ拡張:学習時に画像をランダムに変形(回転・ズームなど)させることで、モデルの汎化性能を高めます。
- Fine-tuning:凍結していたベースモデルの一部層を再学習させることで、よりタスクに最適化されたモデルに仕上げられます。
- GPU活用:Google ColabのGPUを使うことで、学習を高速に行えます(CPUより圧倒的に速いです)。
まとめ
本記事では、KerasとMobileNetV2を使って、事前学習済みモデルによる画像分類に取り組みました。 自分の画像を使って分類を行うことで、ディープラーニングの仕組みを体感しながら学べたのではないでしょうか。
転移学習を活用すれば、少ないデータや短い時間でも、高精度な分類モデルを作成できます。 KerasのAPIはシンプルなので、ぜひこの記事をベースに、自分だけの画像分類プロジェクトに挑戦してみてください!
参考リンク:
Keras Applications公式ドキュメント
0 件のコメント:
コメントを投稿