手写简单的BP(反向传播)算法,实现预测

手写简单的BP(反向传播)算法(一个隐藏层),网络结构如下:

具体代码实现及数据预测结果:

import numpy as np
import matplotlib.pyplot as plt

# 训练集特征2维
X = np.array([[2,1],[-1,1],[-1,-1],[1,-1]])
# 类别标签
y = np.array([1,2,3,4])


# 初始化权重矩阵
np.random.seed(42)
w1 = np.random.randn(2,4)
w2 = np.random.randn(4,4)
print(w1)

# 学习率(先不加正则化处理)
γ= 0.01

# 损失函数使用均方误差 E=1/2*(y-yhat)**2(单个样本)
# 对output进入端梯度E(w2*net_output)求导(使用标准BP算法)
# 对w2求梯度
def grad2_func(y,yhat,net_output):
    res = []
    for j in range(len(y)):
        res.append((y[j]-yhat[j])*(-net_output))
    res = np.array(res).T
    return res
# 对w1求梯度        
def grad1_func(y,yhat,sample,net_output):
    grad1 = (y-yhat).sum() #定值
    grad3 = sample[0],sample[1]
    res = []
    for i in range(w1.shape[0]):
        r1 = []
        for j in range(w1.shape[1]):
            grad2 = -(net_output[j]*(1-net_output[j])*w2[j,0]+
                      net_output[j]*(1-net_output[j])*w2[j,1]+
                      net_output[j]*(1-net_output[j])*w2[j,2]+
                      net_output[j]*(1-net_output[j])*w2[j,3])
            r1.append(grad1*grad2*grad3[i])
        res.append(r1)    
    return np.array(res)
    
# 正向传播 (激活函数使用sigmoid,方便求导)
def sigmoid(net):
    res = []
    for x in net:
        res.append(1/(1+np.exp(-x)))  

    return np.array(res)

# 向前传播	
def forward(X):
    net_input = []
    net_output = []
    output = []
    for i in range(len(X)):
            net_input.append(np.dot(X[i],w1))
    for i in net_input:
            net_output.append(sigmoid(i))
    for i in net_output:
            output.append(np.dot(i,w2))
    return output,net_output


# 反向传播
def back_propagation(w1,w2,epoch=1000):
    error = []
    for epoch in range(epoch):
        # 计算向前传播,输出结果及中间变量(求梯度时,用的到)
        yhat,net_output = forward(X)
        loss = 0
        # 算出所有样本的均方误差
        for i in yhat:
            for j in range(len(i)):
                loss+=1/2*(y[j]-i[j])**2
        #print(f'loss:{loss}')
        error.append(loss)
        
        for index,i in enumerate(yhat):
            # 计算所有权重的梯度
            # 真实标签编码,独热编码,类别位置1,其他为0
            encode_y = np.array([0,0,0,0])
            encode_y[index]=1
            #print(encode_y)
            grad2 = grad2_func(encode_y,i,net_output[index])
            grad1 = grad1_func(encode_y,i,X[index],net_output[index])
            # 梯度更新,未加正则化
            w1 -= γ*grad1
            w2 -= γ*grad2

    return error,w1,w2

# 预测函数
def predict(test_x):
    output,_ = forward(test_x)
    # 使用numpy 进行softmax处理输出
    pred_y = []
    for i in output:
        sum_exp = 0
        res = []
        for j in i:
            sum_exp+=np.exp(j)
        for j in i:
            res.append(np.exp(j)/sum_exp)
        pred_y.append(res.index(max(res))+1)
    return pred_y


if __name__ == '__main__':
    epoch =1000
    loss,w1,w2 = back_propagation(w1,w2,epoch)
    plt.figure(figsize=(12,5))
    plt.plot(range(epoch),loss,label='累计误差')
    plt.xlabel('迭代次数')
    plt.ylabel('loss')
    plt.legend()
    plt.grid()
    plt.show()

    test = np.array([[1,1],[-2,1],[-1,-2],[1,-1]])
    print(predict(test))
    

训练结果

预测结果:

posted @ 2021-10-15 15:17  旁人怎会懂  阅读(263)  评论(0编辑  收藏  举报