<jupyter_start><jupyter_text>Задание 2.1 - Нейронные сетиВ этом задании вы реализуете и натренируете настоящую нейроную сеть своими руками!В некотором смысле это будет расширением прошлого задания - нам нужно просто составить несколько линейных классификаторов вместе!<jupyter_code>import numpy as np
import matplotlib.pyplot as plt

%matplotlib inline

%load_ext autoreload
%load_ext nb_black
%autoreload 2
from dataset import load_svhn, random_split_train_val
from gradient_check import (
    check_layer_gradient,
    check_layer_param_gradient,
    check_model_gradient,
)
from layers import FullyConnectedLayer, ReLULayer
from model import TwoLayerNet
from trainer import Trainer, Dataset
from optim import SGD, MomentumSGD
from metrics import multiclass_accuracy<jupyter_output><empty_output><jupyter_text>Загружаем данныеИ разделяем их на training и validation.<jupyter_code>def prepare_for_neural_network(train_X, test_X):
    train_flat = train_X.reshape(train_X.shape[0], -1).astype(float) / 255.0
    test_flat = test_X.reshape(test_X.shape[0], -1).astype(float) / 255.0

    # Subtract mean
    mean_image = np.mean(train_flat, axis=0)
    train_flat -= mean_image
    test_flat -= mean_image

    return train_flat, test_flat


train_X, train_y, test_X, test_y = load_svhn("data", max_train=10000, max_test=1000)
train_X, test_X = prepare_for_neural_network(train_X, test_X)
# Split train into train and val
train_X, train_y, val_X, val_y = random_split_train_val(train_X, train_y, num_val=1000)
train_X.shape, train_y.shape, val_X.shape, val_y.shape, test_X.shape, test_y.shape<jupyter_output><empty_output><jupyter_text>Как всегда, начинаем с кирпичиковМы будем реализовывать необходимые нам слои по очереди. Каждый слой должен реализовать:- прямой проход (forward pass), который генерирует выход слоя по входу и запоминает необходимые данные- обратный проход (backward pass), который получает градиент по выходу слоя и вычисляет градиент по входу и по параметрамНачнем с ReLU, у которого параметров нет.<jupyter_code># TODO: Implement ReLULayer layer in layers.py
# Note: you'll need to copy implementation of the gradient_check function from the previous assignment

X = np.array([[1, -2, 3], [-1, 2, 0.1]])

assert check_layer_gradient(ReLULayer(), X)<jupyter_output>Gradient check passed!<jupyter_text>А теперь реализуем полносвязный слой (fully connected layer), у которого будет два массива параметров: W (weights) и B (bias).Все параметры наши слои будут использовать для параметров специальный класс `Param`, в котором будут храниться значения параметров и градиенты этих параметров, вычисляемые во время обратного прохода.Это даст возможность аккумулировать (суммировать) градиенты из разных частей функции потерь, например, из cross-entropy loss и regularization loss.<jupyter_code># TODO: Implement FullyConnected layer forward and backward methods
assert check_layer_gradient(FullyConnectedLayer(3, 4), X)
# TODO: Implement storing gradients for W and B
assert check_layer_param_gradient(FullyConnectedLayer(3, 4), X, "W")
assert check_layer_param_gradient(FullyConnectedLayer(3, 4), X, "B")<jupyter_output>Gradient check passed!
Gradient check passed!
Gradient check passed!<jupyter_text>Создаем нейронную сетьТеперь мы реализуем простейшую нейронную сеть с двумя полносвязным слоями и нелинейностью ReLU. Реализуйте функцию `compute_loss_and_gradients`, она должна запустить прямой и обратный проход через оба слоя для вычисления градиентов.Не забудьте реализовать очистку градиентов в начале функции.<jupyter_code># TODO: In model.py, implement compute_loss_and_gradients function
model = TwoLayerNet(n_input=train_X.shape[1], n_output=10, hidden_layer_size=3, reg=0)
loss = model.compute_loss_and_gradients(train_X[:2], train_y[:2])

# TODO Now implement backward pass and aggregate all of the params
check_model_gradient(model, train_X[:2], train_y[:2])<jupyter_output>Checking gradient for fc_1.W
Gradients are different at (538, 1). Analytic: 0.00000, Numeric: 0.00001<jupyter_text>Теперь добавьте к модели регуляризацию - она должна прибавляться к loss и делать свой вклад в градиенты.<jupyter_code># TODO Now implement l2 regularization in the forward and backward pass
model_with_reg = TwoLayerNet(
    n_input=train_X.shape[1], n_output=10, hidden_layer_size=3, reg=1e1
)
loss_with_reg = model_with_reg.compute_loss_and_gradients(train_X[:2], train_y[:2])
assert loss_with_reg > loss and not np.isclose(loss_with_reg, loss, 10e-4), (
    "Loss with regularization (%2.4f) should be higher than without it (%2.4f)!"
    % (loss, loss_with_reg)
)

check_model_gradient(model_with_reg, train_X[:2], train_y[:2])<jupyter_output>Checking gradient for fc_1.W
Gradient check passed!
Checking gradient for fc_1.B
Gradient check passed!
Checking gradient for fc_2.W
Gradient check passed!
Checking gradient for fc_2.B
Gradient check passed!<jupyter_text>Также реализуем функцию предсказания (вычисления значения) модели на новых данных.Какое значение точности мы ожидаем увидеть до начала тренировки?<jupyter_code># Finally, implement predict function!

# TODO: Implement predict function
# What would be the value we expect?
multiclass_accuracy(model_with_reg.predict(train_X[:30]), train_y[:30])<jupyter_output><empty_output><jupyter_text>Допишем код для процесса тренировкиЕсли все реализовано корректно, значение функции ошибки должно уменьшаться с каждой эпохой, пусть и медленно. Не беспокойтесь пока про validation accuracy.<jupyter_code>model = TwoLayerNet(
    n_input=train_X.shape[1], n_output=10, hidden_layer_size=100, reg=1e1
)
dataset = Dataset(train_X, train_y, val_X, val_y)
trainer = Trainer(model, dataset, SGD(), learning_rate=1e-2)

# TODO Implement missing pieces in Trainer.fit function
# You should expect loss to go down every epoch, even if it's slow
loss_history, train_history, val_history = trainer.fit()
plt.plot(train_history)
plt.plot(val_history)<jupyter_output><empty_output><jupyter_text>Улучшаем процесс тренировкиМы реализуем несколько ключевых оптимизаций, необходимых для тренировки современных нейросетей.  Уменьшение скорости обучения (learning rate decay)Одна из необходимых оптимизаций во время тренировки нейронных сетей - постепенное уменьшение скорости обучения по мере тренировки.Один из стандартных методов - уменьшение скорости обучения (learning rate) каждые N эпох на коэффициент d (часто называемый decay). Значения N и d, как всегда, являются гиперпараметрами и должны подбираться на основе эффективности на проверочных данных (validation data). В нашем случае N будет равным 1.<jupyter_code># TODO Implement learning rate decay inside Trainer.fit method
# Decay should happen once per epoch

model = TwoLayerNet(
    n_input=train_X.shape[1], n_output=10, hidden_layer_size=100, reg=1e-1
)
dataset = Dataset(train_X, train_y, val_X, val_y)
trainer = Trainer(model, dataset, SGD(), learning_rate_decay=0.99)

initial_learning_rate = trainer.learning_rate
loss_history, train_history, val_history = trainer.fit()

assert (
    trainer.learning_rate < initial_learning_rate
), "Learning rate should've been reduced"
assert (
    trainer.learning_rate > 0.5 * initial_learning_rate
), "Learning rate shouldn'tve been reduced that much!"<jupyter_output>Epoch: 0, Loss: 41.434652, Train accuracy: 0.997274, val accuracy: 0.996575
Epoch: 1, Loss: 43.098394, Train accuracy: 0.726219, val accuracy: 0.756303
Epoch: 2, Loss: 49.653042, Train accuracy: 0.829557, val accuracy: 0.840376
Epoch: 3, Loss: 31.086640, Train accuracy: 0.819346, val accuracy: 0.852308
Epoch: 4, Loss: 34.675877, Train accuracy: 0.828999, val accuracy: 0.842424
Epoch: 5, Loss: 51.776259, Train accuracy: 0.846696, val accuracy: 0.844660
Epoch: 6, Loss: 34.991629, Train accuracy: 0.874518, val accuracy: 0.891304
Epoch: 7, Loss: 35.295137, Train accuracy: 0.797810, val accuracy: 0.800643
Epoch: 8, Loss: 41.000906, Train accuracy: 0.689912, val accuracy: 0.728659
Epoch: 9, Loss: 34.309963, Train accuracy: 0.854239, val accuracy: 0.856574
Epoch: 10, Loss: 34.084474, Train accuracy: 0.854825, val accuracy: 0.866171
Epoch: 11, Loss: 30.093477, Train accuracy: 0.847356, val accuracy: 0.866310
Epoch: 12, Loss: 50.203479, Train accuracy: 0.832610, val accuracy: 0.843450
Epoch: 13[...]<jupyter_text>Накопление импульса (Momentum SGD)Другой большой класс оптимизаций - использование более эффективных методов градиентного спуска. Мы реализуем один из них - накопление импульса (Momentum SGD).Этот метод хранит скорость движения, использует градиент для ее изменения на каждом шаге, и изменяет веса пропорционально значению скорости.(Физическая аналогия: Вместо скорости градиенты теперь будут задавать ускорение, но будет присутствовать сила трения.)```velocity = momentum * velocity - learning_rate * gradient w = w + velocity````momentum` здесь коэффициент затухания, который тоже является гиперпараметром (к счастью, для него часто есть хорошее значение по умолчанию, типичный диапазон -- 0.8-0.99).Несколько полезных ссылок, где метод разбирается более подробно:  http://cs231n.github.io/neural-networks-3/sgd  https://distill.pub/2017/momentum/<jupyter_code># TODO: Implement MomentumSGD.update function in optim.py

model = TwoLayerNet(
    n_input=train_X.shape[1], n_output=10, hidden_layer_size=100, reg=1e-1
)
dataset = Dataset(train_X, train_y, val_X, val_y)
trainer = Trainer(
    model, dataset, MomentumSGD(), learning_rate=1e-4, learning_rate_decay=0.99
)

# You should see even better results than before!
loss_history, train_history, val_history = trainer.fit()<jupyter_output>Epoch: 0, Loss: 44.724276, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 1, Loss: 43.431613, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 2, Loss: 46.521125, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 3, Loss: 39.192398, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 4, Loss: 40.442832, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 5, Loss: 44.966102, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 6, Loss: 36.099310, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 7, Loss: 39.787739, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 8, Loss: 37.015723, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 9, Loss: 37.193913, Train accuracy: 0.998913, val accuracy: 1.000000
Epoch: 10, Loss: 33.648729, Train accuracy: 0.976070, val accuracy: 0.986622
Epoch: 11, Loss: 33.812997, Train accuracy: 0.968157, val accuracy: 0.963077
Epoch: 12, Loss: 26.709668, Train accuracy: 0.936148, val accuracy: 0.940299
Epoch: 13[...]<jupyter_text>Ну что, давайте уже тренировать сеть!  Последний тест - переобучимся (overfit) на маленьком наборе данныхХороший способ проверить, все ли реализовано корректно - переобучить сеть на маленьком наборе данных.  Наша модель обладает достаточной мощностью, чтобы приблизить маленький набор данных идеально, поэтому мы ожидаем, что на нем мы быстро дойдем до 100% точности на тренировочном наборе. Если этого не происходит, то где-то была допущена ошибка!<jupyter_code>data_size = 15
model = TwoLayerNet(
    n_input=train_X.shape[1], n_output=10, hidden_layer_size=100, reg=1e-1
)
dataset = Dataset(
    train_X[:data_size], train_y[:data_size], val_X[:data_size], val_y[:data_size]
)
trainer = Trainer(
    model, dataset, SGD(), learning_rate=1e-1, num_epochs=150, batch_size=5
)

# You should expect this to reach 1.0 training accuracy
loss_history, train_history, val_history = trainer.fit()<jupyter_output>Epoch: 0, Loss: 12.061030, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 1, Loss: 11.360059, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 2, Loss: 10.490006, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 3, Loss: 19.791552, Train accuracy: 0.000000, val accuracy: 0.000000
Epoch: 4, Loss: 75.211562, Train accuracy: 0.000000, val accuracy: 0.000000
Epoch: 5, Loss: 37.765050, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 6, Loss: 582.247303, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 7, Loss: nan, Train accuracy: 0.666667, val accuracy: 0.571429
Epoch: 8, Loss: nan, Train accuracy: 0.428571, val accuracy: 0.625000
Epoch: 9, Loss: nan, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 10, Loss: nan, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 11, Loss: nan, Train accuracy: 0.000000, val accuracy: 0.000000
Epoch: 12, Loss: nan, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 13, Loss: nan, Train accuracy: 0.0000[...]<jupyter_text>Теперь найдем гипепараметры, для которых этот процесс сходится быстрее.Если все реализовано корректно, то существуют параметры, при которых процесс сходится в **20** эпох или еще быстрее.Найдите их!<jupyter_code># Now, tweak some hyper parameters and make it train to 1.0 accuracy in 20 epochs or less

model = TwoLayerNet(
    n_input=train_X.shape[1], n_output=10, hidden_layer_size=100, reg=1e-1
)
dataset = Dataset(
    train_X[:data_size], train_y[:data_size], val_X[:data_size], val_y[:data_size]
)
# TODO: Change any hyperparamers or optimizators to reach training accuracy in 20 epochs
trainer = Trainer(
    model, dataset, SGD(), learning_rate=1e-1, num_epochs=20, batch_size=5
)

loss_history, train_history, val_history = trainer.fit()<jupyter_output>Epoch: 0, Loss: 11.555207, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 1, Loss: 11.326828, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 2, Loss: 9.369206, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 3, Loss: 9.959574, Train accuracy: 0.000000, val accuracy: 0.000000
Epoch: 4, Loss: 10.827729, Train accuracy: 0.000000, val accuracy: 0.000000
Epoch: 5, Loss: 27.417578, Train accuracy: 0.250000, val accuracy: 0.000000
Epoch: 6, Loss: 63.798544, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 7, Loss: nan, Train accuracy: 1.000000, val accuracy: 1.000000
Epoch: 8, Loss: nan, Train accuracy: 1.000000, val accuracy: 0.000000
Epoch: 9, Loss: nan, Train accuracy: 1.000000, val accuracy: 0.800000
Epoch: 10, Loss: nan, Train accuracy: 0.000000, val accuracy: 0.000000
Epoch: 11, Loss: nan, Train accuracy: 0.666667, val accuracy: 0.200000
Epoch: 12, Loss: nan, Train accuracy: 0.000000, val accuracy: 0.000000
Epoch: 13, Loss: nan, Train accuracy: 0.000000,[...]<jupyter_text>Итак, основное мероприятие!Натренируйте лучшую нейросеть! Можно добавлять и изменять параметры, менять количество нейронов в слоях сети и как угодно экспериментировать. Добейтесь точности лучше **60%** на validation set.<jupyter_code># Let's train the best one-hidden-layer network we can

learning_rates = [1e-4, 1e-5, 1e-3]
reg_strength = [1e-6, 1e-7, 1e-4, 1e-5]
learning_rate_decay = [0.999, 0.99, 0.95]
hidden_layer_size = [128, 64, 256]
num_epochs = 20
batch_size = 64

best_classifier = None
best_val_accuracy = 0

loss_history = []
train_history = []
val_history = []

# TODO find the best hyperparameters to train the network
# Don't hesitate to add new values to the arrays above, perform experiments, use any tricks you want
# You should expect to get to at least 40% of valudation accuracy
# Save loss/train/history of the best classifier to the variables above
dataset = Dataset(train_X, train_y, val_X, val_y)
for lr in learning_rates:
    for reg in reg_strength:
        for ls in hidden_layer_size:
            for decay in learning_rate_decay:
                print(
                    "Learning rate: %f, Reg strength: %f, Hidden layer size: %f, Learning rate decay: %f"
                    % (lr, reg, ls, decay)
                )

                classifier = TwoLayerNet(
                    n_input=train_X.shape[1], n_output=10, hidden_layer_size=ls, reg=reg
                )

                trainer = Trainer(
                    classifier,
                    dataset,
                    MomentumSGD(),
                    learning_rate=lr,
                    learning_rate_decay=decay,
                    num_epochs=num_epochs,
                )

                loss_history, train_history, val_history = trainer.fit(False)
                accuracy = val_history[-1] if val_history else 0
                if accuracy > best_val_accuracy:
                    best_classifier = classifier
                    best_val_accuracy = accuracy

                    loss_history = loss_history
                    train_history = train_history
                    val_history = val_history

print("Best validation accuracy achieved: %f" % best_val_accuracy)
plt.figure(figsize=(15, 7))
plt.subplot(211)
plt.title("Loss")
plt.plot(loss_history)
plt.subplot(212)
plt.title("Train/validation accuracy")
plt.plot(train_history)
plt.plot(val_history)<jupyter_output><empty_output><jupyter_text>Как обычно, посмотрим, как наша лучшая модель работает на тестовых данных<jupyter_code>test_pred = best_classifier.predict(test_X)
test_accuracy = multiclass_accuracy(test_pred, test_y)
print("Neural net test set accuracy: %f" % (test_accuracy,))<jupyter_output>Neural net test set accuracy: 1.000000
{'fraction_non_alphanumeric': 0.091786870768325, 'fraction_numerical': 0.08118928466293789, 'mean_word_length': 5.0469918120327515, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 51, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '593091', 'license': 'MIT', 'n_tokens_mistral': 7106, 'n_tokens_neox': 6432, 'n_words': 1774, 'path': '_dlcourse-ai/assignments/assignment2/Neural Network.ipynb', 'repo_name': 'alxgrin/kaggle', 'size': '16985'}