手撕反向传播

 关于二分类的交叉熵损失部分数学推导过程。

有些地方加以注释,公式太多懒得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

推荐参考:

反向传播由于图片可知这个网络输出层维度为3 第$n+1$层为例第1个神经元参数$w^{n+1}_1,b^{n+1}_1$ - 掘金

反向传播由上图可知,神经元参数的变化会导致损失函数数值的变化。因此,对于输出层第1个神经元,定义 $w_1^{n+1} - 掘金

出现这个错误的原因是在导入seaborn包时,无法从typing模块中导入名为'Protocol'的对象。 解决这个问题的方法有以下几种: 1. 检查你的Python版本是否符合seaborn包的要求,如果不符合,尝试更新Python版本。 2. 检查你的环境中是否安装了typing_extensions包,如果没有安装,可以使用以下命令安装:pip install typing_extensions。 3. 如果你使用的是Python 3.8版本以下的版本,你可以尝试使用typing_extensions包来代替typing模块来解决该问题。 4. 检查你的代码是否正确导入了seaborn包,并且没有其他导入错误。 5. 如果以上方法都无法解决问题,可以尝试在你的代码中使用其他的可替代包或者更新seaborn包的版本来解决该问题。 总结: 出现ImportError: cannot import name 'Protocol' from 'typing'错误的原因可能是由于Python版本不兼容、缺少typing_extensions包或者导入错误等原因造成的。可以根据具体情况尝试上述方法来解决该问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [ImportError: cannot import name ‘Literal‘ from ‘typing‘ (D:\Anaconda\envs\tensorflow\lib\typing....](https://blog.youkuaiyun.com/yuhaix/article/details/124528628)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值