Пример простейшей нейросети: пошаговое создание на Python с нуля

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

Что такое нейросеть и как она устроена

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

Простейшая нейросеть включает три типа слоёв:

  • Входной слой — принимает исходные данные, не выполняет вычислений.
  • Скрытый слой — выполняет основные преобразования, извлекает закономерности.
  • Выходной слой — выдаёт итоговый результат.

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

Обучение нейросети бывает двух основных типов:

  • С учителем — модель обучается на размеченных данных, где известен правильный ответ.
  • Без учителя — сеть самостоятельно ищет закономерности в неразмеченных данных.

Для новичков наиболее понятен первый подход: мы подаём на вход примеры, сравниваем ответ сети с эталоном и корректируем веса.

Какие инструменты понадобятся для создания нейросети

Для написания простейшей нейросети на Python достаточно минимального набора инструментов:

  • Python 3.x — язык программирования, на котором будем писать код.
  • NumPy — библиотека для эффективных численных вычислений. Она позволяет работать с многомерными массивами и матрицами, что идеально подходит для операций с весами и данными.

Установить NumPy можно через pip:

pip install numpy

Опционально можно использовать:

  • Matplotlib — для визуализации процесса обучения (графики ошибки).
  • Scikit-learn — для загрузки готовых наборов данных и предобработки.

Для более сложных проектов применяются фреймворки вроде TensorFlow, PyTorch или Keras, но на начальном этапе они избыточны. Понимание работы нейросети «изнутри» на чистом Python даёт прочную основу для дальнейшего изучения.

Подготовка данных для обучения нейросети

Любое обучение начинается с данных. Рассмотрим классическую задачу — предсказание результата логической операции XOR (исключающее ИЛИ). Таблица истинности для XOR:

| Вход A | Вход B | Выход | |--------|--------|-------| | 0 | 0 | 0 | | 0 | 1 | 1 | | 1 | 0 | 1 | | 1 | 1 | 0 |

Как видно, выход равен 1 только когда входные значения различаются. Это нелинейная задача — простой персептрон (один слой) не способен её решить, поэтому потребуется скрытый слой.

Создадим массивы входных данных (X) и целевых значений (y) с помощью NumPy:

import numpy as np

X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([[0], [1], [1], [0]])

Для задачи регрессии (например, прогнозирования цен) данные обычно загружаются из готовых наборов, таких как California Housing из scikit-learn. Важно нормализовать признаки, чтобы они находились в одном масштабе — это ускоряет обучение и повышает стабильность.

Архитектура нейросети: инициализация весов и функции активации

Для решения XOR используем нейросеть с одним скрытым слоем из 4 нейронов. Размеры слоёв:

  • Входной слой: 2 нейрона (по числу признаков)
  • Скрытый слой: 4 нейрона
  • Выходной слой: 1 нейрон

Создадим две матрицы весов:

  • weights1 — между входным и скрытым слоем (размер 2×4)
  • weights2 — между скрытым и выходным слоем (размер 4×1)

Инициализируем их случайными значениями. Для воспроизводимости результатов зафиксируем seed:

def initialize_weights(input_size, hidden_size, output_size):
    np.random.seed(42)
    weights1 = np.random.randn(input_size, hidden_size)
    weights2 = np.random.randn(hidden_size, output_size)
    return weights1, weights2

input_size = 2
hidden_size = 4
output_size = 1
weights1, weights2 = initialize_weights(input_size, hidden_size, output_size)

Функции активации — ключевой элемент, добавляющий нелинейность. Без них нейросеть оставалась бы просто линейным преобразователем. В скрытом и выходном слоях используем сигмоиду, которая сжимает любое число в диапазон от 0 до 1:

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def sigmoid_derivative(x):
    return x * (1 - x)

Производная сигмоиды понадобится для обратного распространения ошибки.

Прямое распространение: как нейросеть делает предсказание

Прямое распространение (forward pass) — это процесс обработки данных от входа к выходу. На каждом шаге выполняем умножение матриц и применяем функцию активации.

Алгоритм:

  1. Умножаем входные данные на веса между входным и скрытым слоем.
  2. Применяем сигмоиду к полученному значению — получаем выход скрытого слоя.
  3. Умножаем выход скрытого слоя на веса между скрытым и выходным слоем.
  4. Применяем сигмоиду — получаем итоговое предсказание.

Реализация:

def forward_pass(X, weights1, weights2):
    hidden_layer_input = np.dot(X, weights1)
    hidden_layer_output = sigmoid(hidden_layer_input)
    output_layer_input = np.dot(hidden_layer_output, weights2)
    predicted_output = sigmoid(output_layer_input)
    return hidden_layer_output, predicted_output

На первой итерации, пока веса случайны, предсказания будут далеки от истины. Например, для XOR сеть может выдать значения около 0.5 для всех комбинаций. Это нормально — обучение как раз и нужно, чтобы скорректировать веса.

Обратное распространение ошибки: как нейросеть учится

Обратное распространение (backpropagation) — ключевой механизм обучения. После прямого прохода мы сравниваем предсказание сети с правильным ответом и вычисляем ошибку. Затем «идём назад» по слоям, чтобы понять, какой вклад внёс каждый вес в общую ошибку, и корректируем их.

Этапы обратного распространения:

  1. Вычисление ошибки на выходе: output_error = y - predicted_output
  2. Расчёт дельты для выходного слоя: output_delta = output_error * sigmoid_derivative(predicted_output) — учитываем, насколько сильно изменение суммы повлияет на выход.
  3. Распространение ошибки на скрытый слой: hidden_layer_error = output_delta.dot(weights2.T)
  4. Расчёт дельты для скрытого слоя: hidden_layer_delta = hidden_layer_error * sigmoid_derivative(hidden_layer_output)
  5. Обновление весов: каждый вес корректируется пропорционально входному сигналу, дельте и коэффициенту обучения (learning rate).

Реализация:

def backward_pass(X, y, hidden_layer_output, predicted_output, weights1, weights2, learning_rate):
    output_error = y - predicted_output
    output_delta = output_error * sigmoid_derivative(predicted_output)
    hidden_layer_error = output_delta.dot(weights2.T)
    hidden_layer_delta = hidden_layer_error * sigmoid_derivative(hidden_layer_output)
    weights2 += hidden_layer_output.T.dot(output_delta) * learning_rate
    weights1 += X.T.dot(hidden_layer_delta) * learning_rate
    return weights1, weights2

Коэффициент обучения (learning rate) — важный гиперпараметр. Слишком большое значение приведёт к нестабильности, слишком маленькое — к медленному обучению. Для XOR оптимальным часто оказывается значение 0.1.

Цикл обучения и запуск нейросети

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

learning_rate = 0.1
epochs = 10000

weights1, weights2 = initialize_weights(input_size, hidden_size, output_size)

for i in range(epochs):
    hidden_layer_output, predicted_output = forward_pass(X, weights1, weights2)
    weights1, weights2 = backward_pass(X, y, hidden_layer_output, predicted_output, weights1, weights2, learning_rate)
    
    if i % 1000 == 0:
        error = np.mean(np.abs(y - predicted_output))
        print(f"Эпоха {i}, Ошибка: {error:.6f}")

print("\nРезультат после обучения:")
hidden_layer_output, predicted_output = forward_pass(X, weights1, weights2)
print("Округленные предсказания:")
print(np.round(predicted_output))

После 10 000 эпох ошибка снижается до десятых долей процента, а округлённые предсказания совпадают с таблицей истинности XOR: [[0], [1], [1], [0]].

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

Практические примеры применения простых нейросетей

Даже простые нейросети находят применение в реальных проектах. Вот несколько примеров:

1. Система рекомендаций для интернет-магазина Нейросеть анализирует историю покупок и поведения пользователей, предсказывает их интересы и предлагает релевантные товары. Это повышает конверсию и удовлетворённость клиентов.

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

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

4. Автоматизация поддержки клиентов Чат-боты на основе нейросетей обрабатывают запросы, отвечают на типовые вопросы и даже решают проблемы без участия человека. Это снижает нагрузку на службу поддержки и ускоряет обслуживание.

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

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

Ограничения простых нейросетей и пути развития

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

  • Неспособность обрабатывать изображения и последовательности — для этого нужны свёрточные (CNN) и рекуррентные (RNN) сети.
  • Чувствительность к масштабу данных — признаки должны быть нормализованы, иначе обучение может быть нестабильным.
  • Склонность к переобучению — при малом количестве данных модель может запомнить шум вместо закономерностей. Помогают регуляризация (Dropout, L2) и аугментация данных.
  • Ограниченная выразительная способность — один скрытый слой не всегда способен аппроксимировать сложные функции. Увеличение числа слоёв и нейронов расширяет возможности, но требует больше вычислительных ресурсов.

Как развиваться дальше:

  • Изучить фреймворки TensorFlow и PyTorch — они автоматизируют многие процессы и предоставляют готовые компоненты.
  • Освоить свёрточные сети для задач компьютерного зрения.
  • Познакомиться с рекуррентными сетями и трансформерами для обработки текста и временных рядов.
  • Применять технику переноса обучения (transfer learning) — использовать предобученные модели для своих задач с малым объёмом данных.

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

Вопросы и ответы

Сколько времени нужно, чтобы написать простейшую нейросеть?

При наличии базовых знаний Python и понимания алгоритма — от 30 минут до нескольких часов. Готовый код для XOR занимает около 50 строк и запускается за секунды. Для более сложных задач, таких как прогнозирование цен, потребуется дополнительная настройка гиперпараметров и предобработка данных.

Почему для XOR нужен скрытый слой, а персептрон не справляется?

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

Как выбрать количество нейронов в скрытом слое?

Универсального правила нет. Для простых задач (XOR) достаточно 3–4 нейронов. Для более сложных — количество подбирается экспериментально, обычно от 32 до 256. Слишком мало нейронов — сеть не сможет обучиться, слишком много — приведёт к переобучению и замедлению.

Что такое learning rate и как его подобрать?

Learning rate (скорость обучения) определяет, насколько сильно корректируются веса на каждом шаге. Типичные значения: 0.1, 0.01, 0.001. Если ошибка не уменьшается или колеблется — попробуйте уменьшить learning rate. Если обучение идёт слишком медленно — увеличьте. На практике часто используют адаптивные оптимизаторы (Adam), которые автоматически подбирают темп.

Можно ли использовать эту нейросеть для реальных задач?

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

Что такое функция активации и зачем она нужна?

Функция активации — это нелинейное преобразование, которое применяется к выходу нейрона. Без неё нейросеть была бы просто набором линейных преобразований и не смогла бы обучаться сложным зависимостям. Популярные функции: сигмоида (для вероятностей), ReLU (для скрытых слоёв), softmax (для многоклассовой классификации).

Как оценить качество обученной нейросети?

Для классификации используют точность (accuracy), полноту (recall), точность (precision) и F1-меру. Для регрессии — среднеквадратическую ошибку (MSE), среднюю абсолютную ошибку (MAE) и коэффициент детерминации R². Важно оценивать модель на тестовой выборке, которая не участвовала в обучении.