Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Компьютерное зрение. Практикум

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
☆
plt.imshow(result['mask'])
Результат можно посмотреть на рисунке 4.5.
Рисунок 4.5 – Результат сжатия изображения с помощью дополнения
изображения до квадратного размера
Опишем необходимую предобработку для валидации и обучающей выборки
IMG_SIZE = 256 INFER_HEIGHT = IMG_SIZE INFER_WIDTH = IMG_SIZE
defget_training_augmentation(): train_transform = [
albu.LongestMaxSize(max_size=INFER_HEIGHT, always_apply=True), albu.PadIfNeeded(min_height=int(INFER_HEIGHT*1.1),
min_width=int(INFER_WIDTH*1.1), border_mode=2, always_apply=True),
albu.RandomCrop(height=INFER_HEIGHT, width=INFER_WIDTH,
always_apply=True),
81
#Добивим небольшие размытия на изображении albu.OneOf( [ albu.Sharpen(alpha=(0.1, 0.2), lightness=(0.1, 0.2), p=0.5), # Уменьшение
вероятности и уменьшение интенсивности
albu.Blur(blur_limit=[1, 3], p=0.5), # Уменьшение вероятности и уменьшение
интенсивности
albu.GaussNoise(var_limit=(1, 5), p=0.5), # Уменьшение вероятности и
уменьшение интенсивности
], p=0.7, # Уменьшение вероятности применения любой аугментации ),
#Сделаем изменения яркости и контрастности albu.OneOf( [ albu.RandomBrightnessContrast(brightness_limit=0.1, contrast_limit=0.1, p=0.5),
# Уменьшение вероятности и уменьшение интенсивности
albu.HueSaturationValue(hue_shift_limit=5, sat_shift_limit=10, val_shift_limit=5,
p=0.5), # Уменьшение вероятности и уменьшение интенсивности
albu.RGBShift(r_shift_limit=10, g_shift_limit=10, b_shift_limit=10, p=0.5), #
Уменьшение вероятности и уменьшение интенсивности
], p=0.7, # Уменьшение вероятности применения любой аугментации цвета ), ] return albu.Compose(train_transform)
defget_validation_augmentation():
82
#Для валидации сделаем лишь изменение размера изображения до 256
пикселей
test_transform = [albu.LongestMaxSize(max_size=INFER_HEIGHT,
always_apply=True),
albu.PadIfNeeded(min_height=INFER_HEIGHT, min_width=INFER_WIDTH,
border_mode=2, always_apply=True),
albu.CenterCrop(height=INFER_HEIGHT, width=INFER_WIDTH,
always_apply=True)]
return albu.Compose(test_transform)
defto_tensor(x, **kwargs): x = x.transpose(2, 0, 1).astype('float32') return torch.tensor(x)
defget_preprocessing(preprocessing_fn):
# Осуществит стартовую нормализацию данных согласно своим значениям
или готовым для imagenet
_transform = [ albu.Lambda(image=preprocessing_fn), albu.Lambda(image=to_tensor, mask=to_tensor), ] return albu.Compose(_transform)
Загрузим предобученную модель.
import segmentation_models_pytorch as smp from segmentation_models_pytorch import utils
# Создадим нашу модель.
83
EPOCHS = 10 ENCODER = "resnet34" ENCODER_WEIGHTS = "imagenet" ACTIVATION = 'softmax2d' DEVICE = torch.device('cuda'if torch.cuda.is_available() else'cpu')
BATCH_SIZE = 10
INIT_LR = 0.0005 LR_DECREASE_STEP = 4
LR_DECREASE_COEF = 2# LR будет разделен на этот коэфраз в
LR_DECREASE_STEP эпох
loss = utils.losses.DiceLoss()
model = smp.Unet( encoder_name=ENCODER, # Архитектура кодировщика. В качестве примера
мы выбрали resnet34
encoder_weights="imagenet", # Чтобы не обучать кодировщик мы возьмем его
веса обученные на датасете ImageNet
in_channels=3, # Сколько каналов имеет изображение которое подается на
вход. Так как у нас RGB изображение то подается три канала
classes=len(classes), # Сколько каналов должно быть на выходе нейронной
сети. В данном случае их столько сколько классов у нас есть
activation=ACTIVATION# Так как у нас много классов, то на выходе мы
должны для каждого пикселя получить вероятность принадлежности элемента к
каждому классу
)
Получим параметры нормализации значений изображения. Возьмем
обыкновенное RGB изображение, возьмем один канал этого изображения. У нас
84
получится двумерная матрица размерами HxW в которой записаны значения
интенсивности одного из каналов. Теперь у нас получается, что некоторые пиксели
изображения могут быть слишком яркими их значение интесивности высокое, в то
же время на изображении могут быть и пиксели, у которых низкая интенсивность.
Во время обучения мы умножаем эти значения на подбираемые веса, но так как
разброс в значений интесивности велик процесс обучения может быть не
стабильным и долго сходится. Поэтому мы усредним значения интенсивности
каждого пикселя, то есть применим нормализациию. Мы можем получить
необходимые параметры нормализации для предобученной модели с помощью
функции представленной ниже
preprocessing_fn = smp.encoders.get_preprocessing_fn(ENCODER,
ENCODER_WEIGHTS)
train_dataset = SegmentationDataset(x_train, y_train, num_of_classes=len(classes), augmentation=get_training_augmentation(), preprocessing=get_preprocessing(preprocessing_fn))
val_dataset = SegmentationDataset(x_val, y_val, num_of_classes=len(classes), augmentation=get_validation_augmentation(), preprocessing=get_preprocessing(preprocessing_fn))
Обновление весов нейронной сети происходит не после вычисления
результатов одного конкретного изображения. Если бы это было так, то, во-первых,
это занимало бы слишком много времени на обучение, а во вторых сделало процесс
обучения менее стабильным. Поэтому сначала вычисляется результат работы
нейронной сети для группы изображений, потом полученные результаты на этой
группе изображений усредняются и на основе среднего результата корректируются
85
веса нашей сети.
Такие группы называются батчами. Библиотека PyTorch содержит в себе уже
готовый инструментарий для работы с батчами. Для создания батчей используется
объект DataLoader. DataLoader позволяет нам установить размер батча с помощью
параметра batch_size, параметр shuffle означает будет ли батч формироваться из
случайных изображений из выборки (если True) или будут поочередно извлекаться
из выборки изображения.
from torch.utils.data import DataLoader n_cpu = os.cpu_count() #считает количество ядер в процессоре
train_loader = DataLoader(train_dataset, batch_size=10, shuffle=True,
num_workers=n_cpu)
valid_loader = DataLoader(val_dataset, batch_size=5, shuffle=False,
num_workers=n_cpu)
В качестве метрики в задаче сегментации используется метрика IoU
(Intersection over Union).
Вычисляется она как площадь пересечения где значения классов пикселей
между маской сгенерированной моделью и маской, которая была в датасете
совпадают поделенное на общую площадь изображения. Диапазон значений у
метрики от 0 до 1. Чем ближе значение к нулю, тем менее точно нейросеть сумела
сегментировать изображение.
86
Рисунок 4.6 – Наглядное графическое представление формулы метрики
IoU(Intersection over Union)
metrics = [ utils.metrics.Fscore(), utils.metrics.IoU() ]
optimizer = torch.optim.Adam([ dict(params=model.parameters(), lr=INIT_LR), ])
#Cоздадим описание как будет происходить эпоха обучения и валидации
train_epoch = utils.train.TrainEpoch( model, loss=loss, metrics=metrics, optimizer=optimizer, device=DEVICE, verbose=True, #выводить на экран информацию с процессом обучения )
87
valid_epoch = utils.train.ValidEpoch( model, loss=loss, metrics=metrics, device=DEVICE, verbose=True, )
#Создадим директорию куда будем сохранять обученную модель !mkdir models
max_score = 0
loss_logs = {"train": [], "val": []} metric_logs = {"train": [], "val": []} for i inrange(0, EPOCHS):
print('\nEpoch: {}'.format(i)) train_logs = train_epoch.run(train_loader) train_loss, train_metric, train_metric_IOU = list(train_logs.values()) loss_logs["train"].append(train_loss) metric_logs["train"].append(train_metric_IOU)
valid_logs = valid_epoch.run(valid_loader) val_loss, val_metric, val_metric_IOU = list(valid_logs.values()) loss_logs["val"].append(val_loss) metric_logs["val"].append(val_metric_IOU)
if max_score < valid_logs['iou_score']:
88
max_score = valid_logs['iou_score']
#Можно сохранить наилучшую модель в формате .pth но тогда вы сохраняете
только веса самой модели
torch.save(model, 'models/best_model_new.pth')
trace_image = torch.randn(BATCH_SIZE, 3, INFER_HEIGHT, INFER_WIDTH) #Предпочтительный способ сохранения моделей traced_model = torch.jit.trace(model, trace_image.to(DEVICE)) torch.jit.save(traced_model, 'models/best_model_new.pt') print('Model saved!')
print("LR:", optimizer.param_groups[0]['lr']) if i >0and i % LR_DECREASE_STEP == 0: print('Decrease decoder learning rate') optimizer.param_groups[0]['lr'] /= LR_DECREASE_COEF
Посмотрим графики обучения нейронной сети. Можно заметить, что Loss
функция.
Рисунок 4.7 – а) График значений функции потерь в зависимости от
количества эпох обучения модели. б) График значении метрики IoU в зависимости от количества эпох обучения модели
89
Загрузим наилучше обученную модель.
best_model = torch.jit.load('models/best_model_new.pt', map_location=DEVICE)
Отобразим результат работы нашей обученной модели.
test_example = val_dataset[15] #произвольное изображение из валидационной
выборки
test_img = test_example[0].unsqueeze(0) test_img = test_img.to(torch.float32).cuda()
#чтобызагрузитьвнейросетьнужноперевеститензорнаустройство cuda
gt_mask = test_example[1] pred_mask = model(test_img)[0] #возвращает [9,256,256]
pred_mask = multichannel_to_matrix(pred_mask) gt_mask = multichannel_to_matrix(gt_mask)
test_img = test_img.cpu() print(test_img[0].shape)
plt.figure(figsize=(10, 4))
plt.subplot(1,3,1) plt.imshow(test_img[0].permute(1,2,0))
plt.subplot(1,3,2) visualize_mask(pred_mask.cpu(), title="Как предсказал анейросеть",
90
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]