银行客户流失数据分析(BP神经网络与决策树算法)

一、简单介绍

对之前练习项目的一个笔记(所以个人注释比较多),数据集已上传(有期限,到期后的可以评论区留言,看情况会更新),分别通过BP神经网络与决策树算法对银行客户流失分析预测,两种方法各有特点。

数据集链接:

链接:https://pan.baidu.com/s/1seFbXJISoo7MOiJGfHJrxQ?pwd=b4ly
提取码:b4ly

BP神经网络方法需要预安装tensorflow框架(安装过程不在介绍,可以参考网上其他文章),所以准备工作比较多,比较麻烦一些。决策树算法实现(包括和MLP神经网络,SVM支持向量机算法的对比)时没有用TensorFlow框架,因此不用安装,可以自行选择用哪种方法。决策树的预处理更全一些(因为是用的原始数据集),BP神经网络用的数据集是做了一部分预处理后的数据集。

决策树算法绘制流程图需要安装 Graphviz 包,这边简单介绍一下安装过程:
第一步,访问网站,网址如下:

https://graphviz.org/download/
第二步:选择指定系统,指定版本的文件下载,我这边是2.5版本,windows64位的操作系统。
在这里插入图片描述
第三步:下载后双击此文件
在这里插入图片描述
第四步:图片展示
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这边可以自定义路径:
在这里插入图片描述
最后点安装就行,如果安装成功,在命令行窗口(win +R 然后输入 cmd)输入:dot -version。

在这里插入图片描述
出现下图,则安装成功。
在这里插入图片描述

然后在 pycharm 中安装 graphviz 包,在pycharm终端输入:

pip install graphviz

二、BP神经网络分析预测

1.python包

引入一些包,我的是TensorFlow 2.x 版本的,所以需要通过tensorflow.compat.v1等方式来调用TensorFlow 1.x一些函数,如果本身就是TensorFlow 1.x,可以适当修改一下代码:

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' #定义输出级别,减少控制台不必要输出
#import tensorflow as tf

import tensorflow.compat.v1 as tf

tf.disable_v2_behavior()

import pandas as pd
import numpy as np
from sklearn.utils import shuffle
from sklearn.preprocessing import OneHotEncoder
import matplotlib.pyplot  as plt
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['STZhongsong']    # 指定默认字体:解决plot不能显示中文问题

2.数据读取与预处理

属性介绍

共有11个属性,包括10个条件属性,1个决策属性(Exited)

'''
客户流失预测模型
owNumber:行号 ×
CustomerID:用户编号 ×
Surname:用户姓名 ×
CreditScore:信用分数
Geography:用户所在国家/地区 ×
Gender:用户性别
Age:年龄
Tenure:当了本银行多少年用户
Balance:存贷款情况
NumOfProducts:使用产品数量
HasCrCard:是否有本行信用卡
IsActiveMember:是否活跃用户
EstimatedSalary:估计收入
Exited:是否已流失,这将作为我们的标签数据
'''

数据读取

#读取数据
df = pd.read_csv("../data/select-data.csv") #读取实验数据集
df_test = pd.read_csv("../data/scalar-test.csv") #读取测试数据集

构建向量

为了方便数据处理,需要将列表转化为numpy数组,并且要二维,target是一维所以需要reshape转化为二维,具体如下:

# 构建向量
#定义两个列表用来存储条件属性和决策属性
train = []
target = []
for i in range(0, len(df["EstimatedSalary"])):#获得实验数据集行数,并对每一行做循环,读取条件属性和决策属性
    mid = []
    mid.append(df["Geography"][i]) #添加Geography属性列第i行的值
    mid.append(df["Gender"][i])
    mid.append(df["EB"][i])
    mid.append(df["Age"][i])
    mid.append(df["EstimatedSalary"][i])
    mid.append(df["NumOfProducts"][i])
    mid.append(df["CreditScore"][i])
    mid.append(df["Tenure"][i])
    mid.append(df["HasCrCard"][i])
    mid.append(df["IsActiveMember"][i])
    target.append(df["Exited"][i])#添加分类属性
    train.append(mid)#添加条件属性
train = np.array(train) #将列表转化为数组,便于处理
target = np.array(target) #将列表转化为数组,便于处理

#测试数据集的处理,处理数据集不一样,其他步骤同上
test = []
test_target = []
for i in range(0, len(df_test["EstimatedSalary"])):
    mid = []
    mid.append(df_test["Geography"][i])
    mid.append(df_test["Gender"][i])
    mid.append(df_test["EB"][i])
    mid.append(df_test["Age"][i])
    mid.append(df_test["EstimatedSalary"][i])
    mid.append(df_test["NumOfProducts"][i])
    mid.append(df_test["CreditScore"][i])
    mid.append(df_test["Tenure"][i])
    mid.append(df_test["HasCrCard"][i])
    mid.append(df_test["IsActiveMember"][i])
    test_target.append(df_test["Exited"][i])
    test.append(mid)
test = np.array(test)
test_target = np.array(test_target)

# 随机打乱训练集与标签
#shuffle随机打乱数据集顺序,防止模型每次都是按固有顺序提取数据,不过不同数据集相对顺序是不变的,可以用来防止过拟合训练,
train, target = shuffle(train, target, random_state=42)#random_state是随机参数,如果不需要可重复性,可不设置random_state
#print(train, target)
#改变数组形状,保持一致,前一个-1表示第一个维度大小自动计算,第二个维度大小为1,test与train本来就是二维所以不用调整
target = target.reshape(-1, 1)
test_target = test_target.reshape(-1, 1)

# One-Hot编码,将分类变量转为为数值,0,1等形式,便于机器学习处理。其实数据集之前已经处理好了,不用这一步也行
enc = OneHotEncoder()#创建一个OneHotEncoder对象
enc.fit(test_target)#使用test_target数据来“训练”或“适应”编码器。这一步是为了知道每个独特的类别应该如何被编码。
test_target = enc.transform(test_target).toarray()#使用上面训练好的编码器将test_target转换为独热编码形式,并将结果从稀疏矩阵转换为普通的NumPy数组
enc.fit(target)#步骤同上
target = enc.transform(target).toarray()
#enc.fit(test_target)

占位符定义

占位符定义的其实是后面代码中

feed_dict={x: train, y: target, keep: 1}

的内容,类似于预定义变量名,TensorFlow中用到

# 定义输入占位符,由于本实验实验BP神经网络来预测,因此定义的占位符参数也和此相关,具体如下
x = tf.placeholder(tf.float32, shape=(None, 10))
#x是一个形状为(None, 10)的占位符,表示输入数据的特征矩阵,每一行是一个样本,每一列是一个特征,None表示样本的数量可以是任意的(行),10表示特征的数量是固定的(列)
#二分类问题 [0,1]
y = tf.placeholder(tf.float32, shape=(None, 2))
#y是一个形状为(None, 2)的占位符,表示输入数据的标签向量,每一行是一个样本,每一列是一个类别,None表示样本的数量可以是任意的,2表示类别的数量是固定的,这是一个二分类问题,所以有两个类别
keep = tf.placeholder(tf.float32)
#keep是一个形状为标量的占位符,表示在训练时使用的dropout的保留率,即每个神经元被保留的概率,它可以用来防止过拟合,一般在训练时设置为小于1的值,比如0.5,而在测试时设置为1,表示不使用dropout

3.神经网络模型

定义神经网络结构,包括几个隐藏层,隐藏层几个神经元,初始权重,初始偏移量等等。具体几个隐藏层不一定是2,我只是以2作为例子,说不定1个隐藏层或者三个隐藏层效果更好,可以自己慢慢调试看看。
第一个隐藏层的第一维是10,是和条件属性数量对应的,输出成的2是和决策属性类别对应的(二分类,所以是2),具体如下,有注释说明(也包含了其他的一些定义,如准确率,优化器函数等等):

# 定义网络结构,这边定义了两个隐藏层,一个输出,具体几个隐藏层以及隐藏层的维数可自定义调整
# layer1,定义第一个隐藏层
var1 = tf.Variable(tf.truncated_normal([10, 256], stddev=0.1))
#定义初始权重矩阵,随机选取均值为0、标准差为0.1的截断正态分布(之所以要截断正态分布,主要是避免异常值影响,因为用的sigmoid激活函数,所以要避免偏离的值)
bias1 = tf.Variable(tf.zeros([256]))#初始化偏移值,都设为0
hc1 = tf.add(tf.matmul(x, var1), bias1)
#tf.matmul(x, var1) 表示矩阵 x 和 var1 之间的矩阵乘法。然后,我们将偏置 bias1 加到乘积上,得到 hc1,x的定义在前面给出
h1 = tf.sigmoid(hc1)
#使用sigmoid激活函数,对函数非线性转化
h1 = tf.nn.dropout(h1, keep_prob=keep)
# 为了避免过拟合对 h1 进行 dropout 正则化,其中 keep 是 dropout 的保留概率。这意味着在训练过程中,h1 中的每个元素都有 1 - keep 的概率被设置为0。

# layer2 定义第二个隐藏层,具体和上面步骤差不多,不过初始权重矩阵不同,因为进行矩阵乘法,所以要保持前后层 行列对应
var2 = tf.Variable(tf.truncated_normal([256, 256], stddev=0.1))
bias2 = tf.Variable(tf.zeros([256]))
hc2 = tf.add(tf.matmul(h1, var2), bias2)
h2 = tf.sigmoid(hc2)
h2 = tf.nn.dropout(h2, keep_prob=keep)

# layer3
var3 = tf.Variable(tf.truncated_normal([256, 2], stddev=0.1))
bias3 = tf.Variable(tf.zeros([2]))#二分类,所以是2个偏移量
hc3 = tf.add(tf.matmul(h2, var3), bias3)
h3 = tf.nn.softmax(hc3)
#输出层,因为是二分类,所以要用激活函数转化为分类问题
#使用 softmax 激活函数对 hc3 进行非线性转换。softmax 函数通常用于多分类问题的输出层,因为它可以将任意实数值转换为介于0和1之间的概率分布。

#定义损失函数:交叉熵损失函数
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=h3, labels=y))
#logits=h3, labels=y,h3为预测的标签,y为真实的标签,先得到一个损失值,然后用reduce_mean获得平均损失值
tf.summary.scalar('loss', loss)#可视化写入

# 定义正确率
ac = tf.cast(tf.equal(tf.argmax(h3, 1), tf.argmax(y, 1)), tf.float32)
#tf.argmax(h3, 1),沿着列寻找最大值,即[0.8,0.2]=0,[0.2,0.8]=1,equal得到布尔值类型,所以需要转为浮点型以便进行准确率求和与平均
acc = tf.reduce_mean(ac)#求平均值,获得平均准确率
tf.summary.scalar('accuracy', acc)

# 定义优化器,一个学习率的优化方法,动态调整学习率
optimzer = tf.train.AdamOptimizer(1e-3).minimize(loss)
merge_summary = tf.summary.merge_all()
#isTrain判断是训练还是测试
isTrain = 0

4.训练与预测

按照预定义模型进行训练与预测,并给出准确率,这个设计了一个isTrain来决定是训练还是直接用训练好的模型测试,一般是先把 isTrain=1,然后训练出一个最优模型,然后将模型参数保存起来,后面就可以直接用这个模型参数,不用再训练了(将isTrain=0)。不过保存参数与调用的时候主要路径别弄错了。

训练模型

当isTrain等于1时,会先训练模型然后再进行预测,并画出准确率与损失值曲线,然后将模型参数保存起来,具体如下,黄色的是测试集准确率变化,绿色为训练集准确率变化,红色为损失值变化(模型我没有仔细调整,所以准确率不是特别高,有兴趣的可以自己调看看,比如增加神经网络层数,神经元数量,换个激活函数等等,效果应该是可以更好的):

在这里插入图片描述
这是控制台的输出结果,第一列为批次,第二列为训练集预测准确率,第二列为测试预测准确率比训练集低一些,不过也是正常的,最后一列是损失值

在这里插入图片描述

使用训练好的模型直接预测

当isTrain=1,代码已经训练好并保存了模型参数,如果想直接用此模型进行预测,省去训练步骤,则可以直接调用,将isTrain=0,可以直接得到预测结果,给出预测的类别。使用时注意模型的保存路径别弄错。
在这里插入图片描述

代码如下:

# 定义训练
print("正在训练.....")
saver = tf.train.Saver(max_to_keep=1)
#创建一个saver对象,用于保存和恢复模型的参数,max_to_keep参数表示最多保留一个模型文件

with tf.Session() as sess:
    #创建一个session对象,用于执行计算图中的操作和变量
    if isTrain:#判断是否是训练模式,如果是,执行以下的代码
        init_op = tf.global_variables_initializer()#创建一个初始化操作,用于初始化所有的变量
        sess.run(init_op)#执行初始化操作,给所有的变量赋予初始值
        summary_writer = tf.summary.FileWriter('../logs/', sess.graph)
        #创建一个summary_writer对象,用于将计算图和汇总信息写入日志文件,方便在TensorBoard中可视化
        nepoch=[]#创建一个空列表,用于存储训练的轮数
        trainacc=[]#创建一个空列表,用于存储训练集的准确率
        testacc=[]#创建一个空列表,用于存储测试集的准确率
        loss1=[] #创建一个空列表,用于存储训练集的损失值
        for i in range(0, 10001):#训练轮数
            sess.run(optimzer, feed_dict={x: train, y: target, keep: 0.5})
            #执行优化器的minimize操作(梯度下降法之类的,之前定义的optimzer),更新模型的参数,feed_dict参数表示给占位符提供输入数据,x是训练集的特征,y是训练集的标签,keep是dropout的保留率,设置为0.5表示每个神经元有50%的概率被保留
            train_summary = sess.run(merge_summary, feed_dict={x: train, y: target, keep: 1})
            #汇总信息
            summary_writer.add_summary(train_summary, i)
            #汇总信息
            if i % 50 == 0:
                accu = sess.run(acc, feed_dict={x: train, y: target, keep: 1})#训练集准确率
                accuT = sess.run(acc, feed_dict={x: test, y: test_target, keep: 1})#测试集准确率
                losss = sess.run(loss, feed_dict={x: train, y: target, keep: 1})#损失值

                print("epoch:" + str(i) + "   train_acc:" + str(accu) + "   test_acc:" + str(accuT) + "   loss:" + str(
                    losss))

                nepoch.append(i)
                trainacc.append(accu)
                testacc.append(accuT)
                loss1.append(losss)

        plt.title("BP神经网络训练性能曲线")
        plt.xlabel('训练次数')
        plt.ylabel('训练样本和检验样本的准确度')
        plt.plot(nepoch,trainacc,nepoch,testacc)#绘制两条曲线,分别表示训练集和测试集的准确率随训练次数的变化。
        plt.plot(nepoch,trainacc,nepoch,loss1)#绘制两条曲线,分别表示训练集的准确率和损失值随训练次数的变化
        plt.show()
        saver.save(sess, '../model/bank.ckpt', global_step=i)
        #保存模型的参数到’./model/bank.ckpt’文件中,并在文件名后面添加当前的训练步数
    #使用训练好的模型参数文件进行预测,预测时用不到y,所以y其实可以省略,具体用不用到得看使用的函数用没用到y
    else:
        f = open("../result/target.txt", "w")#预测结果写入此文件
        model_file = tf.train.latest_checkpoint('../model/')#saver.save保存的路径
        saver.restore(sess, model_file)#使用saver对象恢复模型的参数,需要一个session对象和一个模型文件的路径,这里使用sess和model_file作为参数
        tar = sess.run(h3, feed_dict={x: test, y: test_target, keep: 1})
        #h3为模型输出层,由此可以得到模型预测值,y: test_target可以去掉,因为预测时候用不到
        tar = sess.run(tf.argmax(tar, 1))
        for i in range(0, len(tar)):
            f.write(str(tar[i]) + " ")
        f.close()
        print(tar)#显示预测结果

三.决策树分析预测

1.导入包

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from graphviz import Source
#Graphviz是一个开源的图形可视化软件,它可以将数据以图形的方式显示出来,通常用于绘制决策树、流程图、组织结构图等
from sklearn.tree import export_graphviz
import os
from sklearn.metrics import roc_curve #导入ROC曲线函数
import matplotlib.pyplot as plt #导入作图库
from sklearn.metrics import confusion_matrix #导入混淆矩阵函数
from sklearn.model_selection import StratifiedKFold
from sklearn import svm
from sklearn.neural_network import MLPClassifier

2.特征数值化

由于原始数据集数据有非数值型比如地址等等,因此需要对这些特征数值化,函数如下(要注意文件的保存路径问题):

def quantification(dataPath,outputPath):
    df=pd.read_csv(dataPath)#使用pandas库读取位于dataPath路径下的CSV文件
    x=pd.factorize(df['Geography'])
    y=pd.factorize(df['Gender'])
    #使用pd.factorize()函数对Geography和Gender两列进行数值化处理。数值化是将分类标签(如字符串)转换为数值表示
    #pd.factorize()函数返回两个值:一个是数值处理后的数组,另一个是原始标签的数组。
    df['Geography']=x[0]
    df['Gender']=y[0]
    #我们使用了数值化后的数组(x[0]和y[0])替代原来的分类标签数据
    df.to_csv(outputPath,index=True)#保存修改数据到新的地址,可通过index来决定是否保存索引到文件中
quantification("../data/Churn-Modelling-new.csv","../data/Churn-Modelling-newT.csv")#调用特征数值化函数,两个参数是原始数据地址和特征数值化后的地址

结果如下图所示:
在这里插入图片描述
在这里插入图片描述

3.数据离散化处理

由于决策树需要的是离散型数据,而原本的数据集中比如年龄,收入等是连续型数据,因此如果想要处理,需要转化为离散型数据.
具体如何离散化,每个特征列分为几等分根据实际情况判断,如下函数:

def discretization(dataPath,outputPath):
    df=pd.read_csv(dataPath)
    CreditScore=[];Age=[];Balance=[];EstimatedSalary=[];Exited=[];temp=[]
    # 信用分数划分成四等份,根据计算设置为:584,652,718
    for i in range(len(df)):
        if df["CreditScore"][i]<584:
            CreditScore.append(0)#赋值为 0
        elif df["CreditScore"][i]<652:
            CreditScore.append(1)#赋值为 1
        elif df["CreditScore"][i]<718:
            CreditScore.append(2)#赋值为 2
        else:
            CreditScore.append(3)#赋值为 3
    df["CreditScore"]=CreditScore #替代原数据
    #年龄
    for i in range(len(df)):
        if df["Age"][i]<=20:
            Age.append(0)
        elif df["Age"][i]<=40:
            Age.append(1)
        else:
            Age.append(2)
    df["Age"]=Age
    #存款情况,存款为0的单独列出来
    for i in range(len(df)):
        if (df["Balance"][i]!=0):
            temp.append(df["Balance"][i])
    temp.sort()
    q1=temp[len(temp)//4]#对一个列表(或其他类似的可索引对象)temp进行索引操作。这里的索引值是 len(temp) // 4,即列表长度的四分之一(向下取整)
    q2 = temp[len(temp) // 4*2]
    q3 = temp[len(temp) // 4*3]
    for i in range(len(df)):
        if df["Balance"][i]==0:
            Balance.append(0)
        elif df["Balance"][i]<q1:
            Balance.append(1)
        elif df["Balance"][i]<q2:
            Balance.append(2)
        elif df["Balance"][i]<q3:
            Balance.append(3)
        else:
            Balance.append(4)
    df["Balance"]=Balance
    temp.clear()
    #估计收入
    for i in range(len(df)):
        temp.append(df["EstimatedSalary"][i])
    temp.sort()
    q1=temp[len(temp)//4]#对一个列表(或其他类似的可索引对象)temp进行索引操作。这里的索引值是 len(temp) // 4,即列表长度的四分之一(向下取整)
    q2 = temp[len(temp) // 4*2]
    q3 = temp[len(temp) // 4*3]
    for i in range(len(df)):
        if df["EstimatedSalary"][i]<q1:
            EstimatedSalary.append(0)
        elif df["EstimatedSalary"][i]<q2:
            EstimatedSalary.append(1)
        elif df["EstimatedSalary"][i]<q3:
            EstimatedSalary.append(2)
        else:
            EstimatedSalary.append(3)
    df["EstimatedSalary"]=EstimatedSalary
    temp.clear()
    df.to_csv(outputPath,index=True)
discretization("../data/Churn-Modelling-newT.csv","../data/Churn-Modelling-new-tree.csv")#处理后保存文件位置

保存后文件如下图:
在这里插入图片描述

数据筛选

删除无用的特征列,比如编号等等,并筛选数据用欠采样解决类别不均衡问题,它从原始数据集中选取一定数量的样本,使得在输出的数据集中,代码如下函数所示:

def filtering(dataPath, outputPath):
    df = pd.read_csv(dataPath)
    df_new = pd.DataFrame(
        columns=['Geography', 'EB', 'Age', 'EstimatedSalary', 'NumOfProducts', 'CreditScore', 'Tenure', 'HasCrCard','IsActiveMember', 'Exited', 'Gender'])
    ones = sum(df["Exited"])#只有0和1,因此是类别为1的数量,计算输入数据中流失客户的数量,即标签为 1 的样本数量
    length = len(df["EstimatedSalary"])# 计算输入数据中总的样本数量
    zeros = length - ones# 计算输入数据中未流失客户的数量,即标签为 0 的样本数量
    i = 0# 初始化一个循环变量,用于遍历输入数据的每一行
    flag_0 = 0
    flag_1 = 0
    # 初始化两个计数变量,用于记录输出数据中已经添加的流失和未流失客户的数量
    # 当循环变量小于总的样本数量时,执行循环
    while i != length:
        if df["Exited"][i] == 0 and flag_1 < ones:# 如果当前行的标签为 0,即未流失客户,且输出数据中已添加的未流失客户数量小于流失客户数量
            df_new = df_new.append(pd.DataFrame(
                {'Gender': df["Gender"][i], 'Geography': df["Geography"][i], 'EB': df["EB"][i], 'Age': df["Age"][i],'EstimatedSalary': df["EstimatedSalary"][i], 'NumOfProducts': df["NumOfProducts"][i],'CreditScore': df["CreditScore"][i], 'Tenure': df["Tenure"][i], 'HasCrCard': df["HasCrCard"][i],'IsActiveMember': df["IsActiveMember"][i], 'Exited': df["Exited"][i]}, index=[i]))
            # 将当前行的数据添加到输出数据中
            # 将输出数据中已添加的未流失客户数量加1
            flag_1 = flag_1 + 1
        # 如果当前行的标签为 1,即流失客户,且输出数据中已添加的流失客户数量小于未流失客户数量,具体步骤同上
        if df["Exited"][i] == 1 and flag_0 < zeros:
            df_new = df_new.append(pd.DataFrame(
                {'Gender': df["Gender"][i], 'Geography': df["Geography"][i], 'EB': df["EB"][i], 'Age': df["Age"][i],'EstimatedSalary': df["EstimatedSalary"][i], 'NumOfProducts': df["NumOfProducts"][i],'CreditScore': df["CreditScore"][i], 'Tenure': df["Tenure"][i], 'HasCrCard': df["HasCrCard"][i],'IsActiveMember': df["IsActiveMember"][i], 'Exited': df["Exited"][i]}, index=[i]))
            flag_0 = flag_0 + 1
        i = i + 1
    # 将输出数据保存为 csv 文件,路径为参数指定的输出路径
    df_new.to_csv(outputPath)
# 调用函数,传入输入数据和输出数据的路径
filtering("../data/Churn-Modelling-new-tree.csv","../data/final.csv")

结果如下:
在这里插入图片描述

4.划分训练集及测试集

按照4:1的比例划分训练集和测试集,具体比例也可以9:1或者其他,可以自己修改,代码如下:

# 划分训练集及测试集
csv=pd.read_csv("../data/final.csv")
csv_array=np.array(csv)
#print(csv.head())
#标签数据为array第11列 Exited
target=csv_array[:,10]
#第1列为编号,对决策树模型无意义,去除,将剩余列作为特征项
feature=csv_array[:,[1,2,3,4,5,6,7,8,9,11]]
#将数据集按4:1分为训练集和测试集
feature_train, feature_test, target_train, target_test = train_test_split(feature, target, test_size=0.2, random_state=10)
#使用train_test_split函数(通常来自sklearn.model_selection模块,因此要引入此包)将数据集分为训练集和测试集。
#test_size=0.2表示测试集占总数据集的20%,剩下的80%作为训练集。
#random_state=10是一个随机种子,确保每次运行代码时数据集的分割方式相同,使得实验结果可复现。
#feature_train和target_train将用于训练模型,而feature_test和target_test将用于评估模型的性能。

5.数据建模,先设置决策树参数

设置决策树模型参加,具体可以再调整,比如数的最大深度等参数,代码如下:

dt_model = DecisionTreeClassifier(criterion="gini",max_depth=6,min_samples_split=100)
#criterion="gini":这是用来衡量划分质量的指标。"gini" 表示使用基尼不纯度(Gini impurity)作为划分标准。基尼不纯度是一个衡量集合中随机选中的样本被错误分类的概率的度量。它的值越小,表示集合的纯度越高。
#max_depth=6:这是树的最大深度。当树的深度达到这个值时,树的生长就会停止。限制树的最大深度可以避免过拟合,即模型在训练数据上表现很好,但在未知数据上表现不佳
#min_samples_split=200:这是一个节点在被考虑分裂之前必须具有的最小样本数。如果某个节点的样本数少于这个值,则该节点不会被进一步分裂。这个参数同样可以帮助控制过拟合,因为它防止了模型学习训练数据中的噪声或异常值
dt_model.fit(feature_train,target_train)#导入数据,训练模型
scores = dt_model.score(feature_test,target_test)#测试模型准确度
print(scores)
predict_results = dt_model.predict(feature_test)#测试集根据决策树的预测结果,在已经用数据训练好模型后
print(predict_results)#预测的结果

6.创建流程图保存路径

流程图的保存方式可以是pdf,也可以是图片格式,具体可以调整,且在代码中做了注释说明:

image_path = "../images/decision_trees"
# os.makedirs(image_path, exist_ok=True)

export_graphviz(dt_model,
                out_file=os.path.join(image_path, "bank_tree.dot"),
                feature_names=["Geography", "EB", "Age", "EstimatedSalary", "NumOfProducts", "CreditScore", "Tenure",
                               "HasCrCard", "IsActiveMember", "Gender"],
                class_names=["not exited", "exited"],
                rounded=True,
                filled=True)
#export_graphviz 调用 export_graphviz 函数,该函数用于将训练好的决策树模型 dt_model 导出为 Graphviz 的 DOT 格式
#DOT 格式是一种文本文件格式,用于描述图形结构,可以被 Graphviz 工具读取并渲染成可视化的图形
# dt_model 为训练好的模型
#out_file 指定了导出的DOT文件的保存路径和文件名。
#os.path.join 函数用于拼接目录和文件名,确保在不同操作系统下都能正确生成文件路径。bank_tree.dot 是将要保存的 DOT 文件的名称
#class_names参数 用来确定分类名称
#feature_name参数 指定了决策树中用到的特征名称列表。这些名称将在生成的图形中用于标记每个节点的特征。
#rounded=True 这个参数指定生成的图形中的节点应该是圆角矩形的
#filled=True 这个参数指定生成的图形中的节点应该被填充颜色。通常,节点的颜色会根据其所属的类别进行着色,以便于区分。
s = Source.from_file(os.path.join(image_path, "bank_tree.dot"))
# #这行代码使用 Graphviz 的 Source 类从刚才保存的 DOT 文件创建一个源对象 s。这个对象可以用来进一步操作或渲染图形
# #默认输出为pdf文件,不过也可以设置输出为图片 如.png 等其他格式
s.view()

决策树图如下所示:
在这里插入图片描述

7.绘制ROC曲线

绘制ROC曲线 可用来评估模型分类性能,图片中,曲线下的面积越大,代表准确度越高。
代码如下:

fpr, tpr, thresholds = roc_curve(target_test, predict_results, pos_label=1)
#使用roc_curve函数从sklearn.metrics库计算ROC曲线的各个点。
#target_test是真实的目标标签(通常是二进制的,例如0和1)。
#predict_results是模型预测的标签结果
#pos_label=1指定正类标签的值为1。
# 函数返回三个值:
# fpr:假正类率(False Positive Rate)tpr:真正类率(True Positive Rate)thresholds:用于计算上述率的阈值
plt.figure(figsize=(10,10))
plt.plot(fpr, tpr, linewidth=2, label = 'ROC curve') #作出ROC曲线
plt.plot([0,1],[0,1],'k--',label='guess')
plt.title("ROC Curve",fontsize=25)
plt.xlabel('False Positive Rate',fontsize=20) #坐标轴标签
plt.ylabel('True Positive Rate',fontsize=20) #坐标轴标签
plt.ylim(0,1.05) #边界范围
plt.xlim(0,1.05) #边界范围
plt.legend(loc=4,fontsize=20) #图例
plt.show() #显示作图结果

ROC曲线如下图所示:
在这里插入图片描述

8.绘制混淆矩阵

绘制混淆矩阵,混淆矩阵上的4个数字,从左到右,从上到下依次是真正例,假正例,真反例,假反例的数量。
代码如下:

#混淆矩阵
cm = confusion_matrix(target_test, predict_results)  # 计算混淆矩阵
plt.figure(figsize=(10, 10))
plt.matshow(cm, fignum=0, cmap=plt.cm.Blues)#plt.cm.Blues是matplotlib中的一个预定义色彩映射,它使用不同深浅的蓝色来表示不同的值
plt.colorbar()  # 颜色标签
for x in range(len(cm)):  # 数据标签
    for y in range(len(cm)):
        plt.annotate(cm[x, y], xy=(x, y), fontsize=30, horizontalalignment='center', verticalalignment='center')
#cm[x, y]: 这是你想要在图上标注的文本内容。在混淆矩阵的上下文中,cm[x, y]表示混淆矩阵中位于第x行第y列的元素值,通常代表实际类别为y而被预测为x的样本数量
#xy=(x, y): 这个参数定义了文本标注的位置。在混淆矩阵的情境中,xy=(x, y)指的是矩阵中第x行第y列单元格的中心位置,这里x和y是矩阵的索引。
#fontsize=30: 这个参数指定了标注文本的大小,单位是点(pt)。在这个例子中,文本大小被设置为30,你可以根据需要调整这个值。
#forizontalalignment='center': 这个参数定义了文本在水平方向上的对齐方式。'center'意味着文本会在其指定的xy坐标位置上水平居中显示。
#verticalalignment='center': 这个参数定义了文本在垂直方向上的对齐方式。'center'意味着文本会在其指定的xy坐标位置上垂直居中显示。
#混淆矩阵上的4个数字,从左到右,从上到下依次是真正例,假正例,真反例,假反例的数量
plt.ylabel('Hypothesized class', fontsize=20)  # 坐标轴标签
plt.xlabel('True class', fontsize=20)  # 坐标轴标签
plt.show()

混淆矩阵如图:
在这里插入图片描述

9.不同折数交叉验证对比

十折交叉验证

# 10折交叉验证
skfold = StratifiedKFold(n_splits=10,shuffle=False)#10折交叉验证
x_axis=[] ; y_axis=[]
k=0;max=0;min=100;sum=0
for train_index,test_index in skfold.split(feature,target):#返回的是索引,分别为测试数据索引和训练数据索引
    k+=1
    skfold_feature_train=feature[train_index]
    skfold_feature_test=feature[test_index]
    skfold_target_train=target[train_index]
    skfold_target_test=target[test_index]
    dt_model.fit(skfold_feature_train,skfold_target_train)#训练数据训练模型
    scores = dt_model.score(skfold_feature_test,skfold_target_test)#通过训练数据训练的模型测试准确率
    x_axis.append(k)
    y_axis.append(scores)
    if scores>max:
        max=scores
    if scores<min:
        min=scores
    sum+=scores
avg=sum/k
plt.plot(x_axis,y_axis)
plt.ylim(0.6,0.9)
plt.xlim(1,10)
plt.xlabel("Rounds")
plt.ylabel('True Rate')
plt.title("KFold Cross Validation (k=%s) avg=%s"%(k,round(avg*100,2))+"%"+" max:"+"%s"%(round(max*100,2))+"%"+" min:"+"%s"%(round(min*100,2))+"%")
plt.show()

五折交叉验证

#5折交叉验证
skfold_5 = StratifiedKFold(n_splits=5,shuffle=False)

x_axis_5=[] ; y_axis_5=[]
k_5=0;max_5=0;min_5=100;sum_5=0
for train_index,test_index in skfold_5.split(feature,target):
    k_5+=1
    skfold_feature_train=feature[train_index]
    skfold_feature_test=feature[test_index]
    skfold_target_train=target[train_index]
    skfold_target_test=target[test_index]
    dt_model.fit(skfold_feature_train,skfold_target_train)
    scores = dt_model.score(skfold_feature_test,skfold_target_test)
    x_axis_5.append(k_5)
    y_axis_5.append(scores)
    if scores>max_5:
        max_5=scores
    if scores<min_5:
        min_5=scores
    sum_5+=scores
avg_5=sum_5/k_5

plt.plot(x_axis_5,y_axis_5)
plt.ylim(0.6,0.9)
plt.xlim(1,5)
plt.xlabel("Rounds")
plt.ylabel('True Rate')
plt.title("KFold Cross Validation (k=%s) avg=%s"%(k_5,round(avg_5*100,2))+"%"+" max:"+"%s"%(round(max_5*100,2))+"%"+" min:"+"%s"%(round(min_5*100,2))+"%")
plt.show()

十五折交叉验证

skfold_15 = StratifiedKFold(n_splits=15,shuffle=False)

x_axis=[] ; y_axis=[]
k=0;max=0;min=100;sum=0
for train_index,test_index in skfold_15.split(feature,target):
    k+=1
    skfold_feature_train=feature[train_index]
    skfold_feature_test=feature[test_index]
    skfold_target_train=target[train_index]
    skfold_target_test=target[test_index]
    dt_model.fit(skfold_feature_train,skfold_target_train)
    scores = dt_model.score(skfold_feature_test,skfold_target_test)
    x_axis.append(k)
    y_axis.append(scores)
    if scores>max:
        max=scores
    if scores<min:
        min=scores
    sum+=scores
avg=sum/k

plt.plot(x_axis,y_axis)
plt.ylim(0.6,0.9)
plt.xlim(1,15)
plt.xlabel("Rounds")
plt.ylabel('True Rate')
plt.title("KFold Cross Validation (k=%s) avg=%s"%(k,round(avg*100,2))+"%"+" max:"+"%s"%(round(max*100,2))+"%"+" min:"+"%s"%(round(min*100,2))+"%")
plt.show()

对比结果

十折交叉验证:
在这里插入图片描述
五折交叉验证:
在这里插入图片描述
十五折交叉验证:
在这里插入图片描述

10.SVM分类器

给出了SVM分类器的准确率(70.06)和混淆矩阵:

#使用SVM进行分类
clf=svm.SVC()#创建一个支持向量机(Support Vector Machine, SVM)分类器的实例
clf.fit(feature_train,target_train)
clf.predict(feature_test)

#%%

scores_svm = clf.score(feature_test,target_test)
print(scores_svm)#支撑向量机的准确率

cm = confusion_matrix(target_test, clf.predict(feature_test))  # 混淆矩阵

plt.figure(figsize=(10, 10))
plt.matshow(cm, fignum=0, cmap=plt.cm.Blues)
plt.colorbar()  # 颜色标签
for x in range(len(cm)):  # 数据标签
    for y in range(len(cm)):
        plt.annotate(cm[x, y], xy=(x, y), fontsize=30, horizontalalignment='center', verticalalignment='center')

plt.ylabel('Hypothesized class', fontsize=20)  # 坐标轴标签
plt.xlabel('True class', fontsize=20)  # 坐标轴标签
plt.show()

在这里插入图片描述

11.MLP(多层感知机)神经网络

给出了MLP神经网络分类器的准确率(76.44)和混淆矩阵,一开始迭代次数太少没有找到最优的,后来增加了迭代次数:

mlp = MLPClassifier(solver='lbfgs', alpha=1e-5,hidden_layer_sizes=(10, 11),max_iter=10000, random_state=1)
#创建一个MLPClassifier的实例
# solver='lbfgs': 指定优化算法为'lbfgs'。'lbfgs'是一种优化算法,用于找到损失函数的最小值。
# alpha=1e-5: L2惩罚(正则化项)的参数。
# hidden_layer_sizes=(10, 11): 指定隐藏层的神经元数量。这里有两层隐藏层,第一层有10个神经元,第二层有11个神经元。
# random_state=1: 随机数生成器的种子,确保每次运行代码时得到的结果是可复现的。
mlp.fit(feature_train,target_train)#使用训练数据feature_train和对应的标签target_train来训练多层感知机分类器。
mlp.predict(feature_test)#使用训练好的模型进行预测
scores_mlp = mlp.score(feature_test,target_test)#计算准确率
print(scores_mlp)#输出算法准确率

cm = confusion_matrix(target_test, mlp.predict(feature_test))  # 混淆矩阵

plt.figure(figsize=(10, 10))
plt.matshow(cm, fignum=0, cmap=plt.cm.Blues)
plt.colorbar()  # 颜色标签
for x in range(len(cm)):  # 数据标签
    for y in range(len(cm)):
        plt.annotate(cm[x, y], xy=(x, y), fontsize=30, horizontalalignment='center', verticalalignment='center')

plt.ylabel('Hypothesized class', fontsize=20)  # 坐标轴标签
plt.xlabel('True class', fontsize=20)  # 坐标轴标签
plt.show()

四.完整代码

1.BP神经网络数据分析代码

代码如下:

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
#import tensorflow as tf

import tensorflow.compat.v1 as tf

tf.disable_v2_behavior()

import pandas as pd
import numpy as np
from sklearn.utils import shuffle
from sklearn.preprocessing import OneHotEncoder
import matplotlib.pyplot  as plt
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['STZhongsong']    # 指定默认字体:解决plot不能显示中文问题


'''
客户流失预测模型
owNumber:行号 ×
CustomerID:用户编号 ×
Surname:用户姓名 ×
CreditScore:信用分数
Geography:用户所在国家/地区 ×
Gender:用户性别
Age:年龄
Tenure:当了本银行多少年用户
Balance:存贷款情况
NumOfProducts:使用产品数量
HasCrCard:是否有本行信用卡
IsActiveMember:是否活跃用户
EstimatedSalary:估计收入
Exited:是否已流失,这将作为我们的标签数据
'''

#读取数据
df = pd.read_csv("../data/select-data.csv") #读取实验数据集
df_test = pd.read_csv("../data/scalar-test.csv") #读取测试数据集
print("构建BP神经网络需要的向量.........")
# 构建向量
#定义两个列表用来存储条件属性和决策属性
train = []
target = []
for i in range(0, len(df["EstimatedSalary"])):#获得实验数据集行数,并对每一行做循环,读取条件属性和决策属性
    mid = []
    mid.append(df["Geography"][i]) #添加Geography属性列第i行的值
    mid.append(df["Gender"][i])
    mid.append(df["EB"][i])
    mid.append(df["Age"][i])
    mid.append(df["EstimatedSalary"][i])
    mid.append(df["NumOfProducts"][i])
    mid.append(df["CreditScore"][i])
    mid.append(df["Tenure"][i])
    mid.append(df["HasCrCard"][i])
    mid.append(df["IsActiveMember"][i])
    target.append(df["Exited"][i])#添加分类属性
    train.append(mid)#添加条件属性
train = np.array(train) #将列表转化为数组,便于处理
target = np.array(target) #将列表转化为数组,便于处理

#测试数据集的处理,处理数据集不一样,其他步骤同上
test = []
test_target = []
for i in range(0, len(df_test["EstimatedSalary"])):
    mid = []
    mid.append(df_test["Geography"][i])
    mid.append(df_test["Gender"][i])
    mid.append(df_test["EB"][i])
    mid.append(df_test["Age"][i])
    mid.append(df_test["EstimatedSalary"][i])
    mid.append(df_test["NumOfProducts"][i])
    mid.append(df_test["CreditScore"][i])
    mid.append(df_test["Tenure"][i])
    mid.append(df_test["HasCrCard"][i])
    mid.append(df_test["IsActiveMember"][i])
    test_target.append(df_test["Exited"][i])
    test.append(mid)
test = np.array(test)
test_target = np.array(test_target)
# train = np.trunc(train * 100)#train * 100将train中的每个数都乘以100,np.trunc()表示移除小数部分,只保留整数部分


# 随机打乱训练集与标签
#shuffle随机打乱数据集顺序,防止模型每次都是按固有顺序提取数据,不过不同数据集相对顺序是不变的,可以用来防止过拟合训练,
train, target = shuffle(train, target, random_state=42)#random_state是随机参数,如果不需要可重复性,可不设置random_state
#print(train, target)
#改变数组形状,保持一致,前一个-1表示第一个维度大小自动计算,第二个维度大小为1,test与train本来就是二维所以不用调整
target = target.reshape(-1, 1)
test_target = test_target.reshape(-1, 1)

# One-Hot编码,将分类变量转为为数值,0,1等形式,便于机器学习处理。其实数据集之前已经处理好了,不用这一步也行
enc = OneHotEncoder()#创建一个OneHotEncoder对象
enc.fit(test_target)#使用test_target数据来“训练”或“适应”编码器。这一步是为了知道每个独特的类别应该如何被编码。
test_target = enc.transform(test_target).toarray()#使用上面训练好的编码器将test_target转换为独热编码形式,并将结果从稀疏矩阵转换为普通的NumPy数组
enc.fit(target)#步骤同上
target = enc.transform(target).toarray()
#enc.fit(test_target)

# 定义输入占位符,由于本实验实验BP神经网络来预测,因此定义的占位符参数也和此相关,具体如下
x = tf.placeholder(tf.float32, shape=(None, 10))
#x是一个形状为(None, 10)的占位符,表示输入数据的特征矩阵,每一行是一个样本,每一列是一个特征,None表示样本的数量可以是任意的(行),10表示特征的数量是固定的(列)
#二分类问题 [0,1]
y = tf.placeholder(tf.float32, shape=(None, 2))
#y是一个形状为(None, 2)的占位符,表示输入数据的标签向量,每一行是一个样本,每一列是一个类别,None表示样本的数量可以是任意的,2表示类别的数量是固定的,这是一个二分类问题,所以有两个类别
keep = tf.placeholder(tf.float32)
#keep是一个形状为标量的占位符,表示在训练时使用的dropout的保留率,即每个神经元被保留的概率,它可以用来防止过拟合,一般在训练时设置为小于1的值,比如0.5,而在测试时设置为1,表示不使用dropout

# 定义网络结构,这边定义了两个隐藏层,一个输出,具体几个隐藏层以及隐藏层的维数可自定义调整
# layer1,定义第一个隐藏层
var1 = tf.Variable(tf.truncated_normal([10, 256], stddev=0.1))
#定义初始权重矩阵,随机选取均值为0、标准差为0.1的截断正态分布(之所以要截断正态分布,主要是避免异常值影响,因为用的sigmoid激活函数,所以要避免偏离的值)
bias1 = tf.Variable(tf.zeros([256]))#初始化偏移值,都设为0
hc1 = tf.add(tf.matmul(x, var1), bias1)
#tf.matmul(x, var1) 表示矩阵 x 和 var1 之间的矩阵乘法。然后,我们将偏置 bias1 加到乘积上,得到 hc1,x的定义在前面给出
h1 = tf.sigmoid(hc1)
#使用sigmoid激活函数,对函数非线性转化
h1 = tf.nn.dropout(h1, keep_prob=keep)
# 为了避免过拟合对 h1 进行 dropout 正则化,其中 keep 是 dropout 的保留概率。这意味着在训练过程中,h1 中的每个元素都有 1 - keep 的概率被设置为0。

# layer2 定义第二个隐藏层,具体和上面步骤差不多,不过初始权重矩阵不同,因为进行矩阵乘法,所以要保持前后层 行列对应
var2 = tf.Variable(tf.truncated_normal([256, 256], stddev=0.1))
bias2 = tf.Variable(tf.zeros([256]))
hc2 = tf.add(tf.matmul(h1, var2), bias2)
h2 = tf.sigmoid(hc2)
h2 = tf.nn.dropout(h2, keep_prob=keep)

# layer3
var3 = tf.Variable(tf.truncated_normal([256, 2], stddev=0.1))
bias3 = tf.Variable(tf.zeros([2]))#二分类,所以是2个偏移量
hc3 = tf.add(tf.matmul(h2, var3), bias3)
h3 = tf.nn.softmax(hc3)
#输出层,因为是二分类,所以要用激活函数转化为分类问题
#使用 softmax 激活函数对 hc3 进行非线性转换。softmax 函数通常用于多分类问题的输出层,因为它可以将任意实数值转换为介于0和1之间的概率分布。

#定义损失函数:交叉熵损失函数
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=h3, labels=y))
#logits=h3, labels=y,h3为预测的标签,y为真实的标签,先得到一个损失值,然后用reduce_mean获得平均损失值
tf.summary.scalar('loss', loss)#可视化写入

# 定义正确率
ac = tf.cast(tf.equal(tf.argmax(h3, 1), tf.argmax(y, 1)), tf.float32)
#tf.argmax(h3, 1),沿着列寻找最大值,即[0.8,0.2]=0,[0.2,0.8]=1,equal得到布尔值类型,所以需要转为浮点型以便进行准确率求和与平均
acc = tf.reduce_mean(ac)#求平均值,获得平均准确率
tf.summary.scalar('accuracy', acc)

# 定义优化器,一个学习率的优化方法,动态调整学习率
optimzer = tf.train.AdamOptimizer(1e-3).minimize(loss)

merge_summary = tf.summary.merge_all()

#isTrain判断是训练还是测试
isTrain = 1

# 定义训练
print("正在训练.....")
saver = tf.train.Saver(max_to_keep=1)
#创建一个saver对象,用于保存和恢复模型的参数,max_to_keep参数表示最多保留一个模型文件

with tf.Session() as sess:
    #创建一个session对象,用于执行计算图中的操作和变量
    if isTrain:#判断是否是训练模式,如果是,执行以下的代码
        init_op = tf.global_variables_initializer()#创建一个初始化操作,用于初始化所有的变量
        sess.run(init_op)#执行初始化操作,给所有的变量赋予初始值
        summary_writer = tf.summary.FileWriter('../logs/', sess.graph)
        #创建一个summary_writer对象,用于将计算图和汇总信息写入日志文件,方便在TensorBoard中可视化
        nepoch=[]#创建一个空列表,用于存储训练的轮数
        trainacc=[]#创建一个空列表,用于存储训练集的准确率
        testacc=[]#创建一个空列表,用于存储测试集的准确率
        loss1=[] #创建一个空列表,用于存储训练集的损失值
        for i in range(0, 10001):#训练轮数
            sess.run(optimzer, feed_dict={x: train, y: target, keep: 0.5})
            #执行优化器的minimize操作(梯度下降法之类的,之前定义的optimzer),更新模型的参数,feed_dict参数表示给占位符提供输入数据,x是训练集的特征,y是训练集的标签,keep是dropout的保留率,设置为0.5表示每个神经元有50%的概率被保留
            train_summary = sess.run(merge_summary, feed_dict={x: train, y: target, keep: 1})
            #汇总信息
            summary_writer.add_summary(train_summary, i)
            #汇总信息
            if i % 50 == 0:
                accu = sess.run(acc, feed_dict={x: train, y: target, keep: 1})#训练集准确率
                accuT = sess.run(acc, feed_dict={x: test, y: test_target, keep: 1})#测试集准确率
                losss = sess.run(loss, feed_dict={x: train, y: target, keep: 1})#损失值

                print("epoch:" + str(i) + "   train_acc:" + str(accu) + "   test_acc:" + str(accuT) + "   loss:" + str(
                    losss))

                nepoch.append(i)
                trainacc.append(accu)
                testacc.append(accuT)
                loss1.append(losss)

        plt.title("BP神经网络训练性能曲线")
        plt.xlabel('训练次数')
        plt.ylabel('训练样本和检验样本的准确度')
        plt.plot(nepoch,trainacc,nepoch,testacc)#绘制两条曲线,分别表示训练集和测试集的准确率随训练次数的变化。
        plt.plot(nepoch,trainacc,nepoch,loss1)#绘制两条曲线,分别表示训练集的准确率和损失值随训练次数的变化
        plt.show()
        saver.save(sess, '../model/bank.ckpt', global_step=i)
        #保存模型的参数到’./model/bank.ckpt’文件中,并在文件名后面添加当前的训练步数
    #使用训练好的模型参数文件进行预测,预测时用不到y,所以y其实可以省略,具体用不用到得看使用的函数用没用到y
    else:
        f = open("../result/target.txt", "w")
        model_file = tf.train.latest_checkpoint('../model/')#saver.save保存的路径
        saver.restore(sess, model_file)#使用saver对象恢复模型的参数,需要一个session对象和一个模型文件的路径,这里使用sess和model_file作为参数
        tar = sess.run(h3, feed_dict={x: test, y: test_target, keep: 1})
        #h3为模型输出层,由此可以得到模型预测值,y: test_target可以去掉,因为预测时候用不到
        tar = sess.run(tf.argmax(tar, 1))
        for i in range(0, len(tar)):
            f.write(str(tar[i]) + " ")
        f.close()
        print(tar)#显示预测结果





2.决策树数据分析代码

代码如下:

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from graphviz import Source
#Graphviz是一个开源的图形可视化软件,它可以将数据以图形的方式显示出来,通常用于绘制决策树、流程图、组织结构图等
from sklearn.tree import export_graphviz
import os
from sklearn.metrics import roc_curve #导入ROC曲线函数
import matplotlib.pyplot as plt #导入作图库
from sklearn.metrics import confusion_matrix #导入混淆矩阵函数
from sklearn.model_selection import StratifiedKFold
from sklearn import svm
from sklearn.neural_network import MLPClassifier

# #第一步,特征数值化
# #由于原始数据集数据有非数值型比如地址等等,因此需要对这些特征数值化,函数如下:
# def quantification(dataPath,outputPath):
#     df=pd.read_csv(dataPath)#使用pandas库读取位于dataPath路径下的CSV文件
#     x=pd.factorize(df['Geography'])
#     y=pd.factorize(df['Gender'])
#     #使用pd.factorize()函数对Geography和Gender两列进行数值化处理。数值化是将分类标签(如字符串)转换为数值表示
#     #pd.factorize()函数返回两个值:一个是数值处理后的数组,另一个是原始标签的数组。
#     df['Geography']=x[0]
#     df['Gender']=y[0]
#     #我们使用了数值化后的数组(x[0]和y[0])替代原来的分类标签数据
#     df.to_csv(outputPath,index=True)#保存修改数据到新的地址,可通过index来决定是否保存索引到文件中
# quantification("../data/Churn-Modelling-new.csv","../data/Churn-Modelling-newT.csv")#调用特征数值化函数,两个参数是原始数据地址和特征数值化后的地址
#
# #第二步:数据离散化处理
# #由于决策树需要的是离散型数据,而原本的数据集中比如年龄,收入等是连续型数据,因此如果想要处理,需要转化为离散型数据
# #具体如何离散化,每个特征列分为几等分根据实际情况判断,如下函数:
# def discretization(dataPath,outputPath):
#     df=pd.read_csv(dataPath)
#     CreditScore=[];Age=[];Balance=[];EstimatedSalary=[];Exited=[];temp=[]
#     # 信用分数划分成四等份,根据计算设置为:584,652,718
#     for i in range(len(df)):
#         if df["CreditScore"][i]<584:
#             CreditScore.append(0)#赋值为 0
#         elif df["CreditScore"][i]<652:
#             CreditScore.append(1)#赋值为 1
#         elif df["CreditScore"][i]<718:
#             CreditScore.append(2)#赋值为 2
#         else:
#             CreditScore.append(3)#赋值为 3
#     df["CreditScore"]=CreditScore #替代原数据
#     #年龄
#     for i in range(len(df)):
#         if df["Age"][i]<=20:
#             Age.append(0)
#         elif df["Age"][i]<=40:
#             Age.append(1)
#         else:
#             Age.append(2)
#     df["Age"]=Age
#     #存款情况,存款为0的单独列出来
#     for i in range(len(df)):
#         if (df["Balance"][i]!=0):
#             temp.append(df["Balance"][i])
#     temp.sort()
#     q1=temp[len(temp)//4]#对一个列表(或其他类似的可索引对象)temp进行索引操作。这里的索引值是 len(temp) // 4,即列表长度的四分之一(向下取整)
#     q2 = temp[len(temp) // 4*2]
#     q3 = temp[len(temp) // 4*3]
#     for i in range(len(df)):
#         if df["Balance"][i]==0:
#             Balance.append(0)
#         elif df["Balance"][i]<q1:
#             Balance.append(1)
#         elif df["Balance"][i]<q2:
#             Balance.append(2)
#         elif df["Balance"][i]<q3:
#             Balance.append(3)
#         else:
#             Balance.append(4)
#     df["Balance"]=Balance
#     temp.clear()
#     #估计收入
#     for i in range(len(df)):
#         temp.append(df["EstimatedSalary"][i])
#     temp.sort()
#     q1=temp[len(temp)//4]#对一个列表(或其他类似的可索引对象)temp进行索引操作。这里的索引值是 len(temp) // 4,即列表长度的四分之一(向下取整)
#     q2 = temp[len(temp) // 4*2]
#     q3 = temp[len(temp) // 4*3]
#     for i in range(len(df)):
#         if df["EstimatedSalary"][i]<q1:
#             EstimatedSalary.append(0)
#         elif df["EstimatedSalary"][i]<q2:
#             EstimatedSalary.append(1)
#         elif df["EstimatedSalary"][i]<q3:
#             EstimatedSalary.append(2)
#         else:
#             EstimatedSalary.append(3)
#     df["EstimatedSalary"]=EstimatedSalary
#     temp.clear()
#     df.to_csv(outputPath,index=True)
# discretization("../data/Churn-Modelling-newT.csv","../data/Churn-Modelling-new-tree.csv")#处理后保存文件位置
#
#
# #第三步:数据筛选
# #删除无用的特征列,比如编号等等,并筛选数据用欠采样解决类别不均衡问题,它从原始数据集中选取一定数量的样本,使得在输出的数据集中
# # 代码如下函数所示:
# def filtering(dataPath, outputPath):
#     df = pd.read_csv(dataPath)
#     df_new = pd.DataFrame(
#         columns=['Geography', 'EB', 'Age', 'EstimatedSalary', 'NumOfProducts', 'CreditScore', 'Tenure', 'HasCrCard','IsActiveMember', 'Exited', 'Gender'])
#     ones = sum(df["Exited"])#只有0和1,因此是类别为1的数量,计算输入数据中流失客户的数量,即标签为 1 的样本数量
#     length = len(df["EstimatedSalary"])# 计算输入数据中总的样本数量
#     zeros = length - ones# 计算输入数据中未流失客户的数量,即标签为 0 的样本数量
#     i = 0# 初始化一个循环变量,用于遍历输入数据的每一行
#     flag_0 = 0
#     flag_1 = 0
#     # 初始化两个计数变量,用于记录输出数据中已经添加的流失和未流失客户的数量
#     # 当循环变量小于总的样本数量时,执行循环
#     while i != length:
#         if df["Exited"][i] == 0 and flag_1 < ones:# 如果当前行的标签为 0,即未流失客户,且输出数据中已添加的未流失客户数量小于流失客户数量
#             df_new = df_new.append(pd.DataFrame(
#                 {'Gender': df["Gender"][i], 'Geography': df["Geography"][i], 'EB': df["EB"][i], 'Age': df["Age"][i],'EstimatedSalary': df["EstimatedSalary"][i], 'NumOfProducts': df["NumOfProducts"][i],'CreditScore': df["CreditScore"][i], 'Tenure': df["Tenure"][i], 'HasCrCard': df["HasCrCard"][i],'IsActiveMember': df["IsActiveMember"][i], 'Exited': df["Exited"][i]}, index=[i]))
#             # 将当前行的数据添加到输出数据中
#             # 将输出数据中已添加的未流失客户数量加1
#             flag_1 = flag_1 + 1
#         # 如果当前行的标签为 1,即流失客户,且输出数据中已添加的流失客户数量小于未流失客户数量,具体步骤同上
#         if df["Exited"][i] == 1 and flag_0 < zeros:
#             df_new = df_new.append(pd.DataFrame(
#                 {'Gender': df["Gender"][i], 'Geography': df["Geography"][i], 'EB': df["EB"][i], 'Age': df["Age"][i],'EstimatedSalary': df["EstimatedSalary"][i], 'NumOfProducts': df["NumOfProducts"][i],'CreditScore': df["CreditScore"][i], 'Tenure': df["Tenure"][i], 'HasCrCard': df["HasCrCard"][i],'IsActiveMember': df["IsActiveMember"][i], 'Exited': df["Exited"][i]}, index=[i]))
#             flag_0 = flag_0 + 1
#         i = i + 1
#     # 将输出数据保存为 csv 文件,路径为参数指定的输出路径
#     df_new.to_csv(outputPath)
# # 调用函数,传入输入数据和输出数据的路径
# filtering("../data/Churn-Modelling-new-tree.csv","../data/final.csv")


# 划分训练集及测试集
csv=pd.read_csv("../data/final.csv")
csv_array=np.array(csv)
#print(csv.head())
#标签数据为array第11列 Exited
target=csv_array[:,10]
#第1列为编号,对决策树模型无意义,去除,将剩余列作为特征项
feature=csv_array[:,[1,2,3,4,5,6,7,8,9,11]]
#将数据集按4:1分为训练集和测试集
feature_train, feature_test, target_train, target_test = train_test_split(feature, target, test_size=0.2, random_state=10)
#使用train_test_split函数(通常来自sklearn.model_selection模块,因此要引入此包)将数据集分为训练集和测试集。
#test_size=0.2表示测试集占总数据集的20%,剩下的80%作为训练集。
#random_state=10是一个随机种子,确保每次运行代码时数据集的分割方式相同,使得实验结果可复现。
#feature_train和target_train将用于训练模型,而feature_test和target_test将用于评估模型的性能。

#开始数据建模
# 设置决策树参数
dt_model = DecisionTreeClassifier(criterion="gini",max_depth=6,min_samples_split=100)
#criterion="gini":这是用来衡量划分质量的指标。"gini" 表示使用基尼不纯度(Gini impurity)作为划分标准。基尼不纯度是一个衡量集合中随机选中的样本被错误分类的概率的度量。它的值越小,表示集合的纯度越高。
#max_depth=6:这是树的最大深度。当树的深度达到这个值时,树的生长就会停止。限制树的最大深度可以避免过拟合,即模型在训练数据上表现很好,但在未知数据上表现不佳
#min_samples_split=200:这是一个节点在被考虑分裂之前必须具有的最小样本数。如果某个节点的样本数少于这个值,则该节点不会被进一步分裂。这个参数同样可以帮助控制过拟合,因为它防止了模型学习训练数据中的噪声或异常值
dt_model.fit(feature_train,target_train)#导入数据,训练模型
scores = dt_model.score(feature_test,target_test)#测试模型准确度
print(scores)
predict_results = dt_model.predict(feature_test)#测试集根据决策树的预测结果,在已经用数据训练好模型后
print(predict_results)#预测的结果

#创建流程图保存路径
image_path = "../images/decision_trees"
# os.makedirs(image_path, exist_ok=True)

export_graphviz(dt_model,
                out_file=os.path.join(image_path, "bank_tree.dot"),
                feature_names=["Geography", "EB", "Age", "EstimatedSalary", "NumOfProducts", "CreditScore", "Tenure",
                               "HasCrCard", "IsActiveMember", "Gender"],
                class_names=["not exited", "exited"],
                rounded=True,
                filled=True)
#export_graphviz 调用 export_graphviz 函数,该函数用于将训练好的决策树模型 dt_model 导出为 Graphviz 的 DOT 格式
#DOT 格式是一种文本文件格式,用于描述图形结构,可以被 Graphviz 工具读取并渲染成可视化的图形
# dt_model 为训练好的模型
#out_file 指定了导出的DOT文件的保存路径和文件名。
#os.path.join 函数用于拼接目录和文件名,确保在不同操作系统下都能正确生成文件路径。bank_tree.dot 是将要保存的 DOT 文件的名称
#class_names参数 用来确定分类名称
#feature_name参数 指定了决策树中用到的特征名称列表。这些名称将在生成的图形中用于标记每个节点的特征。
#rounded=True 这个参数指定生成的图形中的节点应该是圆角矩形的
#filled=True 这个参数指定生成的图形中的节点应该被填充颜色。通常,节点的颜色会根据其所属的类别进行着色,以便于区分。
s = Source.from_file(os.path.join(image_path, "bank_tree.dot"))
# #这行代码使用 Graphviz 的 Source 类从刚才保存的 DOT 文件创建一个源对象 s。这个对象可以用来进一步操作或渲染图形
# #默认输出为pdf文件,不过也可以设置输出为图片 如.png 等其他格式
s.view()



#绘制ROC曲线 可用来评估模型分类性能
# 绘制的图片曲线下面积越大,代表准确度越高
fpr, tpr, thresholds = roc_curve(target_test, predict_results, pos_label=1)
#使用roc_curve函数从sklearn.metrics库计算ROC曲线的各个点。
#target_test是真实的目标标签(通常是二进制的,例如0和1)。
#predict_results是模型预测的标签结果
#pos_label=1指定正类标签的值为1。
# 函数返回三个值:
# fpr:假正类率(False Positive Rate)tpr:真正类率(True Positive Rate)thresholds:用于计算上述率的阈值
plt.figure(figsize=(10,10))
plt.plot(fpr, tpr, linewidth=2, label = 'ROC curve') #作出ROC曲线
plt.plot([0,1],[0,1],'k--',label='guess')
plt.title("ROC Curve",fontsize=25)
plt.xlabel('False Positive Rate',fontsize=20) #坐标轴标签
plt.ylabel('True Positive Rate',fontsize=20) #坐标轴标签
plt.ylim(0,1.05) #边界范围
plt.xlim(0,1.05) #边界范围
plt.legend(loc=4,fontsize=20) #图例
plt.show() #显示作图结果


#混淆矩阵
cm = confusion_matrix(target_test, predict_results)  # 计算混淆矩阵
plt.figure(figsize=(10, 10))
plt.matshow(cm, fignum=0, cmap=plt.cm.Blues)#plt.cm.Blues是matplotlib中的一个预定义色彩映射,它使用不同深浅的蓝色来表示不同的值
plt.colorbar()  # 颜色标签
for x in range(len(cm)):  # 数据标签
    for y in range(len(cm)):
        plt.annotate(cm[x, y], xy=(x, y), fontsize=30, horizontalalignment='center', verticalalignment='center')
#cm[x, y]: 这是你想要在图上标注的文本内容。在混淆矩阵的上下文中,cm[x, y]表示混淆矩阵中位于第x行第y列的元素值,通常代表实际类别为y而被预测为x的样本数量
#xy=(x, y): 这个参数定义了文本标注的位置。在混淆矩阵的情境中,xy=(x, y)指的是矩阵中第x行第y列单元格的中心位置,这里x和y是矩阵的索引。
#fontsize=30: 这个参数指定了标注文本的大小,单位是点(pt)。在这个例子中,文本大小被设置为30,你可以根据需要调整这个值。
#forizontalalignment='center': 这个参数定义了文本在水平方向上的对齐方式。'center'意味着文本会在其指定的xy坐标位置上水平居中显示。
#verticalalignment='center': 这个参数定义了文本在垂直方向上的对齐方式。'center'意味着文本会在其指定的xy坐标位置上垂直居中显示。
#混淆矩阵上的4个数字,从左到右,从上到下依次是真正例,假正例,真反例,假反例的数量
plt.ylabel('Hypothesized class', fontsize=20)  # 坐标轴标签
plt.xlabel('True class', fontsize=20)  # 坐标轴标签
plt.show()


# 10折交叉验证
skfold = StratifiedKFold(n_splits=10,shuffle=False)#10折交叉验证
x_axis=[] ; y_axis=[]
k=0;max=0;min=100;sum=0
for train_index,test_index in skfold.split(feature,target):#返回的是索引,分别为测试数据索引和训练数据索引
    k+=1
    skfold_feature_train=feature[train_index]
    skfold_feature_test=feature[test_index]
    skfold_target_train=target[train_index]
    skfold_target_test=target[test_index]
    dt_model.fit(skfold_feature_train,skfold_target_train)#训练数据训练模型
    scores = dt_model.score(skfold_feature_test,skfold_target_test)#通过训练数据训练的模型测试准确率
    x_axis.append(k)
    y_axis.append(scores)
    if scores>max:
        max=scores
    if scores<min:
        min=scores
    sum+=scores
avg=sum/k
plt.plot(x_axis,y_axis)
plt.ylim(0.6,0.9)
plt.xlim(1,10)
plt.xlabel("Rounds")
plt.ylabel('True Rate')
plt.title("KFold Cross Validation (k=%s) avg=%s"%(k,round(avg*100,2))+"%"+" max:"+"%s"%(round(max*100,2))+"%"+" min:"+"%s"%(round(min*100,2))+"%")
plt.show()

#5折交叉验证
skfold_5 = StratifiedKFold(n_splits=5,shuffle=False)

x_axis_5=[] ; y_axis_5=[]
k_5=0;max_5=0;min_5=100;sum_5=0
for train_index,test_index in skfold_5.split(feature,target):
    k_5+=1
    skfold_feature_train=feature[train_index]
    skfold_feature_test=feature[test_index]
    skfold_target_train=target[train_index]
    skfold_target_test=target[test_index]
    dt_model.fit(skfold_feature_train,skfold_target_train)
    scores = dt_model.score(skfold_feature_test,skfold_target_test)
    x_axis_5.append(k_5)
    y_axis_5.append(scores)
    if scores>max_5:
        max_5=scores
    if scores<min_5:
        min_5=scores
    sum_5+=scores
avg_5=sum_5/k_5

plt.plot(x_axis_5,y_axis_5)
plt.ylim(0.6,0.9)
plt.xlim(1,5)
plt.xlabel("Rounds")
plt.ylabel('True Rate')
plt.title("KFold Cross Validation (k=%s) avg=%s"%(k_5,round(avg_5*100,2))+"%"+" max:"+"%s"%(round(max_5*100,2))+"%"+" min:"+"%s"%(round(min_5*100,2))+"%")
plt.show()

#15折交叉验证
skfold_15 = StratifiedKFold(n_splits=15,shuffle=False)

x_axis=[] ; y_axis=[]
k=0;max=0;min=100;sum=0
for train_index,test_index in skfold_15.split(feature,target):
    k+=1
    skfold_feature_train=feature[train_index]
    skfold_feature_test=feature[test_index]
    skfold_target_train=target[train_index]
    skfold_target_test=target[test_index]
    dt_model.fit(skfold_feature_train,skfold_target_train)
    scores = dt_model.score(skfold_feature_test,skfold_target_test)
    x_axis.append(k)
    y_axis.append(scores)
    if scores>max:
        max=scores
    if scores<min:
        min=scores
    sum+=scores
avg=sum/k

plt.plot(x_axis,y_axis)
plt.ylim(0.6,0.9)
plt.xlim(1,15)
plt.xlabel("Rounds")
plt.ylabel('True Rate')
plt.title("KFold Cross Validation (k=%s) avg=%s"%(k,round(avg*100,2))+"%"+" max:"+"%s"%(round(max*100,2))+"%"+" min:"+"%s"%(round(min*100,2))+"%")
plt.show()


#使用SVM进行分类
clf=svm.SVC()#创建一个支持向量机(Support Vector Machine, SVM)分类器的实例
clf.fit(feature_train,target_train)
clf.predict(feature_test)

#%%

scores_svm = clf.score(feature_test,target_test)
print(scores_svm)#支撑向量机的准确率

cm = confusion_matrix(target_test, clf.predict(feature_test))  # 混淆矩阵

plt.figure(figsize=(10, 10))
plt.matshow(cm, fignum=0, cmap=plt.cm.Blues)
plt.colorbar()  # 颜色标签
for x in range(len(cm)):  # 数据标签
    for y in range(len(cm)):
        plt.annotate(cm[x, y], xy=(x, y), fontsize=30, horizontalalignment='center', verticalalignment='center')

plt.ylabel('Hypothesized class', fontsize=20)  # 坐标轴标签
plt.xlabel('True class', fontsize=20)  # 坐标轴标签
plt.show()

# 使用MPL(多层感知机)神经网络进行分类
mlp = MLPClassifier(solver='lbfgs', alpha=1e-5,hidden_layer_sizes=(10, 11,10), random_state=1)
#创建一个MLPClassifier的实例
# solver='lbfgs': 指定优化算法为'lbfgs'。'lbfgs'是一种优化算法,用于找到损失函数的最小值。
# alpha=1e-5: L2惩罚(正则化项)的参数。
# hidden_layer_sizes=(10, 11): 指定隐藏层的神经元数量。这里有两层隐藏层,第一层有10个神经元,第二层有11个神经元。
# random_state=1: 随机数生成器的种子,确保每次运行代码时得到的结果是可复现的。
mlp.fit(feature_train,target_train)#使用训练数据feature_train和对应的标签target_train来训练多层感知机分类器。
mlp.predict(feature_test)#使用训练好的模型进行预测
scores_mlp = mlp.score(feature_test,target_test)#计算准确率
print(scores_mlp)#输出算法准确率

cm = confusion_matrix(target_test, mlp.predict(feature_test))  # 混淆矩阵

plt.figure(figsize=(10, 10))
plt.matshow(cm, fignum=0, cmap=plt.cm.Blues)
plt.colorbar()  # 颜色标签
for x in range(len(cm)):  # 数据标签
    for y in range(len(cm)):
        plt.annotate(cm[x, y], xy=(x, y), fontsize=30, horizontalalignment='center', verticalalignment='center')

plt.ylabel('Hypothesized class', fontsize=20)  # 坐标轴标签
plt.xlabel('True class', fontsize=20)  # 坐标轴标签
plt.show()

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值