プロジェクト概要
前提条件
- Pythonプログラミングの基本的な知識(変数、関数、クラスの概念)
- VSCodeエディタの基本操作ができること(記事はVSCodeで説明しているが、他のエディタでも実践可能)
- ターミナル(コマンドライン)の基本的な使用経験
- 中学校レベルの数学知識(確率、統計の基本概念)
開発環境
- Python 3.8以上
- VSCode(Visual Studio Code)
- Jupyter Notebook
プロジェクト内容
このプロジェクトでは、TensorFlowのKerasを使用して畳み込みニューラルネットワーク(CNN)による画像分類モデルを構築します。初心者でも段階的に学習できるよう、開発環境の準備から最終的なモデル評価まで、2つのステップに分けて実装していきます。
プロジェクトの核となるのは、コンピュータビジョン技術を活用した二値分類問題です。TensorFlow Datasetsから取得した高品質な動物画像データを使用して、猫と犬を区別するCNNモデルを構築することで、実際の画像認識システムでも活用できる実用的な深層学習モデルを開発します。
データ拡張技術を用いたオーバーフィッティング対策や、畳み込み層とプーリング層を組み合わせた効率的な特徴抽出手法も学習できます。画像認識の基礎から実践的な応用まで、CNNの仕組みを理解しながら手を動かして学習することで、ディープラーニングの実装スキルを身につけることができます。
実装内容
- Kerasによる畳み込みニューラルネットワーク構築
- 画像データの前処理とデータ拡張(シアー変換、ズーム変換、水平反転)
- TensorFlow Datasetsを使用した大規模画像データセットの取得
- 畳み込み層とプーリング層を組み合わせた特徴抽出
- バイナリクロスエントロピー損失とAdam最適化の実装
- Jupyter Notebookを使用した対話型開発環境
得られるスキル
- TensorFlow(ディープラーニングフレームワークの基本操作)
- CNN構築技術(畳み込みニューラルネットワーク設計)
- 画像データ前処理技術(正規化、データ拡張、リサイズ)
- コンピュータビジョンの基礎知識(特徴抽出、パターン認識)
得られる経験
- 20,000枚規模の画像データセット処理と分析
- 畳み込みニューラルネットワークの構築経験
- コンピュータビジョン技術の実践的適用
- Jupyter Notebookでの対話型開発環境の活用
得られる成果物
- 実行可能なCNN画像分類モデル
- 完全なJupyter Notebookソースコード
このプロジェクトから応用できること
- 商品画像の自動分類システムをCNNで構築し在庫管理の効率化を実現
- 不良品検出システムをCNNで開発し製造業の品質管理を支援
- 医療画像診断支援ツールをCNNで作成し診断精度の向上に貢献
- セキュリティ画像認識システムをCNNで実装し監視業務の自動化を実現
Step1:プロジェクト構成の作成と環境構築
このステップでは、動物画像分類システム用CNNプロジェクトを始めるためのプロジェクト構成の作成と環境構築を行います。機械学習プロジェクトを始める前の準備段階として、とても重要な作業になります。
※仮想環境の詳細な説明については、下記記事にPython仮想環境の構築について詳細を記載しているためご参照ください。
プロジェクトフォルダの作成
まず最初に、プロジェクト専用のフォルダを作成しましょう。
Windows・macOS共通
- デスクトップや任意の場所に新しいフォルダを作成します
- フォルダ名を「animal_image_classifier」に設定します
このフォルダが、今回のプロジェクト全体を管理するルートディレクトリとなります。
VSCodeでプロジェクトを開く
作成したプロジェクトフォルダをVSCodeで開きましょう。
- VSCodeを起動します
- 「ファイル」メニューから「フォルダーを開く」を選択します
- 先ほど作成した「animal_image_classifier」フォルダを選択します
- 「フォルダーの選択」ボタンをクリックします
VSCodeの左側にエクスプローラーパネルが表示され、現在のプロジェクトフォルダの中身が確認できるようになります。
仮想環境の作成とアクティベート
Python開発において仮想環境は非常に重要です。仮想環境とは、プロジェクトごとに独立したPython実行環境を作成する仕組みのことです。これにより、プロジェクト間でのライブラリの競合を防ぎ、クリーンな開発環境を維持できます。
詳しくは下記記事をご参照ください。
Windows環境での仮想環境作成
- VSCodeの上部メニューから「ターミナル」→「新しいターミナル」を選択
- 以下のコマンドで仮想環境を作成します
python -m venv dl_env
- 仮想環境をアクティベートします
dl_env\Scripts\activate
macOS環境での仮想環境作成
- VSCodeの上部メニューから「ターミナル」→「新しいターミナル」を選択
- 以下のコマンドで仮想環境を作成します
python3 -m venv dl_env
- 仮想環境をアクティベートします
source dl_env/bin/activate
重要なポイント
- 仮想環境名は「dl_env」として作成
- ターミナルの行頭に「(dl_env)」と表示されることを確認
- この表示があることで、仮想環境が正常にアクティベートされていることがわかります
プロジェクトファイル構造の作成
次に、今回の動物画像分類プロジェクトに必要なフォルダとファイルを作成していきます。VSCodeのエクスプローラーパネルを使用してファイルを作成しましょう。
Notebook用フォルダの作成
- VSCodeの左側エクスプローラーパネルで「新しいフォルダ」アイコン(フォルダの絵とプラスのマークがあるアイコン)をクリック
- フォルダ名を「notebooks」として作成
この「notebooks」フォルダは、Jupyter Notebookファイルを格納するための専用フォルダです。プロジェクトの整理整頓において重要な役割を果たします。
メインノートブックファイルの作成
- 先ほど作成した「notebooks」フォルダをクリックして選択
- 「新しいファイル」アイコン(ファイルの絵とプラスのマークがあるアイコン)をクリック
- ファイル名を「animal_classifier.ipynb」として作成
この「animal_classifier.ipynb」ファイルが、今回のCNN実装を行うメインファイルとなります。.ipynb拡張子はJupyter Notebookファイルであることを示しており、コードと説明を同時に記述できる便利な形式です。
依存関係管理ファイルの作成
- プロジェクトのルートフォルダ(animal_image_classifier)をクリックして選択
- 「新しいファイル」アイコンをクリック
- ファイル名を「requirements.txt」として作成
requirements.txtファイルは、Pythonプロジェクトで使用する外部ライブラリとそのバージョンを記録するための重要なファイルです。これにより、他の開発者や本番環境でも同じライブラリ環境を再現できます。
requirements.txtの内容設定
作成した「requirements.txt」ファイルをVSCodeで開き、以下の内容を記述してください。
numpy==1.26.4
tensorflow==2.16.2
tensorflow-datasets==4.9.4
scikit-learn==1.7.0
matplotlib==3.10.3
pillow==11.2.1
jupyter==1.1.1
今回のプロジェクトでは、以下のライブラリを使用します
- numpy – 数値計算やデータ操作に必要な基本ライブラリです
- tensorflow – CNNの構築と学習に使用するディープラーニングフレームワークです
- tensorflow-datasets – 公開データセットを簡単にダウンロード・利用できるライブラリです
- scikit-learn – モデル評価指標の計算に使用する機械学習ライブラリです
- matplotlib – グラフや画像の表示に使用する可視化ライブラリです
- pillow – 画像ファイルの読み込みと処理に使用するライブラリです
- jupyter – Jupyter Notebookを実行するために必要なライブラリです
ライブラリのインストール
仮想環境がアクティベートされている状態で、VSCodeのターミナルから以下のコマンドを実行します。
pip install -r requirements.txt
このコマンドにより、requirements.txtに記載されたすべてのライブラリがインストールされます。TensorFlowは容量が大きいため、インストール処理には数分程度かかる場合がありますので、しばらくお待ちください。
インストールが完了すると、ターミナルに「Successfully installed tensorflow-2.16.2 numpy-1.26.4…」のようなメッセージが表示されます。
画像データセットのダウンロードとフォルダ作成
次に、今回のプロジェクトで使用する動物画像データセットをダウンロードし、必要なフォルダ構造を作成します。
Jupyter Notebookの起動
- VSCodeのターミナルで以下のコマンドを実行します
jupyter notebook
- ブラウザが自動的に開き、Jupyter Notebookのインターフェースが表示されます
- 「notebooks」フォルダをクリックし、「animal_classifier.ipynb」ファイルを開きます
データセットダウンロード用コードの実装
作成したNotebookファイルに以下のコードを入力し、実行してください。このコードはTensorFlow Datasetsから猫と犬の画像データセットをダウンロードし、必要なフォルダ構造を自動的に作成します。
import warnings
import os
import tensorflow as tf
import tensorflow_datasets as tfds
import numpy as np
from PIL import Image
# 警告メッセージを非表示にする
warnings.filterwarnings('ignore')
# プロジェクトのベースディレクトリを設定
base_dir = os.path.dirname(os.getcwd())
dataset_dir = os.path.join(base_dir, 'image_datasets')
# データセット用フォルダ構造を作成
folders = [
'training_images/cats',
'training_images/dogs',
'test_images/cats',
'test_images/dogs',
'prediction_samples'
]
for folder in folders:
folder_path = os.path.join(dataset_dir, folder)
os.makedirs(folder_path, exist_ok=True)
print(f"フォルダを作成しました: {folder_path}")
# TensorFlow Datasetsから猫と犬のデータセットをダウンロード
print("猫と犬の画像データセットをダウンロード中...")
dataset, info = tfds.load(
'cats_vs_dogs',
with_info=True,
as_supervised=True,
data_dir=os.path.join(base_dir, 'tensorflow_datasets')
)
# データセット情報を表示
print(f"データセット名: {info.name}")
print(f"総画像数: {info.splits['train'].num_examples}")
print(f"クラス数: {info.features['label'].num_classes}")
print(f"クラス名: {info.features['label'].names}")
# データセットを取得
train_dataset = dataset['train']
def save_images_from_dataset(dataset, save_path, class_name, target_class, max_count):
"""データセットから指定されたクラスの画像を保存する関数"""
count = 0
for image, label in dataset:
if count >= max_count:
break
if label.numpy() == target_class:
# 画像を0-255の範囲に変換
image_array = tf.cast(image, tf.uint8).numpy()
# PIL画像として保存
pil_image = Image.fromarray(image_array)
# ファイル名を生成
filename = f"{class_name}_{count+1:04d}.jpg"
filepath = os.path.join(save_path, filename)
# 画像を保存
pil_image.save(filepath)
count += 1
print(f"{class_name}の画像を{count}枚保存しました")
return count
# データセットをリストに変換
dataset_list = list(train_dataset.take(25000))
np.random.shuffle(dataset_list)
# 猫の画像を分離(ラベル0が猫)
cat_images = [(img, lbl) for img, lbl in dataset_list if lbl.numpy() == 0]
# 犬の画像を分離(ラベル1が犬)
dog_images = [(img, lbl) for img, lbl in dataset_list if lbl.numpy() == 1]
print(f"利用可能な猫の画像: {len(cat_images)}枚")
print(f"利用可能な犬の画像: {len(dog_images)}枚")
# 訓練データ猫画像を保存(8000枚)
train_cats_path = os.path.join(dataset_dir, 'training_images', 'cats')
saved_count = 0
for i, (image, label) in enumerate(cat_images[:8000]):
image_array = tf.cast(image, tf.uint8).numpy()
pil_image = Image.fromarray(image_array)
filename = f"cat_{saved_count+1:04d}.jpg"
filepath = os.path.join(train_cats_path, filename)
pil_image.save(filepath)
saved_count += 1
print(f"訓練データ猫画像を{saved_count}枚保存しました")
# 訓練データ犬画像を保存(8000枚)
train_dogs_path = os.path.join(dataset_dir, 'training_images', 'dogs')
saved_count = 0
for i, (image, label) in enumerate(dog_images[:8000]):
image_array = tf.cast(image, tf.uint8).numpy()
pil_image = Image.fromarray(image_array)
filename = f"dog_{saved_count+1:04d}.jpg"
filepath = os.path.join(train_dogs_path, filename)
pil_image.save(filepath)
saved_count += 1
print(f"訓練データ犬画像を{saved_count}枚保存しました")
# テストデータ猫画像を保存(2000枚)
test_cats_path = os.path.join(dataset_dir, 'test_images', 'cats')
saved_count = 0
for i, (image, label) in enumerate(cat_images[8000:10000]):
image_array = tf.cast(image, tf.uint8).numpy()
pil_image = Image.fromarray(image_array)
filename = f"cat_{saved_count+1:04d}.jpg"
filepath = os.path.join(test_cats_path, filename)
pil_image.save(filepath)
saved_count += 1
print(f"テストデータ猫画像を{saved_count}枚保存しました")
# テストデータ犬画像を保存(2000枚)
test_dogs_path = os.path.join(dataset_dir, 'test_images', 'dogs')
saved_count = 0
for i, (image, label) in enumerate(dog_images[8000:10000]):
image_array = tf.cast(image, tf.uint8).numpy()
pil_image = Image.fromarray(image_array)
filename = f"dog_{saved_count+1:04d}.jpg"
filepath = os.path.join(test_dogs_path, filename)
pil_image.save(filepath)
saved_count += 1
print(f"テストデータ犬画像を{saved_count}枚保存しました")
# 予測用サンプル画像を作成
prediction_samples_dir = os.path.join(dataset_dir, 'prediction_samples')
# 猫のサンプル画像
if len(cat_images) > 10000:
sample_cat_image, _ = cat_images[10000]
cat_array = tf.cast(sample_cat_image, tf.uint8).numpy()
cat_sample_path = os.path.join(prediction_samples_dir, 'sample_cat.jpg')
Image.fromarray(cat_array).save(cat_sample_path)
print(f"猫のサンプル画像を保存しました: {cat_sample_path}")
# 犬のサンプル画像
if len(dog_images) > 10000:
sample_dog_image, _ = dog_images[10000]
dog_array = tf.cast(sample_dog_image, tf.uint8).numpy()
dog_sample_path = os.path.join(prediction_samples_dir, 'sample_dog.jpg')
Image.fromarray(dog_array).save(dog_sample_path)
print(f"犬のサンプル画像を保存しました: {dog_sample_path}")
print("\nデータセットの準備が完了しました!")
print(f"訓練データ: 猫8000枚、犬8000枚")
print(f"テストデータ: 猫2000枚、犬2000枚")
print(f"予測用サンプル: 各クラス1枚ずつ")
このコードを実行すると、以下の処理が自動的に行われます
- TensorFlow Datasetsからの猫と犬のデータセットの自動ダウンロード
- 必要なフォルダ構造の自動作成
- 訓練データとして猫と犬の画像を各8000枚ずつ保存
- テストデータとして猫と犬の画像を各2000枚ずつ保存
- 予測用のサンプル画像の準備
- 警告メッセージの非表示化
TensorFlow Datasetsは、機械学習の研究でよく使われる公開データセットを簡単に利用できるライブラリです。cats_vs_dogsデータセットは、Microsoftが公開している高品質な猫と犬の画像データセットで、実際の研究や商用プロジェクトでも広く使用されています。
プロジェクト構造の確認
この時点で、VSCodeのエクスプローラーパネルには以下のような構造が表示されているはずです。
animal_image_classifier/
├── dl_env/ # 仮想環境フォルダ
├── tensorflow_datasets/ # TensorFlow Datasetsキャッシュフォルダ
├── image_datasets/
│ ├── training_images/
│ │ ├── cats/ # 猫の訓練データ(8000枚)
│ │ └── dogs/ # 犬の訓練データ(8000枚)
│ ├── test_images/
│ │ ├── cats/ # 猫のテストデータ(2000枚)
│ │ └── dogs/ # 犬のテストデータ(2000枚)
│ └── prediction_samples/ # 予測用サンプル画像
├── notebooks/
│ └── animal_classifier.ipynb # メインノートブックファイル
└── requirements.txt # 依存関係管理ファイル
この基本構造が、今回の動物画像分類CNNプロジェクトの基盤となります。画像データも含めて、本格的な機械学習プロジェクトの開発環境が整いました。
仮想環境が正常にアクティベートされていることを再度確認し(ターミナルに「(dl_env)」が表示されている状態)、次のステップに進む準備を整えましょう。
Step2:画像データ前処理とCNNモデルの構築・学習
このステップでは、動物画像分類のための畳み込みニューラルネットワーク(CNN)モデルを実装していきます。まずは必要なライブラリをインポートし、画像データの前処理を行った後、畳み込みニューラルネットワーク(CNN)を構築・学習させていきます。TensorFlowを使用してディープラーニングモデルを段階的に作成していく過程を詳しく解説します。
必要ライブラリのインポート
最初に、今回のプロジェクトで使用する重要なライブラリをインポートしましょう。VSCodeで「notebooks/animal_classifier.ipynb」ファイルを開き、新しいセルを作成して以下のコードを入力してください。
# ディープラーニングフレームワーク
import tensorflow as tf
# 画像データ前処理のためのライブラリ
from tensorflow.keras.preprocessing.image import ImageDataGenerator
このセルを実行した後、TensorFlowのバージョンを確認しましょう。
# TensorFlowのバージョンを確認
tf.__version__
これらのライブラリは以下の役割を果たします。TensorFlowは今回のメインとなるディープラーニングフレームワークで、CNNの構築と学習に使用します。ImageDataGeneratorは画像データの前処理とデータ拡張(Data Augmentation)を行うための専用クラスです。データ拡張は機械学習において非常に重要な技術で、限られた画像データから人工的に新しい画像を生成することで、モデルの汎化性能を向上させます。
データ前処理
画像データの前処理は、CNNにおいて非常に重要なステップです。生の画像データをそのままCNNモデルに投入しても、良い結果を得ることはできません。画像を適切な形式に変換し、モデルが効率的に学習できる状態に整える必要があります。特に画像分類タスクでは、オーバーフィッティング(過学習)を防ぐための前処理が重要になります。
訓練データの前処理
まず、訓練データに対して前処理を適用します。訓練データにはデータ拡張を含む複数の変換処理を適用して、モデルの汎化性能を向上させます。
# 訓練データの前処理設定を作成
train_datagen = ImageDataGenerator(
rescale=1./255, # ピクセル値を0-1の範囲に正規化
shear_range=0.2, # シアー変換(画像の歪み)を最大20%適用
zoom_range=0.2, # ズーム変換を最大20%適用
horizontal_flip=True # 水平反転をランダムに適用
)
# 訓練データを読み込み、前処理を適用
training_set = train_datagen.flow_from_directory(
'../image_datasets/training_images', # 訓練データが格納されているディレクトリ
target_size=(64, 64), # 画像サイズを64x64ピクセルにリサイズ
batch_size=32, # 1回に処理する画像数を32枚に設定
class_mode='binary' # 二値分類(猫と犬)を指定
)
出力:Found 16000 images belonging to 2 classes.
このコードで実行される各パラメータについて詳しく説明します。
rescale=1./255は、特徴量スケーリングを行う設定です。画像のピクセル値は通常0から255の範囲にありますが、これを255で割ることで0から1の範囲に変換します。ニューラルネットワークは0から1の範囲の値で効率的に学習できるため、この正規化は必須の処理です。これにより、すべてのピクセル値が同じスケールになり、CNNが効率的に学習できるようになります。
shear_range=0.2は、シアー変換を適用する設定です。シアー変換は画像を斜めに引き伸ばす幾何学的変換で、同じ物体でも異なる角度から見たような効果を生み出します。0.2という値は、最大で20%の範囲でシアー変換を適用することを意味します。これにより、様々な角度から撮影された動物の画像を人工的に生成できます。
zoom_range=0.2は、ズーム変換を適用する設定です。画像を拡大または縮小することで、同じ物体が異なる距離から撮影されたような効果を生み出します。0.2という値は、最大で20%の範囲でズーム変換を適用することを意味します。これにより、遠くの動物や近くの動物など、様々な距離感の画像を生成できます。
horizontal_flip=Trueは、水平反転を適用する設定です。画像を左右に反転させることで、左向きの動物と右向きの動物のような自然なバリエーションを作り出します。これらの変換により、訓練データのバリエーションが大幅に増加し、オーバーフィッティングを防ぐことができます。
flow_from_directoryメソッドについても詳しく説明します。target_size=(64, 64)
は、すべての画像を64×64ピクセルにリサイズすることを指定します。CNNでは入力サイズを統一する必要があるため、この設定は必須です。batch_size=32は、一度に処理する画像の枚数を指定します。32は一般的に推奨される値で、メモリ効率と学習効率のバランスが良い値です。class_mode='binary'
は、二値分類(猫と犬の2クラス)を行うことを指定します。
テストデータの前処理
次に、テストデータに対して前処理を適用します。テストデータにはデータ拡張は適用せず、正規化のみを行います。これは、テストデータが実際の予測環境における新しいデータを模擬するためです。
# テストデータの前処理設定を作成(データ拡張なし)
test_datagen = ImageDataGenerator(rescale=1./255) # 正規化のみ適用
# テストデータを読み込み、前処理を適用
test_set = test_datagen.flow_from_directory(
'../image_datasets/test_images', # テストデータが格納されているディレクトリ
target_size=(64, 64), # 画像サイズを64x64ピクセルにリサイズ
batch_size=32, # 1回に処理する画像数を32枚に設定
class_mode='binary' # 二値分類(猫と犬)を指定
)
出力:Found 4000 images belonging to 2 classes.
テストデータでは、rescale=1./255のみを適用し、シアー変換、ズーム変換、水平反転は適用しません。これは重要な原則で、テストデータは本番環境での新しいデータを模擬する必要があるため、人工的な変換を加えてはいけません。テストデータに変換を加えてしまうと、モデルの真の性能を正確に評価できなくなってしまいます。
CNNの構築
いよいよ畳み込みニューラルネットワーク(CNN)を構築していきます。CNNは、画像認識タスクに特化した深層学習モデルです。畳み込み層(Convolutional Layer)とプーリング層(Pooling Layer)を組み合わせることで、画像の特徴を階層的に抽出し、高精度な分類を実現します。人間の視覚野の働きを模倣した構造により、従来の機械学習手法では困難だった複雑な画像パターンの認識が可能になります。
CNNの初期化
TensorFlowのKerasを使用してCNNを初期化します。Sequentialモデルは、層を順番に積み重ねる最もシンプルなモデル構築方法です。
# Sequentialモデルで畳み込みニューラルネットワークを初期化
cnn = tf.keras.models.Sequential()
Sequentialモデルは、入力層から出力層まで一方向に層が接続された線形なネットワーク構造を作成します。今回のようなフィードフォワード(順伝播型)ニューラルネットワークであるCNNの構築に適しています。フィードフォワードニューラルネットワークとは、情報が入力層から畳み込み層、プーリング層、そして全結合層へと一方向に流れるタイプのネットワークで、最も基本的なCNN構造です。
畳み込み処理の実装
CNNの核となる畳み込み層(Convolutional Layer)を追加します。畳み込み層は画像の局所的な特徴を検出する役割を果たします。
# 第1畳み込み層を追加
cnn.add(tf.keras.layers.Conv2D(
filters=32, # フィルター(特徴検出器)の数を32個に設定
kernel_size=3, # カーネル(フィルター)のサイズを3x3に設定
activation='relu', # 活性化関数にReLUを使用
input_shape=[64, 64, 3] # 入力画像の形状(64x64ピクセル、3チャンネル)
))
Conv2D層は2次元畳み込み層を表し、画像データの処理に特化した層です。filters=32は、この層で使用するフィルター(カーネル)の数を指定します。各フィルターは特定のパターン(エッジ、角、線など)を検出する役割を持ちます。32個のフィルターにより、32種類の異なる特徴を同時に検出できます。
kernel_size=3は、フィルターのサイズを3×3に設定することを意味します。3×3は最も一般的に使用されるカーネルサイズで、計算効率と特徴検出能力のバランスが良い値です。activation='relu'
は、ReLU(Rectified Linear Unit)活性化関数を指定しており、負の値を0に、正の値はそのまま出力する特性を持ちます。ReLUは計算が高速で、勾配消失問題を軽減する効果があるため、隠れ層でよく使用されます。
input_shape=[64, 64, 3]
は、入力画像の形状を指定します。64×64ピクセルのRGBカラー画像(3チャンネル)を意味します。この設定は最初の層でのみ必要で、後続の層では自動的に形状が推論されます。
プーリング処理の実装
畳み込み層の後にプーリング層(Pooling Layer)を追加します。プーリング層は特徴マップのサイズを縮小し、計算量を削減する役割を果たします。
# 第1最大プーリング層を追加
cnn.add(tf.keras.layers.MaxPool2D(
pool_size=2, # プーリング層のウィンドウサイズを2x2に設定
strides=2 # プーリング層のウィンドウの移動幅を2ピクセルに設定
))
MaxPool2D層は2次元最大プーリング層を表します。pool_size=2は、2×2の領域から最大値を取り出すことを意味します。strides=2は、プーリング層のウィンドウを2ピクセルずつ移動させることを指定します。
最大プーリングの利点について詳しく説明します。まず、ダウンサンプリングにより特徴マップのサイズが半分になり、後続の層での計算量が大幅に削減されます。次に、平行移動不変性が向上し、物体が画像内で少し移動しても同じ特徴として認識されるようになります。また、最も重要な特徴(最大値)のみを保持することで、ノイズ除去の効果も期待できます。
第2畳み込み層の追加
より深いネットワーク構造を作るため、第2の畳み込み層を追加します。深い構造により、より複雑で抽象的な特徴を学習できるようになります。
# 第2畳み込み層を追加
cnn.add(tf.keras.layers.Conv2D(
filters=32, # フィルター数を32個に設定
kernel_size=3, # カーネルサイズを3x3に設定
activation='relu' # 活性化関数にReLUを使用
))
# 第2最大プーリング層を追加
cnn.add(tf.keras.layers.MaxPool2D(
pool_size=2, # プーリング層のウィンドウサイズを2x2に設定
strides=2 # プーリング層のウィンドウの移動幅を2ピクセルに設定
))
第2畳み込み層ではinput_shapeを指定する必要がありません。Kerasが前の層の出力形状を自動的に推論してくれるためです。深い層になるほど、より抽象的で複雑な特徴(例えば、第1層が「エッジ」を検出するなら、第2層は「形状」を検出する)を学習するようになります。
Flatten層の実装
畳み込み層とプーリング層で抽出された特徴マップを、全結合層で処理できるように1次元のベクトルに変換します。
# Flatten層を追加(2次元の特徴マップを1次元ベクトルに変換)
cnn.add(tf.keras.layers.Flatten())
Flatten層は、2次元の特徴マップを1次元のベクトルに平坦化します。例えば、16×16×32の特徴マップがある場合、これを8192(16×16×32)個の要素を持つ1次元ベクトルに変換します。この変換により、後続の全結合層でこれらの特徴を組み合わせた高次の判断を行うことができるようになります。
全結合層の実装
Flatten層で平坦化された特徴ベクトルを処理するための全結合層(Dense Layer)を追加します。
# 全結合層を追加
cnn.add(tf.keras.layers.Dense(
units=128, # ニューロン数を128個に設定
activation='relu' # 活性化関数にReLUを使用
))
Dense層は全結合層とも呼ばれ、前の層のすべてのニューロンが次の層のすべてのニューロンと接続される構造です。この全結合層は隠れ層として機能し、出力層とは区別されます。隠れ層は入力層と出力層の間に位置する中間層で、複雑なパターンの学習を担当します。units=128は、この層に128個のニューロンを配置することを意味します。画像認識のような複雑なタスクでは、多くのニューロンが必要になるため、従来の小規模データセット向けのネットワークより大きな値を設定しています。
この全結合層では、畳み込み層で抽出された局所的な特徴を組み合わせて、より高次の判断を行います。例えば、「尖った耳」「長い尻尾」「縞模様」といった特徴の組み合わせから「猫」であることを判断するような処理が行われます。
出力層の実装
最後に、分類結果を出力する出力層を追加します。
# 出力層を追加
cnn.add(tf.keras.layers.Dense(
units=1, # 出力ニューロン数を1個に設定(二値分類のため)
activation='sigmoid' # 活性化関数にSigmoidを使用
))
出力層ではunits=1として、単一の出力(猫である確率)を生成します。二値分類問題では出力ニューロンが1つだけ必要で、3つ以上のクラスがある多クラス分類の場合はクラス数分の出力ニューロンが必要になります。activation='sigmoid'
は、シグモイド活性化関数を指定しており、この関数は任意の実数値を0から1の間の値に変換します。これにより、出力を確率として解釈することができ、0.5を閾値として二値分類を行うことができます。
CNNの学習
構築したCNNモデルを実際に学習させていきます。学習プロセスでは、モデルが入力画像から正しい分類を行えるように、重みとバイアスを調整していきます。この過程で確率的勾配降下法を使用して、予測誤差を最小化していきます。
CNNのコンパイル
学習を開始する前に、モデルの学習方法を定義するコンパイルを行います。
# CNNをコンパイル(学習の設定を行う)
cnn.compile(
optimizer='adam', # 最適化アルゴリズムにAdamを使用
loss='binary_crossentropy', # 損失関数にバイナリクロスエントロピーを使用
metrics=['accuracy'] # 評価指標に精度を使用
)
optimizer='adam'
は、Adam(最適化アルゴリズム)を指定しています。Adamは学習率を自動調整する機能を持ち、多くの場面で優秀な性能を発揮する最適化手法で、確率的勾配降下法を効率的に実行します。特に画像認識のような複雑なタスクでは、Adamの自動調整機能が非常に有効です。
loss=’binary_crossentropy’は、二値分類問題に適したバイナリクロスエントロピー損失を指定しており、予測確率と実際のラベルとの差を効果的に測定します。多クラス分類の場合は’categorical_crossentropy’を使用します。metrics=['accuracy']
は、学習中に精度も同時に計算して表示することを指定しています。
訓練データでのCNN学習とテストデータでの評価
いよいよモデルの学習を開始します。画像分類では、通常の機械学習より多くのエポックが必要になります。
# 訓練データでモデルを学習し、テストデータで同時評価
cnn.fit(
x=training_set, # 学習に使用する訓練データ
validation_data=test_set, # 各エポック終了時に評価を行うテストデータ
epochs=25 # 学習回数(エポック数)を25回に設定
)
出力例:
Epoch 1/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 113s 222ms/step - accuracy: 0.6193 - loss: 0.6450 - val_accuracy: 0.7385 - val_loss: 0.5200
Epoch 2/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 113s 226ms/step - accuracy: 0.7270 - loss: 0.5370 - val_accuracy: 0.7675 - val_loss: 0.4816
Epoch 3/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 106s 211ms/step - accuracy: 0.7604 - loss: 0.4917 - val_accuracy: 0.7800 - val_loss: 0.4612
Epoch 4/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 96s 191ms/step - accuracy: 0.7765 - loss: 0.4590 - val_accuracy: 0.7868 - val_loss: 0.4478
Epoch 5/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 101s 202ms/step - accuracy: 0.7932 - loss: 0.4409 - val_accuracy: 0.7825 - val_loss: 0.4496
Epoch 6/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 97s 194ms/step - accuracy: 0.8035 - loss: 0.4215 - val_accuracy: 0.8008 - val_loss: 0.4290
Epoch 7/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 98s 195ms/step - accuracy: 0.8105 - loss: 0.4152 - val_accuracy: 0.8120 - val_loss: 0.4146
Epoch 8/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 101s 202ms/step - accuracy: 0.8237 - loss: 0.3873 - val_accuracy: 0.8092 - val_loss: 0.4194
Epoch 9/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 108s 215ms/step - accuracy: 0.8344 - loss: 0.3702 - val_accuracy: 0.8140 - val_loss: 0.4014
Epoch 10/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 98s 196ms/step - accuracy: 0.8334 - loss: 0.3694 - val_accuracy: 0.8025 - val_loss: 0.4659
Epoch 11/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 102s 205ms/step - accuracy: 0.8489 - loss: 0.3456 - val_accuracy: 0.8265 - val_loss: 0.3925
Epoch 12/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 101s 202ms/step - accuracy: 0.8577 - loss: 0.3315 - val_accuracy: 0.7940 - val_loss: 0.4775
Epoch 13/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 99s 198ms/step - accuracy: 0.8611 - loss: 0.3273 - val_accuracy: 0.8240 - val_loss: 0.4150
Epoch 14/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 104s 208ms/step - accuracy: 0.8688 - loss: 0.3088 - val_accuracy: 0.8305 - val_loss: 0.4027
Epoch 15/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 105s 210ms/step - accuracy: 0.8616 - loss: 0.3032 - val_accuracy: 0.8350 - val_loss: 0.3913
Epoch 16/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 103s 206ms/step - accuracy: 0.8803 - loss: 0.2840 - val_accuracy: 0.8292 - val_loss: 0.4022
Epoch 17/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 103s 207ms/step - accuracy: 0.8879 - loss: 0.2679 - val_accuracy: 0.8100 - val_loss: 0.4556
Epoch 18/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 104s 207ms/step - accuracy: 0.8979 - loss: 0.2500 - val_accuracy: 0.8365 - val_loss: 0.4051
Epoch 19/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 140s 202ms/step - accuracy: 0.8956 - loss: 0.2564 - val_accuracy: 0.8288 - val_loss: 0.4081
Epoch 20/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 102s 204ms/step - accuracy: 0.9048 - loss: 0.2401 - val_accuracy: 0.7903 - val_loss: 0.5598
Epoch 21/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 104s 207ms/step - accuracy: 0.9014 - loss: 0.2324 - val_accuracy: 0.8335 - val_loss: 0.4325
Epoch 22/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 101s 203ms/step - accuracy: 0.9128 - loss: 0.2112 - val_accuracy: 0.8303 - val_loss: 0.4445
Epoch 23/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 98s 197ms/step - accuracy: 0.9220 - loss: 0.2001 - val_accuracy: 0.8295 - val_loss: 0.4380
Epoch 24/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 99s 197ms/step - accuracy: 0.9246 - loss: 0.1931 - val_accuracy: 0.8285 - val_loss: 0.4689
Epoch 25/25
500/500 ━━━━━━━━━━━━━━━━━━━━ 100s 200ms/step - accuracy: 0.9256 - loss: 0.1861 - val_accuracy: 0.8385 - val_loss: 0.4400
x=training_setは、学習に使用する訓練データを指定します。ImageDataGeneratorで作成したデータセットオブジェクトを直接指定できます。validation_data=test_setは、各エポック終了時にテストデータで性能を評価することを指定します。これにより、学習の進捗とオーバーフィッティングの兆候を監視できます。
epochs=25は、全訓練データを25回繰り返し学習することを意味します。画像分類では、従来の機械学習よりも多くのエポックが必要になります。各エポックで、モデルは徐々に画像の特徴を学習し、精度を向上させていきます。
学習中は、各エポックで以下の4つの重要な指標が表示されます。lossは訓練データでの損失値を示し、値が小さいほど学習が進んでいることを意味します。accuracyは訓練データでの予測精度を示し、値が大きいほど正確な予測ができています。val_lossはテストデータでの損失値を示し、val_accuracyはテストデータでの予測精度を示します。訓練データとテストデータの精度に大きな差が生じた場合は、オーバーフィッティングの兆候として注意が必要です。
予測したい画像の評価
学習が完了したCNNモデルを使用して、実際の予測を行います。この段階では、モデルをプロダクション環境で使用する際と同様の方法で予測を実行します。今回は、サンプル画像を使用して猫の画像を正しく猫として認識できるかを確認します。
予測用ライブラリのインポート
単一画像での予測に必要な追加ライブラリをインポートします。
# 数値計算用ライブラリ
import numpy as np
# 画像処理用ライブラリ
from tensorflow.keras.preprocessing import image
NumPyは配列操作に使用し、imageモジュールは画像ファイルの読み込みと配列変換に使用します。
予測用画像の読み込みと前処理
まず、予測したい画像を読み込み、モデルが期待する形式に変換します。今回は猫のサンプル画像を使用して、モデルが正しく猫と予測できるかを確認します。
# 予測用画像を読み込み(猫のサンプル画像を使用)
test_image = image.load_img(
'../image_datasets/prediction_samples/sample_cat.jpg', # 予測対象の猫画像ファイルパス
target_size=(64, 64) # 画像サイズを64x64に調整
)
# PIL画像をNumPy配列に変換
test_image = image.img_to_array(test_image)
# バッチ次元を追加(モデルはバッチ入力を期待するため)
test_image = np.expand_dims(test_image, axis=0)
load_img関数では、target_size=(64, 64)
を指定して画像を64×64ピクセルにリサイズします。これは学習時と同じサイズにする必要があるためです。img_to_array関数は、PIL画像をNumPy配列に変換します。expand_dims関数は、バッチ次元を追加します。モデルは常にバッチ単位での入力を期待するため、単一画像であっても4次元配列(バッチサイズ、高さ、幅、チャンネル数)にする必要があります。
予測の実行と結果の解釈
準備された猫の画像を使用して予測を実行し、モデルが正しく猫として認識できるかを確認します。
# CNNモデルで予測を実行
result = cnn.predict(test_image)
# クラスインデックスを確認(どちらが猫でどちらが犬かを確認)
training_set.class_indices
出力:{'cats': 0, 'dogs': 1}
# 予測結果を二値分類で判定
if result[0][0] == 1:
prediction = 'dog' # 結果が1なら犬と判定
else:
prediction = 'cat' # 結果が0なら猫と判定
# 予測結果を表示
print(prediction)
出力:cat
predictメソッドは、0から1の間の確率値を返します。class_indicesを確認することで、どの数値がどのクラスに対応するかを把握できます。通常、アルファベット順で割り当てられるため、catsが0、dogsが1になります。
予測結果は常に2次元配列で返されるため、result[0][0]
のように2重のインデックスでアクセスする必要があります。画像の前処理(リサイズ、配列変換、バッチ次元追加)は、学習時と全く同じ手順で行う必要があります。前処理の不一致は予測精度の著しい低下を招きます。
今回実装したCNNモデルの性能は、通常の構成で80%程度の精度が期待できます。この精度をさらに向上させるためには、データ拡張パラメータの調整(回転、明度変更など)、ネットワーク構造の改良(層数の増加、フィルター数の調整)、学習パラメータの最適化(学習率の調整、エポック数の増加)などの手法を検討することができます。機械学習プロジェクトでは、このような継続的な改良が重要なスキルとなります。
まとめ
このプロジェクトを通じて、TensorFlowのKerasを使用したCNN実装の基礎から実践的な応用まで幅広く学習することができました。
得られたスキル
- TensorFlow – ディープラーニングフレームワークの基本操作
- CNN構築技術 – 畳み込みニューラルネットワーク設計
- 画像データ前処理技術 – 正規化、データ拡張、リサイズ
- コンピュータビジョンの基礎知識 – 特徴抽出、パターン認識
得られた経験
- 20,000枚規模の画像データセット処理と分析
- 畳み込みニューラルネットワークの構築経験
- コンピュータビジョン技術の実践的適用
- Jupyter Notebookでの対話型開発環境の活用
得られた成果物
- 完全に動作するCNN画像分類モデル
- 完全なJupyter Notebookソースコード
次に学ぶべきこと
- 転移学習を使用した事前学習済みモデルの活用
- より複雑なCNNアーキテクチャ(ResNet、VGGなど)の実装
- 多クラス分類による複数カテゴリの画像認識
- オブジェクト検出による画像内の物体位置特定技術
- ハイパーパラメータチューニングによる精度向上技術の習得
このプロジェクトから応用できること
このCNN技術を基盤として、様々な業界での画像認識ニーズに対応できるサービスを開発することが可能です。特に、画像分類や視覚的判断が必要な業務では、高い価値を提供できる技術として活用できます。
- 商品画像の自動分類システムをCNNで構築し在庫管理の効率化を実現
- 不良品検出システムをCNNで開発し製造業の品質管理を支援
- 医療画像診断支援ツールをCNNで作成し診断精度の向上に貢献
- セキュリティ画像認識システムをCNNで実装し監視業務の自動化を実現
本コンテンツへの意見や質問