В этом руководстве мы узнаем о PyTorch и о том, как обучить модель логистической регрессии с использованием библиотеки PyTorch.

ВВЕДЕНИЕ

Логистическая регрессия — это популярный алгоритм машинного обучения, используемый для задач бинарной классификации. Это тип регрессионного анализа, в котором переменная отклика носит категориальный характер с двумя возможными результатами, обычно представленными как «0» и «1». Основная цель логистической регрессии состоит в том, чтобы предсказать вероятность возникновения события при заданном наборе независимых переменных.

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

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

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

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



PyTorch — это библиотека машинного обучения для Python, которая предоставляет API высокого уровня для создания и обучения моделей глубокого обучения. PyTorch предоставляет модуль под названием torch.nn, который предоставляет множество предопределенных модулей для построения моделей глубокого обучения.

МОДУЛИ В PYTORCH

Модули PyTorch — это предварительно определенные стандартные блоки для создания моделей глубокого обучения. Модуль в PyTorch представляет собой одну операцию или слой в нейронной сети. Модуль torch.nn в PyTorch предоставляет ряд предопределенных модулей для построения нейронных сетей.

Некоторые из наиболее часто используемых модулей PyTorch:

  1. nn.Linear: этот модуль представляет полностью связанный слой в нейронной сети с матрицей весов и вектором смещения.
  2. nn.Conv2d: представляет собой двумерный сверточный слой, обычно используемый в задачах классификации изображений.
  3. nn.MaxPool2d: этот модуль представляет собой двухмерный максимальный слой объединения, используемый для уменьшения пространственных размеров тензора.
  4. nn.Dropout: для слоя исключения, используемого для предотвращения переобучения в нейронных сетях.
  5. nn.BatchNorm2d: этот модуль предназначен для уровня пакетной нормализации, используемого для нормализации активаций нейронной сети.
  6. nn.ReLU: функция активации Rectified Linear Unit (ReLU), используемая для введения нелинейности в нейронную сеть.
  7. nn.Sigmoid: Обозначает сигмовидную функцию активации, используемую в задачах бинарной классификации.
  8. Модуль Autograd. PyTorch использует технику, известную как автоматическая дифференциация. Для расчета градиентов регистратор записывает выполненные операции, а затем воспроизводит их в обратном порядке. Этот метод особенно эффективен для создания нейронных сетей, поскольку он может сократить время, затрачиваемое на одну эпоху, путем вычисления дифференциации параметров на прямом проходе.
  9. Optim модуль — torch.optim — это модуль, который реализует различные алгоритмы оптимизации, используемые для построения нейронных сетей. Большинство часто используемых методов уже поддерживаются, поэтому нет необходимости создавать их с нуля.

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

ЧТО ДЕЛАЕТ PYTORCH ТАКИМ ПОПУЛЯРНЫМ?

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

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

  1. Динамический вычислительный граф: PyTorch использует динамический вычислительный граф, который обеспечивает большую гибкость и простоту использования по сравнению со статическими вычислительными графами, используемыми в других средах глубокого обучения, таких как TensorFlow.
  2. Простота в использовании: PyTorch имеет удобный API, который позволяет разработчикам легко создавать и обучать модели глубокого обучения. PyTorch предоставляет функциональность (DataLoader) для загрузки данных в виде пакетов, поскольку данные глубокого обучения могут стать слишком большими для некоторых машин, и могут возникнуть проблемы с простой загрузкой данных в память.
  3. Встроенная поддержка CUDA: PyTorch имеет встроенную поддержку CUDA, платформы параллельных вычислений и API для графических процессоров Nvidia, что упрощает выполнение ресурсоемких операций на графических процессорах.
  4. Быстрая разработка моделей: PyTorch предоставляет высокоуровневый API, который позволяет легко и быстро экспериментировать с различными моделями и архитектурами моделей.
  5. Хорошая поддержка сообщества: PyTorch имеет растущее сообщество разработчиков и пользователей, с множеством ресурсов и руководств, доступных в Интернете.
  6. Взаимодействие с другими инструментами: PyTorch хорошо работает с другими инструментами глубокого обучения, такими как TensorBoard, что позволяет легко визуализировать производительность модели и ход обучения. Он также поддерживает NumPy, который в наши дни является строительным блоком сообщества Data Science, поскольку большинство библиотек построено на основе NumPy.
  7. Поддержка переноса обучения: PyTorch имеет встроенную поддержку переноса обучения, что позволяет повторно использовать предварительно обученные модели для различных задач, экономя время и ресурсы.
  8. Простое развертывание модели. Модели PyTorch можно легко развернуть на различных платформах, включая мобильные устройства и Интернет, с помощью таких инструментов, как TorchScript и ONNX.

ВЫПОЛНЕНИЕ

Мы начнем с установки PyTorch, если он еще не установлен. Чтобы установить PyTorch, выполните следующую команду в своем ноутбуке Jupyter:

!pip install torch

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

import numpy as np 
import pandas as pd 
import torch
import torch.nn as nn
import torch.nn.functional as F
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
from sklearn.impute import KNNImputer
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from torchmetrics.classification import BinaryConfusionMatrix
from sklearn.metrics import roc_curve,  auc 

Мы будем работать с Набором данных космического корабля Титаник, доступным на Kaggle.

О КОРАБЛЕ ТИТАНИК

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

Космический корабль «Титаник» — межзвездный пассажирский лайнер, запущенный месяц назад. С почти 13 000 пассажиров на борту судно отправилось в свой первый рейс, доставив эмигрантов из нашей Солнечной системы к трем новым обитаемым экзопланетам, вращающимся вокруг ближайших звезд.

Обогнув Альфу Центавра по пути к своему первому пункту назначения — жаркому 55 Cancri E — неосторожный космический корабль «Титаник» столкнулся с пространственно-временной аномалией, скрытой в пылевом облаке. К сожалению, его постигла та же участь, что и его тезку 1000 лет назад. Хотя корабль остался цел, почти половина пассажиров была перенесена в другое измерение!

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

Давайте прочитаем наши данные.

train_df = pd.read_csv('train.csv')

Вот как выглядит фрейм данных:

Давайте посмотрим на значения счетчика целевого класса, который является столбцом Transported.

sns.countplot(train_df.Transported)
plt.gca().spines['top'].set_visible(False)
plt.gca().spines['right'].set_visible(False)
plt.title('Target Class Countplot', fontsize=20)
plt.show()

Давайте также посмотрим на возрастное распределение для каждого класса в Целевой переменной. Мы будем использовать библиотеку Seaborn для построения гистограммы.

colors = plt.rcParams["axes.prop_cycle"]()
a = 1
b = 2
c = 1
fig = plt.figure()
for i in train_df.Transported.unique():
    color = next(colors)["color"]
    plt.subplot(a,b,c)
    sns.histplot(train_df[train_df.Transported==i]['Age'], color=color)
    plt.gca().spines['top'].set_visible(False)
    plt.gca().spines['right'].set_visible(False)
    plt.title(f'Age Spread where Transported = {i}')
    c =  c+1
plt.suptitle('Age Distribution', fontsize=20, y=1.02)
plt.tight_layout()    
plt.show()

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

for i in train_df.columns:
    if train_df[i].isnull().sum() != 0:
        dtype = train_df[i].dtype
        percent = round(train_df[i].isnull().sum()/train_df.shape[0],2)*100
        print(f'{i} : {percent}% missing,    Dtype : {dtype}')

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

def clean(df):
    '''
    Takes a dataframe, imputes the missing values, encodes the categorical features, 
     drops na values and returns the dataframe
    '''
    imputer = KNNImputer(n_neighbors=2)
    encoder= LabelEncoder()
    df[df.select_dtypes('float64').columns] = imputer.fit_transform(df.select_dtypes('float64'))
    df.drop(['PassengerId', 'Cabin', 'Name'], axis=1, inplace=True)
    cols = ['CryoSleep', 'VIP', 'Transported']
    for i in cols:
        df[i] = encoder.fit_transform(df[i])        
    df = df.dropna(subset=['HomePlanet', 'Destination'])
    obj_cols = list(df.select_dtypes('object'))
    for i in obj_cols:
        df[i] = encoder.fit_transform(df[i])
    return df

Фрейм данных теперь формируется и начинает выглядеть так:

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

input_cols = cleaned_train.columns[:-1]
scaler = StandardScaler()
scaler.fit(cleaned_train[input_cols])
cleaned_train[input_cols] = scaler.transform(cleaned_train[input_cols])

Далее мы продолжим и разделим данные на независимые и зависимые переменные.

y = cleaned_train.Transported.values
X = cleaned_train[input_cols].values

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

def split(X,y, test_size=0.10):
    '''
    Takes X and y arrays, splits them into training and test set. 
    The function then converts all the sets into tensors only to be returned
    by the function.
    '''
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=1, shuffle=True)
    X_train=torch.from_numpy(X_train.astype(np.float32))
    X_test=torch.from_numpy(X_test.astype(np.float32))
    y_train=torch.from_numpy(y_train.astype(np.float32))
    y_test=torch.from_numpy(y_test.astype(np.float32))
    return X_train, X_test, y_train, y_test


#Calling the function we just created to split the data
X_train, X_test, y_train, y_test = split(X,y)

#Reshaping the y_train and y_test 
y_train=y_train.view(y_train.shape[0],1)
y_test=y_test.view(y_test.shape[0],1)

Давайте создадим нашу модель теперь, когда у нас есть готовые данные. Мы собираемся написать класс, который использует 2 слоя нейронной сети, функцию активации ReLU и сигмовидную функцию.

class LogisticRegression(torch.nn.Module):
    def __init__(self,no_input_features):
        super(LogisticRegression,self).__init__()
        self.layer1=torch.nn.Linear(no_input_features,20)
        self.relu = nn.ReLU()
        self.layer2=torch.nn.Linear(20,1)
        
    def forward(self,x):
        y_predicted=self.relu(self.layer1(x))
        y_predicted=torch.sigmoid(self.layer2(y_predicted))
        return y_predicted

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

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

n_features = 10
model=LogisticRegression(n_features)

#Setting up Loss criteria and the optimizer
criterion=torch.nn.BCELoss()
optimizer=torch.optim.SGD(model.parameters(),lr=0.1, momentum = 0.5, weight_decay=0.1)

def evaluate(preds, target):
    '''
    Function to evaluate the performance of the model. 
    Takes model predictions and targets to return the accuracy of the model.
    '''
    metric = BinaryAccuracy()
    accuracy = metric(preds, target)
    return accuracy

Обратите внимание, что мы используем Binary Cross Entropy Loss для мониторинга потерь и Stochastic Gradient Descent для оптимизатора. Мы продолжим и напишем обучающий цикл, в котором модель будет изучать параметры:

Loss = []
Accuracy = []
number_of_epochs=500
for epoch in range(number_of_epochs):
    y_prediction=model(X_train)
    loss=criterion(y_prediction,y_train)
    accuracy = evaluate(y_prediction, y_train)
    Loss.append(loss)
    Accuracy.append(accuracy)
    loss.backward()
    optimizer.step()
    optimizer.zero_grad()
    if (epoch+1)%100 == 0:
        print(f'Epoch: {epoch+1}, Loss : {loss.item()}, val_acc: {accuracy.item()}')

Итак, теперь, когда модель изучила параметры, давайте продолжим и визуализируем потери и точность в зависимости от продолжительности эпох с помощью Matplotlib.

plt.plot([i.item() for i in Loss], marker='o', markersize = 6,markerfacecolor = 'red',markeredgecolor = 'red',markevery=100)
plt.title('Logistic Regression Model Loss Plot', fontsize=18)
plt.gca().spines['top'].set_visible(False)
plt.gca().spines['right'].set_visible(False)
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.show()

plt.plot([i.item() for i in Accuracy], color='orange', marker='o', markersize = 6,markerfacecolor = 'C0',
         markeredgecolor = 'C0',markevery=100)
plt.title('Logistic Regression Model Accuracy Plot', fontsize=18)
plt.gca().spines['top'].set_visible(False)
plt.gca().spines['right'].set_visible(False)
plt.xlabel('Epochs')
plt.ylabel('Validation Accuracy')
plt.show()

УРА!!! Мы успешно обучили модель логистической регрессии в PyTorch. Прежде чем закончить статью, давайте сделаем еще одну вещь и посмотрим на различные метрики классификации, написав функцию:

def plot_classification_metrics(y_pred, y_test):
    """
    This function takes y_true and y_pred values to print the Classification metrics
    """
    from torchmetrics.classification import BinaryConfusionMatrix
    bcm = BinaryConfusionMatrix()
    target = torch.tensor(y_test)
    y_pred = torch.tensor([1 if i > 0.5 else 0 for i in y_pred])
    y_pred = y_pred.reshape(y_pred.shape[0], 1)
    matrix = bcm(y_pred, target )
    TP = matrix[0][0]
    FP = matrix[0][1]
    FN = matrix[1][0]
    TN = matrix[1][1]
    Accuracy = (TP + TN) / (TP + TN + FP + FN)
    print('== CLASSIFICATION METRICS ==')
    print(f'Accuracy :  {np.around(Accuracy,4)}')
    Precision = (TP) / (TP + FP)
    print(f'Precision :  {np.around(Precision,4)}')
    Recall = (TP) / (TP + FN)
    print(f'Recall : {np.around(Recall,4)}')
    F1_Score = 2 * (Precision*Recall/Precision+Recall)
    print(f'F1 Score : {np.around(F1_Score,4)}')
    Classification_Error = (FP+FN)/ (TP+FP+FN+TN)
    print(f'Classification Error : {np.around(Classification_Error,4)}')
    sensitivity =  TP/(TP+FN)
    print(f'Sensitivity / TPR: {np.around(sensitivity,4)}')
    specificity =  TN/(TP+FN)
    print(f'Specificity / TNR : {np.around(specificity,4)}')

#Calling the function
plot_classification_metrics(X_preds, y_train)

Глядя на метрики, мы можем сказать, что модель хороша примерно на 75%, и есть бесконечные возможности того, что можно сделать в будущем. Я бы хотел, чтобы вы попробовали и изучили их, если хотите.

ЗАКЛЮЧЕНИЕ

  1. Логистическая регрессия — это классический алгоритм машинного обучения, который классифицирует бинарные классы с помощью линейной линии, разделяющей два класса.
  2. PyTorch — это библиотека машинного обучения для Python, которая предоставляет API высокого уровня для создания и обучения моделей глубокого обучения. Его легко использовать благодаря динамическому вычислительному графику, совместимости с другими инструментами, хорошей поддержке сообщества, быстрой разработке моделей, простоте использования и многим другим функциям.
  3. Далее я расскажу о других фреймворках, которые помогут вам в ваших рабочих процессах по обработке и анализу данных и которые легко внедрить.

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