Python 基于BP神经网络的鸢尾花分类

2023-11-20

本文用Python实现了BP神经网络分类算法,根据鸢尾花的4个特征,实现3种鸢尾花的分类。
算法参考文章:纯Python实现鸢尾属植物数据集神经网络模型

2020.07.21更新: 增加了分类结果可视化result_visualization
2020.07.09更新: 完善代码中取数据部分的操作。


代码及鸢尾花数据集可从GitHub上获取:点这里

1.数据准备

鸢尾花数据集包含4种特征,萼片长度(Sepal Length)、萼片宽度(Sepal Width)、花瓣长度(Petal Length)和花瓣宽度(Petal Width),以及3种鸢尾花Versicolor、Virginica和Setosa。

数据集共151行,5列:

  • 第1行是数据说明,“150”表示共150条数据;“4”表示特征数;“setosa、versicolor、virginica”是三类花的名字
  • 第2行至第151行是150条数据
  • 第1至4列是Sepal Length、Sepal Width、Petal Length、Petal
    Width 4个特征
  • 第5列是花的类别,用0、1、2表示
    iris数据集为方便起见,需要对数据集稍作处理:
  1. 将150条数据分隔为两个文件,前120条另存为iris_training.csv,即训练集;后30条另存为iris_test.csv,即测试集;
  2. 训练集和测试集都删去第1行;
  3. 训练集和测试集都删去原来的最后1列,并新增加3列,目的是用3列来表示鸢尾花的分类:如果原来最后一列是0,则新增加的3列为(0,0,0);如果原来最后一列是1,则新增加的3列为(0,1,0);如果原来最后一列是2,则新增加的3列为(0,0,1)。
    iris训练集

2.算法实现

纯Python实现鸢尾属植物数据集神经网络模型 这篇文章中讲解得更为详细。本人对代码做了略微的修改,并增加了评估模型准确率的predict()函数。

import pandas as pd
import numpy as np
import datetime
import matplotlib.pyplot as plt
from pandas.plotting import radviz
'''
    构建一个具有1个隐藏层的神经网络,隐层的大小为10
    输入层为4个特征,输出层为3个分类
    (1,0,0)为第一类,(0,1,0)为第二类,(0,0,1)为第三类
'''


# 1.初始化参数
def initialize_parameters(n_x, n_h, n_y):
    np.random.seed(2)

    # 权重和偏置矩阵
    w1 = np.random.randn(n_h, n_x) * 0.01
    b1 = np.zeros(shape=(n_h, 1))
    w2 = np.random.randn(n_y, n_h) * 0.01
    b2 = np.zeros(shape=(n_y, 1))

    # 通过字典存储参数
    parameters = {'w1': w1, 'b1': b1, 'w2': w2, 'b2': b2}

    return parameters


# 2.前向传播
def forward_propagation(X, parameters):
    w1 = parameters['w1']
    b1 = parameters['b1']
    w2 = parameters['w2']
    b2 = parameters['b2']

    # 通过前向传播来计算a2
    z1 = np.dot(w1, X) + b1     # 这个地方需注意矩阵加法:虽然(w1*X)和b1的维度不同,但可以相加
    a1 = np.tanh(z1)            # 使用tanh作为第一层的激活函数
    z2 = np.dot(w2, a1) + b2
    a2 = 1 / (1 + np.exp(-z2))  # 使用sigmoid作为第二层的激活函数

    # 通过字典存储参数
    cache = {'z1': z1, 'a1': a1, 'z2': z2, 'a2': a2}

    return a2, cache


# 3.计算代价函数
def compute_cost(a2, Y, parameters):
    m = Y.shape[1]      # Y的列数即为总的样本数

    # 采用交叉熵(cross-entropy)作为代价函数
    logprobs = np.multiply(np.log(a2), Y) + np.multiply((1 - Y), np.log(1 - a2))
    cost = - np.sum(logprobs) / m

    return cost


# 4.反向传播(计算代价函数的导数)
def backward_propagation(parameters, cache, X, Y):
    m = Y.shape[1]

    w2 = parameters['w2']

    a1 = cache['a1']
    a2 = cache['a2']

    # 反向传播,计算dw1、db1、dw2、db2
    dz2 = a2 - Y
    dw2 = (1 / m) * np.dot(dz2, a1.T)
    db2 = (1 / m) * np.sum(dz2, axis=1, keepdims=True)
    dz1 = np.multiply(np.dot(w2.T, dz2), 1 - np.power(a1, 2))
    dw1 = (1 / m) * np.dot(dz1, X.T)
    db1 = (1 / m) * np.sum(dz1, axis=1, keepdims=True)

    grads = {'dw1': dw1, 'db1': db1, 'dw2': dw2, 'db2': db2}

    return grads


# 5.更新参数
def update_parameters(parameters, grads, learning_rate=0.4):
    w1 = parameters['w1']
    b1 = parameters['b1']
    w2 = parameters['w2']
    b2 = parameters['b2']

    dw1 = grads['dw1']
    db1 = grads['db1']
    dw2 = grads['dw2']
    db2 = grads['db2']

    # 更新参数
    w1 = w1 - dw1 * learning_rate
    b1 = b1 - db1 * learning_rate
    w2 = w2 - dw2 * learning_rate
    b2 = b2 - db2 * learning_rate

    parameters = {'w1': w1, 'b1': b1, 'w2': w2, 'b2': b2}

    return parameters


# 6.模型评估
def predict(parameters, x_test, y_test):
    w1 = parameters['w1']
    b1 = parameters['b1']
    w2 = parameters['w2']
    b2 = parameters['b2']

    z1 = np.dot(w1, x_test) + b1
    a1 = np.tanh(z1)
    z2 = np.dot(w2, a1) + b2
    a2 = 1 / (1 + np.exp(-z2))

    # 结果的维度
    n_rows = y_test.shape[0]
    n_cols = y_test.shape[1]

    # 预测值结果存储
    output = np.empty(shape=(n_rows, n_cols), dtype=int)

    for i in range(n_rows):
        for j in range(n_cols):
            if a2[i][j] > 0.5:
                output[i][j] = 1
            else:
                output[i][j] = 0

    print('预测结果:')
    print(output)
    print('真实结果:')
    print(y_test)

    count = 0
    for k in range(0, n_cols):
        if output[0][k] == y_test[0][k] and output[1][k] == y_test[1][k] and output[2][k] == y_test[2][k]:
            count = count + 1
        else:
            print(k)

    acc = count / int(y_test.shape[1]) * 100
    print('准确率:%.2f%%' % acc)
	
	return output


# 建立神经网络
def nn_model(X, Y, n_h, n_input, n_output, num_iterations=10000, print_cost=False):
    np.random.seed(3)

    n_x = n_input           # 输入层节点数
    n_y = n_output          # 输出层节点数

    # 1.初始化参数
    parameters = initialize_parameters(n_x, n_h, n_y)

    # 梯度下降循环
    for i in range(0, num_iterations):
        # 2.前向传播
        a2, cache = forward_propagation(X, parameters)
        # 3.计算代价函数
        cost = compute_cost(a2, Y, parameters)
        # 4.反向传播
        grads = backward_propagation(parameters, cache, X, Y)
        # 5.更新参数
        parameters = update_parameters(parameters, grads)

        # 每1000次迭代,输出一次代价函数
        if print_cost and i % 1000 == 0:
            print('迭代第%i次,代价函数为:%f' % (i, cost))

    return parameters


# 结果可视化
# 特征有4个维度,类别有1个维度,一共5个维度,故采用了RadViz图
def result_visualization(x_test, y_test, result):
    cols = y_test.shape[1]
    y = []
    pre = []

    # 反转换类别的独热编码
    for i in range(cols):
        if y_test[0][i] == 0 and y_test[1][i] == 0 and y_test[2][i] == 1:
            y.append('setosa')
        elif y_test[0][i] == 0 and y_test[1][i] == 1 and y_test[2][i] == 0:
            y.append('versicolor')
        elif y_test[0][i] == 1 and y_test[1][i] == 0 and y_test[2][i] == 0:
            y.append('virginica')

    for j in range(cols):
        if result[0][j] == 0 and result[1][j] == 0 and result[2][j] == 1:
            pre.append('setosa')
        elif result[0][j] == 0 and result[1][j] == 1 and result[2][j] == 0:
            pre.append('versicolor')
        elif result[0][j] == 1 and result[1][j] == 0 and result[2][j] == 0:
            pre.append('virginica')
        else:
            pre.append('unknown')

    # 将特征和类别矩阵拼接起来
    real = np.column_stack((x_test.T, y))
    prediction = np.column_stack((x_test.T, pre))

    # 转换成DataFrame类型,并添加columns
    df_real = pd.DataFrame(real, index=None, columns=['Sepal Length', 'Sepal Width', 'Petal Length', 'Petal Width', 'Species'])
    df_prediction = pd.DataFrame(prediction, index=None, columns=['Sepal Length', 'Sepal Width', 'Petal Length', 'Petal Width', 'Species'])

    # 将特征列转换为float类型,否则radviz会报错
    df_real[['Sepal Length', 'Sepal Width', 'Petal Length', 'Petal Width']] = df_real[['Sepal Length', 'Sepal Width', 'Petal Length', 'Petal Width']].astype(float)
    df_prediction[['Sepal Length', 'Sepal Width', 'Petal Length', 'Petal Width']] = df_prediction[['Sepal Length', 'Sepal Width', 'Petal Length', 'Petal Width']].astype(float)

    # 绘图
    plt.figure('真实分类')
    radviz(df_real, 'Species', color=['blue', 'green', 'red', 'yellow'])
    plt.figure('预测分类')
    radviz(df_prediction, 'Species', color=['blue', 'green', 'red', 'yellow'])
    plt.show()


if __name__ == "__main__":
    # 读取数据
    data_set = pd.read_csv('D:\\iris_training.csv', header=None)
    
    # 第1种取数据方法:
    X = data_set.iloc[:, 0:4].values.T          # 前四列是特征,T表示转置
    Y = data_set.iloc[:, 4:].values.T           # 后三列是标签

    # 第2种取数据方法:
    # X = data_set.ix[:, 0:3].values.T
    # Y = data_set.ix[:, 4:6].values.T

    # 第3种取数据方法:
    # X = data_set.loc[:, 0:3].values.T
    # Y = data_set.loc[:, 4:6].values.T

    # 第4种取数据方法:
    # X = data_set[data_set.columns[0:4]].values.T
    # Y = data_set[data_set.columns[4:7]].values.T
    Y = Y.astype('uint8')
    
    # 开始训练
    start_time = datetime.datetime.now()
    # 输入4个节点,隐层10个节点,输出3个节点,迭代10000次
    parameters = nn_model(X, Y, n_h=10, n_input=4, n_output=3, num_iterations=10000, print_cost=True)
    end_time = datetime.datetime.now()
    print("用时:" + str((end_time - start_time).seconds) + 's' + str(round((end_time - start_time).microseconds / 1000)) + 'ms')

    # 对模型进行测试
    data_test = pd.read_csv('D:\\iris_test.csv', header=None)
    x_test = data_test.iloc[:, 0:4].values.T
    y_test = data_test.iloc[:, 4:].values.T
    y_test = y_test.astype('uint8')

    result = predict(parameters, x_test, y_test)

    # 分类结果可视化
    result_visualization(x_test, y_test, result)

最终结果:
结果
分类的可视化效果,左侧为测试集的真实分类,右侧为模型的预测分类结果,采用的是RadViz图
分类可视化
每次运行时准确率可能都不一样,可以通过调整学习率、隐节点数、迭代次数等参数来改善模型的效果。

3.总结

算法的实现总共分为6步:

  1. 初始化参数
  2. 前向传播
  3. 计算代价函数
  4. 反向传播
  5. 更新参数
  6. 模型评估

欢迎关注我的微信公众号:
微信公众号

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python 基于BP神经网络的鸢尾花分类 的相关文章

随机推荐

  • 《零基础入门学习Python》第061讲:论一只爬虫的自我修养9:异常处理

    高级语言的一个有些特性就是它可以从容不迫的处理每一个遇到的错误 不至于说遇到一个小错误就导致整个程序崩溃了 大部分高级语言处理错误的方法都是通过检测异常 处理异常来实现的 Python也是一样 用程序用代码进行互联网访问的时候 会出现异常是
  • Java设计模式之装饰者设计模式Decorator Pattern

    目录 一 基本概念 二 结构 1 图示 三 案例演示 被装饰对象的基类 一个接口 有cost 和description 两个抽象方法 具体被装饰的对象 实现上面这个接口 装饰者抽象类 基类 实现drink接口 具体的装饰者类 糖 具体装饰者
  • 图数据库中的空间值——实现图数据库内容在地理坐标系下展现

    一个帅气的boy 你可以叫我Love And Program 个人主页 Love And Program的个人主页 如果对你有帮助的话希望三连 支持一下博主 坐标参考系统 地理坐标系及地图经纬度拾取 笛卡尔坐标系 在这里插入图片描述 图数据
  • dev-c++官网位置和源码/库位置

    1 http devpaks org 2 http www bloodshed net 3 http www bloodshed net dev 转载于 https www cnblogs com vilyLei articles 1812
  • 亲爱的波特兰——CJ麦科勒姆告别信

    系列文章目录 精选优美英文短文1 Dear Basketball 亲爱的篮球 精选优美英文短文2 Here s to the Crazy Ones 致疯狂的人 我祝你不幸并痛苦 约翰 罗伯茨毕业致辞 Dear Portland You kn
  • 数据库学习笔记(9)——python连接数据库/python操作数据库/sql注入

    1 python连接数据库 1 不管是那一门编程语言都是不可以直接连接数据库的 需要有连接介质 连接池 2 python的连接池有 pymysql 或者 mysqlclient 3 在cmd中导入库 pip install pymysql
  • java文件上传

    简介 java文件上传 1 Commons FileUpload简介 1 Commons FileUpload组件 Commons是Apache开放源代码组织的一个Java子项目 其中的FileUpload是用来处理HTTP文件上传的子项目
  • C++类大小计算

    关于C 类大小计算 这在一本书 深度探索C 对象模型 一书中有非常详细的介绍 我这里归纳整理 本节讨论的是64位地址计算机 指针长度8字节 1 类的大小取决于以下成员 a 非静态成员变量 b 虚函数table c 数据对齐 d 空类 当然还
  • linux下makefile的CC和gcc

    参考自 http www cnblogs com zhouyinhui archive 2010 02 01 1661078 html CC gcc CC arm hisiv400 linux gcc 上面是经常遇到的makefile中编写
  • 阿兰·麦席森·图灵 介绍

    Alan MAthison Turing 英国数学家 逻辑学家 被称为 计算机科学之父 人工智能之父 她曾协助盟军破解德国的著名密码系统Enigma 帮助盟军取得了第二次世界大战的胜利 他对计算机的贡献在于他提出的有限状态自动机也就是图灵机
  • 微信支付商家转账到零钱功能使用教程

    之前的 企业付款到零钱 功能 微信支付已下架 以后用 商家转账到零钱 功能取代 下面介绍如何开通并使用该功能 从运营账户支出 首先需要先去了解一下微信支付的这3个账户的关系 商家转账到零钱 功能 是从运营账户转账给用户的 开通 商家转账到零
  • ATL字符串转换宏

    有比MultiByteToWideChar和WideCharToMultiByte更简单的字符串转换宏 你相信吗 头文件 d program files microsoft visual studio 8 vc atlmfc include
  • Flutter 碰到的各种坑 持续更新

    Android转flutter 也有1年多了 在新公司将一个产品用flutter从零开始开发 感觉flutter 还是不太稳定 各种问题还是比较多 总之这次体验还是比较差 Error on line 21 column 5 of pubsp
  • Kafka——Mac搭建kafka环境

    1 下载Kafka安装包 下载地址 将压缩包移动到 usr local mv kafka 2 12 3 1 0 tgz usr local 解压 tar zxvf kafka 2 12 3 1 0 tgz 2 启动 启动zookeeper
  • WEB安全测试手册

    概述 目的 适用读者 适用范围 注意事项 测试级别说明 测试过程示意图 1 服务器信息收集 1 1 运行帐号权限测试 1 2 Web服务器端口扫描 1 3 HTTP方法测试 1 4 HTTP PUT方法测试 1 5 HTTP DELETE方
  • 前端例程20211213:网页去色(以灰度形式显示)

    文章目录 前言 实现与演示 前言 在每年的一些特殊的日子 比如公祭日等 很多网站会将页面整体去色以灰度形式显示 以示哀悼 这里将对网页中实现该功能进行简单说明 实现与演示 使用CSS的 filter grayscale 属性可以给元素添加灰
  • 主进程退出后子进程还会存在吗?_深度好文

    干了这碗鸡汤 我急切地盼望着可以经历一场放纵的快乐 纵使巨大的悲哀将接踵而至 我也在所不惜 太宰治 人间失格 大家好 这里是周日凌晨4点 仍在笔耕不辍的程序喵大人 下面隆重推出我呕心沥血 耗时半个月完成的精心力作 01 什么是进程 标准定义
  • Element Plus 配置自动按需引入后,手动引入组件,组件样式丢失

    起因 最近在尝试使用 Element Plus 写一些简单的页面 跟着官方文档走配置了自动按需引入 npm install D unplugin vue components unplugin auto import vite config
  • IDEA全局搜索框打不开,全局搜索不全,全局搜索不到解决办法

    IDEA默认全局搜索快捷键是Ctrl Shift F 当我在使用IDEA的全局搜索时 发现IDEA的全局搜索快捷键不起作用 无法弹出全局搜索框 此时想到了应该是快捷键被占用了 首先想到的就是搜狗输入法 打开搜狗输入法设置 高级 把这个简繁切
  • Python 基于BP神经网络的鸢尾花分类

    本文用Python实现了BP神经网络分类算法 根据鸢尾花的4个特征 实现3种鸢尾花的分类 算法参考文章 纯Python实现鸢尾属植物数据集神经网络模型 2020 07 21更新 增加了分类结果可视化result visualization