Цель и задачи практики
Цель практики: применение программных инструментов и технологий для разработки программного обеспечения на примере создания и обучения нейронной сети без использования высокоуровневых фреймворков (TensorFlow, PyTorch, Keras). Допускается использование библиотеки NumPy для матричных операций.
Освоение линейной алгебры, матричных операций, градиентного спуска, цепного правила, производных функций активации.
Разработка полносвязных слоев, алгоритма обратного распространения ошибки, оптимизатора, механизмов регуляризации.
Обучение на эталонных задачах (XOR, make_moons, MNIST), сравнение с эталонными реализациями, анализ влияния гиперпараметров.
Спецификация нейросети
Каждый студент разрабатывает полносвязную нейронную сеть (многослойный перцептрон) с возможностью настройки количества слоев и нейронов. Разрешен только NumPy (для матричных операций), запрещены готовые фреймворки глубокого обучения.
- Архитектура: входной слой, один или два скрытых слоя (функции активации ReLU или Sigmoid), выходной слой (Sigmoid для бинарной классификации, Softmax для многоклассовой классификации, Linear для регрессии).
- Обучение: градиентный спуск (SGD или с моментом). Реализация обратного распространения ошибки (backpropagation) вручную.
- Функции потерь: среднеквадратичная ошибка (MSE), бинарная кросс-энтропия (Binary Cross-Entropy). Для многоклассовой классификации — категориальная кросс-энтропия (Categorical Cross-Entropy).
- Инициализация весов: случайная (Xavier или He).
Этап I
Архитектура нейронной сети
Сеть должна иметь следующую структуру:
- Входной слой: количество нейронов определяется размерностью признакового пространства входных данных (например, 2 для датасета XOR или make_moons, 784 для MNIST).
- Скрытый слой: один полносвязный слой с настраиваемым количеством нейронов (рекомендуется от 4 до 64). Функция активации — ReLU или Sigmoid.
- Выходной слой: полносвязный слой. Количество нейронов определяется числом классов (1 для бинарной классификации, K для многоклассовой). Функция активации — Sigmoid (для бинарной классификации) или Softmax (для многоклассовой).
1. Матричные операции и прямое распространение
Прямое распространение (forward pass) — вычисление выходного сигнала сети по входным данным. Для каждого слоя выполняется:
- Реализация должна использовать векторизованные операции NumPy для эффективности.
- Поддерживается пакетная обработка: входной тензор X имеет размерность (batch_size, input_size).
- Все промежуточные значения (Z, A) должны сохраняться для использования при обратном распространении.
2. Функции активации и их производные
Для реализации обратного распространения необходимо уметь вычислять производные функций активации.
- Преимущества: не страдает от проблемы исчезающего градиента, вычислительно прост.
- Используется для скрытых слоев.
2.2 Sigmoid (логистическая функция)
- Используется для выходного слоя в задачах бинарной классификации.
- Значение функции лежит в интервале (0, 1), что удобно интерпретировать как вероятность.
2.3 Softmax (для многоклассовой классификации)
- Обеспечивает нормированное распределение вероятностей по классам.
- При использовании Softmax функция потерь — категориальная кросс-энтропия.
3. Функции потерь (Loss Functions)
Функция потерь измеряет ошибку предсказания сети и служит целью оптимизации.
3.1 Среднеквадратичная ошибка (MSE) — для регрессии
3.2 Бинарная кросс-энтропия (Binary Cross-Entropy) — для бинарной классификации
- Предполагает, что выходной слой использует Sigmoid активацию.
- Градиент упрощается при комбинации Sigmoid + BCE: ∂L/∂Z = ŷ - y.
3.3 Категориальная кросс-энтропия (Categorical Cross-Entropy) — для многоклассовой классификации
- Используется с Softmax на выходном слое.
- Комбинация Softmax + CCE дает градиент ∂L/∂Z = ŷ - y (one-hot).
4. Обратное распространение ошибки (Backpropagation)
Алгоритм вычисления градиентов функции потерь по всем параметрам сети на основе цепного правила.
4.1 Общая схема
Для выходного слоя градиент ошибки вычисляется непосредственно из производной функции потерь. Для каждого предыдущего слоя градиент распространяется назад:
4.2 Последовательность вычислений при обучении
- Прямой проход: вычисление предсказаний и сохранение промежуточных значений.
- Вычисление градиента функции потерь по выходу сети.
- Обратный проход: последовательное вычисление градиентов для каждого слоя (от выхода к входу).
- Обновление параметров с помощью градиентного спуска.
5. Оптимизатор: стохастический градиентный спуск (SGD)
Стандартный алгоритм оптимизации, обновляющий параметры в направлении, противоположном градиенту.
- η (learning rate) — гиперпараметр, определяющий размер шага.
- Рекомендуемые значения: 0.01, 0.001, 0.0001 в зависимости от задачи.
- Поддержка пакетной обработки (batch_size) — обновление параметров происходит по подвыборке данных.
6. Инициализация весов
Правильная инициализация критически важна для сходимости градиентного спуска.
6.1 Случайная инициализация с малыми значениями
- Простейший метод, но может приводить к проблемам для глубоких сетей.
6.2 Инициализация Xavier/Glorot (рекомендуется)
- Сохраняет дисперсию сигнала при проходе через слой с Sigmoid/tanh.
6.3 Инициализация He (для ReLU)
7. Процесс обучения (Training Loop)
Основной цикл обучения, объединяющий все компоненты.
- Разбиение данных на мини-батчи (batch size настраивается).
- Многократный проход по всем данным (эпохи).
- Вычисление и вывод значения функции потерь после каждой эпохи.
- Сохранение истории потерь для последующей визуализации.
Экспериментальная часть
8.1 Эксперимент 1: Задача XOR
XOR — классическая задача, демонстрирующая необходимость использования скрытого слоя (линейно неразделимая функция).
- Архитектура: 2 → 4 → 1 (скрытый слой с 4 нейронами, ReLU, выходной Sigmoid).
- Ожидаемый результат: точность 100% на обучающей выборке.
- Построить график функции потерь по эпохам.
8.2 Эксперимент 2: Датасет make_moons
Двумерный датасет с двумя классами, образующими "луны". Требует нелинейной разделяющей границы.
- Разделить данные на обучающую (70%) и тестовую (30%) выборки.
- Архитектура: 2 → 8 → 1 (скрытый слой 8 нейронов, ReLU, выходной Sigmoid).
- Достичь точности на тестовой выборке не менее 90%.
- Построить визуализацию разделяющей границы.
8.3 Сравнение с эталонной реализацией
Для проверки корректности необходимо сравнить результаты с реализацией из библиотеки scikit-learn.
- Сравнить точность вашей реализации с MLPClassifier.
- Сравнить время обучения (можно использовать time.perf_counter()).
- Проанализировать возможные расхождения (обычно ваша реализация медленнее, но точность должна быть сопоставима).
Визуализация результатов
Каждый эксперимент должен сопровождаться наглядными графиками.
9.1 График функции потерь
- Построить график loss от номера эпохи.
- Показать, что loss монотонно убывает (или уменьшается с колебаниями).
- Для обучения с валидацией — график train_loss и val_loss на одних осях.
9.2 Визуализация предсказаний (для двумерных данных)
- Создать сетку точек в пространстве признаков.
- Получить предсказания сети для каждой точки.
- Отобразить цветом область принадлежности к классу.
- Наложить обучающие и тестовые точки.
Этап II
1. Ранняя остановка (early stopping)
Механизм, позволяющий прекратить обучение до достижения максимального числа эпох, если качество на валидационной выборке перестает улучшаться. Это предотвращает переобучение и экономит вычислительные ресурсы.
Технические требования к реализации:
- В методе
fit()должна быть возможность передать параметры:validation_split(доля от обучающей выборки, используемая для валидации, например 0.2) или отдельный наборX_val, y_val. - Параметры ранней остановки:
patience(количество эпох без улучшения, по умолчанию 5–10),min_delta(минимальное изменение метрики, которое считается улучшением, например 0.001). - Отслеживаемая метрика: обычно значение функции потерь на валидационной выборке (
val_loss), но может быть и точность (val_accuracy). - После остановки обучения модель должна быть восстановлена до состояния с наилучшей метрикой (лучшие веса).
- В процессе обучения необходимо логировать значения метрики на каждой эпохе как для обучающей, так и для валидационной выборки.
Экспериментальная проверка:
- Обучить модель с ранней остановкой и без нее на одной и той же задаче (например, make_moons или MNIST).
- Построить графики loss и val_loss по эпохам, отметить момент остановки.
- Показать, что модель с ранней остановкой демонстрирует лучшее обобщение (меньший разрыв между train и val loss).
2. Регуляризация L2 или Dropout
Регуляризация — это методы, снижающие переобучение путем ограничения сложности модели. Можно выбрать один из двух вариантов (оба приветствуются, но минимально необходим один).
2.1 Регуляризация L2 (weight decay)
Добавляет штраф к функции потерь за большие значения весов: L = L_original + λ · Σ(w²). Градиент весов изменяется: ∂L/∂w = ∂L_original/∂w + 2λ·w. При обновлении весов это эквивалентно умножению весов на коэффициент (1 - 2λ·lr).
- Параметр
l2_lambda(обычно 0.0001–0.01) должен быть настраиваемым для каждого слоя или глобально для модели. - В отчете необходимо провести эксперимент: сравнить обучение без регуляризации, с малым и с большим λ.
2.2 Dropout
Во время обучения случайным образом "выключает" долю нейронов в слое с вероятностью p. Это заставляет сеть учиться более робастным признакам. Реализация включает:
- В методе
forward()генерацию маски (бинарной матрицы) с вероятностью включенияkeep_prob = 1 - dropout_rate. - Применение маски к выходу слоя:
output = output * mask / keep_prob(масштабирование сохраняет математическое ожидание). - В методе
backward()применение той же маски к градиенту (маска запоминается на этапе forward). - В режиме предсказания (инференс) dropout не применяется, маска не генерируется.
Экспериментальная проверка регуляризации:
- Обучить модель без регуляризации на относительно малом датасете (например, make_moons с шумом) — показать переобучение (высокая train accuracy, низкая test accuracy).
- Добавить регуляризацию и показать снижение разрыва между train и test.
- Для Dropout: показать, что обучение требует больше эпох, но итоговое обобщение лучше.
3. Сохранение и загрузка весов
Возможность сохранять обученную модель на диск и загружать обратно без необходимости повторного обучения. Это важный инженерный компонент.
Требования:
- Реализовать методы
save_weights(filepath)иload_weights(filepath)в классе модели. - Формат хранения: JSON (текстовый, читаемый) или .npy (бинарный, эффективный). Для JSON необходимо преобразовывать массивы NumPy в списки и обратно.
- Должны сохраняться веса и смещения (biases) для каждого слоя. Для пакетной нормализации — также γ, β и бегущие средние.
- При загрузке весов архитектура сети (количество слоев, нейронов, типы активаций) должна быть создана заранее, после чего веса подгружаются.
Экспериментальная проверка:
- Обучить модель, сохранить веса.
- Создать новую модель с той же архитектурой, загрузить веса.
- Убедиться, что предсказания исходной и загруженной модели совпадают (с точностью до вычислительной погрешности).
4. Оптимизатор с моментом (Momentum) или Adam
Более сложные оптимизаторы, чем стандартный SGD, ускоряют сходимость и помогают преодолевать локальные минимумы. Можно реализовать один из двух вариантов.
4.1 SGD с моментом (Momentum)
Накопляет историю градиентов: v = momentum·v - learning_rate·grad, затем w = w + v. Параметр momentum обычно 0.9.
4.2 Optimizer Adam (рекомендуемый для уровня 2)
Adam сочетает momentum и адаптивную скорость обучения для каждого параметра. Формулы обновления:
- Рекомендуемые значения по умолчанию: α=0.001, β₁=0.9, β₂=0.999, ε=1e-8.
- Необходимо хранить для каждого параметра момент (m) и адаптивный момент (v).
- Обязательно отслеживать номер шага t (итерации) для коррекции смещения.
Экспериментальная проверка:
- Сравнить сходимость SGD, SGD+Momentum и Adam на одной задаче (например, классификация MNIST).
- Построить графики loss по эпохам для каждого оптимизатора.
- Показать, что Adam сходится быстрее и требует меньше ручной настройки learning rate.
5. Пакетная нормализация (Batch Normalization)
Batch Norm нормализует выходы слоя по мини-батчу, что стабилизирует обучение и позволяет использовать более высокие learning rates. Реализуется как отдельный слой.
Требования к реализации:
- Обучаемые параметры:
gamma(масштаб) иbeta(сдвиг), инициализируются gamma=1, beta=0. - Необучаемые параметры:
running_meanиrunning_var(бегущие средние для инференса). - В режиме обучения: нормализация по батчу, обновление бегущих средних:
running_mean = momentum * running_mean + (1 - momentum) * batch_mean. - В режиме инференса: используется накопленные running_mean и running_var.
- При обратном распространении необходимо вычислить градиенты по входу, gamma и beta.
Экспериментальная проверка:
- Обучить сеть с Batch Norm и без нее (одинаковая архитектура).
- Показать, что с Batch Norm обучение стабильнее, допустимы более высокие learning rates.
- Проверить распределение активаций до и после Batch Norm (гистограммы).
6. Эксперимент с набором данных MNIST
Классический датасет рукописных цифр (28×28 пикселей, 10 классов). Эксперимент демонстрирует способность сети работать с реальными данными.
Требования к проведению эксперимента:
- Использовать подвыборку MNIST: не менее 5000 изображений для обучения (рекомендуется 10000–20000). Можно загрузить через
keras.datasets.mnistилиsklearn.datasets.fetch_openml. - Нормализовать входные данные: пиксели от 0 до 255 привести к диапазону [0,1] или стандартизовать (среднее 0, дисперсия 1).
- Архитектура сети: вход 784 нейрона (28×28), минимум один скрытый слой (128–256 нейронов, ReLU), выходной слой 10 нейронов + Softmax.
- Функция потерь: категориальная кросс-энтропия.
- Достичь точности на тестовой выборке не менее 90% (для полносвязной сети на MNIST это реально).
Анализ результатов:
- Отчет должен содержать confusion matrix.
- Показать несколько примеров правильно и неправильно классифицированных цифр.
- Визуализировать веса первого слоя (784→128) в виде изображений 28×28 — показать, что сеть выучила паттерны похожие на цифры.
7. Визуализация границ решений или весов первого слоя
Визуализация помогает понять, что именно выучила нейронная сеть.
7.1 Визуализация границ решений (для двумерных данных)
- Применимо к датасетам с двумя признаками: make_moons, make_circles, XOR.
- Создать сетку точек в пространстве признаков, получить предсказания модели для каждой точки.
- Отобразить цветом область предсказания (например, 0 — синий, 1 — красный) и наложить обучающие точки.
- Показать, как граница меняется в процессе обучения (анимация или несколько последовательных кадров).
7.2 Визуализация весов первого слоя (для MNIST или изображений)
- Для каждого нейрона первого скрытого слоя взять веса, связывающие его с входом (784 значения).
- Преобразовать эти веса в изображение 28×28.
- Нормализовать значения весов для отображения (обычно от минимального к максимальному).
- Отобразить сетку из m×n изображений (например, 8×16 = 128 нейронов).
- Проанализировать: какие признаки выучил слой (края, углы, паттерны цифр).
8. Модульные тесты градиентов (численная проверка)
Численная проверка градиентов — важный прием отладки обратного распространения. Она позволяет убедиться, что аналитические градиенты, вычисленные вашим кодом, соответствуют численным.
Реализация:
- Для произвольной функции потерь и слоя численный градиент вычисляется по определению:
grad_num[i] = (f(θ + ε·e_i) - f(θ - ε·e_i)) / (2ε). - ε обычно выбирается 1e-5 или 1e-6.
- Сравнение аналитического и численного градиентов: относительная ошибка
|grad_analyt - grad_num| / (|grad_analyt| + |grad_num| + 1e-10)должна быть порядка 1e-7–1e-8. - Тест должен проходить для каждого слоя (Dense, Activation, Dropout, BatchNorm) и для всей модели целиком.
Включение в отчет:
- Привести код тестов.
- Показать результаты проверки для одного-двух слоев (таблица с относительными ошибками).
- Объяснить, почему численная проверка является золотым стандартом отладки backpropagation.
Обязательные разделы отчета
- Математические основы нейронных сетей: формулы прямого распространения, градиенты для каждого слоя, цепное правило.
- Обзор программных средств: Python, NumPy, Matplotlib, Jupyter Notebook (обоснование выбора).
- Архитектура разработанного программного обеспечения: диаграмма классов, описание модулей.
- Экспериментальная часть: таблицы с показателями точности и функции потерь при различных значениях learning rate, числе нейронов, количестве эпох. Графики обучения.
- Листинг ключевых фрагментов кода (методы forward и backward).
- Дневник практики (ежедневные записи о выполненной работе, возникших трудностях и способах их решения).
- Заключение и сравнение с готовыми фреймворками.
Отчет
- Шаблон отчета: исходный код, сформированный файл.
- Документация по разработке отчетов.
Источники и справочные материалы
Ниже приведены ссылки, которые помогут в освоении теории и практической реализации. Поощряется изучение дополнительных источников.