Поделиться через


Обучение модели анализа данных с помощью PyTorch

На предыдущем этапе выполнения задач этого учебника мы приобрели набор данных, который будем использовать для обучения модели анализа данных с помощью PyTorch. Теперь нам пора применить эти данные.

Для обучения модели данных с помощью PyTorch выполните следующие действия:

  1. Загрузите данные. Если вы выполнили задачи на предыдущем этапе этого руководства, значит, эта часть уже готова.
  2. Определите нейронную сеть.
  3. Определение функции потери.
  4. Обучение модели по данным для обучения.
  5. Тестирование модели по данным для проверки.

Определение нейронной сети

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

Linear -> ReLU -> Linear -> ReLU -> Linear

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

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

Параметры модели

Параметры модели зависят от нашей цели и данных для обучения. Размер входных данных зависит от числа признаков, передаваемых в модель — в нашем случае это четыре. Размер выходных данных равен трем, так как существует три возможных типа Ирисов.

При наличии трех линейных слоев, (4,24) -> (24,24) -> (24,3), сеть будет иметь весовой коэффициент 744 (96 + 576 + 72).

Скорость обучения позволяет задать допустимую степень изменения весовых коэффициентов сети по отношению к градиенту потери. Чем ниже это значение, тем медленнее выполняется обучение. В этом учебнике вы зададите для скорости обучения значение 0,01.

Принципы работы сети

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

Функция прямого распространения позволяет вычислить значение функции потери, а функция обратного распространения — градиенты параметров, по которым выполняется обучение. Когда вы создаете нейронную сеть с помощью PyTorch, вам следует определить только функцию прямого распространения. Функция обратного распространения будет определена автоматически.

  1. Скопируйте указанный ниже код в файл DataClassifier.py в Visual Studio, чтобы определить параметры модели и нейронную сеть.
# Define model parameters 
input_size = list(input.shape)[1]   # = 4. The input depends on how many features we initially feed the model. In our case, there are 4 features for every predict value  
learning_rate = 0.01 
output_size = len(labels)           # The output is prediction results for three types of Irises.  


# Define neural network 
class Network(nn.Module): 
   def __init__(self, input_size, output_size): 
       super(Network, self).__init__() 
        
       self.layer1 = nn.Linear(input_size, 24) 
       self.layer2 = nn.Linear(24, 24) 
       self.layer3 = nn.Linear(24, output_size) 


   def forward(self, x): 
       x1 = F.relu(self.layer1(x)) 
       x2 = F.relu(self.layer2(x1)) 
       x3 = self.layer3(x2) 
       return x3 
 
# Instantiate the model 
model = Network(input_size, output_size) 

Вам также потребуется определить устройство выполнения на основе доступного устройства на компьютере. PyTorch не предоставляет выделенной библиотеки для GPU, но вы можете вручную определить устройство выполнения. Будет использоваться GPU Nvidia, если он установлен на вашем компьютере. Если это не так, будет использоваться обычный ЦП.

  1. Скопируйте следующий код, чтобы определить устройство выполнения:
# Define your execution device 
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") 
print("The model will be running on", device, "device\n") 
model.to(device)    # Convert model parameters and buffers to CPU or Cuda 
  1. На последнем этапе определите функцию для сохранения модели:
# Function to save the model 
def saveModel(): 
    path = "./NetModel.pth" 
    torch.save(model.state_dict(), path) 

Примечание.

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

Определение функции потери

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

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

В PyTorch пакет нейронной сети содержит несколько разных функций потери, которые и лежат в основе создания глубоких нейронных сетей. Если вы хотите узнать больше об этих особенностях, ознакомьтесь с указанной выше заметкой. В этом примере мы будем использовать существующие функции, оптимизированные для такой классификации, а также функцию потери "перекрестная энтропия классификации" и оптимизатор Adam. В оптимизаторе скорость обучения позволяет задать допустимую степень изменения весовых коэффициентов в нейронной сети в отношении градиента потери. В нашем примере задайте значение 0,001. Чем ниже это значение, тем медленнее будет выполняться обучение.

  1. В Visual Studio скопируйте в файл DataClassifier.py приведенный ниже код, который определяет функцию потери и оптимизатор.
# Define the loss function with Classification Cross-Entropy loss and an optimizer with Adam optimizer
loss_fn = nn.CrossEntropyLoss()
optimizer = Adam(model.parameters(), lr=0.001, weight_decay=0.0001)

Обучение модели по данным для обучения.

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

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

  1. Добавьте в файл DataClassifier.py указанный ниже код.
# Training Function 
def train(num_epochs): 
    best_accuracy = 0.0 
     
    print("Begin training...") 
    for epoch in range(1, num_epochs+1): 
        running_train_loss = 0.0 
        running_accuracy = 0.0 
        running_vall_loss = 0.0 
        total = 0 
 
        # Training Loop 
        for data in train_loader: 
        #for data in enumerate(train_loader, 0): 
            inputs, outputs = data  # get the input and real species as outputs; data is a list of [inputs, outputs] 
            optimizer.zero_grad()   # zero the parameter gradients          
            predicted_outputs = model(inputs)   # predict output from the model 
            train_loss = loss_fn(predicted_outputs, outputs)   # calculate loss for the predicted output  
            train_loss.backward()   # backpropagate the loss 
            optimizer.step()        # adjust parameters based on the calculated gradients 
            running_train_loss +=train_loss.item()  # track the loss value 
 
        # Calculate training loss value 
        train_loss_value = running_train_loss/len(train_loader) 
 
        # Validation Loop 
        with torch.no_grad(): 
            model.eval() 
            for data in validate_loader: 
               inputs, outputs = data 
               predicted_outputs = model(inputs) 
               val_loss = loss_fn(predicted_outputs, outputs) 
             
               # The label with the highest value will be our prediction 
               _, predicted = torch.max(predicted_outputs, 1) 
               running_vall_loss += val_loss.item()  
               total += outputs.size(0) 
               running_accuracy += (predicted == outputs).sum().item() 
 
        # Calculate validation loss value 
        val_loss_value = running_vall_loss/len(validate_loader) 
                
        # Calculate accuracy as the number of correct predictions in the validation batch divided by the total number of predictions done.  
        accuracy = (100 * running_accuracy / total)     
 
        # Save the model if the accuracy is the best 
        if accuracy > best_accuracy: 
            saveModel() 
            best_accuracy = accuracy 
         
        # Print the statistics of the epoch 
        print('Completed training batch', epoch, 'Training Loss is: %.4f' %train_loss_value, 'Validation Loss is: %.4f' %val_loss_value, 'Accuracy is %d %%' % (accuracy))

Протестируйте модель по данным для проверки.

Теперь, когда модель обучена, мы можем ее протестировать с помощью набора данных для проверки.

Мы добавим две функции тестирования. Сначала проверяется модель, сохраненная в предыдущей части. Будет проверена модель с набором данных для проверки из 45 элементов и распечатаны данные о точности модели. Вторая функция является необязательной для проверки доверительного уровня модели в прогнозе каждого из трех видов Ириса, представленных вероятностью успешной классификации каждого из видов.

  1. Добавьте в файл DataClassifier.py указанный ниже код.
# Function to test the model 
def test(): 
    # Load the model that we saved at the end of the training loop 
    model = Network(input_size, output_size) 
    path = "NetModel.pth" 
    model.load_state_dict(torch.load(path)) 
     
    running_accuracy = 0 
    total = 0 
 
    with torch.no_grad(): 
        for data in test_loader: 
            inputs, outputs = data 
            outputs = outputs.to(torch.float32) 
            predicted_outputs = model(inputs) 
            _, predicted = torch.max(predicted_outputs, 1) 
            total += outputs.size(0) 
            running_accuracy += (predicted == outputs).sum().item() 
 
        print('Accuracy of the model based on the test set of', test_split ,'inputs is: %d %%' % (100 * running_accuracy / total))    
 
 
# Optional: Function to test which species were easier to predict  
def test_species(): 
    # Load the model that we saved at the end of the training loop 
    model = Network(input_size, output_size) 
    path = "NetModel.pth" 
    model.load_state_dict(torch.load(path)) 
     
    labels_length = len(labels) # how many labels of Irises we have. = 3 in our database. 
    labels_correct = list(0. for i in range(labels_length)) # list to calculate correct labels [how many correct setosa, how many correct versicolor, how many correct virginica] 
    labels_total = list(0. for i in range(labels_length))   # list to keep the total # of labels per type [total setosa, total versicolor, total virginica] 
  
    with torch.no_grad(): 
        for data in test_loader: 
            inputs, outputs = data 
            predicted_outputs = model(inputs) 
            _, predicted = torch.max(predicted_outputs, 1) 
             
            label_correct_running = (predicted == outputs).squeeze() 
            label = outputs[0] 
            if label_correct_running.item():  
                labels_correct[label] += 1 
            labels_total[label] += 1  
  
    label_list = list(labels.keys()) 
    for i in range(output_size): 
        print('Accuracy to predict %5s : %2d %%' % (label_list[i], 100 * labels_correct[i] / labels_total[i])) 

Теперь мы, наконец, добавим основной код. Он отвечает за выполнение цикла обучения модели, ее последующее ее сохранение и отображение результатов на экране. Мы выполняем всего две итерации [num_epochs = 25] по набору для обучения, поэтому процесс не займет много времени.

  1. Добавьте в файл DataClassifier.py указанный ниже код.
if __name__ == "__main__": 
    num_epochs = 10
    train(num_epochs) 
    print('Finished Training\n') 
    test() 
    test_species() 

Давайте запустим тест. В раскрывающихся меню на верхней панели инструментов должен быть указан режим Debug. Для запуска проекта на локальном компьютере укажите для параметра Solution Platform значение x64 для 64-разрядного устройства илиx86 для 32-разрядного.

  1. Чтобы запустить проект, нажмите кнопку Start Debugging на панели инструментов или клавишу F5.

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

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

Output from initial model training

Дальнейшие действия

Итак, у нас есть готовая модель классификации и мы можем перейти к преобразованию модели в формат ONNX.