Skip to content

9. 画像処理 ③:画像分類の実践

第 9 回では、前回学んだ古典的な特徴抽出手法を用いた画像分類の実践について学びます。また、最後に現代の画像認識の中心的手法である CNN(Convolutional Neural Network)についても簡単に紹介します。

9.1 画像分類とは

9.1.1 画像分類の概要

画像分類(Image Classification) は、画像を見て、それがどのカテゴリに属するかを判定するタスクです。

graph LR
    A[入力画像] --> B[特徴抽出]
    B --> C[機械学習<br/>モデル]
    C --> D[クラス予測<br/>猫/犬/鳥など]

    style A fill:#e6f3ff,stroke:#66b3ff,stroke-width:3px
    style B fill:#fff4cc,stroke:#ffcc00,stroke-width:3px
    style C fill:#ccffcc,stroke:#66cc66,stroke-width:3px
    style D fill:#ffcccc,stroke:#ff6666,stroke-width:3px

画像分類の応用例

  • 医療画像診断(X 線画像から病変を検出)
  • 製品の品質検査(良品/不良品の判定)
  • 文字認識(手書き文字の識別)
  • 物体認識(写真に写っている物体の識別)

9.1.2 画像分類のワークフロー

画像分類は以下の手順で行います。

graph TB
    A[1. データ準備<br/>画像とラベル] --> B[2. 特徴抽出<br/>HOGなど]
    B --> C[3. データ分割<br/>訓練/テスト]
    C --> D[4. モデル学習<br/>分類器の訓練]
    D --> E[5. モデル評価<br/>精度の測定]
    E --> F{精度は<br/>十分?}
    F -->|No| G[6. 改善<br/>パラメータ調整]
    G --> B
    F -->|Yes| H[完成]

    style A fill:#e6f3ff,stroke:#66b3ff,stroke-width:2px
    style B fill:#fff4cc,stroke:#ffcc00,stroke-width:2px
    style C fill:#ccffcc,stroke:#66cc66,stroke-width:2px
    style D fill:#ffffcc,stroke:#ffcc00,stroke-width:2px
    style E fill:#ffcccc,stroke:#ff6666,stroke-width:2px
    style F fill:#e6ccff,stroke:#9966ff,stroke-width:2px
    style G fill:#ffe6cc,stroke:#ff9933,stroke-width:2px
    style H fill:#ccffcc,stroke:#66cc66,stroke-width:3px

9.1.3 MNIST データセット

本章では、機械学習の分野で最も有名なベンチマークデータセットの一つである MNIST(Mixed National Institute of Standards and Technology) を使用します。

MNISTデータセット
MNISTデータセットのサンプル画像(Wikipediaより)

MNIST データセットの特徴

項目 内容
内容 0〜9 の手書き数字画像
画像サイズ 28×28 ピクセル(グレースケール)
訓練データ 60,000 枚
テストデータ 10,000 枚
クラス数 10(数字 0〜9)

MNIST は 1998 年に Yann LeCun らによって公開され、以下の理由から機械学習の入門に広く使われています。

  • シンプルな構造: 28×28 の小さな画像で、計算コストが低い
  • 明確なタスク: 10 クラスの分類問題で、評価が容易
  • 研究実績: 多くの手法のベンチマークとして使用されている

本章で使用するサブセット

オリジナルの MNIST は 70,000 枚の画像を含みますが、本講義では学習時間を短縮するため、500〜2000 枚程度のサブセットを使用します。

9.2 生の画素値を用いた画像分類

9.2.1 データセットの準備

以下では、MNIST データセットのサブセット(手書き数字)を使って、画像分類の基本を学んでいきます。サブセットデータをダウンロードし、ローカルまたはクラウドのワークスペースに配置(アップロード)したうえで、以下のコードを実行して中身を確認してみましょう。

import numpy as np
import matplotlib.pyplot as plt

# MNISTサブセットの読み込み
data = np.load('mnist_500_raw.npz')
X = data['X']  # (500, 784) - 500枚の画像(各画像:28×28=784ピクセル)
y = data['y']  # (500,) - ラベル(0-9)

print(f"画像データの形状: {X.shape}")
print(f"画像の枚数: {len(X)}")
print(f"各画像のサイズ: 28×28 ピクセル")
print(f"クラス数: {len(np.unique(y))}")
print(f"クラス: {np.unique(y)}")
print(f"ラベル分布: {np.bincount(y)}")

# 画像を28×28に reshape
X_images = X.reshape(-1, 28, 28)

# サンプル画像の表示
fig, axes = plt.subplots(5, 5, figsize=(8, 8))
for i in range(25):
    ax = axes[i // 5, i % 5]
    ax.imshow(X_images[i], cmap='gray')
    ax.set_title(f'ラベル: {y[i]}')
    ax.axis('off')

plt.tight_layout()
plt.show()
Output
画像データの形状: (500, 784)
画像の枚数: 500
各画像のサイズ: 28×28 ピクセル
クラス数: 10
クラス: [0 1 2 3 4 5 6 7 8 9]
ラベル分布: [50 50 50 50 50 50 50 50 50 50]

9-1

MNIST サブセット

9.2.2 生の画素値で分類

まずは特徴抽出をせず、生の画素値をそのまま使ってロジスティック回帰で分類してみます。

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns

# データの読み込み
data = np.load('mnist_500_raw.npz')
X = data['X']  # (500, 784) - 生の画素値
y = data['y']

# データ分割
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

print(f"訓練データ: {X_train.shape}")
print(f"テストデータ: {X_test.shape}")

# ロジスティック回帰
model = LogisticRegression(max_iter=1000, random_state=42)
model.fit(X_train, y_train)

# 予測
y_pred = model.predict(X_test)

# 精度評価
accuracy = accuracy_score(y_test, y_pred)
print(f"\nテスト精度: {accuracy:.4f} ({accuracy:.2%})")

print("\n分類レポート:")
print(classification_report(y_test, y_pred))

# 混同行列の可視化
cm = confusion_matrix(y_test, y_pred)

plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', cbar_kws={'label': '件数'})
plt.xlabel('予測ラベル')
plt.ylabel('正解ラベル')
plt.title(f'混同行列(精度: {accuracy:.3f})')
plt.tight_layout()
plt.show()
Output
訓練データ: (400, 784)
テストデータ: (100, 784)

テスト精度: 0.8500 (85.00%)

分類レポート:
              precision    recall  f1-score   support

           0       1.00      0.80      0.89        10
           1       1.00      1.00      1.00        10
           2       0.82      0.90      0.86        10
           3       0.88      0.70      0.78        10
           4       0.91      1.00      0.95        10
           5       0.75      0.90      0.82        10
           6       0.90      0.90      0.90        10
           7       0.77      1.00      0.87        10
           8       0.60      0.60      0.60        10
           9       1.00      0.70      0.82        10

    accuracy                           0.85       100
   macro avg       0.86      0.85      0.85       100
weighted avg       0.86      0.85      0.85       100

9-2

生の画素値による分類の性能

MNIST サブセット(500枚)では、生の画素値とロジスティック回帰の組み合わせで、約 80-88% 程度の精度を達成できます。

生の画素値の問題点:

  • ノイズの影響を受けやすい
  • 位置のずれに弱い
  • 照明変化に弱い
  • 特徴量の次元数が多い(784次元)

9.2.3 誤分類された画像の確認

モデルがどのような画像を間違えたのかを確認することで、モデルの弱点を理解できます。弱点を理解することで、モデルの改善策(特徴量の工夫、データ拡張など)が見えてきます。

import numpy as np
import matplotlib.pyplot as plt

# 誤分類されたサンプルを抽出
misclassified_indices = np.where(y_test != y_pred)[0]
print(f"誤分類されたサンプル数: {len(misclassified_indices)}")

# 誤分類された画像を表示
if len(misclassified_indices) > 0:
    # 最大9個まで表示
    num_display = min(9, len(misclassified_indices))
    fig, axes = plt.subplots(3, 3, figsize=(9, 9))
    axes = axes.ravel()

    for i in range(num_display):
        idx = misclassified_indices[i]
        # テストデータのインデックスから元の画像を取得
        test_sample_image = X_test[idx].reshape(28, 28)

        axes[i].imshow(test_sample_image, cmap='gray')
        axes[i].set_title(f'正解: {y_test[idx]}, 予測: {y_pred[idx]}')
        axes[i].axis('off')

    # 残りのサブプロットを非表示
    for i in range(num_display, 9):
        axes[i].axis('off')

    plt.tight_layout()
    plt.show()
else:
    print("誤分類されたサンプルはありません")

9-5

9.3 HOG 特徴量を用いた画像分類

9.3.1 HOG 特徴量の抽出

生の画素値よりも効果的な特徴表現として、HOG(Histogram of Oriented Gradients)特徴量を使うことができます。HOG はエッジの方向のヒストグラムを取ることで、画像の重要な情報を捉えます。

import numpy as np
from skimage.feature import hog
from skimage import exposure
import matplotlib.pyplot as plt

# データの読み込み
data = np.load('mnist_500_raw.npz')
X = data['X']
y = data['y']

# 画像を28×28に reshape
X_images = X.reshape(-1, 28, 28)

# HOG特徴量の抽出
hog_features = []
for image in X_images:
    fd = hog(
        image,
        orientations=9,
        pixels_per_cell=(4, 4),
        cells_per_block=(2, 2),
        visualize=False)
    hog_features.append(fd)

X_hog = np.array(hog_features)

print(f"HOG特徴量の形状: {X_hog.shape}")
print(f"各画像の特徴量次元: {X_hog.shape[1]}")
print(f"元の画素数: {X.shape[1]}")

# サンプル画像とHOG特徴量の可視化
sample_idx = 0
fd, hog_image = hog(
    X_images[sample_idx],
    orientations=9,
    pixels_per_cell=(7, 7),
    cells_per_block=(2, 2),
    visualize=True)

hog_image_rescaled = exposure.rescale_intensity(hog_image, in_range=(0, 10))

fig, axes = plt.subplots(1, 2, figsize=(7, 4))

axes[0].imshow(X_images[sample_idx], cmap='gray')
axes[0].set_title(f'元画像(ラベル: {y[sample_idx]})')
axes[0].axis('off')

axes[1].imshow(hog_image_rescaled, cmap='gray')
axes[1].set_title('HOG 特徴量')
axes[1].axis('off')

plt.tight_layout()
plt.show()
Output
HOG特徴量の形状: (500, 1296)
各画像の特徴量次元: 1296
元の画素数: 784

9-3

9.3.2 モデルの学習と評価

HOG 特徴量を使ってロジスティック回帰で分類モデルを学習するコード例は以下のようになります。

import numpy as np
from skimage.feature import hog
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns

# データの読み込みとHOG特徴量の抽出
data = np.load('mnist_500_raw.npz')
X = data['X']
y = data['y']

X_images = X.reshape(-1, 28, 28)

hog_features = []
for image in X_images:
    fd = hog(
        image, orientations=9,
        pixels_per_cell=(7, 7),
        cells_per_block=(2, 2),
        visualize=False)
    hog_features.append(fd)

X_hog = np.array(hog_features)

# データ分割
X_train, X_test, y_train, y_test = train_test_split(
    X_hog, y, test_size=0.2, random_state=42, stratify=y
)

print(f"訓練データ: {X_train.shape}")
print(f"テストデータ: {X_test.shape}")

# ロジスティック回帰で学習
model = LogisticRegression(max_iter=1000, random_state=42)
model.fit(X_train, y_train)

# 予測
y_pred = model.predict(X_test)

# 精度評価
accuracy = accuracy_score(y_test, y_pred)
print(f"\nテスト精度: {accuracy:.4f} ({accuracy:.2%})")

print("\n分類レポート:")
print(classification_report(y_test, y_pred))

# 混同行列の可視化
cm = confusion_matrix(y_test, y_pred)

plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', cbar_kws={'label': '件数'})
plt.xlabel('予測ラベル')
plt.ylabel('正解ラベル')
plt.title(f'混同行列(精度: {accuracy:.3f})')
plt.tight_layout()
plt.show()
Output
訓練データ: (400, 324)
テストデータ: (100, 324)

テスト精度: 0.9500 (95.00%)

分類レポート:
              precision    recall  f1-score   support

           0       1.00      0.90      0.95        10
           1       0.83      1.00      0.91        10
           2       1.00      1.00      1.00        10
           3       0.91      1.00      0.95        10
           4       1.00      1.00      1.00        10
           5       0.90      0.90      0.90        10
           6       0.90      0.90      0.90        10
           7       1.00      1.00      1.00        10
           8       1.00      0.80      0.89        10
           9       1.00      1.00      1.00        10

    accuracy                           0.95       100
   macro avg       0.95      0.95      0.95       100
weighted avg       0.95      0.95      0.95       100

9-4

HOG + ロジスティック回帰の性能

MNIST サブセット(500枚)では、HOG 特徴量とロジスティック回帰の組み合わせで、約 85-95% 程度の精度を達成できます。

考察のポイント:

  • どの数字が誤認識されやすいか(混同行列で確認)
  • HOG 特徴量のパラメータやデータサイズが精度にどう影響するか

9.4 CNN の紹介

9.4.1 CNN(畳み込みニューラルネットワーク)とは

これまで学んだ古典的な手法では、人間が特徴を設計する必要がありました(HOG、エッジ検出など)。一方、CNN(Convolutional Neural Network: 畳み込みニューラルネットワーク) は、データから自動的に特徴を学習するディープラーニングの手法です。

graph TB
    A[古典的手法] --> B[人間が特徴を設計<br/>HOG, SIFT, エッジなど]
    B --> C[機械学習で分類<br/>ロジスティック回帰, SVMなど]

    D[CNN ディープラーニング] --> E[データから自動で<br/>特徴を学習]
    E --> F[学習した特徴で<br/>分類]

    style A fill:#ccffcc,stroke:#66cc66,stroke-width:2px
    style B fill:#e6ffe6,stroke:#66cc66,stroke-width:1px
    style C fill:#ccffcc,stroke:#66cc66,stroke-width:2px

    style D fill:#ffffcc,stroke:#ffcc00,stroke-width:2px
    style E fill:#fffef0,stroke:#ffcc00,stroke-width:1px
    style F fill:#ffffcc,stroke:#ffcc00,stroke-width:2px

9.4.2 CNN の基本構造

CNN は主に以下の層から構成されます。

graph TB
    A[入力画像] --> B[畳み込み層<br/>Convolution]
    B --> C[プーリング層<br/>Pooling]
    C --> D[畳み込み層<br/>Convolution]
    D --> E[プーリング層<br/>Pooling]
    E --> F[全結合層<br/>Dense]
    F --> G[出力<br/>クラス予測]

    style A fill:#e6f3ff,stroke:#66b3ff,stroke-width:3px
    style B fill:#ccffcc,stroke:#66cc66,stroke-width:2px
    style C fill:#ffffcc,stroke:#ffcc00,stroke-width:2px
    style D fill:#ccffcc,stroke:#66cc66,stroke-width:2px
    style E fill:#ffffcc,stroke:#ffcc00,stroke-width:2px
    style F fill:#ffcccc,stroke:#ff6666,stroke-width:2px
    style G fill:#e6ccff,stroke:#9966ff,stroke-width:3px

主要な構成要素:

  1. 畳み込み層(Convolutional Layer)

  2. フィルタ(カーネル)を画像上でスライドさせて特徴を抽出

  3. 第 8 回で学んだ Sobel フィルタと同じ原理
  4. CNN では、フィルタの値をデータから自動的に学習

  5. プーリング層(Pooling Layer)

  6. 特徴マップのサイズを削減

  7. 位置のずれに対する不変性を高める
  8. 最大プーリング(Max Pooling)が一般的

  9. 全結合層(Fully Connected Layer)

  10. 抽出された特徴から最終的なクラス予測を行う

9.4.3 古典的手法と CNN の比較

項目 古典的手法(HOG + 機械学習) CNN(ディープラーニング)
特徴設計 人間が設計 データから自動学習
精度 中程度 非常に高い
必要なデータ量 少量でも可 大量のデータが必要
計算コスト 低い 高い(GPU 推奨)
解釈性 高い(どの特徴を使うか明確) 低い(ブラックボックス)
実装の難易度 比較的簡単 やや複雑

CNN の性能

  • MNIST(手書き数字): 99%以上の精度
  • ImageNet(100万枚の画像、1000クラス): 90%以上の精度
  • 画像認識コンペティションで人間の精度を超える

9.4.4 CNN の学習プロセス

CNN は以下のような流れで学習します。

graph TB
    A[訓練データ<br/>画像+ラベル] --> B[CNN モデル]
    B --> C[予測結果]
    C --> D[損失計算<br/>予測と正解の差]
    D --> E[誤差逆伝播]
    E --> F[フィルタの更新]
    F --> B

    style A fill:#e6f3ff,stroke:#66b3ff,stroke-width:2px
    style B fill:#ffffcc,stroke:#ffcc00,stroke-width:3px
    style C fill:#ccffcc,stroke:#66cc66,stroke-width:2px
    style D fill:#ffcccc,stroke:#ff6666,stroke-width:2px
    style E fill:#e6ccff,stroke:#9966ff,stroke-width:2px
    style F fill:#ffe6cc,stroke:#ff9933,stroke-width:2px
  1. 順伝播: 画像を入力し、フィルタを通して予測
  2. 損失計算: 予測と正解の差を計算
  3. 逆伝播: 損失を小さくするようにフィルタを更新
  4. これを繰り返してフィルタを最適化

CNN を学ぶには

CNN の詳細な実装を学ぶには、以下のフレームワークを使用します。

  • TensorFlow / Keras: Python の代表的なディープラーニングフレームワーク
  • PyTorch: 研究でよく使われるフレームワーク

興味がある方は、各フレームワークの公式チュートリアルを参照してください。

9.5 演習問題

演習 9-1: 決定木による分類

演習 9-1

MNIST サブセット(500サンプル)を使って、生の画素値と決定木(Decision Tree)による分類を実装してください。

タスク:

  1. MNIST サブセット(500サンプル)を読み込み
  2. データを訓練データとテストデータに分割
  3. 決定木で学習・評価
  4. 混同行列を作成・可視化

データセット: https://ml2025.ibadai.com/mnist_500_raw.npz

演習 9-2: HOG 特徴量のパラメータと精度の関係

演習 9-2

MNIST サブセット(500サンプル)で、HOG特徴量のパラメータを変えて精度への影響を調べてください。

タスク:

  1. MNIST サブセット(500サンプル)を読み込み
  2. 異なる pixels_per_cell パラメータで HOG 特徴量を抽出
  3. 各パラメータでロジスティック回帰を学習・評価

データセット: https://ml2025.ibadai.com/mnist_500_raw.npz

9.6 課題 9: 高精度な画像分類モデルの作成

課題 9

MNIST サブセット(500/1000/2000サンプルのいずれか)を使い、これまでに学んだ知識を組み合わせて、高精度な画像分類モデルを作成してください。

標準タスク:

  1. データセットの選択(500/1000/2000サンプルのいずれか)
  2. 特徴抽出の実施(生の画素値、HOG特徴量など)
  3. 分類モデルの学習
  4. モデルの精度評価(分類レポート、混同行列)
  5. 結果に対する考察

※ 加点要素:複数のアプローチの比較、パラメータチューニング、誤分類分析

提出物:

  • Word ファイルで作成し、PDF 形式で出力して提出(学生番号_氏名_9.pdf)
  • 問題文・ソースコード・結果・考察を PDF 内に含めること(参考:機械学習_レポートサンプル.docx

提出期限: 2025年12月10日(水)23:59

提出先: manaba

データセット

MNIST サブセット:

# データの読み込み例
import numpy as np

data = np.load('mnist_500_raw.npz')
X = data['X']  # (500, 784) - 画像データ
y = data['y']  # (500,) - ラベル

9.7 まとめ

本章では、生の画素値、HOG 特徴量を用いた画像分類の実践と、CNN の概要について学びました。

第 9 回のまとめ

画像分類の基礎

  • 画像を見てカテゴリを判定するタスク
  • 特徴抽出 → 機械学習 → 予測

生の画素値を用いた画像分類

  • 特徴抽出なしでも分類可能(約80-88%の精度)
  • ノイズや位置ずれに弱い
  • 特徴量の次元数が多い(784次元)

HOG特徴量を用いた画像分類

  • エッジの方向ヒストグラムで画像の重要な情報を抽出
  • 高精度(約85-95%の精度)

古典的手法の利点と限界

  • 利点: 少ないデータで動作、解釈性が高い、計算コストが低い
  • 限界: 人間が特徴を設計する必要がある、複雑なパターンは学習困難

CNN の紹介

  • データから自動的に特徴を学習
  • 古典的手法より高精度(MNIST で 99%以上)
  • 大量のデータと計算資源が必要
  • 現代の画像認識の中心的手法