Я учусь, как реализовать классификатор нейронной сети с нуля, используя Numpy и набор данных Iris Flowers. Я использую функцию кросс-энтропийной потери. Набор данных можно найти здесь . Используемая мной нейронная сеть имеет следующую архитектуру:
- Входной слой имеет 4 единицы, поскольку имеется 4 независимых переменных
- 1-й скрытый слой имеет 5 единиц с активацией tanh (x)
- 2-й скрытый слой имеет 4 единицы с активацией tanh (x)
- Последний (выходной) слой имеет 3 единицы для обозначения трех результирующих классов (Versicolor, Setosa & Virginica), которые имеют softmax активация
И соответствующие матрицы весов и смещений имеют следующие функции и размеры
- матрица весов w1 измерения 5 в 4, которая отображает входные данные в первый скрытый слой
- единица смещения b0, размера 5 на 100, которая добавляет смещение к первому скрытому слою
- матрица весов w2 измерения 4 на 5, который отображает первый скрытый слой на второй скрытый слой
- Блок смещения b1 измерения 4 в 100, который добавляет значения смещения ко второму скрытому слою
- весовая матрица w3 измерения 3 в 4, которая отображает второй скрытый слой к выходному слою
- Единица смещения b2 измерения 3 в 100, которая добавляет значения смещения к выходному слою
Следующий код получает данные, один горячий кодирует вывод и инициализирует матрицы весов и смещений
import numpy as np
import pandas as pd
data = pd.read_csv('iris.csv')
def softmax(x):
"""Compute softmax values for each sets of scores in x."""
e_x = np.exp(x - np.max(x))
return e_x / e_x.sum(axis=0)
### One hot encoding
output_vector = {
'setosa':[1,0,0],
'veriscolor':[0,1,0],
'virginica':[0,0,1]
}
vector_df = pd.DataFrame.from_dict(output_vector)
vector_df = vector_df.transpose()
vector_df['Species'] = vector_df.index
vector_df.reset_index(inplace=True)
vector_df.drop('index',axis=1,inplace=True)
data = pd.merge(data,vector_df)
data = data.drop('Unnamed: 0',axis=1)
X = data[['SepalLength','SepalWidth','PetalLength','PetalWidth']].values
y = data[[0,1,2]].values
X = X.T
y = y.T
np.random.seed(42)
w1 = np.random.rand(5,4)
b0 = np.ones((5,100))
w2 = np.random.rand(4,5)
b1 = np.ones((4,100))
w3 = np.random.rand(3,4)
b2 = np.ones((3,100))
alpha = 0.1
loss = []
. После настройки входов прямой проход выполняется следующим образом:
x1 = np.tanh(w1@X+b0)
x2 = np.tanh(w2@x1+b1)
x3 = softmax(w3@x2+b2)
, где x1 - первый скрытый слой, x2 - это второй скрытый слой, а x3 - выходной слой.
После того, как прямой переход сделан, нам нужно вычислить градиенты функции кросс-энтропийной потери относительно весов, используя обратное распространение. Это включает в себя расчет ошибок для каждого слоя, как показано на следующем снимке экрана, взятом из здесь
Код, который я
delta_3 = (x3-y)/100 ### Need to multiply this with derivative of softmax
delta_2 = np.multiply(w3.T@delta_3,1-np.power(x2,2))
delta_1 = np.multiply(w2.T@delta_2,1-np.power(x1,2))
## Weight Update
## Learning Rate
w1 = w1 - (alpha*delta_1@X.T)
b0 = b0 - (alpha*delta_1)
w2 = w2 - (alpha*delta_2@x1.T)
b1 = b1 - (alpha*delta_2)
w3 = w3 - (alpha*delta_3@x2.T)
b2 = b2 - (alpha*delta_3)
Производные функции tanh (x) кажутся прямыми, иначе 1-tanh (x) 2 . Но я застрял с производными от softmax продукции.
Давайте рассмотрим пример одного выхода [0.1,0.2,0.7].
import numpy as np
output = np.array([0.1,0.2,0.7]).reshape(3,1)
e = np.ones((3,1))
diff = np.multiply(output@e.T,(np.eye(3) - e@output.T))
print(diff)
array([[ 0.09, -0.02, -0.07],
[-0.02, 0.16, -0.14],
[-0.07, -0.14, 0.21]])
Результирующий дифференциал будет представлять собой матрицу 3 на 3. Учитывая это, как мы можем расширить это до выходного вектора от 3 до 100 (вместо одного выхода, 100 выходов)? Это потому, что для вычисления термина delta_3 нам нужен дифференциальный член, который имеет размеры от 3 до 100 (из-за продукта Адамара). Есть ли какое-то скручивание / усреднение?
Заранее спасибо!