老白AI算法入门:手写四层BP神经网络解决IRIS分类问题(python)

一、IRIS数据集 

鸢尾花数据集是UCI中最常被利用的数据集。结构简单,共150条数据,不需要清洗。 每条数据包括四个属性特征一个标签

属性的特征分别是【花萼长度、花萼宽度、花瓣长度、花瓣宽度】,

属性标签有三类:【山鸢尾(Iris-setosa)、变色鸢尾(Iris-versicolor)和维吉尼亚鸢尾(Iris-virginica)】        

从UCI:UCI Machine Learning Repository下载的一般是.data结尾的数据集,用pycharm打开数据集如下所示,由逗号分隔。

二、四层BP神经网络公式推导及实现

神经网络作为最常用的人工智能算法之一,已经有非常多的封装库和运用,本文没有调用sklearn的库,通过自己编程实现四层BP神经网络,加深对神经网络的理解,提高小白的编程能力具有非常重要的意义。所谓BP误差逆传播是训练多层网络的多种办法之一,是迄今为止的最成功的网络学习算法。神经网络编程基本的坑我也是都进了一遍,在此一定叮嘱大家注意到理解神经网络的原理,本文最后附有手算草稿。

2.1拓扑结构设计

网络拓扑结构如图所示:

 首先要初始化权重和截距,各层节点的数量,也都很容易理解:

class neuralNetwork(object):
    def __init__(self, InputNodes, hiddenNodes_1, hiddenNodes_2, OutputNodes, learningRate=0.5):
        # 设置节点数
        self.InputNodes = InputNodes
        self.hiddenNodes_1 = hiddenNodes_1
        self.hiddenNodes_2 = hiddenNodes_2
        self.OutputNodes = OutputNodes

        self.wi1 = np.random.normal(0.0, 0.01, (self.hiddenNodes_1, self.InputNodes))
        self.wi2 = np.random.normal(0.0, 0.01, (self.hiddenNodes_2, self.hiddenNodes_1))
        self.wo = np.random.normal(0.0, 0.01, (self.OutputNodes, self.hiddenNodes_2))
        # 阈值随机生成
        self.b1 = np.random.normal(0.0, 0.01, (self.hiddenNodes_1, 1))
        self.b2 = np.random.normal(0.0, 0.01, (self.hiddenNodes_2, 1))
        self.b3 = np.random.normal(0.0, 0.01, (self.OutputNodes, 1))

        self.learnRate = learningRate

2.2激励函数的选择

激励函数选择sigmoid函数,sigmoid函数的优势在其导数的形式很好。

 sigmoid函数的图像:

def sigmoid_activation(self, x):
    return 1 / (1 + np.exp(-x))

2.3 BP学习算法

        BP算法属于监督学习算法,通过调整网络连接权重来体现学习的效果。并且使用梯度下降法去更新参数,也是最重要的一环,推导公式如下:

    def train(self, inputs_list, targets_list):
        X_train = np.array(inputs_list, ndmin=2).T
        Y_train = np.array(targets_list, ndmin=2).T

        L1_IN = np.dot(self.wi1, X_train) - self.b1
        L1_OUT = self.sigmoid_activation(L1_IN)
        L2_IN = np.dot(self.wi2, L1_OUT) - self.b2
        L2_OUT = self.sigmoid_activation(L2_IN)
        Out_IN = np.dot(self.wo, L2_OUT) - self.b3
        Output = self.sigmoid_activation(Out_IN)

        # 误差计算
        output_err = Y_train - Output  # d*1
        hidden_2_err = np.dot(self.wo.T, Output * (1 - Output) * output_err)  # k*d*d*1=k*1
        hidden_1_err = np.dot(self.wi2.T, L2_OUT * (1 - L2_OUT) * hidden_2_err)  # m*k*k*1=m*1

        # 更新权重和阈值
        # d*1*1*k =d*k
        delt_wo = self.learnRate * np.dot(output_err * Output * (1 - Output), np.transpose(L2_OUT))
        # k*1*1*m = k*m
        delt_wi2 = self.learnRate * np.dot(hidden_2_err * L2_OUT * (1 - L2_OUT), np.transpose(L1_OUT))
        # m*1*1*n = m*n
        delt_wi1 = self.learnRate * np.dot(hidden_1_err * L1_OUT * (1 - L1_OUT), np.transpose(X_train))
        # d*1-=d*1=d*N
        self.b3 -= self.learnRate * output_err * Output * (1 - Output)
        self.b2 -= self.learnRate * hidden_2_err * L2_OUT * (1 - L2_OUT)
        self.b1 -= self.learnRate * hidden_1_err * L1_OUT * (1 - L1_OUT)

        self.wo = self.wo + delt_wo
        self.wi2 = self.wi2 + delt_wi2
        self.wi1 = self.wi1 + delt_wi1
        pass

三、数据集处理

数据集处理这里包括数据的读取重新打乱数据行(分训练集和测试集方法的一种)以及数据归一化。首先一般下载的数据都是.data文件,可以通过重新保存的方式修改文件格式。其次,对于将数据集打乱顺序并不是唯一方法,也可以通过python里的其他命令完成训练集和测试集的划分。数据标签的转换是必备的环节,利用一个三列的矩阵来表示鸢尾花三类(【1,0,0】)数据集归一化的必要性,本文没有加以探索,以后探索了可以再出博文。

 if __name__ == '__main__':
     # 数据处理
    iris = open("iris.txt", "r")
    iris_lines = iris.readlines()
    rows = len(iris_lines)
    #print(rows)
    iris.close()
    # 打乱行
    out = open("out.txt", "w")
    lines = []
    with open("iris.txt", 'r') as infile:
        for line in infile:
            lines.append(line)
        random.shuffle(lines)
        for line in lines:
            out.write(line)
    #print(out)

    row = 0
    X_train = np.zeros((rows, 4))
    Y_train = np.zeros((rows, 3))
    for line in lines:
        line = line.strip().split(',')
        #print(line[4])
        if line[4] == 'Iris-setosa':
            Y_train[row, 0] = 1
        if line[4] == 'Iris-versicolor':
            Y_train[row, 1] = 1
        if line[4] == 'Iris-virginica':
            Y_train[row, 2] = 1
        X_train[row, 0:4] = line[0:4]
        row += 1
    print(Y_train)
    iris.close()
    #print(rows)
    # 数据进行归一化处理
    nrow,ncol = X_train.shape
    #print(nrow,ncol)
    datamatrix = np.zeros((nrow, ncol))
    for k in range(ncol):
        eeeee = X_train[:, k]
        # print(k)
        maxVals = np.max(eeeee, axis=0)
        # print(maxVals)
        minVals = np.min(eeeee, axis=0)
        # print(minVals)
        cols1 = X_train[:, k]
        ranges = maxVals - minVals
        b = cols1 - minVals
        normcols = b / ranges
        datamatrix[:, k] = normcols
    #print(datamatrix)
    X_train = datamatrix[:]

四、训练网络

 '''训练网络'''
    InputNodes = 4
    OutputNode = 3
    hiddenNodes_1 = 100
    hiddenNodes_2 = 100
    learnRate = 0.5
    n = neuralNetwork(InputNodes, hiddenNodes_1, hiddenNodes_2, OutputNode, learnRate)
    ncols = 30
    inputs_list = X_train[:ncols]
    targets_list = Y_train[:ncols]
    #print(inputs_list, targets_list)
    maxIter = 7500
    while (maxIter > 0):
        maxIter -= 1
        for i in range(ncols):
            n.train(inputs_list[i], targets_list[i])

五、预测和准确率

因为数据集已经重新打乱顺序,选取了前ncols条数据作为训练集,剩下的作为测试集。

将训练集投入已经训练好的网络中:

    def query(self, X_test):
        # ndmin=2 表示指定最小维数为2
        X_test = np.array(X_test, ndmin=2).T
        # Y_test = np.array(Y_test, ndmin=2).T

        L1_IN = np.dot(self.wi1, X_test) - self.b1
        L1_OUT = self.sigmoid_activation(L1_IN)
        L2_IN = np.dot(self.wi2, L1_OUT) - self.b2
        L2_OUT = self.sigmoid_activation(L2_IN)
        Out_IN = np.dot(self.wo, L2_OUT) - self.b3
        Output = self.sigmoid_activation(Out_IN)

        return Output
    # 预测
    X_test = X_train[-(rows-ncols):]
    #print(X_test)
    Y_test = Y_train[-(rows-ncols):]
    #print(Y_test)
    Output = n.query(X_test)
    Output = Output.T

下面对得到的结果进行预测,也许胜利就在眼前。

   # 训练集准确率
    # test准确率
    CategorySet = ['Iris-setosa', 'Iris-versicolor', 'Iris-virginica']
    P = []
    for i in range(len(Output)):
        Predict = Output[i]
        #print(i)
        Predict = Predict.tolist()
        Index = Predict.index(max(Predict, key=abs))
        Real = Y_test[i]
        Real = Real.tolist()
        Category = Real.index(max(Real, key=abs))
        if Index == Category:
            P.append(1)
            print(Y_test[i], '   ', '实际类别', ':', CategorySet[Category], '   ', '预测类别', ':',
                  CategorySet[Index],
                  '   ', '预测正确', Predict)
        else:
            P.append(0)
            print(Y_test[i], '   ', '实际类别', ':', CategorySet[Category], '   ', '预测类别', ':',
                  CategorySet[Index],
                  '   ', '预测错误', Predict)

    print('准确率', ':', sum(P) / len(P))

得到的预测结果如下所示,可以得的准确率。 

 有兴趣用手算推导的朋友可以参考这里

 

感谢观看到最后,源码可以通过私信找我,第一次发博客,反复斟酌的目的是看到底哪些思考对大家有用,可以帮到你。编程的确是一个提高思维逻辑分析能力的过程,我在编写上述代码最大的一个坑是将连接权和阈值的更新放到了train()函数里,导致每次迭代都重新生成连接权和阈值,也是非常无脑的错误,在此希望大家代码顺利,没有bug。

  • 7
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
下面是一个四层BP神经网络的代码示例: ```python # 导入所需的库 import numpy as np # 定义激活函数 def sigmoid(x): return 1 / (1 + np.exp(-x)) # 定义BP神经网络类 class BPNeuralNetwork: def __init__(self, input_dim, hidden_dim1, hidden_dim2, output_dim): self.input_dim = input_dim self.hidden_dim1 = hidden_dim1 self.hidden_dim2 = hidden_dim2 self.output_dim = output_dim # 初始化权重 self.weights1 = np.random.randn(self.input_dim, self.hidden_dim1) self.weights2 = np.random.randn(self.hidden_dim1, self.hidden_dim2) self.weights3 = np.random.randn(self.hidden_dim2, self.output_dim) # 初始化偏置 self.bias1 = np.random.randn(self.hidden_dim1) self.bias2 = np.random.randn(self.hidden_dim2) self.bias3 = np.random.randn(self.output_dim) def forward(self, X): # 前向传播 self.hidden_layer1 = sigmoid(np.dot(X, self.weights1) + self.bias1) self.hidden_layer2 = sigmoid(np.dot(self.hidden_layer1, self.weights2) + self.bias2) self.output_layer = sigmoid(np.dot(self.hidden_layer2, self.weights3) + self.bias3) return self.output_layer def train(self, X, y, learning_rate, epochs): # 训练网络 for epoch in range(epochs): # 前向传播 output = self.forward(X) # 反向传播,计算梯度 error = y - output delta_output = error * output * (1 - output) delta_hidden2 = np.dot(delta_output, self.weights3.T) * self.hidden_layer2 * (1 - self.hidden_layer2) delta_hidden1 = np.dot(delta_hidden2, self.weights2.T) * self.hidden_layer1 * (1 - self.hidden_layer1) # 更新权重和偏置 self.weights3 += learning_rate * np.dot(self.hidden_layer2.T, delta_output) self.weights2 += learning_rate * np.dot(self.hidden_layer1.T, delta_hidden2) self.weights1 += learning_rate * np.dot(X.T, delta_hidden1) self.bias3 += learning_rate * np.sum(delta_output, axis=0) self.bias2 += learning_rate * np.sum(delta_hidden2, axis=0) self.bias1 += learning_rate * np.sum(delta_hidden1, axis=0) # 创建BP神经网络对象 nn = BPNeuralNetwork(input_dim=2, hidden_dim1=4, hidden_dim2=4, output_dim=1) # 定义输入和目标输出 X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]]) y = np.array([[0], [1], [1], [0]]) # 训练神经网络 nn.train(X, y, learning_rate=0.1, epochs=10000) # 预测 output = nn.forward(X) ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值