(西瓜书)神经网络(BP算法)代码详解

"""文件名:traindata.csv
编号	色泽	根蒂	敲声	纹理	脐部	触感	密度	含糖率	好瓜
1	青绿	蜷缩	浊响	清晰	凹陷	硬滑	0.697	0.46	是
2	乌黑	蜷缩	沉闷	清晰	凹陷	硬滑	0.774	0.376	是
3	乌黑	蜷缩	浊响	清晰	凹陷	硬滑	0.634	0.264	是
4	青绿	蜷缩	沉闷	清晰	凹陷	硬滑	0.608	0.318	是
5	浅白	蜷缩	浊响	清晰	凹陷	硬滑	0.556	0.215	是
6	青绿	稍蜷	浊响	清晰	稍凹	软粘	0.403	0.237	是
7	乌黑	稍蜷	浊响	稍糊	稍凹	软粘	0.481	0.149	是
8	乌黑	稍蜷	浊响	清晰	稍凹	硬滑	0.437	0.211	是
9	乌黑	稍蜷	沉闷	稍糊	稍凹	硬滑	0.666	0.091	否
10	青绿	硬挺	清脆	清晰	平坦	软粘	0.243	0.267	否
11	浅白	硬挺	清脆	模糊	平坦	硬滑	0.245	0.057	否
12	浅白	蜷缩	浊响	模糊	平坦	软粘	0.343	0.099	否
13	青绿	稍蜷	浊响	稍糊	凹陷	硬滑	0.639	0.161	否
14	浅白	稍蜷	沉闷	稍糊	凹陷	硬滑	0.657	0.198	否
15	乌黑	稍蜷	浊响	清晰	稍凹	软粘	0.36	0.37	否
16	浅白	蜷缩	浊响	模糊	平坦	硬滑	0.593	0.042	否
17	青绿	蜷缩	沉闷	稍糊	稍凹	硬滑	0.719	0.103	否
"""


import pandas as pd
import numpy as np
dataset = pd.read_csv('traindata.csv', delimiter="," ,encoding='gbk')

#according to P54--3.2
#process the dataset
attributeMap={}     #特征值字典
#色泽属性的三个取值(存在序的关系),所以可以用0,0.5,1 三个数表示;可参考西瓜书p54上方解释
attributeMap['浅白']=0
attributeMap['青绿']=0.5
attributeMap['乌黑']=1
#同上
attributeMap['蜷缩']=0
attributeMap['稍蜷']=0.5
attributeMap['硬挺']=1

attributeMap['沉闷']=0
attributeMap['浊响']=0.5
attributeMap['清脆']=1

attributeMap['模糊']=0
attributeMap['稍糊']=0.5
attributeMap['清晰']=1

attributeMap['凹陷']=0
attributeMap['稍凹']=0.5
attributeMap['平坦']=1

attributeMap['硬滑']=0
attributeMap['软粘']=1

attributeMap['否']=0
attributeMap['是']=1

del dataset['编号']
dataset=np.array(dataset)
m,n=np.shape(dataset)
for i in range(m):
    for j in range(n):
        if dataset[i,j] in attributeMap:    #如果该样本第i个样本的第j个特征的特征值在特征值字典中
            dataset[i,j]=attributeMap[dataset[i,j]]     #将数据集中第i个样本的第j个特征值数字化
        dataset[i,j]=round(dataset[i,j],3)      #round()函数作用:用于返回浮点数对应的四舍五入值,3表示四舍五入到小数点后三位

trueY=dataset[:,n-2]    #所有样本的标记列表
X=dataset[:,:n-2]       #所有样本的特征数据
m,n=np.shape(X)




#according to P101, 初始化参数
import random
d=n   #输入向量的维数(输入层8个神经元)
q=d+1   #隐层节点的数量(隐层9个神经元)
l=1   #输出向量的维数(输出层1个神经元)
theta=[random.random() for i in range(l)]   #输出节点的阈值(1个)
gamma=[random.random() for i in range(q)]   #隐层节点的阈值(9个)   注:输入层没有阈值和权重
# v size= d*q .输入节点和隐层节点之间的连接权重v(size=d*1,即维数和输入层向量维数相同)
v=[[random.random() for i in range(q)] for j in range(d)]   #逐行生成一个一个的随机数
# w size= q*l .隐层节点和输出节点之间的连接权重w(size=q*1,即维数和隐层向量维数相同)
w=[[random.random() for i in range(l)] for j in range(q)]
eta=0.2    #学习率
maxIter=5000 #最大迭代次数




import math
def sigmoid(iX,dimension):  #iX是一个有维数的矩阵;dimension表示维数(不准确,若iX是向量,dimension取1;若iX是矩阵,dimension取2)
    if dimension==1:    #若是向量,一次更新向量中的每个元素
        for i in range(len(iX)):   #若iX是向量,len(iX)表示向量的维数
            iX[i] = 1 / (1 + math.exp(-iX[i]))      #若iX是向量,iX[i]表示向量中第i个数
    else:   #若不是向量,递归拆分为一行一行的行向量
        for i in range(len(iX)):         #若iX是矩阵,len(iX)表示矩阵的行数
            iX[i] = sigmoid(iX[i],dimension-1)      #i若iX是矩阵,X[i]表示矩阵的第i行向量
    return iX





# do the repeat----standard BP
#每次迭代每个神经元要更新m次,但是这m次更新中相互之前可能会有影响
while(maxIter>0):
    maxIter-=1
    sumE=0
    for i in range(m):  #一个样本一个样本算
        alpha=np.dot(X[i],v)#p101 line 2 from bottom, shape=1*q   X[i]是第i个样本的特征向量
        b=sigmoid(alpha-gamma,1)#b=f(alpha-gamma), shape=1*q
        beta=np.dot(b,w)#shape=(1*q)*(q*l)=1*l
        predictY=sigmoid(beta-theta,1)   #shape=1*l ,p102--5.3
        #计算误差(观察用)
        E = sum((predictY-trueY[i])*(predictY-trueY[i]))/2    #5.4 此处的sum是去列表的作用  此处算出来的误差与下面的更新公式无关,只是print出来可以观察到单个样本的误差是多少
        sumE+=E#5.16    #每次迭代后所有样本的总误差
        #更新权值和阈值(训练用)
        g=predictY*(1-predictY)*(trueY[i]-predictY)#shape=1*l p103--5.10
        e=b*(1-b)*((np.dot(w,g.T)).T) #shape=1*q , p104--5.15
        w+=eta*np.dot(b.reshape((q,1)),g.reshape((1,l)))#5.11  更新隐层节点和输出节点之间的连接权重w
        theta-=eta*g#5.12   更新输出节点的阈值
        v+=eta*np.dot(X[i].reshape((d,1)),e.reshape((1,q)))#5.13   更新输入节点和隐层节点之间的连接权重v
        gamma-=eta*e#5.14   更新隐层节点的阈值
    print(sumE)   #每更新一次总损失就越小




"""
#accumulated BP
#每次迭代每个神经元只更新一次,即用矩阵(向量)的方式让m个样本同时计算更新,并且这m个样本更新出来的权值和阈值相互之前不收影响
trueY=trueY.reshape((m,l))   #把行向量转换为列向量
while(maxIter>0):
   maxIter-=1
   sumE=0
   alpha = np.dot(X, v)#p101 line 2 from bottom, shape=m*q
   b = sigmoid(alpha - gamma,2)  # b=f(alpha-gamma), shape=m*q
   beta = np.dot(b, w)  # shape=(m*q)*(q*l)=m*l
   predictY = sigmoid(beta - theta,2)  # shape=m*l ,p102--5.3
   # 计算误差(观察用)
   sumE = sum(sum((predictY - trueY) * (predictY - trueY))) / 2  # 5.4  每次迭代后所有样本的总误差   (predictY - trueY) * (predictY - trueY)和np.dot(predictY - trueY, predictY - trueY)是有区别的,前者直接用*是数据相乘,并非线性代数里的向量相乘,后者才是线代里的向量相乘(此处无法向量相乘)
   #体会sum除了把值相加外还有去除列表的作用
   #(predictY - trueY) * (predictY - trueY)=[[0.001709491750980586], [0.0015331872837118347]...]
   #sum((predictY - trueY) * (predictY - trueY))=[8.29007457992029]
   #sum(sum((predictY - trueY) * (predictY - trueY)))=8.29007457992029
   print(round(sumE,5))
   # 更新权值和阈值(训练用)
   g = predictY * (1 - predictY) * (trueY - predictY)  # shape=m*l p103--5.10
   e = b * (1 - b) * ((np.dot(w, g.T)).T)  # shape=m*q , p104--5.15
   w += eta * np.dot(b.T, g)  # 5.11 shape (q*l)=(q*m) * (m*l)
   theta -= eta * g  # 5.12
   v += eta * np.dot(X.T, e)  # 5.13 (d,q)=(d,m)*(m,q)
   gamma -= eta * e  # 5.14
"""



#通过输入样本的特征矩阵得到输出层的值
def predict(iX):
    alpha = np.dot(iX, v)  # p101 line 2 from bottom, shape=m*q
    b=sigmoid(alpha-gamma,2)#b=f(alpha-gamma), shape=m*q
    beta = np.dot(b, w)  # shape=(m*q)*(q*l)=m*l
    predictY=sigmoid(beta - theta,2)  # shape=m*l ,p102--5.3
    return predictY

print(predict(X))   #因为用的是sigmoid函数,所以输出的预测值表示的是测试样本为正例的概率

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值