Перейти к содержанию

← Практика и промптинг

Практика и промптинг

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

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

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

  • Python 3.8+ - бесплатно, скачать с python.org без ограничений из РФ.
  • NumPy - бесплатная библиотека, ставится одной командой.
  • TensorFlow/Keras или PyTorch - опционально, для шага с фреймворком. Оба бесплатны, скачиваются без VPN. Облачные GPU (Google Colab, AWS) из России оплатить сложно - используйте локальную машину или ищите российские облачные альтернативы.
  • Текстовый редактор или Jupyter Notebook.

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

Шаг 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]].

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

  • Нулевая инициализация весов. Все нейроны обучаются одинаково и сеть не учится. Всегда используйте случайную инициализацию - He для ReLU, Xavier для Sigmoid.
  • Слишком большой learning rate. Потери начинают расти или уходят в NaN. Уменьшите в 10 раз и попробуйте снова.
  • Забыли epsilon в логарифме. log(0) дает -inf, и все параметры превращаются в NaN уже на первой эпохе.
  • Переобучение. Сеть идеально работает на тренировочных данных, но ошибается на новых. Добавьте Dropout или L2-регуляризацию, следите за потерями на валидационной выборке.

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

Зачем писать нейросеть с нуля, если есть 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 на своих данных

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

Зарегистрироваться бесплатно
Обучаем команды работе с нейросетями под ваши процессы. Узнать о корпоративном обучении