Как сделать свою нейросеть с нуля на Python

Коротко: Нейросеть с нуля - это несколько слоёв математических операций: умножение матриц, функция активации и алгоритм обратного распространения ошибки. Для базовой реализации достаточно Python и библиотеки NumPy - никакие фреймворки не нужны. В результате вы получите рабочую сеть, которая принимает данные, обучается на них и делает предсказания.

Разбираемся, как построить нейросеть с нуля - без магии фреймворков, только Python и математика. В конце у вас будет рабочая сеть, которая умеет обучаться на данных и делать предсказания. Это фундамент: поняв его, вы будете осознанно работать с TensorFlow и PyTorch, а не просто копировать чужой код.

Содержание
  1. Что понадобится
  2. Шаг 1. Установка библиотек
  3. Шаг 2. Определите архитектуру сети
  4. Шаг 3. Инициализируйте веса
  5. Шаг 4. Реализуйте прямое распространение
  6. Шаг 5. Вычислите функцию потерь
  7. Шаг 6. Обратное распространение
  8. Шаг 7. Обновите параметры
  9. Шаг 8. Цикл обучения
  10. Шаг 9. Предсказание
  11. Частые ошибки
  12. Вопросы и ответы

Что понадобится

Никаких платных подписок для базового урока не нужно.

Шаг 1. Установка библиотек

Откройте терминал и выполните:

pip install numpy

Если хотите сразу попробовать готовый фреймворк:

# TensorFlow/Keras
pip install tensorflow

# PyTorch (для CPU, без GPU)
pip install torch torchvision torchaudio

Для PyTorch с GPU зайдите на pytorch.org, выберите свою ОС и версию CUDA - сайт сгенерирует точную команду.

Шаг 2. Определите архитектуру сети

intro Скриншот: ahammadnafiz.github.io

Перед кодом ответьте на три вопроса:

  1. Сколько входных признаков у ваших данных? Например, 2 признака для задачи XOR.
  2. Сколько скрытых слоев и нейронов в каждом? Начните с одного скрытого слоя из 4 нейронов.
  3. Сколько выходных нейронов? Для бинарной классификации - 1, для многоклассовой - по числу классов.

Функции активации: для скрытых слоев берите ReLU, для выходного - Sigmoid (бинарная задача) или Softmax (несколько классов). Sigmoid и Tanh в глубоких сетях вызывают затухание градиентов - это известная проблема, ReLU её решает.

Шаг 3. Инициализируйте веса

Создайте файл neural_net.py и добавьте:

import numpy as np

def initialize_parameters(layer_dims):
 params = {}
 for l in range(1, len(layer_dims)):
 # He initialization для ReLU
 params[f'W{l}'] = np.random.randn(layer_dims[l], layer_dims[l-1]) * np.sqrt(2. / layer_dims[l-1])
 params[f'b{l}'] = np.zeros((layer_dims[l], 1))
 return params

# Пример: 2 входа, 4 нейрона в скрытом слое, 1 выход
layer_dims = [2, 4, 1]
params = initialize_parameters(layer_dims)

Нули для весов не подходят - все нейроны будут обучаться одинаково. He initialization специально подобрана под ReLU и предотвращает взрывающиеся градиенты.

Шаг 4. Реализуйте прямое распространение

def relu(Z):
 return np.maximum(0, Z)

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

def forward_pass(X, params, num_layers):
 cache = {'A0': X}
 A = X
 for l in range(1, num_layers):
 Z = np.dot(params[f'W{l}'], A) + params[f'b{l}']
 A = relu(Z)
 cache[f'Z{l}'] = Z
 cache[f'A{l}'] = A
 # Выходной слой
 Z_out = np.dot(params[f'W{num_layers}'], A) + params[f'b{num_layers}']
 AL = sigmoid(Z_out)
 cache[f'Z{num_layers}'] = Z_out
 cache[f'A{num_layers}'] = AL
 return AL, cache

Сохраняйте все промежуточные значения в cache - они понадобятся на обратном проходе.

Шаг 5. Вычислите функцию потерь

loss Скриншот: ahammadnafiz.github.io

def compute_loss(AL, Y):
 epsilon = 1e-8 # защита от log(0)
 loss = -np.mean(Y * np.log(AL + epsilon) + (1 - Y) * np.log(1 - AL + epsilon))
 return loss

epsilon - маленькое число, которое спасает от NaN при логарифме нуля. Без него сеть сломается на первой же эпохе.

Шаг 6. Обратное распространение

Это самый сложный шаг. Двигаемся от выходного слоя к входному и считаем градиенты:

def backward_pass(AL, Y, cache, params, num_layers):
 grads = {}
 m = Y.shape[1]
 # Градиент выходного слоя (Sigmoid + бинарная кросс-энтропия)
 dA = -(Y / (AL + 1e-8) - (1 - Y) / (1 - AL + 1e-8))
 for l in reversed(range(1, num_layers + 1)):
 Z = cache[f'Z{l}']
 A_prev = cache[f'A{l-1}']
 if l == num_layers:
 dZ = dA * AL * (1 - AL) # производная Sigmoid
 else:
 dZ = dA * (Z > 0) # производная ReLU
 grads[f'dW{l}'] = np.dot(dZ, A_prev.T) / m
 grads[f'db{l}'] = np.mean(dZ, axis=1, keepdims=True)
 dA = np.dot(params[f'W{l}'].T, dZ)
 return grads

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

Шаг 7. Обновите параметры

def update_parameters(params, grads, learning_rate, num_layers):
 for l in range(1, num_layers + 1):
 params[f'W{l}'] -= learning_rate * grads[f'dW{l}']
 params[f'b{l}'] -= learning_rate * grads[f'db{l}']
 return params

learning_rate обычно берут от 0.001 до 0.1. Начните с 0.01 и смотрите на потери.

Шаг 8. Цикл обучения

def train(X, Y, layer_dims, epochs=1000, learning_rate=0.01):
 num_layers = len(layer_dims) - 1
 params = initialize_parameters(layer_dims)
 for epoch in range(epochs):
 AL, cache = forward_pass(X, params, num_layers)
 loss = compute_loss(AL, Y)
 grads = backward_pass(AL, Y, cache, params, num_layers)
 params = update_parameters(params, grads, learning_rate, num_layers)
 if epoch % 100 == 0:
 print(f'Epoch {epoch}, Loss: {loss:.4f}')
 return params

Потери должны монотонно убывать. Если они растут или прыгают - скорее всего, слишком высокий learning_rate.

Шаг 9. Предсказание

def predict(X, params, num_layers):
 AL, _ = forward_pass(X, params, num_layers)
 return (AL > 0.5).astype(int) # порог 0.5 для бинарной классификации

Запустите всё вместе на задаче XOR:

X = np.array([[0,0,1,1],[0,1,0,1]])
Y = np.array([[0,1,1,0]])
trained_params = train(X, Y, [2, 4, 1], epochs=5000, learning_rate=0.1)
print(predict(X, trained_params, 2))

Если всё сделано правильно, получите [[0 1 1 0]].

Частые ошибки

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

Зачем писать нейросеть с нуля, если есть TensorFlow и PyTorch?

Фреймворки скрывают детали реализации. Когда вы напишете backpropagation руками, вы поймете, почему выбор функции активации влияет на скорость обучения, откуда берутся градиенты и что происходит внутри model.fit(). Это знание помогает отлаживать реальные модели и осознанно выбирать архитектуру. Посмотрите каталог LLM-моделей - там видно, насколько разные архитектуры решают разные задачи.

Можно ли обучать такую сеть без GPU в России?

Да, для учебных задач CPU вполне хватает. NumPy-реализация работает на любом ноутбуке. Проблемы начинаются с большими датасетами и глубокими сетями - тогда нужен GPU. Облачные сервисы с GPU (Google Colab, AWS) из России оплатить сложно, поэтому реалистичный вариант - собственная видеокарта NVIDIA или российские облачные платформы.

Когда переходить с NumPy на TensorFlow или PyTorch?

Как только поняли принцип - сразу. NumPy-реализация не масштабируется: нет GPU-ускорения, нет автоматического дифференцирования, нет готовых слоев. Для реальных проектов берите PyTorch (гибче, популярен в исследованиях) или Keras (проще API, быстрее прототипировать). Реализация с нуля - это учебный тренажер, не продакшн-инструмент.

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

Мнение редакции ENGRAM

Рекомендуем начинать именно с этого пути: сначала реализовать backpropagation вручную на NumPy, и только потом переходить к PyTorch - он доступен из РФ без VPN и устанавливается одной командой. Google Colab для GPU оплатить из России затруднительно, поэтому на этапе обучения используйте локальную машину: для задач уровня XOR и небольших датасетов CPU более чем достаточно. На нашем опыте именно понимание градиентного спуска "изнутри" принципиально меняет качество работы с готовыми моделями - без этого фундамента тонкая настройка корпоративных решений превращается в угадывание.

ENGRAM решает это

Знания и наработки команды теряются в переписке. ENGRAM собирает их в единую ИИ-память с поиском по смыслу - данные остаются в России.

Читайте также