Глубокое обучение. Трансферное обучение может сыграть жизненно важную роль в достижении лучших результатов в моделях глубокого обучения.

Медицинские изображения важны для клинической диагностики и принятия решений. Модальность изображения - важный первичный шаг, так как он может помочь клиницистам получить доступ к необходимым медицинским изображениям в поисковых системах. Традиционные методы классификации модальности зависят от выбора созданных вручную функций и требуют четкого понимания предшествующих знаний в предметной области. Подход к обучению функциям может эффективно обнаруживать визуальные характеристики различных модальностей, но он ограничен количеством обучающих наборов данных. Чтобы преодолеть отсутствие помеченных данных, с одной стороны, мы берем глубокие сверточные нейронные сети (VGGNet, ResNet) с разной глубиной, предварительно обученные в ImageNet, исправляем большинство более ранних слоев, чтобы сохранить общие черты естественных изображений, и только обучаем их часть более высокого уровня на ImageCLEF, чтобы изучить специфические для предметной области особенности медицинских фигур.

Что такое трансферное обучение?

При трансферном обучении знания уже обученной модели глубокого обучения применяются к другой, но связанной проблеме. Например, если вы обучили простой классификатор предсказывать, есть ли на изображении кошка, вы можете использовать знания, полученные моделью во время обучения, для распознавания других животных.

При трансферном обучении мы в основном пытаемся использовать то, что было изучено в одной задаче, для улучшения обобщения в другой. Мы переносим веса, которые сеть получила в «задаче A», в новую «задачу B.»

Чем трансферное обучение отличается от традиционного машинного обучения?

Подходы к передаче обучения

1. ОБУЧЕНИЕ МОДЕЛИ ДЛЯ ПОВТОРНОГО ИСПОЛЬЗОВАНИЯ

Представьте, что вы хотите решить задачу A, но у вас недостаточно данных для обучения глубокой нейронной сети. Один из способов обойти это - найти связанную задачу B с большим количеством данных. Обучите глубокую нейронную сеть по задаче B и используйте модель в качестве отправной точки для решения задачи A. Будет ли вам нужно использовать всю модель или только несколько слоев, во многом зависит от проблемы, которую вы пытаетесь решить.

Если у вас одинаковые входные данные в обеих задачах, возможно, можно будет повторно использовать модель и делать прогнозы для ваших новых входных данных. В качестве альтернативы можно изучить изменение и повторное обучение различных слоев для конкретных задач и выходного уровня.

2. ИСПОЛЬЗОВАНИЕ ПРЕДВАРИТЕЛЬНОЙ МОДЕЛИ

Второй подход - использовать уже предварительно обученную модель. Таких моделей очень много, поэтому обязательно проведите небольшое исследование. Сколько слоев использовать повторно и сколько повторно обучать зависит от проблемы.

Keras, например, предоставляет девять предварительно обученных моделей, которые можно использовать для обучения передачи, прогнозирования, извлечения функций и точной настройки. Вы можете найти эти модели, а также несколько кратких руководств по их использованию, здесь. Есть также много исследовательских институтов, которые выпускают обученные модели.

Этот тип трансферного обучения чаще всего используется в глубоком обучении.

3. ИЗВЛЕЧЕНИЕ ФУНКЦИЙ

Другой подход - использовать глубокое обучение, чтобы лучше всего представить вашу проблему, что означает поиск наиболее важных функций. Этот подход также известен как обучение представлению и часто может привести к гораздо более высокой производительности, чем может быть получено с помощью ручного представления.

Проект: Прогнозирование пневмонии с помощью трансферного обучения

Источник набора данных: https://drive.google.com/drive/folders/1Vz7QDBwASNCUgE8dzCWPLjiGOpEg1mS5?usp=sharing

Исследовательские статьи, на которые была сделана ссылка:

  1. Https://arxiv.org/abs/1403.6382
  2. Https://arxiv.org/abs/1411.1792

Код и подход

Импортируйте необходимые библиотеки

import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
import numpy as np
import torchvision
from torchvision import datasets, models, transforms
import matplotlib.pyplot as plt
import time
import os
import copy

Дополнение и нормализация данных для обучения и проверки

mean = np.array([0.5, 0.5, 0.5])
std = np.array([0.25, 0.25, 0.25])
data_transforms = {
    'train': transforms.Compose([
        transforms.RandomResizedCrop(224),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize(mean, std)
    ]),
    'val': transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize(mean, std)
    ]),
}

Загрузка данных. Для загрузки данных мы будем использовать пакеты torchvision и torch.utils.data.

data_dir = r'D:\Assign\X_ray\chest_xray\chest_xray'
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x),
                                          data_transforms[x])
                  for x in ['train', 'val']}
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=4,
                                             shuffle=True, num_workers=0)
              for x in ['train', 'val']}
dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}
class_names = image_datasets['train'].classes
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(class_names)

Визуализируйте несколько изображений

def imshow(inp, title):
    """Imshow for Tensor."""
    inp = inp.numpy().transpose((1, 2, 0))
    inp = std * inp + mean
    inp = np.clip(inp, 0, 1)
    plt.imshow(inp)
    plt.title(title)
    plt.show()
# Get a batch of training data
inputs, classes = next(iter(dataloaders['train']))
# Make a grid from batch
out = torchvision.utils.make_grid(inputs)
imshow(out, title=[class_names[x] for x in classes])

Обучение модели

  1. Планирование скорости обучения
  2. Сохранение лучшей модели
def train_model(model, criterion, optimizer, scheduler, num_epochs=25):
    since = time.time()
best_model_wts = copy.deepcopy(model.state_dict())
    best_acc = 0.0
for epoch in range(num_epochs):
        print('Epoch {}/{}'.format(epoch, num_epochs - 1))
        print('-' * 10)
# Each epoch has a training and validation phase
        for phase in ['train', 'val']:
            if phase == 'train':
                model.train()  # Set model to training mode
            else:
                model.eval()   # Set model to evaluate mode
running_loss = 0.0
            running_corrects = 0
# Iterate over data.
            for inputs, labels in dataloaders[phase]:
                inputs = inputs.to(device)
                labels = labels.to(device)
# forward
                # track history if only in train
                with torch.set_grad_enabled(phase == 'train'):
                    outputs = model(inputs)
                    _, preds = torch.max(outputs, 1)
                    loss = criterion(outputs, labels)
# backward + optimize only if in training phase
                    if phase == 'train':
                        optimizer.zero_grad()
                        loss.backward()
                        optimizer.step()
# statistics
                running_loss += loss.item() * inputs.size(0)
                running_corrects += torch.sum(preds == labels.data)
if phase == 'train':
                scheduler.step()
epoch_loss = running_loss / dataset_sizes[phase]
            epoch_acc = running_corrects.double() / dataset_sizes[phase]
print('{} Loss: {:.4f} Acc: {:.4f}'.format(
                phase, epoch_loss, epoch_acc))
# deep copy the model
            if phase == 'val' and epoch_acc > best_acc:
                best_acc = epoch_acc
                best_model_wts = copy.deepcopy(model.state_dict())
print()
time_elapsed = time.time() - since
    print('Training complete in {:.0f}m {:.0f}s'.format(
        time_elapsed // 60, time_elapsed % 60))
    print('Best val Acc: {:4f}'.format(best_acc))
# load best model weights
    model.load_state_dict(best_model_wts)
    return model

Тонкая настройка Convnet, загрузка предварительно обученной модели и сброс последнего полностью подключенного слоя.

model = models.resnet18(pretrained=True)
num_ftrs = model.fc.in_features
# Here the size of each output sample is set to 2.
# Alternatively, it can be generalized to nn.Linear(num_ftrs, len(class_names)).
model.fc = nn.Linear(num_ftrs, 2)
model = model.to(device)
criterion = nn.CrossEntropyLoss()
  1. StepLR Уменьшает скорость обучения каждой группы параметров на гамму каждые эпохи step_size
  2. Спад LR в 0,1 раза каждые 7 эпох
  3. Планирование скорости обучения должно применяться после обновления оптимизатора, например, вы должны написать свой код следующим образом:
    # для эпохи в диапазоне (100):
    # train (…)
    # validate (…)
    # scheduler.step ()
step_lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)
model = train_model(model, criterion, optimizer, step_lr_scheduler, num_epochs=10)

Теперь применяем обученную модель к нашей постановке задачи

model_conv = torchvision.models.resnet18(pretrained=True)
for param in model_conv.parameters():
    param.requires_grad = False
# Parameters of newly constructed modules have requires_grad=True by default
num_ftrs = model_conv.fc.in_features
model_conv.fc = nn.Linear(num_ftrs, 2)
model_conv = model_conv.to(device)
criterion = nn.CrossEntropyLoss()
# Observe that only parameters of final layer are being optimized as
# opposed to before.
optimizer_conv = optim.SGD(model_conv.fc.parameters(), lr=0.001, momentum=0.9)
# Decay LR by a factor of 0.1 every 6epochs
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_conv, step_size=7, gamma=0.1)
model_conv = train_model(model_conv, criterion, optimizer_conv,
                         exp_lr_scheduler, num_epochs=6)

Визуализируйте результат на тестовых изображениях

def visualize_model(model, num_images=2):
    was_training = model.training
    model.eval()
    images_so_far = 0
    fig = plt.figure()
with torch.no_grad():
        for i, (inputs, labels) in enumerate(dataloaders['val']):
            inputs = inputs.to(device)
            labels = labels.to(device)
outputs = model(inputs)
            _, preds = torch.max(outputs, 1)
for j in range(inputs.size()[0]):
                images_so_far += 1
                ax = plt.subplot(num_images//2, 2, images_so_far)
                ax.axis('off')
                ax.set_title('predicted: {}'.format(class_names[preds[j]]))
                imshow(inputs.cpu().data[j])
if images_so_far == num_images:
                    model.train(mode=was_training)
                    return
        model.train(mode=was_training)
visualize_model(model,num_images=4)