现在的深度学习模型能够借助一些功能十分强大的集成框架(tensorflow,pytorch,keras,caffe)很轻松的实现。但自己动手写一个BP神经网络,亲手实现网络的前向传播,计算损失,链式法则逐层求导,更新模型参数还是很有意义的,对网络训练也会有更加深刻的理解。同时,为了让初学者能够轻松的理解代码的含义,我为每一行代码都加了详细的注解。PS:转载请注明本文链接。
1.数据集下载地址
2.相关资源
我的另一篇博文是利用朴素贝叶斯实现相同任务,感兴趣的同学可以去看看。
3.代码运行结果
先放一个代码运行完的曲线图,证明这个代码是可以运行的。
4.实现代码
# 导入程序依赖包
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import time
from sklearn.decomposition import PCA
from mpl_toolkits.mplot3d import Axes3D
def relu(Z):
"""
功能:
relu激活函数,网络前向传播的非线性计算部分,在网络中的前L-1层使用
输入参数:
Z - 线性运算后的值
返回值:
A - 神经元激活值
cache - 激活函数数据缓存区,为反向传播提供数据
"""
A = np.maximum(0, Z) # 将Z中所有负数置0
cache = Z # 将Z存入缓存区
return A, cache
def relu_backward(dA, cache):
"""
功能:
relu激活函数值的反向传播
输入参数:
dA - 神经元激活值的导数
cache - 数据缓存区,为反向传播提供数据
返回值:
dZ - 线性运算部分的导数,用来更新神经元权重和偏置
"""
Z = cache # 从缓存区中取出Z
dZ = np.array(dA, copy=True) # 将dA进行复制,并转换为array赋值给dZ
dZ[Z <= 0] = 0 # 将dZ中负数部分置0
return dZ
def softmax(Z):
"""
功能:
softmax激活函数,网络前向传播的非线性计算部分,在网络中的第L层,也就是最后一层使用,实现多分类任务
输入参数:
Z - 线性运算后的值
返回值:
A - 神经元激活值
cache - 激活函数数据缓存区,为反向传播提供数据
"""
A = np.exp(Z) / (np.sum(np.exp(Z), axis=0)) # 计算激活值
cache = Z # 将Z存入缓存区
return A, cache
def load_datasets(filepath):
"""
功能:
导入数据,并将标签集进行one-hot编码
输入参数:
filepath - 数据文件路径
返回值:
feature - 特征集,一个n*m矩阵,n为特征维度,m为样本数
label - 标签集,一个4*m的one-hot矩阵,4为标签维度,m为样本数
"""
dataset = pd.read_csv(filepath) # 读取csv文件,数据类型为DataFrame
dataset['class'] = dataset['class'].map({"s ": 0, "h ": 1, "d ": 2, "o ": 3}) # 将字符转换为数值,注意字符后面的空格不能去掉,这是一个坑
label = dataset.loc[:, ['class']] # 读取标签,数据类型为DataFrame
label = np.array(label) # 转换为array
label = np.eye(4).reshape(dataset.shape[0], 4).T # 将array转换为one-hot
feature = dataset.iloc[:, 1:dataset.shape[1]] # 将特征数据分割出来
feature = np.array(feature).T # 转换为array
return feature, label
def decoding(labels_one_hot):
"""
功能:
进行one_hot解码,将one_hot矩阵还原为原始标签
输入参数:
labels_one_hot - 标签集,一个m*4的one-hot矩阵,m为样本数,4为标签维度
返回值:
label - 标签集,一个m*1的数据帧,m为样本数,元素为字符类别
"""
labels = labels_one_hot.argmax(axis=1).reshape(labels_one_hot.shape[0], -1) # 按行提取矩阵中最大的元素的索引
labels = pd.DataFrame(labels) # 将array转换为dataframe
labels.columns = ['class'] # 设置帧头
labels['class'] = labels['class'].map({0: "s ", 1: "h ", 2: "d ", 3: "o "}) # 将数字类别还原为原始的字符类别
return labels
def random_dataset(train_x, train_y, test_x, test_y, seed):
"""
功能:
打乱训练集和测试集数据,使数据同分布
输入参数:
train_x - 训练特征集,一个n*m1矩阵,n为特征维度,m1为训练样本数
train_y - 训练标签集,一个4*m1的one-hot矩阵,4为标签维度,m1为训练样本数
test_x - 测试特征集,一个n*m2矩阵,n为特征维度,m2为测试样本数
test_y - 测试标签集,一个4*m2的one-hot矩阵,4为标签维度,m2为测试样本数
seed - 程序随机种子
返回值:
new_train_x.T - 打乱后的训练特征集,一个n*m1矩阵,n为特征维度,m1为训练样本数
new_train_y.T - 打乱后的训练标签集,一个4*m1的one-hot矩阵,4为标签维度,m1为训练样本数
new_test_x.T - 打乱后的测试特征集,一个n*m2矩阵,n为特征维度,m2为测试样本数
new_test_y.T - 打乱后的测试标签集,一个4*m2的one-hot矩阵,4为标签维度,m2为测试样本数
"""
if seed > 0:
np.random.seed(seed) # 随机种子大于0,就打乱数据集
m = train_x.shape[1] # 训练样本数
new_x = np.concatenate((train_x.T, test_x.T), axis=0) # 将特征集进行拼接
new_y = np.concatenate((train_y.T, test_y.T), axis=0) # 将标签集进行拼接
per = np.random.permutation(new_x.shape[0]) # 打乱特征集行号
new_x = new_x[per, :] # 获取打乱后的特征数据
new_y = new_y[per, :] # 获取打乱后的标签数据
new_train_x, new_test_x = np.vsplit(new_x, [m]) # 按原训练样本数进行分割
new_train_y, new_test_y = np.vsplit(new_y, [m]) # 按原训练样本数进行分割
else:
new_train_x, new_train_y, new_test_x, new_test_y = train_x, train_y, test_x, test_y # 随机种子小于零就不打乱数据集
return new_train_x.T, new_train_y.T, new_test_x.T, new_test_y.T
def data_preprocessing(features):
"""
功能:
进行特征集预处理,对特征集进行归一化,返回均值和方差
输入参数:
features - 特征集,一个n*m的矩阵,n为特征维度,m为样本数
返回值:
features - 经过归一化的特征集,一个n*m的矩阵,n为特征维度,m为样本数
mu - 特征集均值
sigma2 - 特征集方差
"""
mu = np.mean(features, axis=1).reshape(features.shape[0], -1) # 计算特征集均值
features -= mu # 对特征集进行零均值
sigma2 = np.var(features, axis=1).reshape(features.shape[0], -1) # 计算特征集方差
features /= sigma2 # 对特征集方差进行归一化
return features, mu, sigma2
def initialize_parameters(layers_dims, seed):
"""
功能:
按照输入的神经网络结构进行权值抑梯度异常初始化,偏置零初始化
输入参数:
layers_dims - 列表,表示神经网络的结构
seed - 随机种子
返回值:
parameters - 参数字典,包含进行初始化后的权值和偏置
"""
seed = max(seed, 1) # 随机种子要大于0
np.random.seed(seed) # 为了实验能够复现,固定随机种子
parameters_init = {} # 初始化参数字典
L = len(layers_dims) # 网络层数
for l in range(1, L):
parameters_init["W" + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) / np.sqrt(
layers_dims[l - 1]) # 对网络权重进行抑梯度异常初始化
parameters_init["b" + str(l)] = np.zeros((layers_dims[l], 1)) # 对网络偏置进行零初始化
return parameters_init
def linear_forward(A, W, b):
"""
功能:
网络前向传播的线性计算部分
输入参数:
A - 上一层神经元的激活值
W - 本层神经元的权重
b - 本层神经元的偏置
返回值:
Z - 线性运算后的值
cache - 单层神经网络前向传播线性部分数据缓存区,元组,包含输入参数的值,在该部分的反向传播会用到
"""
Z = np.dot(W, A) + b # 进行矩阵乘法
cache = (A, W, b) # 将输入参数存入缓冲区
return Z, cache
def linear_activation_forward(A_prev, W, b):
"""
功能:
单层网络的前向传播
输入参数:
A_prev - 上一层神经元的激活值
W - 本层神经元的权重
b - 本层神经元的偏置
返回值:
A - 本层神经元的激活值
cache - 单层神经网络前向传播数据缓存区,元组,包含线性部分和激活函数部分的数据
"""
Z, linear_cache = linear_forward(A_prev, W, b) # 本层神经元的线性运算
A, activation_cache = relu(Z) # 本层神经元的激活函数运算
cache = (linear_cache, activation_cache) # 将本层的相关参数存入缓存区
return A, cache
def model_forward(X, parameters, keep_prob):
"""
功能:
实现神经网络的前向传播
输入参数:
X - 样本
parameters - 神经网络的参数
keep_prob - dropout正则化的阈值
返回值:
AL - 神经网络对样本的类别预测概率
caches - 前向传播数据缓存区,元组,包含前向传播的所有中间参数,会在反向传播用到
"""
caches = [] # 开辟数据缓存区
# np.random.seed() # 取消随机种子效果,实现dropout,其实也可以注释掉,同样有正则化的效果,并且结果能够复现
L = len(parameters) // 2 # 计算神经网络层数
# 神经网络第一层,输入层的激活值为输入样本
A = X
# 神经网络的隐藏层[1,L-1]为Liner + relu
for l in range(1, L):
A_prev = A # 本层的输入值为上一层的激活值
A, cache = linear_activation_forward(A_prev, parameters['W' + str(l)], parameters['b' + str(l)]) # 计算一层网络的前向传播
# dropout正则化,防止网络过拟合
D = np.random.rand(A.shape[0], A.shape[1]) # 按照激活值的大小随机初始化矩阵dropout矩阵D
D = D < keep_prob # 使用keep_prob作为阈值,将D矩阵中大于keep_prob的元素值置0
A = A * D # 舍弃A的一些节点(D中为0的节点将被舍弃)
A = A / keep_prob # 缩放未舍弃的节点(不为0)的值
D_cache = (D, cache) # 保存dropout矩阵D
caches.append(D_cache) # 将dropout矩阵D和每一层的数据缓存区存入前向缓存区
# 神经网络的输出层为Liner + softmax
Z, linear_cache = linear_forward(A, parameters['W' + str(L)], parameters['b' + str(L)]) # 输出层的线性计算
AL, activation_cache = softmax(Z) # 输出层的softmax激活函数计算
cache = (linear_cache, activation_cache) # 保存输出层的中间参数
caches.append(cache) # 将输出层的中间参数存入前向缓存区
return AL, caches
def compute_cost(AL, Y):
"""
功能:
计算网络交叉熵损失值
输入参数:
AL - 神经网络对样本的类别预测概率
Y - 样本的真实标签,一个4*m的one_hot矩阵
返回值:
cost - 交叉熵损失值
"""
m = Y.shape[1] # 样本数
cost = -np.sum(np.multiply(np.log(AL), Y)) / m # 计算交叉熵
cost = np.squeeze(cost) # 数据降维,将array变为标量
return cost
def linear_backward(dZ, cache):
"""
功能:
网络线性运算的反向传播
输入参数:
dZ - 本层网络激活函数反向传播的输出
cache - 线性缓存区,存有前向传播的相关数据
返回值:
dA_prev - 上一层网络激活函数的输入
dW - 本层网络权值的梯度
db - 本层网络偏置的梯度
"""
A_prev, W, b = cache # 从缓存区中取得数据
m = A_prev.shape[1] # 上一层网络的神经元个数
dW = np.dot(dZ, A_prev.T) / m # 计算权值梯度
db = np.sum(dZ, axis=1, keepdims=True) / m # 计算偏置梯度
dA_prev = np.dot(W.T, dZ) # 计算反向传播时上一层网络激活函数的输入
return dA_prev, dW, db
def linear_activation_backward(dA, cache):
"""
功能:
网络单层反向传播
输入参数:
dA - 本层网络激活函数反向传播的输入
cache - 线性缓存区,存有前向传播的相关数据
返回值:
dA_prev - 上一层网络激活函数的输入
dW - 本层网络权值的梯度
db - 本层网络偏置的梯度
"""
linear_cache, activation_cache = cache # 取得相关数据
dZ = relu_backward(dA, activation_cache) # 激活函数反向传播
dA_prev, dW, db = linear_backward(dZ, linear_cache) # 线性部分反向传播
return dA_prev, dW, db
def model_backward(AL, Y, caches, keep_prob):
"""
功能:
神经网络模型的反向传播
输入参数:
AL - 神经网络对样本的类别预测概率矩阵
Y - 样本的真实标签,一个4*m的one_hot矩阵
caches - 前向传播数据缓存区
keep_prob - dropout正则化的阈值
返回值:
grads - 用于更新权值和偏置的梯度
"""
grads = {} # 初始化梯度字典
L = len(caches) # 计算网络层数
Y = Y.reshape(AL.shape) # 匹配预测矩阵和真实标签的维度
# 输出层linear + softmax的反向传播
current_cache = caches[L - 1] # 取得输出层缓冲区
linear_cache, activation_cache = current_cache # 取得输出层的前向数据
dZ = AL - Y # 损失函数对softmax输入的导数
A_prev, W, b = linear_cache # 取得输出层的前向数据
m = A_prev.shape[1] # 前一层神经元个数
dW = np.dot(dZ, A_prev.T) / m # 输出层的权重梯度
db = np.sum(dZ, axis=1, keepdims=True) / m # 输出层的偏置梯度
dA_prev = np.dot(W.T, dZ) # 计算前一层的网络激活函数的输入
grads["dA" + str(L)], grads["dW" + str(L)], grads["db" + str(L)] = dA_prev, dW, db # 将梯度存入字典
# 隐藏层到输入层[L-1,0]linear + relu的反向传播
for l in reversed(range(L - 1)):
D, current_cache = caches[l] # 取出数据
grades = grads["dA" + str(l + 2)] # 传递输出层的梯度
# dropout正则化的反向传播
grades = grades * D # 使用正向传播期间相同的节点,舍弃那些关闭的节点
grades = grades / keep_prob # 缩放未舍弃的节点(不为0)的值
dA_prev_temp, dW_temp, db_temp = linear_activation_backward(grades, current_cache) # 当前层的反向传播
grads["dA" + str(l + 1)] = dA_prev_temp # 存入当前层的激活值梯度
grads["dW" + str(l + 1)] = dW_temp # 存入当前层的权值梯度
grads["db" + str(l + 1)] = db_temp # 存入当前层的偏置梯度
return grads
def update_parameters(parameters, grads, learning_rate):
"""
功能:
更新神经网络模型的参数
输入参数:
parameters - 神经网络模型的参数
grads - 参数梯度字典
learning_rate - 学习率
返回值:
parameters - 更新后的参数
"""
L = len(parameters) // 2 # 神经网络层数
for l in range(L):
parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * grads["dW" + str(l + 1)] # 更新权值
parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * grads["db" + str(l + 1)] # 更新偏置
return parameters
def predict(X, Y, parameters):
"""
功能:
预测输入样本的种类,并根据真实标签计算准确率
输入参数:
X - 输入样本,n*m矩阵,n为特征维度,m为样本数
Y - 真实标签,4*m的one-hot矩阵,4为标签种类数,m为样本数
parameters - 神经网络模型的参数
返回值:
acc - 预测准确率,标量
Y_pre - 输入样本的预测种类,4*m矩阵
"""
L = len(parameters) // 2 # 神经网络层数
A = X # 输入层的激活值
# 隐藏层的前向传播[1,L-1],linear + relu
for l in range(1, L):
A_prev = A # 本层神经元的激活值为下一层神经元的输入
A, cache = linear_activation_forward(A_prev, parameters['W' + str(l)], parameters['b' + str(l)]) # 本层神经元的前向传播
# 输出层的前向传播,linear + softmax
Z, linear_cache = linear_forward(A, parameters['W' + str(L)], parameters['b' + str(L)]) # 输出层的前向传播线性部分
Y_pre, caches = softmax(Z) # softmax激活函数
Y_pre = list(map(lambda x: x == max(x), Y_pre.T)) * np.ones(shape=Y_pre.T.shape) # 将预测矩阵转换为one-hot,m*4矩阵
Y_pre = Y_pre.T # 将预测矩阵转置为4*m矩阵
acc = np.sum(Y * Y_pre) / Y.shape[1] # 计算预测准确率
return acc, Y_pre.T
def model(train_features, train_labels, test_features, test_labels, layers_dims, seed, learning_rate,
num_iterations, keep_prob, print_cost_acc, is_plot):
"""
功能:
神经网络模型的搭建和训练
输入参数:
train_features - 训练样本的特征,n1*m1矩阵,n1为特征维度,m1为样本数
train_labels - 训练样本的真实标签,4*m1的one-hot矩阵,4为标签种类数,m1为样本数
test_features - 测试样本的特征,n2*m2矩阵,n2为特征维度,m2为样本数
test_labels - 测试样本的真实标签,4*m2的one-hot矩阵,4为标签种类数,m2为样本数
layers - 网络结构
seed - 随机种子
learning_rate - 学习率
num_iterations - 迭代次数
keep_prob - dropout正则化阈值
print_cost_acc - 是否打印损失和准确率
is_plot - 是否绘图
返回值:
parameters - 神经网络模型训练好的参数
"""
iters = [] # 记录迭代次数
costs = [] # 记录交叉熵损失值
acc_trainings = [] # 记录训练集准确率
acc_testings = [] # 记录测试集准确率
parameters = initialize_parameters(layers_dims, seed) # 抑梯度异常随机初始化
for i in range(0, num_iterations):
AL, caches = model_forward(train_features, parameters, keep_prob) # 前向传播
cost = compute_cost(AL, train_labels) # 计算交叉熵损失
grads = model_backward(AL, train_labels, caches, keep_prob) # 误差反向传播
parameters = update_parameters(parameters, grads, learning_rate) # 更新参数
# 打印成本值,如果print_cost_acc=False则忽略
if i % 100 == 0:
# 记录数值
iters.append(i) # 记录迭代次数
costs.append(cost) # 记录损失值
acc_training, label_pre = predict(train_features, train_labels, parameters) # 训练集预测
acc_testing, label_pre = predict(test_features, test_labels, parameters) # 测试集预测
acc_trainings.append(acc_training) # 记录训练集准确率
acc_testings.append(acc_testing) # 记录测试集准确率
# 是否打印训练过程中的值
if print_cost_acc:
print("第", i, "次迭代,成本值为:", np.squeeze(cost), "训练集准确率为:", np.squeeze(acc_training), "测试集准确率为:",
np.squeeze(acc_testing))
# 迭代完成,绘图,is_plot=False则忽略
if is_plot:
plt.plot(np.squeeze(costs), color='black', label='cost', linewidth=0.8)
plt.plot(np.squeeze(acc_trainings), color='blue', label='acc_training', linewidth=0.8)
plt.plot(np.squeeze(acc_testings), color='red', label='acc_testing', linewidth=0.8)
plt.legend(loc='upper right')
plt.xlabel('iterations (per hundred)')
plt.title("layers:" + str(layers) + " Seed:" + str(seed) + " Lr:" + str(learning_rate) + " Iters:" + str(
num_iterations) + " Kp:" + str(keep_prob))
plt.show()
return parameters
def PCA_plot(feature, label, dimensionality=3):
"""
功能:
将输入的数据经过PCA降为二维后,画出种类散点图,方便进行数据分析
输入参数:
feature - 特征,一个n*m矩阵,n为特征维度,m为样本数
label - 标签,一个4*m的one-hot矩阵,4为标签维度,m为样本数
返回值:
无
"""
# PCA降维
pca = PCA(n_components=dimensionality) # 设置维度
feature_reduction = pca.fit_transform(feature.T) # 特征数据降维
# print(pca.explained_variance_ratio_)#打印所保留的各个特征的方差百分比,二维可以保留87%信息,三维可以保存91%信息
label = label.T # 将标签转置为m*4的矩阵
label = label.argmax(axis=1).reshape(label.shape[0], -1) # 取出每一行中最大元素的索引
# 画二维图
if dimensionality == 2:
# 将特征和标签按列进行拼接
data = np.concatenate((label, np.zeros((label.shape[0], label.shape[1]))), axis=1) # 为了能够拼接矩阵想要填充0数据
data = np.concatenate((feature_reduction, data), axis=1) # 按列将特征和标签进行拼接
# 将矩阵转化为dataframe,并画图
data = pd.DataFrame(data) # 将矩阵转化为dataframe
data.columns = ['x', 'y', 'class', 'fill'] # 帧头
colors = ['b', 'g', 'r', 'orange'] # 颜色
markers = ['v', 's', '*', 'o'] # 图标
labels = ['s', 'h', 'd', 'o'] # 图标签
for i in range(4):
# 根据类别区分样本坐标
x = data.loc[data['class'] == i]['x']
y = data.loc[data['class'] == i]['y']
plt.scatter(x, y, c=colors[i], marker=markers[i], label=labels[i], cmap='brg', alpha=0.8, linewidth=0.2)
plt.legend(loc='upper right') # 将图标签设置在右上角
plt.show() # 显示图形
# 画三维图
elif dimensionality == 3:
# 将特征和标签按列进行拼接
data = np.concatenate((label, np.zeros((label.shape[0], label.shape[1] + 1))), axis=1) # 为了能够拼接矩阵想要填充0数据
data = np.concatenate((feature_reduction, data), axis=1) # 按列将特征和标签进行拼接
# 将矩阵转化为dataframe,并画图
data = pd.DataFrame(data) # 将矩阵转化为dataframe
data.columns = ['x', 'y', 'z', 'class', 'fill1', 'fill2'] # 帧头
colors = ['b', 'g', 'r', 'orange'] # 颜色
markers = ['v', 's', '*', 'o'] # 图标
labels = ['s', 'h', 'd', 'o'] # 图标签
ax = plt.axes(projection='3d') # 三维图
for i in range(4):
# 根据类别区分样本坐标
x = data.loc[data['class'] == i]['x']
y = data.loc[data['class'] == i]['y']
z = data.loc[data['class'] == i]['z']
ax.scatter3D(x, y, z, c=colors[i], marker=markers[i], label=labels[i], cmap='brg', alpha=0.8, linewidth=0.2)
plt.legend(loc='upper right') # 将图标签设置在右上角
plt.show() # 显示图形
else:
print("不符合要求的维度!")
if __name__ == '__main__':
start = time.time() # 记录当前时间
seed = 3 # 程序随机种子
# 训练文件和测试文件路径
TrainingFilePath = 'training.csv'
TestingFilePath = 'testing.csv'
# 加载训练数据和测试数据
train_x, train_y = load_datasets(TrainingFilePath)
test_x, test_y = load_datasets(TestingFilePath)
# 使训练集和测试集同分布
train_x, train_y, test_x, test_y = random_dataset(train_x, train_y, test_x, test_y, seed)
# PCA_plot(train_x, train_y,dimensionality = 3)#原始训练集绘图
# PCA_plot(test_x, test_y,dimensionality = 3)#原始测试集绘图
# train_x_copy = copy.deepcopy(train_x)# 保存原始训练集特征
# test_x_copy = copy.deepcopy(test_x)# 保存原始测试集特征
# 对训练数据进行预处理,进行归一化
train_x, mu, sigma2 = data_preprocessing(train_x)
# 对测试数据用相同均值和方差进行归一化
test_x = (test_x - mu) / sigma2
# PCA_plot(train_x, train_y,dimensionality = 3)#归一化训练集绘图
# PCA_plot(test_x, test_y,dimensionality = 3)#归一化测试集绘图
# 设置神经网络结构
layers = [27, 21, 15, 9, 4]
# 搭建神经网络,利用训练数据进行训练,在训练过程中利用测试数据进行模型评估
parameters = model(train_x, train_y, test_x, test_y, layers, seed=seed, learning_rate=0.02, num_iterations=40000,
keep_prob=0.8, print_cost_acc=False, is_plot=True)
# 对训练集进行预测,返回测试集准确率和预测类别
print("**************************训练集评估**************************")
acc_training, train_label_pre = predict(train_x, train_y, parameters)
# PCA_plot(train_x_copy, train_label_pre.T,dimensionality = 3)#训练集预测效果
print("训练集准确率为" + str(acc_training))
# print("训练数据预测类别:\n" + str(decoding(train_label_pre)))
# 对测试集进行预测,返回测试集准确率和预测类别
print("**************************测试集评估**************************")
acc_testing, test_label_pre = predict(test_x, test_y, parameters)
# PCA_plot(test_x_copy, test_label_pre.T,dimensionality = 3)#测试集预测效果
print("测试集准确率为" + str(acc_testing))
# print("测试数据预测类别:\n" + str(decoding(test_label_pre)))
print("***************************总体评估***************************")
acc_average = acc_testing * test_x.shape[1] / (test_x.shape[1] + train_x.shape[1]) \
+ acc_training * train_x.shape[1] / (test_x.shape[1] + train_x.shape[1]) # 计算平均准确率
print("平均准确率为" + str(acc_average))
print("程序运行时间为:" + str(time.time() - start))