はじめに
こんにちは、SHOUです。
「画像認識を始めたいけど、何から手をつければいいのか分からない…」そんな悩みを持つ方も多いのではないでしょうか。
この記事では、画像認識の代表的な手法である 畳み込みニューラルネットワーク(CNN) と、既に学習済みの強力なモデルを再利用する 事前学習(転移学習) を組み合わせて、手軽に高精度な画像分類を行う方法を紹介します。
Python と Keras を使って実際に動かしながら学べる内容になっており、初めて画像分類に挑戦する方にもおすすめです。
CNNとは何か?
CNN(Convolutional Neural Network) は、画像や映像のような空間的な情報を効率よく処理できる、ディープラーニングでよく使われるモデルです。
画像分類や物体検出といったタスクにおいて高い性能を発揮するCNNは、次のような層で構成されています:
- 畳み込み層:画像からエッジや形状などの特徴を抽出
- プーリング層:特徴量を圧縮し、計算効率を向上
- 全結合層:抽出した特徴を使って分類や予測を行う
これらの層が組み合わさることで、画像内のパターンを認識することが可能になります。
事前学習(転移学習)とは?
事前学習とは、すでに大規模なデータで学習されたモデルを活用し、自分の課題に合わせて再利用する方法です。これにより、ゼロから学習を行うよりも効率よく、かつ高精度なモデルを作成することができます。
主なメリット:
- 大量のデータを用意しなくても良い
- 学習時間を大幅に短縮できる
- 精度の高いモデルを素早く構築できる
代表的な事前学習済みモデルには、以下のようなものがあります:
- VGG16 / VGG19:シンプルで理解しやすい構造
- ResNet50:深いネットワーク構造でも学習が安定
- InceptionV3:計算効率と精度のバランスが良い
- MobileNetV2:軽量でモバイルデバイスでも動作可能
今回は、数字(0〜9)の画像分類というタスクに対して、軽量で高性能な MobileNetV2 を利用してみます。
軽量モデル MobileNetV2 について
CNN の応用として、実務でよく使われる軽量モデルに MobileNetV2 があります。 モバイル端末や組み込み環境でも高速に動くよう設計されており、 少ないパラメータで高い精度を出せるのが特徴です。
from tensorflow.keras.applications import MobileNetV2
model = MobileNetV2(weights='imagenet', include_top=True)
model.summary()
モデルサマリ
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/mobilenet_v2/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_224.h5 14536120/14536120 ━━━━━━━━━━━━━━━━━━━━ 0s 0us/step Model: "mobilenetv2_1.00_224" ┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ Connected to ┃ ┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩ │ input_layer │ (None, 224, 224, │ 0 │ - │ │ (InputLayer) │ 3) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ Conv1 (Conv2D) │ (None, 112, 112, │ 864 │ input_layer[0][0] │ │ │ 32) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ bn_Conv1 │ (None, 112, 112, │ 128 │ Conv1[0][0] │ │ (BatchNormalizatio… │ 32) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ Conv1_relu (ReLU) │ (None, 112, 112, │ 0 │ bn_Conv1[0][0] │ │ │ 32) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ expanded_conv_dept… │ (None, 112, 112, │ 288 │ Conv1_relu[0][0] │ │ (DepthwiseConv2D) │ 32) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ expanded_conv_dept… │ (None, 112, 112, │ 128 │ expanded_conv_de… │ │ (BatchNormalizatio… │ 32) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ expanded_conv_dept… │ (None, 112, 112, │ 0 │ expanded_conv_de… │ │ (ReLU) │ 32) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ expanded_conv_proj… │ (None, 112, 112, │ 512 │ expanded_conv_de… │ │ (Conv2D) │ 16) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ expanded_conv_proj… │ (None, 112, 112, │ 64 │ expanded_conv_pr… │ │ (BatchNormalizatio… │ 16) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_1_expand │ (None, 112, 112, │ 1,536 │ expanded_conv_pr… │ │ (Conv2D) │ 96) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_1_expand_BN │ (None, 112, 112, │ 384 │ block_1_expand[0… │ │ (BatchNormalizatio… │ 96) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_1_expand_relu │ (None, 112, 112, │ 0 │ block_1_expand_B… │ │ (ReLU) │ 96) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_1_pad │ (None, 113, 113, │ 0 │ block_1_expand_r… │ │ (ZeroPadding2D) │ 96) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_1_depthwise │ (None, 56, 56, │ 864 │ block_1_pad[0][0] │ │ (DepthwiseConv2D) │ 96) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_1_depthwise_… │ (None, 56, 56, │ 384 │ block_1_depthwis… │ │ (BatchNormalizatio… │ 96) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_1_depthwise_… │ (None, 56, 56, │ 0 │ block_1_depthwis… │ │ (ReLU) │ 96) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_1_project │ (None, 56, 56, │ 2,304 │ block_1_depthwis… │ │ (Conv2D) │ 24) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_1_project_BN │ (None, 56, 56, │ 96 │ block_1_project[… │ │ (BatchNormalizatio… │ 24) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_2_expand │ (None, 56, 56, │ 3,456 │ block_1_project_… │ │ (Conv2D) │ 144) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_2_expand_BN │ (None, 56, 56, │ 576 │ block_2_expand[0… │ │ (BatchNormalizatio… │ 144) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_2_expand_relu │ (None, 56, 56, │ 0 │ block_2_expand_B… │ │ (ReLU) │ 144) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_2_depthwise │ (None, 56, 56, │ 1,296 │ block_2_expand_r… │ │ (DepthwiseConv2D) │ 144) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_2_depthwise_… │ (None, 56, 56, │ 576 │ block_2_depthwis… │ │ (BatchNormalizatio… │ 144) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_2_depthwise_… │ (None, 56, 56, │ 0 │ block_2_depthwis… │ │ (ReLU) │ 144) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_2_project │ (None, 56, 56, │ 3,456 │ block_2_depthwis… │ │ (Conv2D) │ 24) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_2_project_BN │ (None, 56, 56, │ 96 │ block_2_project[… │ │ (BatchNormalizatio… │ 24) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_2_add (Add) │ (None, 56, 56, │ 0 │ block_1_project_… │ │ │ 24) │ │ block_2_project_… │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_3_expand │ (None, 56, 56, │ 3,456 │ block_2_add[0][0] │ │ (Conv2D) │ 144) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_3_expand_BN │ (None, 56, 56, │ 576 │ block_3_expand[0… │ │ (BatchNormalizatio… │ 144) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_3_expand_relu │ (None, 56, 56, │ 0 │ block_3_expand_B… │ │ (ReLU) │ 144) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_3_pad │ (None, 57, 57, │ 0 │ block_3_expand_r… │ │ (ZeroPadding2D) │ 144) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_3_depthwise │ (None, 28, 28, │ 1,296 │ block_3_pad[0][0] │ │ (DepthwiseConv2D) │ 144) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_3_depthwise_… │ (None, 28, 28, │ 576 │ block_3_depthwis… │ │ (BatchNormalizatio… │ 144) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_3_depthwise_… │ (None, 28, 28, │ 0 │ block_3_depthwis… │ │ (ReLU) │ 144) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_3_project │ (None, 28, 28, │ 4,608 │ block_3_depthwis… │ │ (Conv2D) │ 32) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_3_project_BN │ (None, 28, 28, │ 128 │ block_3_project[… │ │ (BatchNormalizatio… │ 32) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_4_expand │ (None, 28, 28, │ 6,144 │ block_3_project_… │ │ (Conv2D) │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_4_expand_BN │ (None, 28, 28, │ 768 │ block_4_expand[0… │ │ (BatchNormalizatio… │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_4_expand_relu │ (None, 28, 28, │ 0 │ block_4_expand_B… │ │ (ReLU) │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_4_depthwise │ (None, 28, 28, │ 1,728 │ block_4_expand_r… │ │ (DepthwiseConv2D) │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_4_depthwise_… │ (None, 28, 28, │ 768 │ block_4_depthwis… │ │ (BatchNormalizatio… │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_4_depthwise_… │ (None, 28, 28, │ 0 │ block_4_depthwis… │ │ (ReLU) │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_4_project │ (None, 28, 28, │ 6,144 │ block_4_depthwis… │ │ (Conv2D) │ 32) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_4_project_BN │ (None, 28, 28, │ 128 │ block_4_project[… │ │ (BatchNormalizatio… │ 32) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_4_add (Add) │ (None, 28, 28, │ 0 │ block_3_project_… │ │ │ 32) │ │ block_4_project_… │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_5_expand │ (None, 28, 28, │ 6,144 │ block_4_add[0][0] │ │ (Conv2D) │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_5_expand_BN │ (None, 28, 28, │ 768 │ block_5_expand[0… │ │ (BatchNormalizatio… │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_5_expand_relu │ (None, 28, 28, │ 0 │ block_5_expand_B… │ │ (ReLU) │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_5_depthwise │ (None, 28, 28, │ 1,728 │ block_5_expand_r… │ │ (DepthwiseConv2D) │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_5_depthwise_… │ (None, 28, 28, │ 768 │ block_5_depthwis… │ │ (BatchNormalizatio… │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_5_depthwise_… │ (None, 28, 28, │ 0 │ block_5_depthwis… │ │ (ReLU) │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_5_project │ (None, 28, 28, │ 6,144 │ block_5_depthwis… │ │ (Conv2D) │ 32) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_5_project_BN │ (None, 28, 28, │ 128 │ block_5_project[… │ │ (BatchNormalizatio… │ 32) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_5_add (Add) │ (None, 28, 28, │ 0 │ block_4_add[0][0… │ │ │ 32) │ │ block_5_project_… │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_6_expand │ (None, 28, 28, │ 6,144 │ block_5_add[0][0] │ │ (Conv2D) │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_6_expand_BN │ (None, 28, 28, │ 768 │ block_6_expand[0… │ │ (BatchNormalizatio… │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_6_expand_relu │ (None, 28, 28, │ 0 │ block_6_expand_B… │ │ (ReLU) │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_6_pad │ (None, 29, 29, │ 0 │ block_6_expand_r… │ │ (ZeroPadding2D) │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_6_depthwise │ (None, 14, 14, │ 1,728 │ block_6_pad[0][0] │ │ (DepthwiseConv2D) │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_6_depthwise_… │ (None, 14, 14, │ 768 │ block_6_depthwis… │ │ (BatchNormalizatio… │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_6_depthwise_… │ (None, 14, 14, │ 0 │ block_6_depthwis… │ │ (ReLU) │ 192) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_6_project │ (None, 14, 14, │ 12,288 │ block_6_depthwis… │ │ (Conv2D) │ 64) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_6_project_BN │ (None, 14, 14, │ 256 │ block_6_project[… │ │ (BatchNormalizatio… │ 64) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_7_expand │ (None, 14, 14, │ 24,576 │ block_6_project_… │ │ (Conv2D) │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_7_expand_BN │ (None, 14, 14, │ 1,536 │ block_7_expand[0… │ │ (BatchNormalizatio… │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_7_expand_relu │ (None, 14, 14, │ 0 │ block_7_expand_B… │ │ (ReLU) │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_7_depthwise │ (None, 14, 14, │ 3,456 │ block_7_expand_r… │ │ (DepthwiseConv2D) │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_7_depthwise_… │ (None, 14, 14, │ 1,536 │ block_7_depthwis… │ │ (BatchNormalizatio… │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_7_depthwise_… │ (None, 14, 14, │ 0 │ block_7_depthwis… │ │ (ReLU) │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_7_project │ (None, 14, 14, │ 24,576 │ block_7_depthwis… │ │ (Conv2D) │ 64) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_7_project_BN │ (None, 14, 14, │ 256 │ block_7_project[… │ │ (BatchNormalizatio… │ 64) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_7_add (Add) │ (None, 14, 14, │ 0 │ block_6_project_… │ │ │ 64) │ │ block_7_project_… │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_8_expand │ (None, 14, 14, │ 24,576 │ block_7_add[0][0] │ │ (Conv2D) │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_8_expand_BN │ (None, 14, 14, │ 1,536 │ block_8_expand[0… │ │ (BatchNormalizatio… │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_8_expand_relu │ (None, 14, 14, │ 0 │ block_8_expand_B… │ │ (ReLU) │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_8_depthwise │ (None, 14, 14, │ 3,456 │ block_8_expand_r… │ │ (DepthwiseConv2D) │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_8_depthwise_… │ (None, 14, 14, │ 1,536 │ block_8_depthwis… │ │ (BatchNormalizatio… │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_8_depthwise_… │ (None, 14, 14, │ 0 │ block_8_depthwis… │ │ (ReLU) │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_8_project │ (None, 14, 14, │ 24,576 │ block_8_depthwis… │ │ (Conv2D) │ 64) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_8_project_BN │ (None, 14, 14, │ 256 │ block_8_project[… │ │ (BatchNormalizatio… │ 64) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_8_add (Add) │ (None, 14, 14, │ 0 │ block_7_add[0][0… │ │ │ 64) │ │ block_8_project_… │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_9_expand │ (None, 14, 14, │ 24,576 │ block_8_add[0][0] │ │ (Conv2D) │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_9_expand_BN │ (None, 14, 14, │ 1,536 │ block_9_expand[0… │ │ (BatchNormalizatio… │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_9_expand_relu │ (None, 14, 14, │ 0 │ block_9_expand_B… │ │ (ReLU) │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_9_depthwise │ (None, 14, 14, │ 3,456 │ block_9_expand_r… │ │ (DepthwiseConv2D) │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_9_depthwise_… │ (None, 14, 14, │ 1,536 │ block_9_depthwis… │ │ (BatchNormalizatio… │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_9_depthwise_… │ (None, 14, 14, │ 0 │ block_9_depthwis… │ │ (ReLU) │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_9_project │ (None, 14, 14, │ 24,576 │ block_9_depthwis… │ │ (Conv2D) │ 64) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_9_project_BN │ (None, 14, 14, │ 256 │ block_9_project[… │ │ (BatchNormalizatio… │ 64) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_9_add (Add) │ (None, 14, 14, │ 0 │ block_8_add[0][0… │ │ │ 64) │ │ block_9_project_… │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_10_expand │ (None, 14, 14, │ 24,576 │ block_9_add[0][0] │ │ (Conv2D) │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_10_expand_BN │ (None, 14, 14, │ 1,536 │ block_10_expand[… │ │ (BatchNormalizatio… │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_10_expand_re… │ (None, 14, 14, │ 0 │ block_10_expand_… │ │ (ReLU) │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_10_depthwise │ (None, 14, 14, │ 3,456 │ block_10_expand_… │ │ (DepthwiseConv2D) │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_10_depthwise… │ (None, 14, 14, │ 1,536 │ block_10_depthwi… │ │ (BatchNormalizatio… │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_10_depthwise… │ (None, 14, 14, │ 0 │ block_10_depthwi… │ │ (ReLU) │ 384) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_10_project │ (None, 14, 14, │ 36,864 │ block_10_depthwi… │ │ (Conv2D) │ 96) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_10_project_BN │ (None, 14, 14, │ 384 │ block_10_project… │ │ (BatchNormalizatio… │ 96) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_11_expand │ (None, 14, 14, │ 55,296 │ block_10_project… │ │ (Conv2D) │ 576) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_11_expand_BN │ (None, 14, 14, │ 2,304 │ block_11_expand[… │ │ (BatchNormalizatio… │ 576) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_11_expand_re… │ (None, 14, 14, │ 0 │ block_11_expand_… │ │ (ReLU) │ 576) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_11_depthwise │ (None, 14, 14, │ 5,184 │ block_11_expand_… │ │ (DepthwiseConv2D) │ 576) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_11_depthwise… │ (None, 14, 14, │ 2,304 │ block_11_depthwi… │ │ (BatchNormalizatio… │ 576) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_11_depthwise… │ (None, 14, 14, │ 0 │ block_11_depthwi… │ │ (ReLU) │ 576) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_11_project │ (None, 14, 14, │ 55,296 │ block_11_depthwi… │ │ (Conv2D) │ 96) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_11_project_BN │ (None, 14, 14, │ 384 │ block_11_project… │ │ (BatchNormalizatio… │ 96) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_11_add (Add) │ (None, 14, 14, │ 0 │ block_10_project… │ │ │ 96) │ │ block_11_project… │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_12_expand │ (None, 14, 14, │ 55,296 │ block_11_add[0][… │ │ (Conv2D) │ 576) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_12_expand_BN │ (None, 14, 14, │ 2,304 │ block_12_expand[… │ │ (BatchNormalizatio… │ 576) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_12_expand_re… │ (None, 14, 14, │ 0 │ block_12_expand_… │ │ (ReLU) │ 576) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_12_depthwise │ (None, 14, 14, │ 5,184 │ block_12_expand_… │ │ (DepthwiseConv2D) │ 576) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_12_depthwise… │ (None, 14, 14, │ 2,304 │ block_12_depthwi… │ │ (BatchNormalizatio… │ 576) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_12_depthwise… │ (None, 14, 14, │ 0 │ block_12_depthwi… │ │ (ReLU) │ 576) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_12_project │ (None, 14, 14, │ 55,296 │ block_12_depthwi… │ │ (Conv2D) │ 96) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_12_project_BN │ (None, 14, 14, │ 384 │ block_12_project… │ │ (BatchNormalizatio… │ 96) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_12_add (Add) │ (None, 14, 14, │ 0 │ block_11_add[0][… │ │ │ 96) │ │ block_12_project… │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_13_expand │ (None, 14, 14, │ 55,296 │ block_12_add[0][… │ │ (Conv2D) │ 576) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_13_expand_BN │ (None, 14, 14, │ 2,304 │ block_13_expand[… │ │ (BatchNormalizatio… │ 576) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_13_expand_re… │ (None, 14, 14, │ 0 │ block_13_expand_… │ │ (ReLU) │ 576) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_13_pad │ (None, 15, 15, │ 0 │ block_13_expand_… │ │ (ZeroPadding2D) │ 576) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_13_depthwise │ (None, 7, 7, 576) │ 5,184 │ block_13_pad[0][… │ │ (DepthwiseConv2D) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_13_depthwise… │ (None, 7, 7, 576) │ 2,304 │ block_13_depthwi… │ │ (BatchNormalizatio… │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_13_depthwise… │ (None, 7, 7, 576) │ 0 │ block_13_depthwi… │ │ (ReLU) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_13_project │ (None, 7, 7, 160) │ 92,160 │ block_13_depthwi… │ │ (Conv2D) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_13_project_BN │ (None, 7, 7, 160) │ 640 │ block_13_project… │ │ (BatchNormalizatio… │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_14_expand │ (None, 7, 7, 960) │ 153,600 │ block_13_project… │ │ (Conv2D) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_14_expand_BN │ (None, 7, 7, 960) │ 3,840 │ block_14_expand[… │ │ (BatchNormalizatio… │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_14_expand_re… │ (None, 7, 7, 960) │ 0 │ block_14_expand_… │ │ (ReLU) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_14_depthwise │ (None, 7, 7, 960) │ 8,640 │ block_14_expand_… │ │ (DepthwiseConv2D) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_14_depthwise… │ (None, 7, 7, 960) │ 3,840 │ block_14_depthwi… │ │ (BatchNormalizatio… │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_14_depthwise… │ (None, 7, 7, 960) │ 0 │ block_14_depthwi… │ │ (ReLU) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_14_project │ (None, 7, 7, 160) │ 153,600 │ block_14_depthwi… │ │ (Conv2D) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_14_project_BN │ (None, 7, 7, 160) │ 640 │ block_14_project… │ │ (BatchNormalizatio… │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_14_add (Add) │ (None, 7, 7, 160) │ 0 │ block_13_project… │ │ │ │ │ block_14_project… │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_15_expand │ (None, 7, 7, 960) │ 153,600 │ block_14_add[0][… │ │ (Conv2D) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_15_expand_BN │ (None, 7, 7, 960) │ 3,840 │ block_15_expand[… │ │ (BatchNormalizatio… │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_15_expand_re… │ (None, 7, 7, 960) │ 0 │ block_15_expand_… │ │ (ReLU) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_15_depthwise │ (None, 7, 7, 960) │ 8,640 │ block_15_expand_… │ │ (DepthwiseConv2D) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_15_depthwise… │ (None, 7, 7, 960) │ 3,840 │ block_15_depthwi… │ │ (BatchNormalizatio… │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_15_depthwise… │ (None, 7, 7, 960) │ 0 │ block_15_depthwi… │ │ (ReLU) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_15_project │ (None, 7, 7, 160) │ 153,600 │ block_15_depthwi… │ │ (Conv2D) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_15_project_BN │ (None, 7, 7, 160) │ 640 │ block_15_project… │ │ (BatchNormalizatio… │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_15_add (Add) │ (None, 7, 7, 160) │ 0 │ block_14_add[0][… │ │ │ │ │ block_15_project… │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_16_expand │ (None, 7, 7, 960) │ 153,600 │ block_15_add[0][… │ │ (Conv2D) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_16_expand_BN │ (None, 7, 7, 960) │ 3,840 │ block_16_expand[… │ │ (BatchNormalizatio… │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_16_expand_re… │ (None, 7, 7, 960) │ 0 │ block_16_expand_… │ │ (ReLU) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_16_depthwise │ (None, 7, 7, 960) │ 8,640 │ block_16_expand_… │ │ (DepthwiseConv2D) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_16_depthwise… │ (None, 7, 7, 960) │ 3,840 │ block_16_depthwi… │ │ (BatchNormalizatio… │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_16_depthwise… │ (None, 7, 7, 960) │ 0 │ block_16_depthwi… │ │ (ReLU) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_16_project │ (None, 7, 7, 320) │ 307,200 │ block_16_depthwi… │ │ (Conv2D) │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ block_16_project_BN │ (None, 7, 7, 320) │ 1,280 │ block_16_project… │ │ (BatchNormalizatio… │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ Conv_1 (Conv2D) │ (None, 7, 7, │ 409,600 │ block_16_project… │ │ │ 1280) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ Conv_1_bn │ (None, 7, 7, │ 5,120 │ Conv_1[0][0] │ │ (BatchNormalizatio… │ 1280) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ out_relu (ReLU) │ (None, 7, 7, │ 0 │ Conv_1_bn[0][0] │ │ │ 1280) │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ global_average_poo… │ (None, 1280) │ 0 │ out_relu[0][0] │ │ (GlobalAveragePool… │ │ │ │ ├─────────────────────┼───────────────────┼────────────┼───────────────────┤ │ predictions (Dense) │ (None, 1000) │ 1,281,000 │ global_average_p… │ └─────────────────────┴───────────────────┴────────────┴───────────────────┘ Total params: 3,538,984 (13.50 MB) Trainable params: 3,504,872 (13.37 MB) Non-trainable params: 34,112 (133.25 KB)画像分類の基礎を学んだ後、より実践寄りのモデルとして挑戦しやすいので、 Kerasユーザーに人気のアーキテクチャです。
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はシンプルなので、ぜひこの記事をベースに、自分だけの画像分類プロジェクトに挑戦してみてください!

0 件のコメント:
コメントを投稿