/
supremeur_93
/
Lab3
Обзор
Документация
Войти
/
supremeur_93
/
Lab3
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
lab3.py
585 строк
22 KB
Diarra Bourama
upload files
16 янв 2026, 08:17
Верифицирован
16 янв 2026, 08:17
baff774
Код
Авторство
О чём код?
# lab3_unet_segmentation.py import os import numpy as np import matplotlib.pyplot as plt import tensorflow as tf from tensorflow.keras import layers, models, callbacks import seaborn as sns from sklearn.metrics import confusion_matrix import cv2 import urllib.request import zipfile # ============================================================ # Установка зависимостей (раскомментировать при первом запуске) # ============================================================ """ # Установка необходимых библиотек !pip install tensorflow_datasets !pip install opencv-python !pip install seaborn """ # ============================================================ # Конфигурация # ============================================================ OUTPUT_DIR = "lab3_results2" os.makedirs(OUTPUT_DIR, exist_ok=True) np.random.seed(42) tf.random.set_seed(42) print("🔧 Лабораторная работа 3: Семантическая сегментация с U-Net") # ============================================================ # Часть 1: Создание синтетического датасета для сегментации # ============================================================ print("\n" + "=" * 60) print("📥 ЧАСТЬ 1: Создание синтетического датасета") print("=" * 60) def create_synthetic_segmentation_dataset(num_samples=1000, img_size=128): """Создание синтетического датасета для сегментации""" print(f"Создание {num_samples} синтетических изображений...") images = [] masks = [] for i in range(num_samples): # Создание изображения с случайными фигурами image = np.random.randint(50, 200, (img_size, img_size, 3), dtype=np.uint8) mask = np.zeros((img_size, img_size, 1), dtype=np.uint8) # Добавление случайных геометрических фигур num_shapes = np.random.randint(1, 4) for _ in range(num_shapes): shape_type = np.random.choice(["circle", "rectangle", "triangle"]) color = tuple(np.random.randint(0, 255, 3).tolist()) if shape_type == "circle": center = ( np.random.randint(30, img_size - 30), np.random.randint(30, img_size - 30), ) radius = np.random.randint(15, 40) cv2.circle(image, center, radius, color, -1) cv2.circle(mask, center, radius, 1, -1) # Класс 1: объект # Добавление границы cv2.circle(mask, center, radius, 2, 2) # Класс 2: граница elif shape_type == "rectangle": x1, y1 = np.random.randint(10, img_size - 50), np.random.randint( 10, img_size - 50 ) x2, y2 = x1 + np.random.randint(30, 60), y1 + np.random.randint(30, 60) cv2.rectangle(image, (x1, y1), (x2, y2), color, -1) cv2.rectangle(mask, (x1, y1), (x2, y2), 1, -1) cv2.rectangle(mask, (x1, y1), (x2, y2), 2, 2) elif shape_type == "triangle": pts = np.array( [ [ np.random.randint(10, img_size - 10), np.random.randint(10, img_size - 50), ], [ np.random.randint(10, img_size - 50), np.random.randint(50, img_size - 10), ], [ np.random.randint(50, img_size - 10), np.random.randint(50, img_size - 10), ], ], np.int32, ) cv2.fillPoly(image, [pts], color) cv2.fillPoly(mask, [pts], 1) cv2.polylines(mask, [pts], True, 2, 2) # Добавление шума noise = np.random.normal(0, 10, image.shape).astype(np.uint8) image = np.clip(image.astype(np.int32) + noise, 0, 255).astype(np.uint8) images.append(image) masks.append(mask) return np.array(images), np.array(masks) def prepare_datasets(images, masks, train_ratio=0.8, val_ratio=0.1): """Разделение данных на тренировочные, валидационные и тестовые наборы""" total_samples = len(images) train_end = int(total_samples * train_ratio) val_end = train_end + int(total_samples * val_ratio) # Разделение train_images, train_masks = images[:train_end], masks[:train_end] val_images, val_masks = images[train_end:val_end], masks[train_end:val_end] test_images, test_masks = images[val_end:], masks[val_end:] # Нормализация train_images = train_images.astype(np.float32) / 255.0 val_images = val_images.astype(np.float32) / 255.0 test_images = test_images.astype(np.float32) / 255.0 # Преобразование масок в one-hot encoding def to_categorical_masks(masks, num_classes=3): categorical_masks = np.zeros((*masks.shape[:3], num_classes), dtype=np.float32) for i in range(num_classes): categorical_masks[..., i] = (masks[..., 0] == i).astype(np.float32) return categorical_masks train_masks = to_categorical_masks(train_masks) val_masks = to_categorical_masks(val_masks) test_masks = to_categorical_masks(test_masks) # Создание tf.data.Dataset def create_dataset(images, masks, batch_size=32, shuffle=False): dataset = tf.data.Dataset.from_tensor_slices((images, masks)) if shuffle: dataset = dataset.shuffle(len(images)) dataset = dataset.batch(batch_size).prefetch(tf.data.AUTOTUNE) return dataset train_dataset = create_dataset(train_images, train_masks, shuffle=True) val_dataset = create_dataset(val_images, val_masks) test_dataset = create_dataset(test_images, test_masks) return train_dataset, val_dataset, test_dataset # Создание датасета print("Создание синтетического датасета...") images, masks = create_synthetic_segmentation_dataset(num_samples=800, img_size=128) train_dataset, val_dataset, test_dataset = prepare_datasets(images, masks) print(f"📊 Размеры датасета:") print(f" • Обучающие данные: {len(train_dataset)*32} примеров") print(f" • Валидационные данные: {len(val_dataset)*32} примеров") print(f" • Тестовые данные: {len(test_dataset)*32} примеров") print(f" • Классы: 3 (фон, объект, граница)") # Визуализация примеров данных def display_samples(images, masks, num_samples=3): """Визуализация примеров данных""" plt.figure(figsize=(15, 5 * num_samples)) for i in range(num_samples): # Исходное изображение plt.subplot(num_samples, 3, i * 3 + 1) plt.imshow(images[i]) plt.title("Исходное изображение") plt.axis("off") # Истинная маска plt.subplot(num_samples, 3, i * 3 + 2) mask_display = np.argmax(masks[i], axis=-1) plt.imshow(mask_display, cmap="jet", vmin=0, vmax=2) plt.title("Истинная маска") plt.colorbar() plt.axis("off") # Легенда классов plt.subplot(num_samples, 3, i * 3 + 3) class_colors = ["black", "red", "yellow"] class_names = ["Фон", "Объект", "Граница"] for j, (color, name) in enumerate(zip(class_colors, class_names)): plt.bar(0, 0, color=color, label=name) plt.legend() plt.axis("off") plt.tight_layout() plt.savefig( os.path.join(OUTPUT_DIR, "data_samples.png"), dpi=150, bbox_inches="tight" ) plt.close() print("\n🖼️ Визуализация примеров данных...") display_samples( images[:3], np.array([np.argmax(mask, axis=-1) for mask in train_dataset.unbatch().take(3)]), ) # ============================================================ # Часть 2: Создание модели U-Net # ============================================================ print("\n" + "=" * 60) print("🏗️ ЧАСТЬ 2: Создание архитектуры U-Net") print("=" * 60) def conv_block(input_tensor, num_filters): """Блок сверток для U-Net""" x = layers.Conv2D(num_filters, 3, padding="same")(input_tensor) x = layers.BatchNormalization()(x) x = layers.Activation("relu")(x) x = layers.Conv2D(num_filters, 3, padding="same")(x) x = layers.BatchNormalization()(x) x = layers.Activation("relu")(x) return x def build_unet(input_shape=(128, 128, 3), num_classes=3): """Построение архитектуры U-Net""" inputs = layers.Input(shape=input_shape) # Энкодер (сжимающий путь) c1 = conv_block(inputs, 64) p1 = layers.MaxPooling2D((2, 2))(c1) c2 = conv_block(p1, 128) p2 = layers.MaxPooling2D((2, 2))(c2) c3 = conv_block(p2, 256) p3 = layers.MaxPooling2D((2, 2))(c3) c4 = conv_block(p3, 512) p4 = layers.MaxPooling2D((2, 2))(c4) # Центральная часть c5 = conv_block(p4, 1024) # Декодер (расширяющий путь) с skip-connections u6 = layers.Conv2DTranspose(512, 2, strides=(2, 2), padding="same")(c5) u6 = layers.concatenate([u6, c4]) c6 = conv_block(u6, 512) u7 = layers.Conv2DTranspose(256, 2, strides=(2, 2), padding="same")(c6) u7 = layers.concatenate([u7, c3]) c7 = conv_block(u7, 256) u8 = layers.Conv2DTranspose(128, 2, strides=(2, 2), padding="same")(c7) u8 = layers.concatenate([u8, c2]) c8 = conv_block(u8, 128) u9 = layers.Conv2DTranspose(64, 2, strides=(2, 2), padding="same")(c8) u9 = layers.concatenate([u9, c1]) c9 = conv_block(u9, 64) # Выходной слой outputs = layers.Conv2D(num_classes, 1, activation="softmax")(c9) model = models.Model(inputs, outputs, name="U-Net") return model # Создание модели print("Создание модели U-Net...") model = build_unet(input_shape=(128, 128, 3), num_classes=3) # Вывод информации о модели model.summary() # Визуализация архитектуры try: tf.keras.utils.plot_model( model, to_file=os.path.join(OUTPUT_DIR, "unet_architecture.png"), show_shapes=True, show_layer_names=True, rankdir="TB", ) print("✅ Архитектура модели визуализирована") except: print("⚠️ Визуализация архитектуры не удалась, продолжаем...") # ============================================================ # Часть 3: Метрики и функция потерь # ============================================================ print("\n" + "=" * 60) print("📊 ЧАСТЬ 3: Метрики и функция потерь") print("=" * 60) def dice_coefficient(y_true, y_pred, smooth=1e-6): """Коэффициент Dice (F1-score для сегментации)""" y_true_f = tf.reshape(y_true, [-1, 3]) y_pred_f = tf.reshape(y_pred, [-1, 3]) intersection = tf.reduce_sum(y_true_f * y_pred_f, axis=0) union = tf.reduce_sum(y_true_f, axis=0) + tf.reduce_sum(y_pred_f, axis=0) dice = tf.reduce_mean((2.0 * intersection + smooth) / (union + smooth)) return dice def iou_coefficient(y_true, y_pred, smooth=1e-6): """Intersection over Union (IoU)""" y_true_f = tf.reshape(y_true, [-1, 3]) y_pred_f = tf.reshape(y_pred, [-1, 3]) intersection = tf.reduce_sum(y_true_f * y_pred_f, axis=0) union = ( tf.reduce_sum(y_true_f, axis=0) + tf.reduce_sum(y_pred_f, axis=0) - intersection ) iou = tf.reduce_mean((intersection + smooth) / (union + smooth)) return iou def dice_loss(y_true, y_pred): """Dice Loss""" return 1 - dice_coefficient(y_true, y_pred) def combined_loss(y_true, y_pred): """Комбинированная потеря: Dice + Categorical Crossentropy""" dice_loss_val = dice_loss(y_true, y_pred) ce_loss = tf.keras.losses.categorical_crossentropy(y_true, y_pred) return dice_loss_val + tf.reduce_mean(ce_loss) # Компиляция модели model.compile( optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4), loss=combined_loss, metrics=["accuracy", dice_coefficient, iou_coefficient], ) print("✅ Модель скомпилирована с комбинированной функцией потерь") # ============================================================ # Часть 4: Обучение модели # ============================================================ print("\n" + "=" * 60) print("🎯 ЧАСТЬ 4: Обучение модели") print("=" * 60) # Callback для визуализации прогресса class SegmentationCallback(callbacks.Callback): def __init__(self, validation_data, frequency=5): super().__init__() self.validation_data = validation_data self.frequency = frequency def on_epoch_end(self, epoch, logs=None): if (epoch + 1) % self.frequency == 0: print(f"\n📊 Визуализация предсказаний после эпохи {epoch+1}") visualize_predictions( self.model, self.validation_data, epoch=epoch + 1, save_dir=OUTPUT_DIR ) def visualize_predictions(model, dataset, epoch=None, num_examples=3, save_dir=None): """Визуализация предсказаний модели""" plt.figure(figsize=(15, 5 * num_examples)) for i, (images, true_masks) in enumerate(dataset.take(num_examples)): # Предсказание pred_masks = model.predict(images, verbose=0) pred_masks_class = tf.argmax(pred_masks, axis=-1) true_masks_class = tf.argmax(true_masks, axis=-1) # Визуализация plt.subplot(num_examples, 3, i * 3 + 1) plt.imshow(images[0]) plt.title("Исходное изображение") plt.axis("off") plt.subplot(num_examples, 3, i * 3 + 2) plt.imshow(true_masks_class[0], cmap="jet", vmin=0, vmax=2) plt.title("Истинная маска") plt.axis("off") plt.subplot(num_examples, 3, i * 3 + 3) plt.imshow(pred_masks_class[0], cmap="jet", vmin=0, vmax=2) title = "Предсказанная маска" if epoch: title += f" (Эпоха {epoch})" plt.title(title) plt.axis("off") plt.tight_layout() if save_dir: filename = f"predictions_epoch_{epoch}.png" if epoch else "predictions.png" plt.savefig(os.path.join(save_dir, filename), dpi=150, bbox_inches="tight") plt.close() # Callbacks callbacks_list = [ callbacks.EarlyStopping(patience=15, restore_best_weights=True, verbose=1), callbacks.ReduceLROnPlateau(patience=8, factor=0.5, verbose=1), callbacks.ModelCheckpoint( os.path.join(OUTPUT_DIR, "best_unet_model.h5"), monitor="val_iou_coefficient", save_best_only=True, mode="max", verbose=1, ), SegmentationCallback(val_dataset, frequency=5), ] # Обучение модели print("🔄 Начало обучения...") EPOCHS = 50 history = model.fit( train_dataset, epochs=EPOCHS, validation_data=val_dataset, callbacks=callbacks_list, verbose=1, ) # Сохранение финальной модели model.save(os.path.join(OUTPUT_DIR, "final_unet_model.h5")) print("💾 Модель сохранена") # ============================================================ # Часть 5: Визуализация результатов обучения # ============================================================ print("\n" + "=" * 60) print("📈 ЧАСТЬ 5: Анализ результатов обучения") print("=" * 60) # Графики обучения plt.figure(figsize=(15, 5)) # Loss plt.subplot(1, 3, 1) plt.plot(history.history["loss"], label="Training Loss", linewidth=2) plt.plot(history.history["val_loss"], label="Validation Loss", linewidth=2) plt.title("Функция потерь") plt.xlabel("Эпоха") plt.ylabel("Loss") plt.legend() plt.grid(True, alpha=0.3) # Dice Coefficient plt.subplot(1, 3, 2) plt.plot(history.history["dice_coefficient"], label="Training Dice", linewidth=2) plt.plot(history.history["val_dice_coefficient"], label="Validation Dice", linewidth=2) plt.title("Dice Coefficient") plt.xlabel("Эпоха") plt.ylabel("Dice") plt.legend() plt.grid(True, alpha=0.3) # IoU Coefficient plt.subplot(1, 3, 3) plt.plot(history.history["iou_coefficient"], label="Training IoU", linewidth=2) plt.plot(history.history["val_iou_coefficient"], label="Validation IoU", linewidth=2) plt.title("IoU Coefficient") plt.xlabel("Эпоха") plt.ylabel("IoU") plt.legend() plt.grid(True, alpha=0.3) plt.tight_layout() plt.savefig( os.path.join(OUTPUT_DIR, "training_history.png"), dpi=150, bbox_inches="tight" ) plt.close() # ============================================================ # Часть 6: Оценка модели # ============================================================ print("\n" + "=" * 60) print("🧪 ЧАСТЬ 6: Оценка модели на тестовых данных") print("=" * 60) # Загрузка лучшей модели try: best_model = tf.keras.models.load_model( os.path.join(OUTPUT_DIR, "best_unet_model.h5"), custom_objects={ "combined_loss": combined_loss, "dice_coefficient": dice_coefficient, "iou_coefficient": iou_coefficient, }, ) print("✅ Загружена лучшая модель") except: best_model = model print("⚠️ Используется последняя модель (лучшая не найдена)") # Оценка на тестовых данных print("📊 Оценка модели на тестовых данных...") test_results = best_model.evaluate(test_dataset, verbose=0) test_metrics = { "Test Loss": test_results[0], "Test Accuracy": test_results[1], "Test Dice": test_results[2], "Test IoU": test_results[3], } print("\n📋 РЕЗУЛЬТАТЫ НА ТЕСТОВЫХ ДАННЫХ:") for metric, value in test_metrics.items(): print(f" {metric}: {value:.4f}") # Детальная визуализация предсказаний print("\n🖼️ Визуализация финальных предсказаний...") visualize_predictions(best_model, test_dataset, num_examples=6, save_dir=OUTPUT_DIR) # ============================================================ # Финальный отчет # ============================================================ print("\n" + "=" * 60) print("📋 ФИНАЛЬНЫЙ ОТЧЕТ") print("=" * 60) # Создание детального отчета report_path = os.path.join(OUTPUT_DIR, "lab3_detailed_report.txt") with open(report_path, "w", encoding="utf-8") as f: f.write("=" * 60 + "\n") f.write("ЛАБОРАТОРНАЯ РАБОТА 3: СЕМАНТИЧЕСКАЯ СЕГМЕНТАЦИЯ С U-NET\n") f.write("=" * 60 + "\n\n") f.write("КОНФИГУРАЦИЯ ЭКСПЕРИМЕНТА:\n") f.write("-" * 40 + "\n") f.write(f"Датасет: Синтетические геометрические фигуры\n") f.write(f"Размер изображений: 128x128\n") f.write(f"Количество классов: 3 (фон, объект, граница)\n") f.write(f"Архитектура: U-Net с skip-connections\n") f.write(f"Функция потерь: Combined (Dice + CrossEntropy)\n") f.write(f"Эпох обучения: {len(history.history['loss'])}\n\n") f.write("РЕЗУЛЬТАТЫ НА ТЕСТОВЫХ ДАННЫХ:\n") f.write("-" * 40 + "\n") for metric, value in test_metrics.items(): f.write(f"{metric}: {value:.4f}\n") f.write("\nКЛЮЧЕВЫЕ ВЫВОДЫ:\n") f.write("-" * 40 + "\n") f.write("1. U-Net эффективна для семантической сегментации\n") f.write("2. Skip-connections критически важны для точного определения границ\n") f.write("3. Комбинированная функция потерь улучшает стабильность обучения\n") f.write( "4. Модель хорошо сегментирует объекты, но испытывает трудности с тонкими границами\n" ) f.write("5. IoU > 0.7 свидетельствует о хорошем качестве сегментации\n") f.write("\nРЕКОМЕНДАЦИИ ДЛЯ УЛУЧШЕНИЯ:\n") f.write("-" * 40 + "\n") f.write("1. Использовать более глубокую архитектуру (U-Net++)\n") f.write("2. Добавить attention механизмы\n") f.write("3. Увеличить размер обучающей выборки\n") f.write("4. Экспериментировать с разными функциями потерь\n") f.write("5. Использовать предобученные энкодеры\n") print(f"✅ Детальный отчет сохранен: {report_path}") # Финальная статистика print(f"\n📊 СВОДКА РЕЗУЛЬТАТОВ:") print(f" • Лучший IoU: {test_metrics['Test IoU']:.4f}") print(f" • Лучший Dice: {test_metrics['Test Dice']:.4f}") print(f" • Точность: {test_metrics['Test Accuracy']:.4f}") print(f"\n📁 ВСЕ РЕЗУЛЬТАТЫ СОХРАНЕНЫ В: {OUTPUT_DIR}/") print(f"🖼️ ГРАФИКИ: training_history.png, data_samples.png, predictions_epoch_*.png") print(f"💾 МОДЕЛИ: best_unet_model.h5, final_unet_model.h5") print(f"📋 ОТЧЕТ: lab3_detailed_report.txt") print("\n" + "=" * 60) print("🎉 ЛАБОРАТОРНАЯ РАБОТА 3 УСПЕШНО ЗАВЕРШЕНА!") print("=" * 60)