画像分類・顔認識AI実装ガイド|深層学習で画像解析を極める
概要
画像分類と顔認識は、コンピュータビジョンの中核技術として様々な分野で活用されています。深層学習の発展により、人間を超える精度での画像認識が実現可能になりました。本記事では、CNN(畳み込みニューラルネットワーク)を用いた画像分類システムと、顔認識システムの実装方法を詳しく解説します。
画像分類・顔認識の応用分野
産業応用例
画像認識技術は幅広い分野で実用化されています:
- 製造業: 品質検査、欠陥検出
- 医療: 医療画像診断、病変検出
- 小売: 商品認識、在庫管理
- セキュリティ: 監視カメラ、入退室管理
- 自動車: 自動運転、歩行者検出
- 農業: 作物の成長監視、病害検出
技術的課題と解決アプローチ
- 照明条件の変化: データ拡張、正規化
- 角度・スケール変動: 転移学習、幾何変換
- リアルタイム処理: モデル軽量化、エッジ推論
- 少数学習: ファインチューニング、データ生成
CNN による画像分類システム
基本的な CNN アーキテクチャの実装
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
# サンプル画像データの生成
def create_sample_image_data():
"""
分類用のサンプル画像データを生成
"""
np.random.seed(42)
# 画像サイズとクラス数
img_height, img_width, channels = 64, 64, 3
n_classes = 5
n_samples_per_class = 200
# クラス名
class_names = ['犬', '猫', '鳥', '魚', '花']
X = []
y = []
for class_id, class_name in enumerate(class_names):
for _ in range(n_samples_per_class):
# 擬似的な画像生成(実際はデータセットを使用)
if class_name == '犬':
# 茶色っぽい色調
img = np.random.normal(0.6, 0.2, (img_height, img_width, channels))
elif class_name == '猫':
# グレー系の色調
img = np.random.normal(0.4, 0.2, (img_height, img_width, channels))
elif class_name == '鳥':
# 明るい色調
img = np.random.normal(0.8, 0.15, (img_height, img_width, channels))
elif class_name == '魚':
# 青系の色調
img = np.random.normal(0.3, 0.2, (img_height, img_width, channels))
img[:, :, 2] += 0.3 # 青チャンネルを強化
else: # 花
# カラフルな色調
img = np.random.normal(0.7, 0.3, (img_height, img_width, channels))
# 値の範囲を [0, 1] にクリップ
img = np.clip(img, 0, 1)
X.append(img)
y.append(class_id)
X = np.array(X)
y = np.array(y)
return X, y, class_names
# CNNモデルの構築
def build_cnn_model(input_shape, n_classes):
"""
画像分類用のCNNモデルを構築
"""
model = keras.Sequential([
# 第1畳み込みブロック
layers.Conv2D(32, (3, 3), activation='relu', input_shape=input_shape),
layers.BatchNormalization(),
layers.MaxPooling2D((2, 2)),
layers.Dropout(0.25),
# 第2畳み込みブロック
layers.Conv2D(64, (3, 3), activation='relu'),
layers.BatchNormalization(),
layers.MaxPooling2D((2, 2)),
layers.Dropout(0.25),
# 第3畳み込みブロック
layers.Conv2D(128, (3, 3), activation='relu'),
layers.BatchNormalization(),
layers.MaxPooling2D((2, 2)),
layers.Dropout(0.25),
# 全結合層
layers.Flatten(),
layers.Dense(512, activation='relu'),
layers.BatchNormalization(),
layers.Dropout(0.5),
layers.Dense(n_classes, activation='softmax')
])
# モデルのコンパイル
model.compile(
optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
return model
# データ拡張の実装
def create_data_augmentation():
"""
データ拡張のための前処理パイプライン
"""
data_augmentation = keras.Sequential([
layers.RandomFlip("horizontal"),
layers.RandomRotation(0.1),
layers.RandomZoom(0.1),
layers.RandomBrightness(0.1),
layers.RandomContrast(0.1)
])
return data_augmentation
# モデル訓練の実行
def train_image_classifier():
"""
画像分類モデルの訓練
"""
# データ準備
X, y, class_names = create_sample_image_data()
# 訓練・テストデータの分割
from sklearn.model_selection import train_test_split
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}")
print(f"クラス数: {len(class_names)}")
# モデル構築
input_shape = X_train.shape[1:]
n_classes = len(class_names)
model = build_cnn_model(input_shape, n_classes)
# モデル概要の表示
model.summary()
# コールバックの設定
callbacks = [
keras.callbacks.EarlyStopping(patience=10, restore_best_weights=True),
keras.callbacks.ReduceLROnPlateau(factor=0.5, patience=5)
]
# モデル訓練
history = model.fit(
X_train, y_train,
batch_size=32,
epochs=50,
validation_split=0.2,
callbacks=callbacks,
verbose=1
)
# テストデータでの評価
test_loss, test_accuracy = model.evaluate(X_test, y_test, verbose=0)
print(f"\nテスト精度: {test_accuracy:.4f}")
# 予測と詳細評価
y_pred = model.predict(X_test)
y_pred_classes = np.argmax(y_pred, axis=1)
# 分類レポート
print("\n分類レポート:")
print(classification_report(y_test, y_pred_classes,
target_names=class_names))
# 混同行列の可視化
plt.figure(figsize=(8, 6))
cm = confusion_matrix(y_test, y_pred_classes)
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=class_names, yticklabels=class_names)
plt.title('混同行列')
plt.xlabel('予測')
plt.ylabel('実際')
plt.show()
return model, history, class_names
# 訓練の実行
model, history, class_names = train_image_classifier()
転移学習の実装
# 事前訓練済みモデルを使用した転移学習
def build_transfer_learning_model(input_shape, n_classes):
"""
転移学習を使用したモデルの構築
"""
# 事前訓練済みのMobileNetV2をベースモデルとして使用
base_model = keras.applications.MobileNetV2(
input_shape=input_shape,
include_top=False,
weights='imagenet'
)
# ベースモデルの重みを凍結
base_model.trainable = False
# カスタム分類器を追加
model = keras.Sequential([
base_model,
layers.GlobalAveragePooling2D(),
layers.BatchNormalization(),
layers.Dropout(0.5),
layers.Dense(128, activation='relu'),
layers.BatchNormalization(),
layers.Dropout(0.3),
layers.Dense(n_classes, activation='softmax')
])
# 最初は低い学習率でコンパイル
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.0001),
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
return model, base_model
# ファインチューニングの実装
def fine_tune_model(model, base_model, X_train, y_train, X_val, y_val):
"""
ベースモデルのファインチューニング
"""
# 初期訓練
print("=== 初期訓練(特徴抽出)===")
history1 = model.fit(
X_train, y_train,
batch_size=32,
epochs=10,
validation_data=(X_val, y_val),
verbose=1
)
# ベースモデルの上位層を解凍してファインチューニング
base_model.trainable = True
# より低い学習率でファインチューニング
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.0001/10),
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
print("\n=== ファインチューニング ===")
history2 = model.fit(
X_train, y_train,
batch_size=32,
epochs=10,
validation_data=(X_val, y_val),
verbose=1
)
return model, history1, history2
# 転移学習の実行例
def demonstrate_transfer_learning():
X, y, class_names = create_sample_image_data()
# データ分割
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
X_train, X_val, y_train, y_val = train_test_split(
X_train, y_train, test_size=0.25, random_state=42, stratify=y_train
)
# 転移学習モデルの構築
transfer_model, base_model = build_transfer_learning_model(
X_train.shape[1:], len(class_names)
)
# ファインチューニング
fine_tuned_model, hist1, hist2 = fine_tune_model(
transfer_model, base_model, X_train, y_train, X_val, y_val
)
# 最終評価
test_loss, test_accuracy = fine_tuned_model.evaluate(X_test, y_test, verbose=0)
print(f"\n転移学習モデル テスト精度: {test_accuracy:.4f}")
return fine_tuned_model
transfer_model = demonstrate_transfer_learning()
顔認識システムの実装
顔検出とランドマーク抽出
# 顔認識システムの実装
class FaceRecognitionSystem:
def __init__(self):
self.face_encoder = None
self.known_faces = {}
self.face_detector = None
def create_face_detector(self):
"""
顔検出用のモデル(簡略版)
"""
# 実際の実装では OpenCV の Haar Cascade や MTCNN を使用
print("顔検出器を初期化中...")
# 擬似的な顔検出器
self.face_detector = self._mock_face_detector
def _mock_face_detector(self, image):
"""
モック顔検出器(実際は cv2.CascadeClassifier を使用)
"""
# 画像内に顔があると仮定して、中央部分を顔領域として返す
h, w = image.shape[:2]
face_x, face_y = w // 4, h // 4
face_w, face_h = w // 2, h // 2
return [(face_x, face_y, face_w, face_h)]
def extract_face_features(self, face_image):
"""
顔画像から特徴量を抽出
"""
# 実際の実装では FaceNet や ArcFace を使用
# ここでは簡略化して平均ピクセル値を特徴量とする
# 顔画像を正規化
if len(face_image.shape) == 3:
face_gray = np.mean(face_image, axis=2)
else:
face_gray = face_image
# 簡単な特徴量:画像の統計量
features = np.array([
np.mean(face_gray),
np.std(face_gray),
np.median(face_gray),
np.min(face_gray),
np.max(face_gray)
])
return features
def register_face(self, name, face_images):
"""
顔を登録
"""
if not isinstance(face_images, list):
face_images = [face_images]
all_features = []
for face_image in face_images:
features = self.extract_face_features(face_image)
all_features.append(features)
# 複数画像の平均特徴量を保存
avg_features = np.mean(all_features, axis=0)
self.known_faces[name] = avg_features
print(f"{name} の顔を登録しました")
def recognize_face(self, face_image, threshold=0.5):
"""
顔認識を実行
"""
if not self.known_faces:
return "未知", 1.0
# 入力画像の特徴量抽出
input_features = self.extract_face_features(face_image)
# 各登録済み顔との距離を計算
min_distance = float('inf')
best_match = "未知"
for name, known_features in self.known_faces.items():
# ユークリッド距離を計算
distance = np.linalg.norm(input_features - known_features)
if distance < min_distance:
min_distance = distance
best_match = name
# 閾値判定
if min_distance > threshold:
return "未知", min_distance
return best_match, min_distance
def process_image(self, image):
"""
画像内の全ての顔を検出・認識
"""
if self.face_detector is None:
self.create_face_detector()
# 顔検出
face_locations = self.face_detector(image)
results = []
for (x, y, w, h) in face_locations:
# 顔領域の切り出し
face_image = image[y:y+h, x:x+w]
# 顔認識
name, confidence = self.recognize_face(face_image)
results.append({
'name': name,
'confidence': confidence,
'location': (x, y, w, h)
})
return results
# 顔認識システムの使用例
def demonstrate_face_recognition():
"""
顔認識システムのデモ
"""
# 顔認識システムの初期化
face_system = FaceRecognitionSystem()
# サンプル顔画像の生成(実際は実画像を使用)
def generate_sample_face(person_id, variation=0):
np.random.seed(42 + person_id * 10 + variation)
# 64x64のグレースケール顔画像(模擬)
base_intensity = 0.3 + person_id * 0.1
face_image = np.random.normal(base_intensity, 0.1, (64, 64))
face_image = np.clip(face_image, 0, 1)
return face_image
# 複数人の顔を登録
people = ['田中', '佐藤', '鈴木']
for i, person in enumerate(people):
# 1人につき3枚の画像で登録
face_images = [generate_sample_face(i, var) for var in range(3)]
face_system.register_face(person, face_images)
print("\n=== 顔認識テスト ===")
# テスト画像での認識
for i, person in enumerate(people):
test_image = generate_sample_face(i, 5) # 新しいバリエーション
recognized_name, confidence = face_system.recognize_face(test_image)
print(f"実際: {person}, 認識結果: {recognized_name}, 信頼度: {confidence:.3f}")
# 未知の人のテスト
unknown_image = generate_sample_face(99)
recognized_name, confidence = face_system.recognize_face(unknown_image)
print(f"実際: 未知の人, 認識結果: {recognized_name}, 信頼度: {confidence:.3f}")
return face_system
# デモ実行
face_recognition_system = demonstrate_face_recognition()
リアルタイム顔認識システム
# リアルタイム処理用の最適化クラス
class RealtimeFaceRecognition:
def __init__(self, face_system):
self.face_system = face_system
self.processing_queue = []
self.results_cache = {}
self.cache_timeout = 30 # 30フレーム
def process_video_frame(self, frame, frame_number):
"""
ビデオフレームの処理
"""
# フレーム間隔での処理(計算負荷軽減)
if frame_number % 5 != 0: # 5フレームに1回処理
return self.get_cached_results()
# 顔検出・認識
recognition_results = self.face_system.process_image(frame)
# 結果をキャッシュ
self.results_cache = {
'results': recognition_results,
'frame_number': frame_number
}
return recognition_results
def get_cached_results(self):
"""
キャッシュされた結果を返す
"""
if 'results' in self.results_cache:
return self.results_cache['results']
return []
def optimize_for_realtime(self):
"""
リアルタイム処理用の最適化
"""
# 画像サイズの削減
self.target_size = (320, 240)
# 処理スキップフレーム数の調整
self.skip_frames = 3
print("リアルタイム処理用に最適化されました")
# 顔認識の精度向上テクニック
class AdvancedFaceRecognition(FaceRecognitionSystem):
def __init__(self):
super().__init__()
self.face_alignments = {}
self.quality_threshold = 0.3
def assess_face_quality(self, face_image):
"""
顔画像の品質評価
"""
# ブラー検出(ラプラシアン分散)
if len(face_image.shape) == 3:
gray = np.mean(face_image, axis=2)
else:
gray = face_image
laplacian_var = np.var(gray)
# 明度チェック
brightness = np.mean(gray)
brightness_score = 1.0 - abs(brightness - 0.5) * 2
# 総合品質スコア
quality_score = min(laplacian_var / 100, 1.0) * brightness_score
return quality_score
def register_face_with_quality_check(self, name, face_images):
"""
品質チェック付き顔登録
"""
if not isinstance(face_images, list):
face_images = [face_images]
high_quality_images = []
for face_image in face_images:
quality = self.assess_face_quality(face_image)
if quality >= self.quality_threshold:
high_quality_images.append(face_image)
print(f"品質スコア: {quality:.3f} - 採用")
else:
print(f"品質スコア: {quality:.3f} - 却下")
if high_quality_images:
self.register_face(name, high_quality_images)
return True
else:
print(f"{name} の登録に失敗しました(品質不足)")
return False
def enhanced_recognition(self, face_image):
"""
拡張顔認識(複数アルゴリズムの組み合わせ)
"""
# 品質チェック
quality = self.assess_face_quality(face_image)
if quality < self.quality_threshold:
return "品質不足", 1.0
# 基本認識
name, confidence = self.recognize_face(face_image)
# 信頼度に品質スコアを反映
adjusted_confidence = confidence * (1.0 - quality * 0.2)
return name, adjusted_confidence
# 拡張システムのデモ
def demonstrate_advanced_recognition():
"""
拡張顔認識システムのデモ
"""
advanced_system = AdvancedFaceRecognition()
# 異なる品質の画像でテスト
def generate_quality_varied_face(person_id, quality_level):
np.random.seed(42 + person_id * 10)
base_intensity = 0.3 + person_id * 0.1
if quality_level == 'high':
noise_level = 0.05
elif quality_level == 'medium':
noise_level = 0.15
else: # low
noise_level = 0.3
face_image = np.random.normal(base_intensity, noise_level, (64, 64))
face_image = np.clip(face_image, 0, 1)
return face_image
# 品質別画像での登録テスト
test_person = "テスト太郎"
print("=== 品質別顔登録テスト ===")
high_quality_faces = [generate_quality_varied_face(0, 'high') for _ in range(3)]
medium_quality_faces = [generate_quality_varied_face(0, 'medium') for _ in range(3)]
low_quality_faces = [generate_quality_varied_face(0, 'low') for _ in range(3)]
print("高品質画像での登録:")
advanced_system.register_face_with_quality_check(test_person + "_高品質", high_quality_faces)
print("\n中品質画像での登録:")
advanced_system.register_face_with_quality_check(test_person + "_中品質", medium_quality_faces)
print("\n低品質画像での登録:")
advanced_system.register_face_with_quality_check(test_person + "_低品質", low_quality_faces)
# 認識テスト
print("\n=== 拡張認識テスト ===")
test_image = generate_quality_varied_face(0, 'medium')
name, confidence = advanced_system.enhanced_recognition(test_image)
print(f"認識結果: {name}, 調整済み信頼度: {confidence:.3f}")
demonstrate_advanced_recognition()
顔認識システムの性能評価
# 性能評価とベンチマーク
class FaceRecognitionEvaluator:
def __init__(self):
self.test_results = []
def evaluate_accuracy(self, face_system, test_data):
"""
認識精度の評価
"""
correct_predictions = 0
total_predictions = 0
confusion_data = {'true_labels': [], 'pred_labels': []}
for true_name, face_images in test_data.items():
for face_image in face_images:
pred_name, confidence = face_system.recognize_face(face_image)
confusion_data['true_labels'].append(true_name)
confusion_data['pred_labels'].append(pred_name)
if pred_name == true_name:
correct_predictions += 1
total_predictions += 1
accuracy = correct_predictions / total_predictions
return accuracy, confusion_data
def evaluate_speed(self, face_system, test_images, n_runs=100):
"""
処理速度の評価
"""
import time
times = []
for _ in range(n_runs):
test_image = np.random.choice(test_images)
start_time = time.time()
face_system.recognize_face(test_image)
end_time = time.time()
times.append(end_time - start_time)
avg_time = np.mean(times)
fps = 1.0 / avg_time if avg_time > 0 else 0
return avg_time, fps, times
def generate_performance_report(self, face_system, test_data):
"""
総合性能レポートの生成
"""
print("=== 顔認識システム性能評価 ===")
# 精度評価
accuracy, confusion_data = self.evaluate_accuracy(face_system, test_data)
print(f"認識精度: {accuracy:.3f}")
# 速度評価
all_test_images = [img for images in test_data.values() for img in images]
avg_time, fps, _ = self.evaluate_speed(face_system, all_test_images)
print(f"平均処理時間: {avg_time:.4f}秒")
print(f"処理可能FPS: {fps:.1f}")
# 詳細分析
unique_labels = list(set(confusion_data['true_labels']))
print("\n=== クラス別精度 ===")
for label in unique_labels:
true_count = confusion_data['true_labels'].count(label)
correct_count = sum(1 for t, p in zip(confusion_data['true_labels'],
confusion_data['pred_labels'])
if t == label and p == label)
class_accuracy = correct_count / true_count if true_count > 0 else 0
print(f"{label}: {class_accuracy:.3f} ({correct_count}/{true_count})")
return {
'accuracy': accuracy,
'avg_processing_time': avg_time,
'fps': fps,
'confusion_data': confusion_data
}
# 性能評価の実行
def run_performance_evaluation():
"""
性能評価の実行例
"""
# テストデータの生成
def create_test_dataset():
test_data = {}
for i, person in enumerate(['山田', '田中', '佐藤']):
# 各人物について5枚のテスト画像
test_images = []
for j in range(5):
np.random.seed(42 + i * 10 + j)
base_intensity = 0.3 + i * 0.1
face_image = np.random.normal(base_intensity, 0.1, (64, 64))
face_image = np.clip(face_image, 0, 1)
test_images.append(face_image)
test_data[person] = test_images
return test_data
# 顔認識システムの準備
face_system = FaceRecognitionSystem()
# 登録用データ
for i, person in enumerate(['山田', '田中', '佐藤']):
registration_images = []
for j in range(3):
np.random.seed(100 + i * 10 + j)
base_intensity = 0.3 + i * 0.1
face_image = np.random.normal(base_intensity, 0.1, (64, 64))
face_image = np.clip(face_image, 0, 1)
registration_images.append(face_image)
face_system.register_face(person, registration_images)
# テストデータ
test_data = create_test_dataset()
# 評価実行
evaluator = FaceRecognitionEvaluator()
results = evaluator.generate_performance_report(face_system, test_data)
return results
# 評価実行
performance_results = run_performance_evaluation()
まとめ
画像分類と顔認識は、深層学習技術の進歩により高精度な実装が可能になりました。CNNを用いた画像分類システムでは、適切なアーキテクチャ設計とデータ拡張により、様々な画像認識タスクに対応できます。
顔認識システムでは、顔検出、特徴抽出、マッチングの各段階での最適化が重要です。リアルタイム処理や品質評価を組み込むことで、実用的なシステムの構築が可能です。
継続的な性能評価とモデル改善により、変化する要求に対応した画像認識システムを維持できます。転移学習やファインチューニングを活用することで、少ないデータでも高精度なモデルの構築が実現できます。
■テックジム「AIエンジニア養成コース」
■プロンプトだけでオリジナルアプリを開発・公開してみた!!
■AI時代の第一歩!「AI駆動開発コース」はじめました!
テックジム東京本校で先行開始。
■テックジム東京本校
「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。
<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。
<月1開催>放送作家による映像ディレクター養成講座
<オンライン無料>ゼロから始めるPython爆速講座