导入包以及函数准备,这里用的tanh为激活函数
import math
import random
import numpy as np
from sklearn.metrics import classification_report
from sklearn.preprocessing import OneHotEncoder
random.seed(0)
# 生成区间[a, b)内的随机数
def rand(a, b):
return (b-a)*random.random() + a
# 函数 sigmoid,这里采用 tanh
def sigmoid(x):
return 1/(1+np.exp(-x))
# 函数 sigmoid 的派生函数, 为了得到输出 (即:y) 求导
def dsigmoid(y):
return y*(1-y)
BP神经网络的训练过程的基本步骤:
-
初始化网络参数:
- 定义网络的结构,包括输入层、隐藏层和输出层的节点数。
- 初始化连接输入层和隐藏层的权重矩阵(通常为随机值)和偏置项。
- 初始化连接隐藏层和输出层的权重矩阵和偏置项。
-
前向传播(Forward Propagation):
- 将训练数据输入到网络的输入层。
- 对每个隐藏层节点,计算加权和并通过激活函数得到节点的输出。
- 将隐藏层的输出作为输入,重复上述过程,计算输出层的输出。
- 计算模型的预测输出。
-
计算误差:
- 将网络的预测输出与真实标签进行比较,计算误差。常用的误差计算方法包括均方误差(Mean Squared Error)等。
-
反向传播(Backward Propagation):
- 从输出层开始,计算每个神经元对误差的贡献,即误差对权重的偏导数。
- 通过链式法则,将误差逐层传播回隐藏层,计算隐藏层每个神经元对误差的贡献。
- 更新权重和偏置项,以减小误差。这里使用梯度下降等优化算法。
-
重复迭代:
- 重复进行前向传播、计算误差、反向传播和权重更新的过程,直到满足停止条件,如达到最大迭代次数或误差降到足够小。
下面是代码:
class BPNN:
''' 三层反向传播神经网络 '''
def __init__(self, ni, nh1,nh2, no):
# 输入层、隐藏层、输出层的节点(数)
self.ni = ni + 1 # 增加一个偏置节点
self.nh1 = nh1
self.nh2 = nh2
self.no = no
# 激活神经网络的所有节点(向量)
self.inputL = [1.0]*self.ni
self.hiddenL1 = [1.0]*self.nh1
self.hiddenL2 = [1.0]*self.nh2
self.outputL = [1.0]*self.no
# 建立权重(矩阵)
self.wi = np.ones((self.ni, self.nh1))
self.wt = np.ones((self.nh1, self.nh2))
self.wo = np.ones((self.nh2, self.no))
# 设为随机值
for i in range(self.ni):
for j in range(self.nh1):
self.wi[i][j] = rand(-0.2, 0.2)
for t in range(self.nh1):
for j in range(self.nh2):
self.wt[t][j] = rand(-1.0, 1.0)
for j in range(self.nh2):
for k in range(self.no):
self.wo[j][k] = rand(-2.0, 2.0)
def forward(self, inputs):
if len(inputs) != self.ni-1:
raise ValueError('与输入层节点数不符!')
# 激活输入层
for i in range(self.ni-1):
self.inputL[i] = inputs[i]
# 激活隐藏层L1
for j in range(self.nh1):
sum = 0.0
for i in range(self.ni):
sum = sum + self.inputL[i] * self.wi[i][j]
self.hiddenL1[j] = sigmoid(sum)
# 激活隐藏层L2
for j in range(self.nh2):
sum = 0.0
for i in range(self.nh1):
sum = sum + self.hiddenL1[i] * self.wt[i][j]
self.hiddenL2[j] = sigmoid(sum)
# 激活输出层
for k in range(self.no):
sum = 0.0
for j in range(self.nh2):
sum = sum + self.hiddenL2[j] * self.wo[j][k]
self.outputL[k] = sigmoid(sum)
return self.outputL[:]
def backPropagate(self, targets, N):
''' 反向传播 '''
if len(targets) != self.no:
raise ValueError('与输出层节点数不符!')
# 计算输出层的误差
output_deltas = [0.0] * self.no
for k in range(self.no):
error = targets[k]-self.outputL[k] ##yk-Ok
output_deltas[k] = dsigmoid(self.outputL[k]) * error
# 更新输出层权重
for j in range(self.nh2):
for k in range(self.no):
change = self.hiddenL2[j]*output_deltas[k]
self.wo[j][k] = self.wo[j][k]+N*change
# 计算隐藏层L2的误差
hidden2_deltas = [0.0] * self.nh2
for j in range(self.nh2):
error = 0.0
for k in range(self.no):
error = error * output_deltas[k]*self.wo[j][k]
hidden2_deltas[j] = dsigmoid(self.hiddenL2[j]) * error
# 更新隐藏层L2的权重
for i in range(self.nh1):
for j in range(self.nh2):
change = self.hiddenL1[i]*hidden2_deltas[j] #Oi*deltsj
self.wt[i][j] = self.wt[i][j] + N*change
# 计算隐藏层L1的误差
hidden1_deltas = [0.0] * self.nh1
for j in range(self.nh1):
error = 0.0
for k in range(self.nh2):
error = error + hidden2_deltas[k]*self.wt[j][k]
hidden1_deltas[j] = dsigmoid(self.hiddenL1[j]) * error
# 更新输入层权重
for i in range(self.ni):
for j in range(self.nh1):
change = hidden1_deltas[j]*self.inputL[i]
self.wi[i][j] = self.wi[i][j] + N*change
# 计算误差
error = 0.0
for k in range(len(targets)):
error = error + 0.5*(targets[k]-self.outputL[k])**2
return error
def test(self, patterns):
y_pred=[]
for p in patterns:
y_pred.append(self.forward(p))
return y_pred
def weights(self):
print('输入层权重:')
for i in range(self.ni):
print(self.wi[i])
print()
print('输出层权重:')
for j in range(self.no):
print(self.wo[j])
def train(self, x_data,y_data, iterations=10000, N=1):
for i in range(iterations):
error = 0.0
for p in range(len(x_data)):
inputs = x_data[p]
targets = y_data[p]
self.forward(inputs)
error = error + self.backPropagate(targets , N)
if i % 500 == 0:
print('误差 %-.5f' % error)
然后导入鸢尾花的数据集
train_data = np.loadtxt(r'E:\data_code\iris-train.txt', delimiter='\t')
test_data = np.loadtxt(r'E:\data_code\iris-test.txt', delimiter='\t')
使用One-hot独热编码处理数据
def onehot_y(y,cols):
res=[]
for i in range(len(y)):
y_array=np.zeros(cols)
y_array[int(y[i])]=1
res.append(y_array)
return np.array(res)
x_data = np.array(train_data[:,:4])
y_data = np.array(train_data[:,4])
y_onehot = onehot_y(y_data,3)
令神经网络结构为:[4,10,6,3]—— 其中,输入层为4个节点,第一个隐含层神经元个数为10个节点;第二个隐含层神经元个数为6个节点,输出层为3个节点
bpnet = BPNN(4,10,6,3)
bpnet.train(x_data,y_onehot)

bpnet.weights()

def show_accuracy(net,X,y):
h = net.test(X)
y_pred = np.argmax(h,axis=1)
print(classification_report(y,y_pred))
x_test = np.array(test_data[:,:4])
y_test = np.array(test_data[:,4])
show_accuracy(bpnet,x_test,y_test)

这是得到的答案
注意这里学习率被我改的比较离谱,因为正常都是0.1、0.01、0.001酱紫 (但是尝试过发现误差很大,准确率也不高,所以改成了1)
主要是这个数据可能因为噪声或者别的什么原因,需要很快的收敛速度,从误差也可以看到前两个数字也就是前1000左右那边下降的很快,然后后面非常平缓,或许要更大的迭代次数才能使得准确率更高
下面是N=0.01和0.1的正确率:


可以加个动量来加速梯度下降
具体.....0.9上下也不错了,准备摆烂(doge/别骂
任何问题欢迎指正~
7239

被折叠的 条评论
为什么被折叠?



