1.BP神经网络代码实现

import numpy as np
import pandas as pd
from sklearn import model_selection
from matplotlib import pyplot as plt 

#对模型进行封装
class module:
    def __init__(self, input, hidden, ena, lamda):
        self.hidden = hidden
        self.ena = ena
        self.V = np.random.rand(input,hidden)*0.1
        self.a = np.random.rand(hidden)*0.1
        self.W = np.random.rand(hidden)*0.1
        self.theta = np.random.rand()*0.1
        self.m = input
        self.lamda = lamda
        self.J = []
        
    def fit(self, X, Y, iteration, accumulation):
        if(~accumulation):
            self.BP(X, Y, iteration)
        else:
            self.BP2(X, Y, iteration)
    def predict(self, X):
        beta = 1/(1 + np.exp(self.a - X.dot(self.V)))
        result = 1/(1 + np.exp(self.theta - beta.dot(self.W)))
        return result
        
    #标准BP算法
    def BP(self, X_train, y_train, iteration):
        self.V = np.random.rand(X_train.shape[1], self.hidden)*0.1
        self.a = np.random.rand(self.hidden)*0.1
        self.W = np.random.rand(self.hidden)*0.1
        self.theta = np.random.rand()*0.1
        self.J = []
        for d in range(iteration):
            temp = []
            for i in range(X_train.shape[0]):
                B = 1 / ( 1 + np.exp(self.a - X_train[i].dot(self.V)))
                y = 1/(1 + np.exp(self.theta - B.dot(self.W)))

                g = y * (1 - y) * (y_train[i] - y)
                e = (B * (1 - B))* self.W * g
                self.W = (1 - self.lamda)*self.W + self.ena * g * B 
                self.theta = self.theta - self.ena * g
                self.a = self.a - self.ena * e
                self.V = self.V + self.ena * X_train[i].reshape(-1, 1) * e
                temp.append((y - y_train[i])**2)
            self.J.append(sum(temp))
            
        #累计BP算法
        def BP2(self, X_train, y_train, iteration):
            self.V = np.random.rand(X_train.shape[1], self.hidden)*0.1
            self.a = np.random.rand(self.hidden)*0.1
            self.W = np.random.rand(self.hidden)*0.1
            self.theta = np.random.rand()*0.1
            self.J = []
            for i in range(iteration):
                B = 1 / ( 1 + np.exp(self.a - X_train.dot(self.V)))
                Y0 = 1/(1 + np.exp(self.theta - B.dot(self.W))) 

                g = Y0 * (1 - Y0) * (y_train - Y0)
                e = (B * (1 - B))* (W.reshape((1,-1))*g.reshape((-1,1)))
                
                self.W = (1 - self.lamda)*self.W + self.ena * B.T.dot(g)/self.m
                self.theta = self.theta - self.ena * g.sum(axis = 0)/self.m
                self.V = self.V + self.ena * X_train.T.dot(e)/self.m
                self.a = self.a - self.ena * e.sum(axis = 0)/self.m

                self.J.append(sum((Y0 - y_train)**2))
    #绘制误差变化函数
    def plot(self):
        plt.plot(self.J)

#数据读取
f1 = pd.read_excel("log.xlsx")
temp1 = f1[["色泽","根蒂","敲声","纹理","脐部","触感"]]
temp2 = f1[["密度","含糖率"]]
#独热码处理
onehot = pd.get_dummies(temp1)
data = onehot.join(temp2)
X = np.array(data)
Y = np.array(f1["好瓜"])
#数据集划分
X_train, X_test, y_train, y_test = model_selection.train_test_split(X, Y, test_size=0.3)

#寻找最合适的正则项系数
lamda = [0,0.001,0.005,0.1,0.2,0.4,0.8,0.16]
j = []
for i in lamda:
    temp = module(X_train.shape[1], 10, 0.2,i)
    temp.fit(X_train, y_train, 1000, True)
    result = temp.predict(X_test)
    j.append(sum((result - y_test)**2))
#绘制出泛化误差曲线,并找出最合适的正则化系数
plt.plot(j)
plt.xticks(range(8),lamda)

#利用累计BP算法训练
ddd = module(X_train.shape[1], 10, 0.2,0.001)
ddd.fit(X_train, y_train, 2000, True)
ddd.plot()
result = ddd.predict(X_test)
result[result >= 0.5] = 1
result[result < 0.5] = 0
accuracy = 1 - sum((np.abs(result - y_test)))/result.shape[0]
print(accuracy)

#利用标准BP算法进行训练
ddd.fit(X_train, y_train, 1500, False)
ddd.plot()
result = ddd.predict(X_test)
result[result >= 0.5] = 1
result[result < 0.5] = 0
accuracy = 1 - sum((np.abs(result - y_test)))/result.shape[0]
print(accuracy)

2. 运行结果展示分析

2.1 正则项的选择

这里采用的公式为:E = 1/m * Ek的求和 + lamda * W的平方求和

lamda 的选择分别为:0,0.001,0.005,0.1,0.2,0.4,0.8,0.16

最后绘制的泛化误差结果为:

bp神经网络处理数据代码 bp神经网络代码实现_泛化

可以看出当lamda选择为0.001时能够使泛化误差变为最小。

2.2 两种算法结果比较

2.2.1 标准BP算法

bp神经网络处理数据代码 bp神经网络代码实现_初始化_02

2.2.2 累计BP算法

bp神经网络处理数据代码 bp神经网络代码实现_初始化_03

2.2.3 结果分析

从两个图像可以看出,尽管累计BP算法对各参数的更新次数较少,但由于它巧妙地避免了参数修改的往返性,导致其在参数更新次数较少时也能取得较好的效果。

2.3 是否正则化的结果比较

2.3.1 无正则化

bp神经网络处理数据代码 bp神经网络代码实现_泛化_04

2.3.2 正则化

bp神经网络处理数据代码 bp神经网络代码实现_泛化_05

2.3.3 结果分析

从两个图像可以看出,尽管正则化后训练误差还是较高,但不同于未正则化的模型可能出现过拟合现象,正则化之后准确率达到了100%,尽管这与训练集较少可能有一定的关系,但也说明了正则化对于降低泛化误差的重要意义。

3.分析总结

3.1 关于各参数的初始化问题

BP神经网络参数的选择一般采用随机初始化,但随机初始化的范围需要注意,观察sigmoid函数,可以发现这个函数的导数出现了两边和中间都较低的问题,如果最开始就选用了较大的参数,那么收敛速度将会很慢,结果一般也不会好。
还有千万不要全部初始化为0,不然会导致反向传递时同意神经元对后续的所有权重有相同的效果,即同意神经元的前置权重相同,可想而知结果肯定不会好。

3.2 关于离散数据的处理问题

离散数据的处理需要查看数据的类型是否是可排序的,本次数据集中根茎等属性是离散属性,但并没有顺序,因此需要采用独热码来进行处理,使用panda库中的get_dummies可以方便对数据进行处理。

关于正则化参数的选择问题

lamda值的选择不建议人为指定,推荐的方法是绘制泛化误差曲线,设置一系列lamda来分别训练,最后看泛化误差曲线来进行判别。