В предыдущем посте (здесь) мы загрузили и преобразовали пользовательские изображения из каталога наборов данных для обучения и проверки в соответствующим образом обработанные тензоры; Теперь мы готовы загрузить, изменить, обучить и протестировать существующую модель с нашими готовыми данными в четыре этапа:

  • Загрузка модели нейронной сети
  • Построение классификатора и обучение сети
  • Тестирование модифицированной сети
  • Сохранение КПП

Загрузка модели нейронной сети

Существует множество существующих нейронных сетей (NN), обученных на огромном количестве наборов данных, таких как Imagenet, Kaggle и репозиторий UCI, и это лишь некоторые из них. График ниже описывает такие общедоступные модели NN в шкале точности, достигнутой при зачатии, в зависимости от размера набора данных, используемого для обучения.

Чтобы загрузить модель NN предпочтительного типа, импортируйте пакет «models» из «torchvision» и вызовите желаемую модель с необходимыми параметрами:

#import models from torchvision
from torchvision import models
#build the pretrained model (vgg16 in this case)
model = models.vgg16(pretrained = True)

NB: Из огромного количества моделей выбрана «ВГГ-16». Для предварительно обученного параметра установлено значение True, потому что мы хотим начать построение из предварительно обученной модели с оптимизированными весами и смещениями.

Построение классификатора и обучение сети

Как и любая другая архитектура модели, vgg-16 состоит из большого количества слоев свертки и объединения для извлечения пространственных объектов с полностью связанными слоями в конце, состоящими из классификатора. Здесь вступает в игру самая техническая часть, известная как трансферное обучение.

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

#import OrderedDicted to corectly align the network layers
#import nn you use activation and dropout features
from collections import OrderedDict
from torch import nn
#create classifier
classifier
         =nn.Sequential(OrderedDict([('fc1', nn.Linear(25088, 512)),
                           ('relu', nn.ReLU()), 
                           ('dropout', nn.Dropout(p=0.337)),
                           ('fc2', nn.Linear(512, 102)),
                           ('output', nn.LogSoftmax(dim=1))
                             ]))
#replace the model's classifier with this new classifier 
#transfer learning connection applied here
model.classifier = classifier

NB: Здесь стоит отметить две вещи: размер ввода (в данном случае 25088) должен быть эквивалентен тому, который указан в сети, а размер вывода (102) должен быть эквивалентен количеству всех классов, представленных набором данных.

Теперь обучаем сеть. Для этого сначала определяется функция потерь (обычно используется кросс-энтропийная потеря) и оптимизатор сети (в данном случае стохастический градиентный спуск [SGD]) с соответствующими параметрами:

#import optimizer for 
from torch import optim
#define criteria and optimizer
criteria = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr = 0.005, momentum = 0.5)

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

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

  • Первая - это обучающая функция, которая принимает заданную модель, набор данных (обучающий загрузчик) и критерий потерь, а затем возвращает потери и точность, достигнутые для каждой эпохи, следующим образом:
#define training function
def train (model, loader, criterion, gpu):
    model.train()
    current_loss = 0
    current_correct = 0
    for train, y_train in iter(loader):
        if gpu:
            train, y_train = train.to('cuda'), y_train.to('cuda')
        optimizer.zero_grad()
        output = model.forward(train)
        _, preds = torch.max(output,1)
        loss = criterion(output, y_train)
        loss.backward()
        optimizer.step()
        current_loss += loss.item()*train.size(0)
        current_correct += torch.sum(preds == y_train.data)
    epoch_loss = current_loss / len(trainLoader.dataset)
    epoch_acc = current_correct.double() / len(trainLoader.dataset)
        
    return epoch_loss, epoch_acc
  • Вторая - это функция проверки, которая принимает заданную модель, набор данных (загрузчик проверки) и критерий потерь; он также возвращает потерю и точность для каждой эпохи следующим образом:
#define validation function
def validation (model, loader, criterion, gpu):
    model.eval()
    valid_loss = 0
    valid_correct = 0
    for valid, y_valid in iter(loader):
        if gpu:
            valid, y_valid = valid.to('cuda'), y_valid.to('cuda')
        output = model.forward(valid)
        valid_loss += criterion(output, y_valid).item()*valid.size(0)
        equal = (output.max(dim=1)[1] == y_valid.data)
        valid_correct += torch.sum(equal)#type(torch.FloatTensor)
    
    epoch_loss = valid_loss / len(validLoader.dataset)
    epoch_acc = valid_correct.double() / len(validLoader.dataset)
    
    return epoch_loss, epoch_acc

NB: Здесь стоит отметить две вещи. Функция обучения переключает модель в режим обучения и инициализирует градиенты оптимизатора до нуля перед обновлением. С другой стороны, функция проверки вместо этого переключает модель в режим оценки и получает обновленные градиенты от функции обучения для оценки.

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

#Initialize training params  
#freeze gradient parameters in pretrained model
for param in model.parameters():
    param.require_grad = False
#train and validate
epochs = 10  
epoch = 0
#send model to GPU
if args.gpu:
    model.to('cuda')
    
for e in range(epochs):
    epoch +=1
    print(epoch)
    with torch.set_grad_enabled(True):
        epoch_train_loss, epoch_train_acc = train(model,trainLoader, criteria, args.gpu)
    print("Epoch: {} Train Loss : {:.4f}  Train Accuracy: {:.4f}".format(epoch,epoch_train_loss,epoch_train_acc))
with torch.no_grad():
        epoch_val_loss, epoch_val_acc = validation(model, validLoader, criteria, args.gpu)
    print("Epoch: {} Validation Loss : {:.4f}  Validation Accuracy {:.4f}".format(epoch,epoch_val_loss,epoch_val_acc))

NB: Здесь стоит отметить две вещи: градиенты должны быть установлены истинными на этапе обучения, а затем отключены на этапе проверки перед вызовом соответствующих функций. Во-вторых, потери при проверке, как правило, должны быть ниже по сравнению с потерями при обучении в текущую эпоху (итерация), и в то же время точность проверки должна быть выше по сравнению с точностью обучения. В сочетании с последним наблюдением, если потери постоянно уменьшаются, а точность постоянно увеличивается, то вы имеете дело с созданием точной сети для вашего пользовательского набора данных.

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

Тестирование модифицированной сети

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

model.eval()
total = 0
correct = 0 
count = 0
#iterating for each sample in the test dataset once
for test, y_test in iter(testLoader):
    test, y_test = test.to('cuda'), y_test.to('cuda')
#Calculate the class probabilities (softmax) for img
    with torch.no_grad():
        output = model.forward(test)
        ps = torch.exp(output)
        _, predicted = torch.max(output.data,1)
        total += y_test.size(0)
        correct += (predicted == y_test).sum().item() 
        count += 1
        print("Accuracy of network on test images is ... {:.4f}....count: {}".format(100*correct/total,  count ))

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

С общей точки зрения следующий визуальный рисунок показывает, что алгоритм делает на данный момент:

Сохранение КПП

Поскольку мы хотим избежать потерь времени и вычислений, рекомендуется всегда сохранять контрольную точку модели. С облегчением следует отметить, что загруженная нами модель vgg-16 была контрольной точкой, изначально сохраненной кем-то другим, и мы использовали полученные в результате функции и веса модели, без необходимости обучать все с нуля. Мы также хотим сохранить нашу модель, чтобы позволить третьей стороне использовать ее, особенно если наборы данных похожи. В Torch есть функция save для сохранения наших моделей. Все, что требуется в качестве параметров, - это модель и путь к каталогу, в котором мы хотим сохранить контрольную точку:

#create the checkpoint and save every sensitive information starting #from the model state dictionary, model criterion, optimizer, to the #number of epochs
checkpoint = {'model_state': model.state_dict(),
              'criterion_state': criteria.state_dict(),
              'optimizer_state': optimizer.state_dict(),
              'class_to_idx': train_datasets.class_to_idx,
              'epochs': epochs,
              'Best train loss': epoch_train_loss,
              'Best train accuracy': epoch_train_accuracy,
              'Best Validation loss': epoch_val_loss,
              'Best Validation accuracy': epoch_val_acc}
torch.save(checkpoint, args.checkpoint)

У нас есть собственный набор данных проверки и тестирования обучения (из предыдущего поста здесь); мы просто создаем модель, адаптированную к нашему набору данных с помощью трансферного обучения; мы обучили и проверили модель, настраивая ряд гиперпараметров, таких как скорость обучения, размер эпохи, размер пакета, импульс; мы протестировали производительность нашей модели с тестовыми данными, предсказывая классы каждого тестового образца с конкурентоспособной точностью; мы сохранили нашу модель как контрольную точку в заданном каталоге. Теперь мы можем с уверенностью развернуть нашу модель на любой платформе и позволить ей выполнить некоторую прогностическую работу за нас.

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

В следующем посте мы будем работать с докерами и создавать связанные приложения в корпоративной среде.

#DataIsPower

Удачи!