关于二分类的交叉熵损失部分数学推导过程。
有些地方加以注释,公式太多懒得MD格式了
#%%
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn import datasets
iris_data =datasets.load_iris()
in_put_data = iris_data.data
correct = iris_data.target
print(correct)
n_data= len(in_put_data)
in_put_data#numpy array
#%%
avg_data=np.average(in_put_data,axis=0)#列平均
std_data=np.std(in_put_data,axis=0)
in_put_data=(in_put_data-avg_data)/std_data#归一化
print(in_put_data)
#%%
correct_data=np.zeros((n_data,3))
# print(correct_data)
for i in range(n_data):
correct_data[i,correct[i]]=1#one-hot correct=0 就是第一列=1
print(correct_data)
#%%
index=np.arange(n_data)
index_train=index[index%2==0]
index_test=index[index%2!=0]
input_train=in_put_data[index_train,:]#:所有列
input_test=in_put_data[index_test,:]
correct_train=correct_data[index_train,:]#独热编码len行 3列
correct_test=correct_data[index_test,:]
n_train=len(index_train)
n_test=input_test.shape[0]
#%%
#神经网络参数
n_in=4
n_mid=10
n_out=3
wb_width=0.1#随机权重
eta=0.01#学习率
max_epoch=10
batch_size=10
interval=10#显示间隔
v_w=1
v_b=1
class BaseLayer:
def __init__(self, n_upper, n):
'''
子类实例化传入MidLayer(n_in, n_mid)
上一层的神经元个数和当前层的神经元个数,实例化指定了
假设上层是2个这层是3,初始化矩阵为:
xxx b1
xxx b2
b3
'''
# 初始化权重和偏置
self.w = wb_width * np.random.randn(n_upper, n)
self.b = wb_width * np.random.randn(n)
def update(self, eta):
# 更新权重和偏置
self.w = self.w - eta * self.grad_w
self.b = self.b - eta * self.grad_b
class MidLayer(BaseLayer):
'''
forward_propagation(x):
第一层 训练数据输入(10,4)
mid_layer_1.forward(x)
[10,4][4,10]=[10,10]
第二层 输入第一层的(10,10)
mid_layer_2.forward(mid_layer_1.y)#10,10
[10,10][10,10]=[10,10]
---------------------------------------
backward_propagation(t):
倒数第二层 输入倒数第一的(10,10)
'''
def forward(self, x):
self.x = x
self.u = np.dot(x, self.w) + self.b
self.y = np.where(self.u <= 0, 0.01 * self.u, self.u) # Leaky ReLU
def backward(self, grad_y):
delta = grad_y * np.where(self.u <= 0, 0.01, 1.0) # ReLU导数
self.grad_w = np.dot(self.x.T, delta) # 权重梯度
self.grad_b = np.sum(delta, axis=0) # 偏置梯度
self.delta = np.dot(delta, self.w.T) # 将梯度传播回前一层
return self.delta
class OutLayer(BaseLayer):
'''
forward_propagation(x):
第三层 输入第二层的(10,10)
out_layer.forward(mid_layer_2.y)#10,3
[10,10][10,3]=[10,3]
------------------------------------
backward_propagation(t):
#方向第一层 标签数据输入(10,3)
out_layer.backward(t)#(10,10)
grad_w[10,10][10,3]=[10,3]
grad_b(3,)
[10,3][3,10]=[10,10]
'''
def forward(self, x):
self.x = x
u = np.dot(x, self.w) + self.b
self.y = np.exp(u) / np.sum(np.exp(u), axis=1, keepdims=True) # Softmax
def backward(self, t):
delta = self.y - t # 交叉熵损失函数 对每个类别yi的偏导数为 (y - t)
self.grad_w = np.dot(self.x.T, delta) # Loss 对 w 的导数. 权重梯度 x.T 依然是【10,10】
self.grad_b = np.sum(delta, axis=0) # Loss 对 b 的导数. 偏置梯度 (列和)
self.delta = np.dot(delta, self.w.T) # 将梯度传播回前一层 w.T[3,10]
return self.delta
mid_layer_1 = MidLayer(n_in, n_mid)#4,10
mid_layer_2 = MidLayer(n_mid, n_mid)#10,10
out_layer = OutLayer(n_mid, n_out)#10,3
def forward_propagation(x):
#训练输入(10,4)
mid_layer_1.forward(x)
# print("mid_layer_1.forward(x)",mid_layer_1.y.shape)
mid_layer_2.forward(mid_layer_1.y)#10,10
# print("mid_layer_2.forward(mid_layer_1.y)",mid_layer_2.y.shape)
out_layer.forward(mid_layer_2.y)#10,3
# print("out_layer.forward(mid_layer_2.y)",out_layer.y.shape)
return out_layer.y
def backward_propagation(t):
#输入(10,3)
out_layer.backward(t)#(10,10)
# print("out_layer.backward(t)",out_layer.delta.shape)
mid_layer_2.backward(out_layer.delta)
mid_layer_1.backward(mid_layer_2.delta)
def update_wb():
mid_layer_1.update(eta)
mid_layer_2.update(eta)
out_layer.update(eta)
#%%
#计算交叉熵损失
def get_error_rate(t,batch_size):
return -np.sum(t*np.log(out_layer.y+1e-7))/batch_size
train_error_x=[]
train_error_y=[]
test_error_x=[]
test_error_y=[]
n_batch=n_train//batch_size#7
print(n_batch)
for i in range(10):
print("epoch:", i)
# #评估当前模型的性能 向前传播训练集和测试集合
# forward_propagation(input_train)
# error_train=get_error_rate(correct_train,n_train) # y,长度
#
# forward_propagation(input_test)
# error_test=get_error_rate(correct_test,n_test)
#
# train_error_x.append(i) #[]
# train_error_y.append(error_train)#[]
#
# test_error_x.append(i)
# test_error_y.append(error_test)
#开始训练
# 随机打乱
index_random=np.arange(n_train)
np.random.shuffle(index_random)
for j in range(n_batch):
mb_index=index_random[j*batch_size:(j+1)*batch_size]#随机取batch_size个下标
x=input_train[mb_index,:]
t=correct_train[mb_index,:]
# print(x.shape)#(10,4)
# print("t:",t.shape)#(10,3)
forward_propagation(x)
backward_propagation(t)
update_wb()
#%%
plt.plot(train_error_x,train_error_y,label='train')
plt.plot(test_error_x,test_error_y,label='test')
plt.legend()
# plt.show()
这是是全连接神经网络,输入2个神经元,1个中间层3个神经元,输出层2个神经元
$$o _ { 1 } = \frac { e ^ { y _ { 1 } } } { e ^ { y _ { 1 } } + e ^ { y _ { 2 } } }$$
$$L o s s = - ( o _ { 1 } ^ { * } \log ( o _ { 1 } ) + o _ { 2 } ^ { * } \log ( o _ { 2 } ) )$$
$$\frac { \partial L o s s } { \partial w _ { 1 1 } ^ { ( 2 ) } } = \frac { \partial L o s s } { \partial y _ { 1 } } \cdot \frac { \partial y _ { 1 } } { \partial w _ { 1 1 } ^ { ( 2 ) } }=( \frac { \partial L o s s } { \partial o _ { 1 } } \cdot \frac { \partial o _ { 1 } } { \partial y _ { 1 } } + \frac { \partial L o s s } { \partial o _ { 2 } } \cdot \frac { \partial o _ { 2 } } { \partial y _ { 1 } } ) \cdot \frac { \partial y _ { 1 } } { \partial w _ { 1 1 } ^ { ( 2 ) } }$$
反向传播:损失函数对权重的梯度
ppt图片来源:1.2 卷积神经网络基础补充_哔哩哔哩_bilibili