"""文件名: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函数,所以输出的预测值表示的是测试样本为正例的概率
(西瓜书)神经网络(BP算法)代码详解
最新推荐文章于 2024-06-06 20:29:46 发布