Обратное распространение ошибки… на пальцах… без формул. математика на пальцах.. математика на пальцах. математика нейросетей.. математика на пальцах. математика нейросетей. Машинное обучение.. математика на пальцах. математика нейросетей. Машинное обучение. нейросети python.. математика на пальцах. математика нейросетей. Машинное обучение. нейросети python. нейросети и machine learning.. математика на пальцах. математика нейросетей. Машинное обучение. нейросети python. нейросети и machine learning. обратное распространение ошибки.. математика на пальцах. математика нейросетей. Машинное обучение. нейросети python. нейросети и machine learning. обратное распространение ошибки. обучение нейронных сетей.. математика на пальцах. математика нейросетей. Машинное обучение. нейросети python. нейросети и machine learning. обратное распространение ошибки. обучение нейронных сетей. обучение нейросетей.. математика на пальцах. математика нейросетей. Машинное обучение. нейросети python. нейросети и machine learning. обратное распространение ошибки. обучение нейронных сетей. обучение нейросетей. производная.. математика на пальцах. математика нейросетей. Машинное обучение. нейросети python. нейросети и machine learning. обратное распространение ошибки. обучение нейронных сетей. обучение нейросетей. производная. производная сложной функции.
Все эти игры не для нас....

Все эти игры не для нас….

Хотелось быпоказать «суть» метода обратного распространения ошибки (Backpropagation) в нейросетях. Ведь ее сложно увидеть за нагромождением формул. Статья, конечно, не для профессионалов индустрии и математиков… Но знать производные нужно.

Рассматривать будем самый примитивный пример. С наименьшим количеством формул. А они, конечно, будут :-( Заранее прошу прощения за мои французские математические обозначения и термины. См. заголовок статьи (… на пальцах..).

Рассмотрим вот такую "сеть".

Рассмотрим вот такую “сеть”.

У нас только 2 нейрона, в каждом по одному коэффициенту (обозначим их a и b ). Нейроны последовательно передают свои результаты друг другу. В конце результат проходит через функцию потерь. Она квадратическая, имеет вид (х-y)**2 .

И, пока никакой функции активации у нейронов не будет. Ее мы добавим позже, принцип там тот же.

Ну вот теперь, к производным. Как вы можете знать, для подстройки коэффициентов (весов) нейросети, необходимо вычислить значение частной производной для конкретного коэффициента. Попробуем это сделать.

Возьмем частную производную по коэффициенту b , находящемуся в Нейроне 2.

https://www.wolframalpha.com/input?i=D[((ax%29b-y%29**2%2C%7Bb%2C1%7D%5D

Цепное правило

Цепное правило

Обратим внимание, что она состоит из 2х компонент. Это «цепное правило».

Не будем заострять внимание на нем самом, оно простое. Здесь важно заметить, что второй множитель это «входные данные». То есть, то что поступило на вход нашего множителя b.

Таким образом, производная для нашего коэффициента = значение производной «внешней» функции Х «входные данные» для данного коэффициента.

Прогнав данные через нашу «сеть» в прямом направление (forward) мы получим значения этих производных (ЧИСЛЕННО). То есть, они будут готовыми числами, и никаких формул писать не придется. Формулы нужны только сейчас Продолжим…

Теперь производная по коэффициенту а , находящемуся в Нейроне 1.

https://www.wolframalpha.com/input?i=D[((ax)b-y)**2%2C{a%2C1}]

Такая картина.

Такая картина.

Вот и ключ. Значение производной по внешней (для Нейрона 1) функции – это произведение всех предыдущих производных. Значение производной функции потерь у нас уже есть, а значение производной по Нейрону 2 – это просто коэффициент (b).

Итого

Вперед и назад

Вперед и назад

Теперь к коду на Python

# сами нейроны будут обьектами класса Neuron
class Neuron():
    
    def __init__(self):
        

            
        # У нейрона 1 коээфициет (вес). Установим его вот таким случайным числом.
        self.ves = np.random.random()
        
        
        # Те самые "входные данные". Будем хранить их в нейроне.
        self.input = 0
        
        
        # Поправки для весов 
        self.delta_ves = 0
        
    # вычисление (прямой прогон)     
    def forward (self, x): 
        
        # сохранем те самые "ВХОДНЫЕ ДАННЫЕ". Они нам потребуются для обучения сети
        self.input = x
        
        # умножаем х на наш вес и возващаем 
        return x * self.ves
    
    
    
    # с
    
    def back (self, grad): # принимаем производную предыдущей функции
        
        
    # производная для веса нашего нейрона  = производная предыдущей функции * "входные данные"
        self.delta_ves = grad * self.input 
        
        
        
    # производная для следующего нейрона = производная предыдущей функции * ТЕКУЩИЙ ВЕС ДАННОГО нейрона
    # возвращаем их  
        return self.ves * grad
    
    
    # применяем изменения к весу нейрона (ГРАДИЕНТНЫЙ СПУСК)
    # Это можно делать ТОЛЬКО после передачи неизмененного (предыдущего) значения веса. 
    # здесь в виде отдельного метода
    def fit (self, lr): 
        
        self.ves = self.ves - self.delta_ves * lr # производная * лернинг-рейn   
    
    
# Модель будет просто списком из обьектов класса Neuron
class Model():
    def __init__(self, num_neurons):
        
        self.neurons = []
        np.random.seed(5) # сид, для эксперементов
      
        for i in range (num_neurons):
            self.neurons.append( Neuron() )
            
        
    
m = Model(10) # пусть 10 нейронов

x = 5 # вход
y = 7 # ответ
# это нужно крутить в цикле..  

# прямой проход
for neuron in m.neurons:
    ovtet =  x
    ovtet = neuron.forward(ovtet) # передаем данные с нейрона в нейрон

print(ovtet)

loss = (ovtet - y) **2 # вычисляем ЛОСЯ , квадратического

print("Лось = ", loss)

gradient = 2 * (ovtet - y) # берем производную последней функции (функции потерь) 


lr = 0.01 # коэффициент шага для градиентного спуска


# ОБРАТНОЕ РАСПРОСТРОНЕНИЕ ОШИБКИ

for neuron in reversed(m.neurons): # двигаемся в обратном направлении (от последнего нейрона к первому) 
    
    # передаем значения производных
    gradient = neuron.back(gradient)
    
    # после передачи производной для следующего нейрона, применяем изменение к текущему нейрону 
    neuron.fit (lr)
  

Посмотрим на более сложную модельку.

Обратное распространение ошибки… на пальцах… без формул - 6

Ситуация тут аналогичная. Каждый нейрон возвращает назад свой коэффициент.

Но может быть еще один вариант распространения данных по нейронам сети.

Обратное распространение от 2х нейронов

Обратное распространение от 2х нейронов

Теперь наш нейрон отдает результат в несколько нейронов на следующем слое. У нас полносвязная сеть. Теперь «возвратов» при обратном распространении несколько, а нейрон только один.

Попробуйте самостоятельно взять такую производную. Но ответ такой….. Их возвраты СКЛАДЫВАЮТСЯ.

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

Там уже есть создание слоев с нейронами и добавление функции активации. Код, конечно, не учебный, с комментариями туго.

Спасибо.

Автор: falkorn

Источник

Рейтинг@Mail.ru
Rambler's Top100