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

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

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

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

В этом уроке мы рассмотрим:

  1. Создание однослойной нейронной сети с помощью PyTorch
  2. Обучение однослойной нейронной сети
  3. Использование нейронной сети для классификации данных

ПОДГОТОВКА ДАННЫХ

Мы напишем код, который генерирует синтетические данные X и присваивает соответствующие метки Y для задачи классификации с тремя классами. Метки назначаются на основе определенных диапазонов значений в X, где элементы меньше или равные -10 получают метку 1,0, элементы между -10 и 10 (исключительно) получают метку 0,5, а элементы больше 10 получают метку 0.

import torch
import matplotlib.pyplot as plt

# Generate synthetic data for X ranging from -30 to 29 with a step size of 1
X = torch.arange(-30, 30, 1).view(-1, 1).type(torch.FloatTensor)

# Initialize an empty tensor Y to store the labels (target values)
Y = torch.zeros(X.shape[0])

# Assign label 1.0 to elements in Y where the corresponding X value is less than or equal to -10
Y[X[:, 0] <= -10] = 1.0

# Assign label 0.5 to elements in Y where the corresponding X value falls between -10 and 10 (exclusive)
Y[(X[:, 0] > -10) & (X[:, 0] < 10)] = 0.5

# Assign label 0 to elements in Y where the corresponding X value is greater than 10
Y[X[:, 0] > 10] = 0

Понимание тензоров:

Тензор — это фундаментальная структура данных, используемая в PyTorch для глубокого обучения. В простейшей форме тензор можно представить как многомерный массив. Это обобщение скаляров (0-мерных), векторов (1-мерных) и матриц (2-мерных) на более высокие измерения. Тензоры могут иметь произвольное количество измерений, что делает их универсальными для представления и обработки данных различной формы и размера.

Вот некоторые распространенные типы тензоров:

  1. Скаляр (0-мерный тензор): одно числовое значение, например действительное число.
  2. Вектор (одномерный тензор): упорядоченный набор значений по одному измерению, часто представляющий собой список чисел.
  3. Матрица (двумерный тензор): упорядоченный набор значений, расположенных в строках и столбцах, представляющий двумерную таблицу чисел.
  4. Многомерные тензоры: тензоры с более чем двумя измерениями, такие как трехмерные тензоры, четырехмерные тензоры и т. д. Они используются для представления более сложных структур данных, таких как изображения (трехмерные тензоры) или видеоданные (четырехмерные тензоры).

Давайте теперь построим данные, которые мы сгенерировали:

import matplotlib.pyplot as plt


# Create a plot of X against Y
plt.plot(X, Y)

# Add X and Y labels to the plot
plt.xlabel('X')
plt.ylabel('Y')

# Display the plot
plt.show()

СОЗДАЙТЕ МОДЕЛЬ С ПОМОЩЬЮ PYTORCH nn.MODULE

nn.Module — это фундаментальный класс в PyTorch, используемый для создания пользовательских архитектур нейронных сетей. Это базовый класс для всех модулей нейронных сетей в PyTorch, который предоставляет несколько важных функций для создания нейронных сетей и управления ими.

При создании пользовательской нейронной сети в PyTorch вы обычно создаете подкласс nn.Module и определяете архитектуру, указывая слои и операции, из которых состоит ваша нейронная сеть.

Вот как ваша однослойная нейронная сеть выглядит в коде:

class SingleLayerNet(nn.Module):
    def __init__(self, input_size, hidden_neurons, output_size):
        super(SingleLayerNet, self).__init__()
        # Define the hidden layer with input_size input features and hidden_neurons neurons
        self.hidden_layer = nn.Linear(input_size, hidden_neurons)

        # Define the output layer with hidden_neurons input features and output_size neurons
        self.output_layer = nn.Linear(hidden_neurons, output_size)
        
#Define a Prediction Function
    def forward(self, x):
        # Pass the input through the hidden layer and apply the sigmoid activation function
        hidden_output = torch.sigmoid(self.hidden_layer(x))

        # Pass the hidden layer output through the output layer and apply the sigmoid activation function
        y_pred = torch.sigmoid(self.output_layer(hidden_output))

        return y_pred

Разобьем этот код построчно:

  • Код определяет класс однослойной нейронной сети с именем SingleLayerNet.
  • Класс наследуется от nn.Module, который является базовым классом для модулей нейронной сети PyTorch.
  • Конструктор (метод __init__) инициализирует архитектуру нейронной сети с размером ввода, размером скрытого слоя и размером вывода.
  • Он создает скрытый слой (self.hidden_layer), используя nn.Linear с указанными входными данными и размером скрытого слоя.
  • Он создает выходной слой (self.output_layer), используя nn.Linear со скрытым размером слоя и выходным размером.
  • Метод forward реализует прямой проход нейронной сети.
  • Входные данные (x) проходят через скрытый слой и применяют сигмовидную функцию активации, получая hidden_output.
  • Затем hidden_output проходит через выходной слой и применяет сигмовидную функцию активации, создавая окончательный прогноз y_pred.
  • Метод forward возвращает прогноз y_pred в качестве вывода нейронной сети.

Создайте экземпляр объекта модели:

# create the model 
model = SingleLayerNet(1, 2, 1)  
# 2 represents two neurons in one hidden layer

ОБУЧЕНИЕ ВАШЕЙ МОДЕЛИ

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

# Define the loss function (criterion)
def criterion(y_pred, y_true):
    # Binary Cross Entropy Loss
    # y_pred: predicted probabilities, y_true: true labels (0 or 1)
    
    # Compute the negative log likelihood loss using binary cross-entropy formula
    # (y * log(y_pred) + (1 - y) * log(1 - y_pred))
    loss = -1 * (y_true * torch.log(y_pred) + (1 - y_true) * torch.log(1 - y_pred))
    
    # Calculate the mean loss over the batch
    mean_loss = torch.mean(loss)
    
    return mean_loss

# Assuming 'model' is an instance of your custom neural network
# Create an optimizer (Stochastic Gradient Descent - SGD)
optimizer = optim.SGD(model.parameters(), lr=0.01)

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

Функция принимает два аргумента:

  • y_pred: Предсказанные вероятности по модели (выход сигмовидной функции).
  • y_true: Истинные метки (наземная правда) для соответствующих входов.

Внутри функции бинарная кросс-энтропийная потеря вычисляется по формуле - (y * log(y_pred) + (1 - y) * log(1 - y_pred)), где y представляет истинные метки, а y_pred представляет предсказанные вероятности.

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

Оптимизатор создается с использованием стохастического градиентного спуска (SGD) для обновления параметров model в процессе обучения. В качестве аргументов он принимает параметры модели (model.parameters()) и скорость обучения (lr=0.01). Скорость обучения определяет размер шага во время оптимизации.

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

# Define the training loop
epochs = 5000
cost = []  # List to store the total loss at each epoch

for epoch in range(epochs):
    total_loss = 0  # Variable to store the total loss for the current epoch
    epoch = epoch + 1  # Increment the epoch count

    for x, y in zip(X, Y):
        # Forward pass: Calculate the predicted output (yhat) using the model
        yhat = model(x)

        # Calculate the loss between the predicted output (yhat) and the actual target (y)
        loss = criterion(yhat, y)

        # Backpropagation: Compute gradients of the model parameters with respect to the loss
        loss.backward()

        # Update the model parameters using the computed gradients
        optimizer.step()

        # Zero out the gradients for the next iteration to avoid accumulation
        optimizer.zero_grad()

        # Accumulate the loss for this batch of data
        total_loss += loss.item()

    # Append the total loss for this epoch to the cost list
    cost.append(total_loss)

    if epoch % 1000 == 0:
        print(f"Epoch {epoch} done!")  # Print status after every 1000 epochs

        # Plot the result of the function approximator
        predicted_values = model(X).detach().numpy()
        plt.plot(X.numpy(), predicted_values)  # Plot the predicted values
        plt.plot(X.numpy(), Y.numpy(), 'm')  # Plot the ground truth data (Y)
        plt.xlabel('x')
        plt.ylabel('y')
        plt.legend(['Predicted', 'Ground Truth'])
        plt.title(f'Epoch {epoch} - Function Approximation')
        plt.show()

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

График снижения потерь во время обучения:

# Plot the cost (loss) over epochs
plt.plot(cost, marker='o', linestyle='-', color='b', label='Training Loss')

# Set labels and title
plt.xlabel('Epochs')
plt.ylabel('Cross Entropy Loss')
plt.title('Training Progress - Cross Entropy Loss')

# Add grid for better readability
plt.grid(True)

# Show legend
plt.legend()

# Display the plot
plt.show()

Вот оно! Вы создали свою первую нейронную сеть с помощью PyTorch!

ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ:

Ознакомьтесь с некоторыми другими статьями, которые я написал о нейронных сетях: