Реалізація Зворотного Поширення Помилки
Загальний підхід
Під час прямого поширення кожен шар l приймає вихідні значення попереднього шару, al−1, як вхідні дані та обчислює власні виходи. Тому метод forward()
класу Layer
приймає вектор попередніх виходів як єдиний параметр, а решта необхідної інформації зберігається всередині класу.
Під час зворотного поширення кожному шару l потрібен лише dal для обчислення відповідних градієнтів і повернення dal−1, тому метод backward()
приймає вектор dal як параметр. Вся інша необхідна інформація вже зберігається в класі Layer
.
Похідні функцій активації
Оскільки для зворотного поширення потрібні похідні функцій активації, такі функції активації як ReLU та sigmoid слід реалізовувати у вигляді класів, а не окремих функцій. Це дозволяє визначити як:
- Саму функцію активації (реалізується через метод
__call__()
), що дозволяє застосовувати її безпосередньо в класіLayer
за допомогоюself.activation(z)
; - Її похідну (реалізується через метод
derivative()
), що забезпечує ефективне зворотне поширення та використовується в класіLayer
якself.activation.derivative(z)
.
Структуруючи функції активації як об'єкти, їх можна легко передавати до класу Layer
та використовувати динамічно.
ReLu
Похідна функції активації ReLU виглядає наступним чином, де zi — це елемент вектора преактивацій z:
f′(zi)={1,zi>00,zi≤0class ReLU:
def __call__(self, z):
return np.maximum(0, z)
def derivative(self, z):
return (z > 0).astype(float)
Сигмоїда
Похідна сигмоїдної функції активації має вигляд:
f′(zi)=f(zi)⋅(1−f(zi))class Sigmoid:
def __call__(self, x):
return 1 / (1 + np.exp(-z))
def derivative(self, z):
sig = self(z)
return sig * (1 - sig)
Для обох цих функцій активації застосування відбувається до всього вектора z, так само і для їх похідних. NumPy внутрішньо застосовує операцію до кожного елемента вектора. Наприклад, якщо вектор z містить 3 елементи, похідна обчислюється так:
f′(z)=f′(z1z2z3)=f′(z1)f′(z2)f′(z3)Метод backward()
Метод backward()
відповідає за обчислення градієнтів за допомогою наступних формул:
a^{l-1} та zl зберігаються як атрибути inputs
і outputs
відповідно у класі Layer
. Функція активації f зберігається як атрибут activation
.
Після обчислення всіх необхідних градієнтів можна оновити ваги та зміщення, оскільки вони більше не потрібні для подальших обчислень:
Wlbl=Wl−α⋅dWl=bl−α⋅dblТаким чином, learning_rate
(α) є ще одним параметром цього методу.
def backward(self, da, learning_rate):
dz = ...
d_weights = ...
d_biases = ...
da_prev = ...
self.weights -= learning_rate * d_weights
self.biases -= learning_rate * d_biases
return da_prev
Оператор *
виконує покомпонентне множення, тоді як функція np.dot()
виконує скалярний добуток у NumPy. Атрибут .T
транспонує масив.
Дякуємо за ваш відгук!
Запитати АІ
Запитати АІ
Запитайте про що завгодно або спробуйте одне із запропонованих запитань, щоб почати наш чат
Awesome!
Completion rate improved to 4
Реалізація Зворотного Поширення Помилки
Свайпніть щоб показати меню
Загальний підхід
Під час прямого поширення кожен шар l приймає вихідні значення попереднього шару, al−1, як вхідні дані та обчислює власні виходи. Тому метод forward()
класу Layer
приймає вектор попередніх виходів як єдиний параметр, а решта необхідної інформації зберігається всередині класу.
Під час зворотного поширення кожному шару l потрібен лише dal для обчислення відповідних градієнтів і повернення dal−1, тому метод backward()
приймає вектор dal як параметр. Вся інша необхідна інформація вже зберігається в класі Layer
.
Похідні функцій активації
Оскільки для зворотного поширення потрібні похідні функцій активації, такі функції активації як ReLU та sigmoid слід реалізовувати у вигляді класів, а не окремих функцій. Це дозволяє визначити як:
- Саму функцію активації (реалізується через метод
__call__()
), що дозволяє застосовувати її безпосередньо в класіLayer
за допомогоюself.activation(z)
; - Її похідну (реалізується через метод
derivative()
), що забезпечує ефективне зворотне поширення та використовується в класіLayer
якself.activation.derivative(z)
.
Структуруючи функції активації як об'єкти, їх можна легко передавати до класу Layer
та використовувати динамічно.
ReLu
Похідна функції активації ReLU виглядає наступним чином, де zi — це елемент вектора преактивацій z:
f′(zi)={1,zi>00,zi≤0class ReLU:
def __call__(self, z):
return np.maximum(0, z)
def derivative(self, z):
return (z > 0).astype(float)
Сигмоїда
Похідна сигмоїдної функції активації має вигляд:
f′(zi)=f(zi)⋅(1−f(zi))class Sigmoid:
def __call__(self, x):
return 1 / (1 + np.exp(-z))
def derivative(self, z):
sig = self(z)
return sig * (1 - sig)
Для обох цих функцій активації застосування відбувається до всього вектора z, так само і для їх похідних. NumPy внутрішньо застосовує операцію до кожного елемента вектора. Наприклад, якщо вектор z містить 3 елементи, похідна обчислюється так:
f′(z)=f′(z1z2z3)=f′(z1)f′(z2)f′(z3)Метод backward()
Метод backward()
відповідає за обчислення градієнтів за допомогою наступних формул:
a^{l-1} та zl зберігаються як атрибути inputs
і outputs
відповідно у класі Layer
. Функція активації f зберігається як атрибут activation
.
Після обчислення всіх необхідних градієнтів можна оновити ваги та зміщення, оскільки вони більше не потрібні для подальших обчислень:
Wlbl=Wl−α⋅dWl=bl−α⋅dblТаким чином, learning_rate
(α) є ще одним параметром цього методу.
def backward(self, da, learning_rate):
dz = ...
d_weights = ...
d_biases = ...
da_prev = ...
self.weights -= learning_rate * d_weights
self.biases -= learning_rate * d_biases
return da_prev
Оператор *
виконує покомпонентне множення, тоді як функція np.dot()
виконує скалярний добуток у NumPy. Атрибут .T
транспонує масив.
Дякуємо за ваш відгук!