Читаем cтр. 16 – 24. в учебном пособии Гафаров Ф.М. Искусственные нейронные сети и приложения.
Слой нейронной сети, состоящий из нескольких нейронов можно было бы представить в виде списка объектов класса Neuron из прошлой лабораторной. Но значительно лучше переходить массивам numpy, потому что это будет проще и быстрее.
где $n$ - количество входов, $m$ - количество нейронов в слое.
Создать класс полносвязной однослойной нейронной сети с возможностью произвольного задания количества нейронов и произвольного количества входов нейронов. Добавить инициализацию значений весов случайными малыми значениями $[0.001; 0.2]$. Функцию активации использовать линейную.
import numpy as np
class NeuralNetwork:
weight : np.ndarray
bias : np.ndarray
def __init__(self, n: int, m: int):
pass
def _activation_function(self, x: np.ndarray) -> int:
pass
def predict(self, x : np.ndarray) -> np.ndarray:
pass
def fit(self, x : np.ndarray, y : np.ndarray):
pass
Нейрон с двумя входами и линейной функцией активации: $y = 3x_1 + 2x_2$
Данные для обучения:
| № | $x_1$ | $x_2$ | y |
|---|---|---|---|
| 1 | 1 | 3 | 8 |
| 2 | 2 | 4 | 11 |
| 3 | 1 | 5 | 9 |
| $n$ | .. | .. | .. |
Расчет ошибки предсказания для первого примера:
$y_{1pred} = 3 * 1 + 2 * 3 = 9$
$ε = 1$
$(3 + Δ_1) * 1 + (2 + Δ_1) * 3 = 8$
$4Δ_1 = 8 - 9$
$Δ_1 = -0.25$
Нейрон с обновленными весами: $y = 2.75x_1 + 1.75x_2$
Формула для обновления весов нейронов (для NeuralNetwork.fit_1()):
$ ω_{ij}^{t+1}=ω_{ij}^t + \frac {y_{ki} - \sum_{j=1}^n ω_{ij}^t x_{kj}}{\sum_{j=1}^n ω_{ij}^t },$ (1)
где $t$ - номер шага обучения, $k$ - номер обучающего примера, $i$ - номер нейрона в слое, $j$ - номер входа.
Аналитическое решение (математические формулы) – это идеально, если оно есть. В случаях, когда красивой формулы нет или она есть, но ее надо представить в виде программного кода, мы обращаемся к вычислительной математике.
Надо понимать, что машинные числа являются дискретной проекцией вещественных чисел на конкретную архитектуру компьютера, следовательно, неизбежно падает точность.
Оптимизация подразумевает нахождение экстремума целевой функции, когда заданы переменные (что можно изменять) и ограничения (какие условия должны обязательно выполняться). Большинство численных методов оптимизации – итеративные: в цикле определяется новое значение $X$ (предположительно, приближающееся к глобальному экстремуму), для которого рассчитывается значение $Y$. Критерием останова для них является момент, когда изменение значения функции за шаг становится меньше заданной точности ε.
Как нам известно, градиент – вектор, указывающий направление наискорейшего роста некоторой скалярной величины. Поэтому, чтобы найти минимум функции, нам надо двигаться в сторону антиградиента (это все при условии, что нам известна формула, выражающая функцию, и она дифференцируема).

$\bar x ^{k+1} = \bar x^k - α∇Φ(\bar x^k )$, (2)
где $∇Φ(\bar x) = \left(\begin{matrix} \frac {∂Φ}{∂x_1} \\ \frac {∂Φ}{∂x_2} \\ .. \\ .. \end{matrix} \right) $ ,
$α$ – задаваемый шаг, лучше делать меньше 0.01.
В случае обучения нейронной сети, оптимизируемая функция – функция ошибки обучения. Пусть будет среднеквадратичная ошибка:
$E=\frac 12 (y-y_{расчетный} )^2$ (3)
Подставив в формулу (2) вес c индексом (вместо вектора) и оптимизируемую функцию, получим:
$ω_{ij}^{t+1} = ω_{ij}^t - α \frac {∂(\frac 12 ( \sum_{j=1}^n f(ω_{ij}^t x_{j})-y_i)^2}{∂x_j},$
где $t$ - номер шага обучения, $i$ - номер нейрона в слое, $j$ - номер входа.
В случае линейной функции активации $f$, производная считается легко.
Получаем формулу для обновления весов нейронов (для NeuralNetwork.fit_2()):
$ω_{ij}^{t+1} = ω_{ij}^t - α \left( \sum_{j=1}^n (ω_{ij}^t x_{kj} - y_{ki}) * x_{kj} \right), $ (4)
где $k$ - номер обучающего примера.
Или в векторном виде:
$ W^{t+1} = W^t - α (W^t \times x - y) \times x^T, $
где $W$ - матрица весовых коэффициентов, $t$ - номер шага обучения, $x$ - вектор-столбец входных значений, $y$ - вектор-столбец выходных значений, $v^T$ - операция транспонирования.
За один проход ничего не обучится, поэтому реализуем в методе fit() последовательность шагов при обучении однослойной нейронной сети по правилу обучения Уидроу-Хоффа:
В теле программы создаем сеть из 3-х нейронов. У каждого нейрона 6 входов. Обучаем нейронную сеть на данных из файла 2lab_data.csv.
Обучаем сеть распознавание красного цвета на картинке:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
a = np.asarray(Image.open('pic.jpg'))
mas = a.reshape(1920*1080,1,3)
res = []
for i in range(len(mas)):
res.append(100 * your_precious_NeuralNetwork.predict(mas[i]))
plt.figure(figsize=(15.,10.))
plt.imshow(Image.fromarray(our_array, mode="L"))