|
架构:构建具有 2 个中间层的 3 分类的 BP 神经网络.
原模型效果(数据集选用鸢尾花数据集)
原神经网络模型
原模型代码:
import numpy as np
import tensorflow as tf
from sklearn import datasets
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
iris_data=datasets.load_iris()
#数据划分
data=iris_data.data
label=iris_data.target
#z分数标准化
avg=np.average(data,axis=0)
std=np.std(data,axis=0)
data=(data-avg)/std
#转换独热编码
label=tf.keras.utils.to_categorical(label,3)
#切分train和dev
train_data,dev_data,train_label,dev_label=train_test_split(data,label,train_size=0.8)
#params
#输入维度
n_in=4
#中间层神经元个数
n_mid=25
#输出维度
n_out=3
#w和b的扩散程度
wb_width=0.1
#学习率
eta=0.01
#训练次数
epoch=100
#一次训练个数
batch_size=8
class Baselayer:
def __init__(self,n_upper,n):
#随机产生w的初始值
self.w=wb_width*np.random.randn(n_upper,n)
# 随机产生b的初始值
self.b=wb_width*np.random.randn(n)
def updata(self,eta):
# 根据学习率更新w
self.w-=eta*self.grad_w
# 根据学习率更新b
self.b-=eta*self.grad_b
#Baselayer为父类,MiddleLayer为子类(中间层)
class MiddleLayer (Baselayer):
#定义前项传播算法
def forward(self,x):
self.x=x
#结合权重和b计算函数值
self.u=np.dot(x,self.w)+self.b
#relu函数激活层,小于0=0,否则返回本身
self.y=np.where(self.u<=0,0,self.u)
#定义反向传播算法
def backward(self,grad_y):
#relu函数导数,根据正向传播结果计算梯度
delta=grad_y*np.where(self.u<=0,0,1)
#计算新的w
self.grad_w=np.dot(self.x.T,delta)
#计算新的b
self.grad_b = np.sum(delta,axis=0)
#计算新的x
self.grad_x=np.dot(delta,self.w.T)
#Baselayer为父类,OutputLayer为子类(输出层)
#输出层与中间层情况一样,不在做过多的解释(最后换为SoftMax函数)
class OutputLayer (Baselayer):
def forward(self,x):
self.x=x
u=np.dot(x,self.w)+self.b
#SoftMax函数
self.y=np.exp(u)/np.sum(np.exp(u),axis=1,keepdims=True)
def backward(self,t):
delta=self.y-t
self.grad_w=np.dot(self.x.T,delta)
self.grad_b=np.sum(delta,axis=0)
self.grad_x=np.dot(delta,self.w.T)
#输入层,输入四维张量数据,神经元个数为25个
middle_layer1=MiddleLayer(n_in,n_mid)
#第一层神经元个数为25个,所以第二层输入也为25个,神经元个数为25个
middle_layer2=MiddleLayer(n_mid,n_mid)
#第二层输入也为25个,神经元个数为25个
middle_layer3=MiddleLayer(n_mid,n_mid)
#输出层,第三层神经元个数为25个,输出为三维张量数据。
output_layer=OutputLayer(n_mid,n_out)
#集合函数把类定义的函数改为模型可接受的数据形式
#前向传播
def forward_propagation(x):
#第一层数据传入
middle_layer1.forward(x)
#第一层结果传入第二层
middle_layer2.forward(middle_layer1.y)
#第二层结果传到第三层
middle_layer3.forward(middle_layer2.y)
#第三层结果传到输出层
output_layer.forward(middle_layer3.y)
#反向传播
def back_propagation(t):
#从后往前,链式法则更新梯度
output_layer.backward(t)
middle_layer3.backward(output_layer.grad_x)
middle_layer2.backward(middle_layer3.grad_x)
middle_layer1.backward(middle_layer2.grad_x)
#数据更新
def updata_wb():
#依据学习率更新第一层的w和b
middle_layer1.updata(eta)
#依据学习率更新第二层的w和b
middle_layer2.updata(eta)
#依据学习率更新第三层的w和b
middle_layer3.updata(eta)
# 依据学习率更新最后一层的w和b
output_layer.updata(eta)
#交叉熵计算loss
def get_erro(t,batch_size):
#信息量公式 I(x)=-log(P(x))
#交叉熵公式H(p,q)= ∑ p(x i )log(q(x i ))
return - np.sum(t*np.log(output_layer.y+1e-7))/batch_size
#建立数据收集列表
train_error_data=[]
train_error_label=[]
dev_error_data=[]
dev_error_label=[]
#学习
#查看学习组数
batch=train_data.shape[0]//batch_size
#统计误差
for i in range(epoch):
#train
#调用向前传播算法函数
forward_propagation(train_data)
#计算交叉熵的loss
error_train=get_erro(train_label,train_data.shape[0])
#记录到数据收集列表
train_error_data.append(i)
train_error_label.append(error_train)
#test
# 调用向前传播算法函数
forward_propagation(dev_data)
# 计算交叉熵的loss
error_test = get_erro(dev_label, dev_data.shape[0])
# 记录到数据收集列表
dev_error_data.append(i)
dev_error_label.append(error_test)
#取索引
index_random=np.arange(train_data.shape[0])
#打乱索引
np.rndom.shuffle(index_random)
#
for j in range(batch):
#选取batch中的一组,每组随机8个数据
mb_index=index_random[j*batch_size:(j+1)*batch_size]
#根据索引切出数据
x=train_data[mb_index,:]
#根据索引切出标签
t=train_label[mb_index,:]
#调用向前传播
forward_propagation(x)
#调用向后传播
back_propagation(t)
#更新数据
updata_wb()
plt.plot(train_error_data, train_error_label, label=&#34;Train&#34;)
plt.plot(dev_error_data,dev_error_label,label=&#34;Test&#34;)
plt.legend()
plt.xlabel(&#34;epoch&#34;)
plt.ylabel(&#34;error&#34;)
plt.show()
#准确率
#调用向前传播算法
#train
forward_propagation(train_data)
#取最大预测值的概率索引与正确标签,如果相等则加一做和
count_train=np.sum(np.argmax(output_layer.y,axis=1)==np.argmax(train_label,axis=1))
#dev
forward_propagation(dev_data)
#取最大预测值的概率索引与正确标签,如果相等则加一做和
count_dev=np.sum(np.argmax(output_layer.y,axis=1)==np.argmax(dev_label,axis=1))
print(&#39;训练集准确率&#39;)
print(count_train/train_data.shape[0])
print(&#39;验证集准确率&#39;)
print(count_dev/dev_data.shape[0])一、分析不同优化算法对神经网络结果的影响.(以下代码为片段的更改请大家注意)
1.AdaGrad优化
加入AdaGrad优化
AdaGrad优化,是基于梯度进行保留。设置b的值为,积累量初始设置为r=0。之后对积累历史梯度求平方和。之后通过公式对梯度进行运算。
AdaGrad原理
AdaGrad优化部分:
def updata(self,eta):
#adagrad
b=1e-10
r=0
i=0
#累积历史梯度的平方和
r=r+self.grad_w**2
i=i+self.grad_b**2
#更新参数
self.w-=eta/(np.sqrt(r+b))*self.grad_w
self.b-=eta/(np.sqrt(i+b))*self.grad_b2.Dropout优化
加入Dropout优化
简单的说就是,随机抛弃一定中间层神经元的数量,共两种方法实现,一个是针对数据集进行随机抛弃,还有一个是根据神经元的个数进行随机抛弃。
方法一:数集改变
def forward(self,x):
self.x=x
self.u=np.dot(x,self.w)+self.b
#drop函数
mask=np.random.binomial(1,1-rate,self.u.shape)
self.u*=mask
self.u=self.u/(1-rate)
self.y=np.where(self.u<=0,0,self.u)
方法二:神经元个数的更改
class MiddleLayer (Baselayer):
def forward(self,x):
self.x=x
self.u=(np.dot(x,self.w)+self.b)*discard_prob
self.y=np.where(self.u<=0,0,self.u)
.......
def dropout (n_mid,discard_prob):
# Dropout算法
# 每个神经元被丢弃的概率
#discard_prob = 0.5
# 计算时还成retain_prob
retain_prob = 1 - discard_prob
# np.random.binomial这个函数是用来计算一个随机变量X,每个神经元判断
# 一次,size是有多少神经元生成0,1向量,0表示屏蔽。
sample = np.random.binomial(n=1, p=retain_prob, size=n_mid)
sum=sample.sum()
return sum
n_mid_1=dropout(25,discard_prob)
n_mid_2=dropout(25,discard_prob)
n_mid_3=dropout(25,discard_prob)
middle_layer1=MiddleLayer(n_in,n_mid_1)
middle_layer2=MiddleLayer(n_mid_1,n_mid_2)
middle_layer3=MiddleLayer(n_mid_2,n_mid_3)
output_layer=OutputLayer(n_mid_3,n_out) |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|