神经网络结构大概如下图1-1:
图1-1
包括输入层,隐层和输出层。包含一层隐层的神经网络称为浅层神经网络,即SNN。包含多层隐层的神经网络称为深度神经网络,即DNN。理论上说单隐层神经网络可以逼近任何连续函数(只要隐层的神经元数足够多)。但从数学表达能力上,多隐层的神经网络工程效果更好。但过多的隐层和神经元节点,会带来过拟合问题。不要试图通过降低神经网络参数量来减缓过拟合,用正则化或者dropout。
对输入进行非线性变换,常用的传递函数,也是本实验所使用的,即S函数(sigmoid):
其作用在每个隐层与输出层。正如大脑中的神经网络,并非所有的外界信号都能引起我们强烈的反应。S函数的作用往往会对信号进行相应的筛选,放有用的信号通过神经元,将无用的信号压缩为零。对于通过的信号,控制其以多大的程度通过 。
神经网络之BP算法:正向传播求损失,反向传播回传误差。根据误差信号修正每层的权重 与偏置
以3层感知器为例,来进行BP算法推导。其结构如图1-2:
图1-2
计算某一层的第j个单元,i和k分别为其前层和后层的单元,代表本层输出,为输入。
的输出:
神经网络之SGD(随机梯度下降):
误差E有了,如何调整权重让误差不断减小?由上述式子可知E是权重w的函数,我们需要找到使得函数值最小的w.
为了使连接权值沿着E 的梯度变化方向得以改善,网络逐渐收敛,取:
,其中 k=1,2,...,n3;j=1,2,...n2; i=1,2,...,n1
对于输出层:
其中:
对于隐层:
自此,公式推到完毕。公式推到中未添加偏置,在代码编程中添加。
本次实验提供的样本数据有410个,每个数据提取5个特征,即身高、体重、是否喜欢数学、是否喜欢文学及是否喜欢运动。将其中360个样本数据作为训练数据,另外50个数据作为测试数据。 将样本数据用于对BP神经网络分类器 。BP神经网络--自行编写代码完成后向传播算法,采用交叉验证的方式实现对于性能指标的评判(包含SE,SP,ACC和AUC,AUC的计算可以基于平台的软件包) 。
本实验编程应用Python完成。其中,读取Excel数据应用了扩展工具包xlrd,BP算法中前向运算中涉及到矩阵的运算,应用到了扩展工具包numpy。
输入360个样本数据进行训练后(循环重复训练10次),输入50个测试数据进行测试,测试结果如表2-1所示。通过数据可以看出共50个测试数据,其中有两个正样本被分为负样本,即男生分类成了女生。其中ROC曲线(受试者工作特性曲线)如图2-1,可见该曲线的AUC值为0.96表示分类器的效果是较好的,从敏感性(SE)、特异性(SP)和准确率(ACC)也可以看出分类器的效果还是不错的。
TP |
FN |
TN |
FP |
SE |
SP |
ACC |
39 |
0 |
6 |
5 |
1 |
0.54 |
0.9 |
表2-1
图2-2
# @Date : 2018-10-18 17:37:02
# @Author : Taylen Lee
# @Version : 0.1
# @modify :
# @Description:
import xlrd
import math
import random
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn import tree
from sklearn import metrics
from sklearn import model_selection
'''
/**************************task1**************************/
采用BP神经网络设计男女生分类器。自行编写代码完成后向传播算法
注:本次设计的BP算法分类器指针带5-5-1的神经网络结构,不具有通
用化,后续仍需慢慢更改,不断完善。
/**************************task1**************************/
'''
#神经元使用Sigmoid函数作为激活函数,其公式为:
def sigmoid(parameter_x):
parameter_x_dimensions=parameter_x.shape
for i in range(parameter_x_dimensions[0]):
parameter_x[i,0]=1.0/(1+math.exp(-parameter_x[i,0]))
#parameter_x_modify=parameter_x
return parameter_x
#数据归一化,将身高,体重映射到[0,1]之间
def Data_normalization(input_array):
Normalization_data=[]
for x in input_array:
Normalization_data.append((x-min(input_array))/(max(input_array)-min(input_array)))
return Normalization_data
# 神经网络类
class NeuralNetwork:
#设置学习率
Learning_Rate = 0.5
def __init__(self, sizes):
'''
:sizes:list类型,存储每层神经网络的神经元数目
本次实验输入层有5个神经单元,隐层有5个神经单元,1个输出神经单元。
故sizes=[5,5,1]
'''
#获取该神经网络各层层数
self.sizes=sizes
self.num_input_layers=sizes[0]
self.num_hidden_layers=sizes[1]
self.num_output_layers=sizes[2]
#随机产生隐层与输出层中每个神经元的偏置(0-1)
self.bias=[np.random.randn(i,1) for i in sizes[1:]]
#随机产生每条连线的权重
self.weights=[np.random.randn(i,j) for i,j in zip(sizes[:-1],sizes[1:])]
def forward_propagation(self,inputs):
"""
前向传播
"""
self.inputs=inputs
self.Neural_Output_All_Layer=[]
for b,w in zip(self.bias,self.weights):
inputs=sigmoid(np.dot(w.T,inputs)+b)
self.Neural_Output_All_Layer.append(inputs)
return self.Neural_Output_All_Layer
def calculate_error(self, target_output):
#输出的每个神经元的误差由平均平方误差法计算(cost function)
Neural_Output_Lastlayer=self.Neural_Output_All_Layer[1]
return 0.5*(target_output - Neural_Output_Lastlayer[0,0]) ** 2
def calculate_local_gradient_delta(self,target_output):
'''
我们可以根据cost function对神经元激活函数输出值Oⱼ的偏导数和
激活函数输出值Oⱼ对激活函数输入值z=wx+b的偏导数计算delta(δ).
δⱼ = ∂E/∂netⱼ = ∂E/∂Oⱼ * dOⱼ/dnetⱼ 关键key,即求局部梯度
'''
Neural_Output_Lastlayer=self.Neural_Output_All_Layer[1]
return (-(target_output - Neural_Output_Lastlayer[0,0]))*\
(Neural_Output_Lastlayer[0,0]*(1 - Neural_Output_Lastlayer[0,0]))
#def train(self, training_inputs, training_outputs):
def train(self, training_outputs):
'''
使用在线学习方式,训练每个实例之后对权值进行更新
反向传播
'''
# 1. Output neuron deltas输出层deltas
local_gradient_delta_output_layler=self.calculate_local_gradient_delta(training_outputs)
# 2. Hidden neuron deltas隐藏层deltas
Neural_Output_Hidden_Layer=self.Neural_Output_All_Layer[0]
local_gradient_delta_hidden_layler=[0]*self.num_hidden_layers
for h in range(self.num_hidden_layers):
'''
隐层节点不是输出节点, 其输出对后层的全部节点都有影响;
我们需要计算误差对每个隐藏层神经元的输出的导数,由于不是输出层
所以∂E/∂Oⱼ需要根据下一层反向进行计算,即根据输出层的函数进行计算
∂E/∂Oⱼ = Σ ∂E/∂net_k * ∂net_k/∂Oⱼ = Σ ∂E/∂net_k * w_jk = Σ δ_k * w_jk(Σ以k为累加下标)
'''
error_to_hidden_neuron_output_derivative=local_gradient_delta_output_layler*self.weights[1][h,0]
# δⱼ = ∂E/∂netⱼ=∂E/∂Oⱼ * dOⱼ/dnetⱼ
local_gradient_delta_hidden_layler[h] = error_to_hidden_neuron_output_derivative*\
(Neural_Output_Hidden_Layer[h,0]*(1 - Neural_Output_Hidden_Layer[h,0]))
# 3. Update output neuron weights 更新输出层权重
for w_ho in range(len(self.weights[1])):
# 注意:输出层权重是隐藏层神经元与输出层神经元连接的权重
self.weights[1][w_ho,0]-=self.Learning_Rate*(local_gradient_delta_output_layler*self.Neural_Output_All_Layer[0][w_ho,0])
# 4. Update hidden neuron weights 更新隐藏层权重
for h in range(self.num_hidden_layers):
for w_ih in range(len(self.weights[0])):
# 注意:隐藏层权重是输入层神经元与隐藏层神经元连接的权重
self.weights[0][w_ih,h]-=self.Learning_Rate*(local_gradient_delta_hidden_layler[h]*self.inputs[w_ih,0])
# 5. Update output neuron bias 更新输出层偏值
self.bias[1][0,0]-=self.Learning_Rate*local_gradient_delta_output_layler
# 6. Update hidden neuron bias 更新隐藏层偏值
for h in range(self.num_hidden_layers):
self.bias[0][h,0]-=self.Learning_Rate*local_gradient_delta_hidden_layler[h]
#从excel中读取样本数据
mydata = xlrd.open_workbook('D:/program/py_code/data_2018.xls')
mysheet1 = mydata.sheet_by_name("Sheet1")
#获取行数、列数
nRows=mysheet1.nrows
nCols=mysheet1.ncols
height=[]
weight=[]
likemath=[]
likeliterature=[]
likesport=[]
lable_gender=[] #男1女0
#存储相应的样本数据
for i in range(nRows):
if i+1=Category_division_threshold) and (lable_gender[i+360]==1):
Test_results_TP+=1
elif (Predict_result=Category_division_threshold) and (lable_gender[i+360]==0):
Test_results_FP+=1
def Display_performance_index(results_TP,results_FN,results_TN,results_FP,test_num,lable_test,lable_pred_test):
print('TP:',results_TP)
print('FN:',results_FN)
print('TN:',results_TN)
print('FP:',results_FP)
results_SE=results_TP/(results_TP+results_FN) #敏感性,真正的正样本中有多少比列可以被正确划分出来
results_SP=results_TN/(results_TN+results_FP) #特异性,真正的负样本中有多少比列可以被正确划分出来
results_ACC=(results_TP+results_TN)/test_num #准确率
print('SE:',results_SE)
print('SP:',results_SP)
print('ACC:',results_ACC)
'''
利用库sklearn来计算AUC.使用 AUC 值作为评价标准是因为很多时候 ROC 曲线并不能
清晰的说明哪个分类器的效果更好,而作为一个数值,对应 AUC 更大的分类器效果更好。
'''
fpr, tpr, thresholds = metrics.roc_curve(lable_test,lable_pred_test)
results_AUC=metrics.auc(fpr,tpr)
print('AUC:',results_AUC)
plt.figure()
plt.figure(figsize=(10,10))
plt.plot(fpr, tpr, color='darkorange', lw=2, label='ROC curve (area = %0.2f)' % results_AUC) #假正率为横坐标,真正率为纵坐标做曲线
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.legend(loc="lower right")
plt.show()
Display_performance_index(Test_results_TP,Test_results_FN,Test_results_TN,Test_results_FP,Test_num,lable_gender[360:], np.array(scores))