Assistant: 
User: Выполни задание, предполагая, что пишешь Jupyter Notebook и придерживайся хорошего стиля его написания: пиши красивые пояснения в markdown, логично разделяй код на ячейки, выводи полученные результаты как объекты или через print. Использй заголовки и другие способы выделения текста в markdown. Повествование имеет логику, вступление, заключение. Отвечай в формате json по заданному примеру: {0: ["markdown", "В этом ноутбуке будем использовать формулу $y_i=a_0 + a_1 + \epsilon_i$."], 1: ["code", "import numpy as np"], 2: ["markdown", "Заключение..."]}. Пиши на русском, если задание на русском. Начинай сразу с написания ячеек. Не останавливай генерацию, пока не выполнишь всё задание. Задание: Исходный код:
{0: ['markdown', 'В этом ноутбуке мы будем создавать нейронные сети с использованием библиотеки `torch`. Мы начнем с создания нейронов и полносвязных слоев.'], 1: ['code', 'import torch'], 2: ['markdown', '## 2.1 Создание нейронов и полносвязных слоев'], 3: ['markdown', '### 2.1.1. Реализация нейрона'], 4: ['markdown', 'Используя операции над матрицами и векторами из библиотеки `torch`, реализуем нейрон с заданными весами `weights` и `bias`. Прогоняем вектор `inputs` через нейрон и выводим результат.'], 5: ['code', 'class Neuron:\n    def __init__(self, weights, bias):\n        self.weights = weights\n        self.bias = bias\n\n    def forward(self, inputs):\n        return torch.dot(inputs, self.weights) + self.bias'], 6: ['code', 'inputs = torch.tensor([1.0, 2.0, 3.0, 4.0])\nweights = torch.tensor([-0.2, 0.3, -0.5, 0.7])\nbias = 3.14\nneuron = Neuron(weights, bias)\noutput = neuron.forward(inputs)\nprint(output)', 'tensor(4.8400)\n'], 7: ['markdown', '### 2.1.2 Реализация полносвязного слоя'], 8: ['markdown', 'Используя операции над матрицами и векторами из библиотеки `torch`, реализуем полносвязный слой с заданными весами `weights` и `biases`. Прогоняем вектор `inputs` через слой и выводим результат.'], 9: ['code', 'class Linear:\n    def __init__(self, weights, biases):\n        self.weights = weights\n        self.biases = biases\n\n    def forward(self, inputs):\n        return torch.matmul(inputs, self.weights) + self.biases'], 10: ['code', 'inputs = torch.tensor([1.0, 2.0, 3.0, 4.0])\nweights = torch.tensor([[-0.2, 0.3, -0.5, 0.7],\n [0.5, -0.91, 0.26, -0.5],\n [-0.26, -0.27, 0.17, 0.87]]).T\n\nbiases = torch.tensor([3.14, 2.71, 7.2])\nlinear_layer = Linear(weights, biases)\noutput = linear_layer.forward(inputs)\nprint(output)', 'tensor([ 4.8400,  0.1700, 10.3900])\n'], 11: ['markdown', '### 2.1.3 Реализация полносвязного слоя для батчей\n\nТеперь реализуем полносвязный слой так, чтобы он мог принимать на вход матрицу (батч) с данными. Результатом прогона сквозь слой должна быть матрица размера `batch_size` x `n_neurons`.'], 12: ['code', 'inputs = torch.tensor([[1, 2, 3, 2.5], \n [2, 5, -1, 2], \n [-1.5, 2.7, 3.3, -0.8]])'], 13: ['markdown', '### 2.1.4 Реализация полносвязного слоя с инициализацией весов\n\nИспользуя операции над матрицами и векторами из библиотеки `torch`, реализуем полносвязный слой из `n_neurons` нейронов с `n_features` весами у каждого нейрона (инициализируются из стандартного нормального распределения). Прогоняем вектор `inputs` через слой и выводим результат.'], 14: ['code', 'class Linear:\n def __init__(self, n_features, n_neurons):\n self.weights = torch.randn(n_features, n_neurons)\n self.biases = torch.zeros(n_neurons)\n \n def forward(self, inputs):\n return torch.matmul(inputs, self.weights) + self.biases', '\x1b[0;36m  Cell \x1b[0;32mIn[7], line 3\x1b[0;36m\x1b[0m\n\x1b[0;31m    self.weights = torch.randn(n_features, n_neurons)\x1b[0m\n\x1b[0m    ^\x1b[0m\n\x1b[0;31mIndentationError\x1b[0m\x1b[0;31m:\x1b[0m expected an indented block after function definition on line 2\n'], 15: ['markdown', '### 2.1.5 Создание двух полносвязных слоев\n\nИспользуя решение из __2.1.4__, создадим 2 полносвязных слоя и пропустим матрицу `inputs` последовательно через эти два слоя. Количество нейронов в первом слое выберем произвольно, а количество нейронов во втором слое выберем так, чтобы результатом прогона являлась матрица (3x7).'], 16: ['code', 'inputs = torch.tensor([[1, 2, 3, 2.5], \n [2, 5, -1, 2], \n [-1.5, 2.7, 3.3, -0.8]])\n\nlayer1 = Linear(n_features=4, n_neurons=5)\nlayer2 = Linear(n_features=5, n_neurons=7)\noutput1 = layer1.forward(inputs)\noutput2 = layer2.forward(output1)\nprint(output2)', '\x1b[0;31m---------------------------------------------------------------------------\x1b[0m'], 17: ['markdown', '## 2.2 Создание функций активации'], 18: ['markdown', '### 2.2.1 Реализация функции активации ReLU\n\nИспользуя операции над матрицами и векторами из библиотеки `torch`, реализуем функцию активации ReLU:\n\n![](https://wikimedia.org/api/rest_v1/media/math/render/svg/f4353f4e3e484130504049599d2e7b040793e1eb)\n\nСоздадим матрицу размера (4,3), заполненную числами из стандартного нормального распределения, и проверим работоспособность функции активации.'], 19: ['code', 'class ReLU:\n    def forward(self, inputs):\n        return torch.maximum(inputs, torch.tensor(0.0))\n\nrelu = ReLU()\ninputs_relu = torch.randn(4, 3)\noutput_relu = relu.forward(inputs_relu)\nprint(output_relu)', 'tensor([[0.0000, 0.0000, 0.0000],\n'], 20: ['markdown', '### 2.2.2 Реализация функции активации softmax\n\nИспользуя операции над матрицами и векторами из библиотеки `torch`, реализуем функцию активации softmax:\n\n![](https://wikimedia.org/api/rest_v1/media/math/render/svg/6d7500d980c313da83e4117da701bf7c8f1982f5)\n\nСоздадим матрицу размера (4,3), заполненную числами из стандартного нормального распределения, и проверим работоспособность функции активации. Строки матрицы трактуем как выходы линейного слоя некоторого классификатора для 4 различных примеров.'], 21: ['code', 'class Softmax:\n    def forward(self, inputs):\n        exp_values = torch.exp(inputs - torch.max(inputs, dim=1, keepdim=True).values)\n        return exp_values / torch.sum(exp_values, dim=1, keepdim=True)\n\nsoftmax = Softmax()\ninputs_softmax = torch.randn(4, 3)\noutput_softmax = softmax.forward(inputs_softmax)\nprint(output_softmax)', 'tensor([[0.2295, 0.1582, 0.6123],\n'], 22: ['markdown', '### 2.2.3 Реализация функции активации ELU\n\nИспользуя операции над матрицами и векторами из библиотеки `torch`, реализуем функцию активации ELU:\n\n![](https://wikimedia.org/api/rest_v1/media/math/render/svg/eb23becd37c3602c4838e53f532163279192e4fd)\n\nСоздадим матрицу размера (4,3), заполненную числами из стандартного нормального распределения, и проверим работоспособность функции активации.'], 23: ['code', 'class ELU:\n    def __init__(self, alpha):\n        self.alpha = alpha\n\n    def forward(self, inputs):\n        return torch.where(inputs > 0, inputs, self.alpha * (torch.exp(inputs) - 1))\n\nelu = ELU(alpha=1.0)\ninputs_elu = torch.randn(4, 3)\noutput_elu = elu.forward(inputs_elu)\nprint(output_elu)', 'tensor([[ 1.7771, -0.7470, -0.9262],\n'], 24: ['markdown', '## 2.3 Создание функции потерь'], 25: ['markdown', '### 2.3.1 Реализация функции потерь MSE\n\nИспользуя операции над матрицами и векторами из библиотеки `torch`, реализуем функцию потерь MSE:\n\n![](https://wikimedia.org/api/rest_v1/media/math/render/svg/e258221518869aa1c6561bb75b99476c4734108e)\n\nСоздадим полносвязный слой с 1 нейроном, прогоняем через него батч `inputs` и считаем значение MSE, трактуя вектор `y` как вектор правильных ответов.'], 26: ['code', 'class MSELoss:\n def forward(self, y_pred, y_true):\n return torch.mean((y_pred - y_true) ** 2)\n\ninputs = torch.tensor([[1, 2, 3, 2.5], \n [2, 5, -1, 2], \n [-1.5, 2.7, 3.3, -0.8]])\n\ny = torch.tensor([2.0, 3.0, 4.0])\n\nmse_loss = MSELoss()\noutput_loss = mse_loss.forward(inputs, y)\nprint(output_loss)', '\x1b[0;36m  Cell \x1b[0;32mIn[12], line 3\x1b[0;36m\x1b[0m\n\x1b[0;31m    return torch.mean((y_pred - y_true) ** 2)\x1b[0m\n\x1b[0m    ^\x1b[0m\n\x1b[0;31mIndentationError\x1b[0m\x1b[0;31m:\x1b[0m expected an indented block after function definition on line 2\n'], 27: ['markdown', '### 2.3.2 Реализация функции потерь Categorical Cross-Entropy\n\nИспользуя операции над матрицами и векторами из библиотеки `torch`, реализуем функцию потерь Categorical Cross-Entropy.\n\nСоздадим полносвязный слой с 3 нейронами и прогоняем через него батч `inputs`. Полученный результат пропускаем через функцию активации softmax. Считаем значение CCE, трактуя вектор `y` как вектор правильных ответов.'], 28: ['code', 'class CategoricalCrossentropyLoss:\n def forward(self, y_pred, y_true):\n return -torch.sum(y_true * torch.log(y_pred + 1e-12)) / y_true.size(0)\n\ninputs = torch.tensor([[1, 2, 3, 2.5], \n [2, 5, -1, 2], \n [-1.5, 2.7, 3.3, -0.8]])\ny = torch.tensor([1, 0, 0])\n\nlinear_layer = Linear(n_features=4, n_neurons=3)\noutput_layer = linear_layer.forward(inputs)\nsoftmax_layer = Softmax()\ny_pred = softmax_layer.forward(output_layer)\ncce_loss = CategoricalCrossentropyLoss()\noutput_cce = cce_loss.forward(y_pred=y_pred, y_true=y)\nprint(output_cce)', '\x1b[0;36m  Cell \x1b[0;32mIn[13], line 3\x1b[0;36m\x1b[0m\n\x1b[0;31m    return -torch.sum(y_true * torch.log(y_pred + 1e-12)) / y_true.size(0)\x1b[0m\n\x1b[0m    ^\x1b[0m\n\x1b[0;31mIndentationError\x1b[0m\x1b[0;31m:\x1b[0m expected an indented block after function definition on line 2\n'], 29: ['markdown', '### 2.3.3 Модификация функции потерь MSE с добавлением L2-регуляризации\n\nТеперь модифицируем 2.3.1, добавив L2-регуляризацию. Это поможет предотвратить переобучение модели.'], 30: ['markdown', '## 2.4 Обратное распространение ошибки'], 31: ['markdown', '### 2.4.1 Используя один нейрон и SGD (1 пример за шаг), решите задачу регрессии'], 32: ['code', 'from sklearn.datasets import make_regression\n\nX, y, coef = make_regression(n_features=4, n_informative=4, coef=True, bias=0.5)\nX = torch.tensor(X, dtype=torch.float32)\ny = torch.tensor(y, dtype=torch.float32)'], 33: ['markdown', '[Граф вычислений для этой задачи](https://i.ibb.co/2dhDxZx/photo-2021-02-15-17-18-04.jpg)'], 34: ['markdown', '2.4.1.1 Реализуйте класс `SquaredLoss`'], 35: ['code', 'class SquaredLoss:\n    def forward(self, y_pred, y_true):\n        return torch.mean((y_pred - y_true) ** 2)\n    \n    def backward(self, y_pred, y_true):\n        self.dinput = 2 * (y_pred - y_true) / y_true.numel()'], 36: ['markdown', '2.4.1.2. Модифицируйте класс `Neuron` из __2.1.1__:\n\n 1) Сделайте так, чтобы веса нейрона инициализировались из стандартного нормального распределения\n\n 2) Реализуйте расчет градиента относительно весов `weights` и `bias`'], 37: ['code', 'class Neuron:\n    def __init__(self, n_inputs):\n        self.weights = torch.randn(n_inputs)\n        self.bias = torch.randn(1)\n\n    def forward(self, inputs):\n        return torch.dot(inputs, self.weights) + self.bias\n\n    def backward(self, dvalue):\n        self.dweights = dvalue * self.inputs\n        self.dbias = dvalue\n        self.dinput = dvalue * self.weights'], 38: ['markdown', '2.4.1.3 Допишите цикл для настройки весов нейрона\n\n[SGD](https://ru.wikipedia.org/wiki/Случайный_градиентный_спуск)\n\n![](https://wikimedia.org/api/rest_v1/media/math/render/svg/dda3670f8a8996a0d3bf80856bb4a166cc8db6d4)'], 39: ['code', 'n_inputs = 4\nlearning_rate = 0.1  # скорость обучения\nn_epoch = 100  # количество эпох\n\nneuron = Neuron(n_inputs)\nloss = SquaredLoss()\n\nlosses = []\nfor epoch in range(n_epoch):\n    for x_example, y_example in zip(X, y):\n        # forward pass\n        y_pred = neuron.forward(x_example)\n        curr_loss = loss.forward(y_pred, y_example)\n        losses.append(curr_loss)\n\n        # backprop\n        dvalue = loss.backward(y_pred, y_example)\n        neuron.backward(dvalue)\n\n        # обновление весов\n        neuron.weights -= learning_rate * neuron.dweights\n        neuron.bias -= learning_rate * neuron.dbias', '\x1b[0;31m---------------------------------------------------------------------------\x1b[0m'], 40: ['markdown', '### 2.4.2 Решите задачу 2.4.1, используя пакетный градиентный спуск'], 41: ['markdown', 'Вычисления для этой задачи: \n[1](https://i.ibb.co/rmtQT6P/photo-2021-02-15-18-00-43.jpg)\n[2](https://i.ibb.co/NmCFVnQ/photo-2021-02-15-18-01-17.jpg)'], 42: ['markdown', '2.4.1.1 Модифицируйте класс `MSELoss` из __2.3.1__, реализовав расчет производной относительно предыдущего слоя с учетом того, что теперь работа ведется с батчами, а не с индивидуальными примерами'], 43: ['code', 'class MSELoss:\n    def forward(self, y_pred, y_true):\n        return torch.mean((y_pred - y_true) ** 2)\n\n    def backward(self, y_pred, y_true):\n        self.dinput = 2 * (y_pred - y_true) / y_true.numel()'], 44: ['markdown', '### 2.4.2.2. Модифицируйте класс `Neuron` из __2.4.1.2__:\n\n1) Реализуйте метод `forward` таким образом, чтобы он мог принимать на вход матрицу (батч) с данными.\n\n2) Реализуйте расчет градиента относительно весов `weights` и `bias` с учетом того, что теперь работа ведется с батчами, а не с индивидуальными примерами.'], 45: ['code', 'class Neuron:\n    def __init__(self, n_inputs):\n        self.weights = torch.randn(n_inputs)\n        self.bias = torch.randn(1)\n\n    def forward(self, inputs):\n        return torch.matmul(inputs, self.weights) + self.bias\n\n    def backward(self, dvalue):\n        self.dweights = dvalue.unsqueeze(1) * self.inputs\n        self.dbias = dvalue.sum(axis=0)\n        self.dinput = dvalue @ self.weights.T'], 46: ['markdown', '2.4.2.3 Допишите цикл для настройки весов нейрона'], 47: ['markdown', '### 2.4.3 Используя один полносвязный слой и пакетный градиентный спуск, решите задачу регрессии из __2.4.1__'], 48: ['markdown', '### 2.4.3.1 Модифицируйте класс `Linear` из __2.1.4__. ([вычисление градиентов](https://i.ibb.co/kgVR6m6/photo-2021-02-15-21-30-28.jpg))'], 49: ['code', 'class Linear:\n    def __init__(self, n_features, n_neurons):\n        self.weights = torch.randn(n_features, n_neurons)\n        self.biases = torch.zeros(n_neurons)\n    \n    def forward(self, inputs):\n        return torch.matmul(inputs, self.weights) + self.biases\n    \n    def backward(self, dvalues):\n        self.dweights = inputs.T @ dvalues / inputs.shape[0]\n        self.dbiases = dvalues.sum(axis=0) / inputs.shape[0]\n        self.dinputs = dvalues @ self.weights.T'], 50: ['markdown', '### 2.4.3.2 Создайте слой с одним нейроном. Используя класс MSELoss из 2.4.2, убедитесь, что модель обучается'], 51: ['markdown', '### 2.4.4 Используя наработки из 2.4, создайте нейросеть и решите задачу регрессии.\n\nПредлагаемая архитектура: \n1. Полносвязный слой с 10 нейронами\n2. Активация ReLU\n3. Полносвязный слой с 1 нейроном'], 52: ['code', 'X = torch.linspace(-1, 1, 100).view(-1, 1)\ny = X.pow(2) + 0.2 * torch.rand(X.size())'], 53: ['code', ''], 54: ['code', ''], 55: ['code', '']}Исправь ошибки в ноутбукеПродолжи генерацию предыдущего ответа, опираясь на те же требования. Отвечай в форме json со следующего по порядку номера ячейки в прерыдущем ответе, не переписывая код предыдущих ячеек!. Не закрывай скобку, пока все задания не были выполнены!{0: ['markdown', 'В этом ноутбуке мы будем создавать нейронные сети с использованием библиотеки `torch`. Мы начнем с создания нейронов и полносвязных слоев.'], 1: ['code', 'import torch'], 2: ['markdown', '## 2.1 Создание нейронов и полносвязных слоев'], 3: ['markdown', '### 2.1.1. Реализация нейрона'], 4: ['markdown', 'Используя операции над матрицами и векторами из библиотеки `torch`, реализуем нейрон с заданными весами `weights` и `bias`. Прогоняем вектор `inputs` через нейрон и выводим результат.'], 5: ['code', 'class Neuron:\n    def __init__(self, weights, bias):\n        self.weights = weights\n        self.bias = bias\n\n    def forward(self, inputs):\n        return torch.dot(inputs, self.weights) + self.bias'], 6: ['code', 'inputs = torch.tensor([1.0, 2.0, 3.0, 4.0])\nweights = torch.tensor([-0.2, 0.3, -0.5, 0.7])\nbias = 3.14\nneuron = Neuron(weights, bias)\noutput = neuron.forward(inputs)\nprint(output)', 'tensor(4.8400)'], 7: ['markdown', '### 2.1.2 Реализация полносвязного слоя'], 8: ['markdown', 'Используя операции над матрицами и векторами из библиотеки `torch`, реализуем полносвязный слой с заданными весами `weights` и `biases`. Прогоняем вектор `inputs` через слой и выводим результат.'], 9: ['code', 'class Linear:\n    def __init__(self, weights, biases):\n        self.weights = weights\n        self.biases = biases\n\n    def forward(self, inputs):\n        return torch.matmul(inputs, self.weights) + self.biases'], 10: ['code', 'inputs = torch.tensor([1.0, 2.0, 3.0, 4.0])\nweights = torch.tensor([[-0.2, 0.3, -0.5, 0.7],\n [0.5, -0.91, 0.26, -0.5],\n [-0.26, -0.27, 0.17, 0.87]]).T\n\nbiases = torch.tensor([3.14, 2.71, 7.2])\nlinear_layer = Linear(weights, biases)\noutput = linear_layer.forward(inputs)\nprint(output)', 'tensor([ 4.8400, 0.1700, 10.3900])'], 11: ['markdown', '### 2.1.3 Реализация полносвязного слоя для батчей\n\nТеперь реализуем полносвязный слой так, чтобы он мог принимать на вход матрицу (батч) с данными. Результатом прогона сквозь слой должна быть матрица размера `batch_size` x `n_neurons`.'], 12: ['code', 'inputs = torch.tensor([[1, 2, 3, 2.5], \n [2, 5, -1, 2], \n [-1.5, 2.7, 3.3, -0.8]])'], 13: ['markdown', '### 2.1.4 Реализация полносвязного слоя с инициализацией весов\n\nИспользуя операции над матрицами и векторами из библиотеки `torch`, реализуем полносвязный слой из `n_neurons` нейронов с `n_features` весами у каждого нейрона (инициализируются из стандартного нормального распределения). Прогоняем вектор `inputs` через слой и выводим результат.'], 14: ['code', 'class Linear:\n    def __init__(self, n_features, n_neurons):\n        self.weights = torch.randn(n_features, n_neurons)\n        self.biases = torch.zeros(n_neurons)\n\n    def forward(self, inputs):\n        return torch.matmul(inputs, self.weights) + self.biases'], 15: ['markdown', '### 2.1.5 Создание двух полносвязных слоев\n\nИспользуя решение из 2.1.4, создадим 2 полносвязных слоя и пропустим матрицу `inputs` последовательно через эти два слоя. Количество нейронов в первом слое выберем произвольно, а количество нейронов во втором слое выберем так, чтобы результатом прогона являлась матрица (3x7).'], 16: ['code', 'inputs = torch.tensor([[1, 2, 3, 2.5], \n [2, 5, -1, 2], \n [-1.5, 2.7, 3.3, -0.8]])\n\nlayer1 = Linear(n_features=4, n_neurons=5)\nlayer2 = Linear(n_features=5, n_neurons=7)\noutput1 = layer1.forward(inputs)\noutput2 = layer2.forward(output1)\nprint(output2)'], 17: ['markdown', '## 2.2 Создание функций активации'], 18: ['markdown', '### 2.2.1 Реализация функции активации ReLU\n\nИспользуя операции над матрицами и векторами из библиотеки `torch`, реализуем функцию активации ReLU:\n\n![](https://wikimedia.org/api/rest_v1/media/math/render/svg/f4353f4e3e484130504049599d2e7b040793e1eb)\n\nСоздадим матрицу размера (4,3), заполненную числами из стандартного нормального распределения, и проверим работоспособность функции активации.'], 19: ['code', 'class ReLU:\n    def forward(self, inputs):\n        return torch.maximum(inputs, torch.tensor(0.0))\n\nrelu = ReLU()\ninputs_relu = torch.randn(4, 3)\noutput_relu = relu.forward(inputs_relu)\nprint(output_relu)', 'tensor([[0.0000, 0.0000, 0.0000],'], 20: ['markdown', '### 2.2.2 Реализация функции активации softmax\n\nИспользуя операции над матрицами и векторами из библиотеки `torch`, реализуем функцию активации softmax:\n\n![](https://wikimedia.org/api/rest_v1/media/math/render/svg/6d7500d980c313da83e4117da701bf7c8f1982f5)\n\nСоздадим матрицу размера (4,3), заполненную числами из стандартного нормального распределения, и проверим работоспособность функции активации. Строки матрицы трактуем как выходы линейного слоя некоторого классификатора для 4 различных примеров.'], 21: ['code', 'class Softmax:\n    def forward(self, inputs):\n        exp_values = torch.exp(inputs - torch.max(inputs, dim=1, keepdim=True).values)\n        return exp_values / torch.sum(exp_values, dim=1, keepdim=True)\n\nsoftmax = Softmax()\ninputs_softmax = torch.randn(4, 3)\noutput_softmax = softmax.forward(inputs_softmax)\nprint(output_softmax)', 'tensor([[0.2295, 0.1582, 0.6123],'], 22: ['markdown', '### 2.2.3 Реализация функции активации ELU\n\nИспользуя операции над матрицами и векторами из библиотеки `torch`, реализуем функцию активации ELU:\n\n![](https://wikimedia.org/api/rest_v1/media/math/render/svg/eb23becd37c3602c4838e53f532163279192e4fd)\n\nСоздадим матрицу размера (4,3), заполненную числами из стандартного нормального распределения, и проверим работоспособность функции активации.'], 23: ['code', 'class ELU:\n    def __init__(self, alpha):\n        self.alpha = alpha\n\n    def forward(self, inputs):\n        return torch.where(inputs > 0, inputs, self.alpha * (torch.exp(inputs) - 1))\n\nelu = ELU(alpha=1.0)\ninputs_elu = torch.randn(4, 3)\noutput_elu = elu.forward(inputs_elu)\nprint(output_elu)'], 24: ['markdown', '## 2.3 Создание функции потерь'], 25: ['markdown', '### 2.3.1 Реализация функции потерь MSE\n\nИспользуя операции над матрицами и векторами из библиотеки `torch`, реализуем функцию потерь MSE:\n\n![](https://wikimedia.org/api/rest_v1/media/math/render/svg/e258221518869aa1c6561bb75b99476c4734108e)\n\nСоздадим полносвязный слой с 1 нейроном, прогоняем через него батч `inputs` и считаем значение MSE, трактуя вектор `y` как вектор правильных ответов.'], 26: ['code', 'class MSELoss:\n    def forward(self, y_pred, y_true):\n        return torch.mean((y_pred - y_true) ** 2)\n\n    def backward(self, y_pred, y_true):\n        self.dinput = 2 * (y_pred - y_true) / y_true.numel()'], 27: ['markdown', '### 2.3.2 Реализация функции потерь Categorical Cross-Entropy\n\nИспользуя операции над матрицами и векторами из библиотеки `torch`, реализуем функцию потерь Categorical Cross-Entropy.\n\nСоздадим полносвязный слой с 3 нейронами и прогоняем через него батч `inputs`. Полученный результат пропускаем через функцию активации softmax. Считаем значение CCE, трактуя вектор `y` как вектор правильных ответов.'], 28: ['code', 'class CategoricalCrossentropyLoss:\n    def forward(self, y_pred, y_true):\n        return -torch.sum(y_true * torch.log(y_pred + 1e-12)) / y_true.size(0)\n\ninputs = torch.tensor([[1, 2, 3, 2.5], \n                       [2, 5, -1, 2], \n                       [-1.5, 2.7, 3.3, -0.8]])\ny = torch.tensor([[1, 0, 0], [0, 1, 0], [0, 0, 1]])\n\nlinear_layer = Linear(n_features=4, n_neurons=3)\noutput_layer = linear_layer.forward(inputs)\nsoftmax_layer = Softmax()\ny_pred = softmax_layer.forward(output_layer)\ncce_loss = CategoricalCrossentropyLoss()\noutput_cce = cce_loss.forward(y_pred=y_pred, y_true=y)\nprint(output_cce)'], 29: ['markdown', '### 2.3.3 Модификация функции потерь MSE с добавлением L2-регуляризации\n\nТеперь модифицируем 2.3.1, добавив L2-регуляризацию. Это поможет предотвратить переобучение модели.'], 30: ['markdown', '## 2.4 Обратное распространение ошибки'], 31: ['markdown', '### 2.4.1 Используя один нейрон и SGD (1 пример за шаг), решите задачу регрессии'], 32: ['code', 'from sklearn.datasets import make_regression\n\nX, y, coef = make_regression(n_features=4, n_informative=4, coef=True, bias=0.5)\nX = torch.tensor(X, dtype=torch.float32)\ny = torch.tensor(y, dtype=torch.float32)'], 33: ['markdown', '[Граф вычислений для этой задачи](https://i.ibb.co/2dhDxZx/photo-2021-02-15-17-18-04.jpg)'], 34: ['markdown', '2.4.1.1 Реализуйте класс `SquaredLoss`'], 35: ['code', 'class SquaredLoss:\n    def forward(self, y_pred, y_true):\n        return torch.mean((y_pred - y_true) ** 2)\n    \n    def backward(self, y_pred, y_true):\n        self.dinput = 2 * (y_pred - y_true) / y_true.numel()'], 36: ['markdown', '2.4.1.2. Модифицируйте класс `Neuron` из __2.1.1__:\n\n 1) Сделайте так, чтобы веса нейрона инициализировались из стандартного нормального распределения\n\n 2) Реализуйте расчет градиента относительно весов `weights` и `bias`'], 37: ['code', 'class Neuron:\n    def __init__(self, n_inputs):\n        self.weights = torch.randn(n_inputs)\n        self.bias = torch.randn(1)\n    \n    def forward(self, inputs):\n        return torch.dot(inputs, self.weights) + self.bias\n    \n    def backward(self, dvalue):\n        self.dweights = dvalue * self.inputs\n        self.dbias = dvalue\n        self.dinput = dvalue * self.weights'], 38: ['markdown', '2.4.1.3 Допишите цикл для настройки весов нейрона\n\n[SGD](https://ru.wikipedia.org/wiki/Случайный_градиентный_спуск)\n\n![](https://wikimedia.org/api/rest_v1/media/math/render/svg/dda3670f8a8996a0d3bf80856bb4a166cc8db6d4)'], 39: ['code', 'n_inputs = 4\nlearning_rate = 0.1 # скорость обучения\nn_epoch = 100 # количество эпох\n\nneuron = Neuron(n_inputs)\nloss = SquaredLoss()\n\nlosses = []\nfor epoch in range(n_epoch):\n    for x_example, y_example in zip(X, y):\n        # forward pass\n        y_pred = neuron.forward(x_example)\n        curr_loss = loss.forward(y_pred, y_example)\n        losses.append(curr_loss)\n\n        # backprop\n        dvalue = loss.backward(y_pred, y_example)\n        neuron.backward(dvalue)\n\n        # обновление весов\n        neuron.weights -= learning_rate * neuron.dweights\n        neuron.bias -= learning_rate * neuron.dbias'], 40: ['markdown', '### 2.4.2 Решите задачу 2.4.1, используя пакетный градиентный спуск'], 41: ['markdown', 'Вычисления для этой задачи: \n[1](https://i.ibb.co/rmtQT6P/photo-2021-02-15-18-00-43.jpg)\n[2](https://i.ibb.co/NmCFVnQ/photo-2021-02-15-18-01-17.jpg)'], 42: ['markdown', '2.4.1.1 Модифицируйте класс `MSELoss` из __2.3.1__, реализовав расчет производной относительно предыдущего слоя с учетом того, что теперь работа ведется с батчами, а не с индивидуальными примерами']}
Assistant: