鸢尾花数据集
from sklearn.datasets import load_iris
X = load_iris().data
y = load_iris().target
print(f'特征空间:{X}')
print(f'目标值:{y}')
特征空间:
[[5.1 3.5 1.4 0.2]
[4.9 3. 1.4 0.2]
[4.7 3.2 1.3 0.2]
[4.6 3.1 1.5 0.2]
[5. 3.6 1.4 0.2]
[5.4 3.9 1.7 0.4]
[4.6 3.4 1.4 0.3]
[5. 3.4 1.5 0.2]
[4.4 2.9 1.4 0.2]
[4.9 3.1 1.5 0.1]
[5.4 3.7 1.5 0.2]
[4.8 3.4 1.6 0.2]
[4.8 3. 1.4 0.1]
[4.3 3. 1.1 0.1]
[5.8 4. 1.2 0.2]
[5.7 4.4 1.5 0.4]
[5.4 3.9 1.3 0.4]
[5.1 3.5 1.4 0.3]
[5.7 3.8 1.7 0.3]
[5.1 3.8 1.5 0.3]
[5.4 3.4 1.7 0.2]
[5.1 3.7 1.5 0.4]
[4.6 3.6 1. 0.2]
[5.1 3.3 1.7 0.5]
[4.8 3.4 1.9 0.2]
[5. 3. 1.6 0.2]
[5. 3.4 1.6 0.4]
[5.2 3.5 1.5 0.2]
…]
目标值:
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2]
神经网络的代码实现
import math
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import minmax_scale
class ANN:
def __init__(self, hidden_num, input_node_num, print_node_num, hidden_node_num, learning_rate):
self.hn = hidden_num # 隐藏层数
self.inn = input_node_num # 输入层节点数
self.pnn = print_node_num # 输出层节点数
self.hnn1 = int(hidden_node_num) # 第一层隐藏层节点数
self.lr = learning_rate # 学习率
@staticmethod
def sigmoid(w, x): # 神经元
return 1 / (1 + np.exp(-np.dot(w, x)))
def one_hidden_FC(self): # 单个隐藏层的全连接(full connected)
wih, Wih, whp, Whp = [], [], [], []
for i1 in range(self.hnn1):
for j1 in range(self.inn):
wi = np.random.rand(1)
wih.append(wi[0])
Wih.append(wih) # 输入层与隐藏层之间的所有权重
for p in range(self.pnn):
for q in range(self.hnn1):
wi = np.random.rand(1)
whp.append(wi[0])
Whp.append(whp) # 隐藏层与输出层之间的所有权重
Wih, Whp = np.array(Wih), np.array(Whp)
return [Wih, Whp]
# def more_hidden_FC(self): # 多个隐藏层的全连接(full connected)
# pass
@staticmethod
def hidden_output_output(Wih, Whp, x): # 隐藏层与输出层的输出值
A, Y = [], []
for i2 in range(len(Wih)):
ai = ANN.sigmoid(Wih[i2], x)
A.append(ai) # 隐藏层的输出值
for j2 in range(len(Whp)):
y = ANN.sigmoid(Whp[j2], A)
Y.append(y) # 输出层的输出值
return [A, Y]
def output_error(self, x, yi): # 输出层的误差 yi:真实值 y:预测值
error_list, y = [], ANN.hidden_output_output(ANN.one_hidden_FC(self)[0], ANN.one_hidden_FC(self)[1], x)[1]
for i3 in range(self.pnn):
error = y[i3] * (1 - y[i3]) * (yi - y[i3])
error_list.append(error)
return error_list # len(error_list) == pnn
def hidden_error(self, Whp, A, oe): # 隐藏层的误差 oe:output_error Whp:隐藏层与输出层之间的权值 A:隐藏层的输出
error, error_list = 0, []
for i4 in range(self.hnn1):
for j in range(self.pnn):
error_part = A[i4] * (1 - A[i4]) * Whp[i4][j] * oe[j]
error += error_part
error_list.append(error)
return error_list
def one_hidden_BP(self, Wih, Whp, x, lr, yi): # 单个隐藏层-反向传播算法(Back Propagation) lr:学习率
whp_new, Whp_new, wih_new, Wih_new = [], [], [], []
Whp_trans, Wih_trans = Whp.transpose(), Wih.transpose()
for i5 in range(self.hnn1):
for j in range(self.pnn):
Whp_trans[i5][j] += lr * ANN.output_error(self, x, yi)[j] * \
ANN.hidden_output_output(ANN.one_hidden_FC(self)[0],
ANN.one_hidden_FC(self)[1], x)[0][i5] # 更新隐藏层与输出层之间的权重
whp_new.append(Whp_trans[i5][j])
Whp_new.append(whp_new)
Whp_new_arr = np.array(Whp_new)
for p in range(self.inn):
for q in range(self.hnn1):
Wih_trans[p][q] += lr * ANN.hidden_error(self, Whp_new_arr,
ANN.hidden_output_output(ANN.one_hidden_FC(self)[0],
ANN.one_hidden_FC(self)[1], x)[0],
ANN.output_error(self, x, yi))[q] * x[p] # 更新输入层与隐藏层之间的权重
wih_new.append(Wih_trans[p][q])
Wih_new.append(wih_new)
Wih_new_arr = np.array(Wih_new)
return [Wih_new_arr.transpose(), Whp_new_arr.transpose()]
# def more_hidden_BP(self): # 多个隐藏层-反向传播算法(Back Propagation)
# return
训练与测试
if __name__ == '__main__':
# 鸢尾花数据预处理
X = load_iris().data
y = load_iris().target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
a, b, c, d = np.array(X_train), np.ones(len(X_train)), np.array(X_test), np.ones(len(X_test))
X_train, X_test, y_train, y_test = np.insert(a, 0, b, axis=1), np.insert(c, 0, d, axis=1), np.array(y_train), np.array(y_test)
print(f'数据集目标真实值:{y_train}')
print(f'测试集目标真实值:{y_test}')
# BP之前的预测值与损失
input_node_num, print_node_num = 5, 5
inn_multi_pnn = input_node_num * print_node_num
hidden_node_num1 = math.sqrt(inn_multi_pnn)
ann = ANN(1, input_node_num, print_node_num, hidden_node_num1, 0.5)
Wih, Whp = ann.one_hidden_FC()[0], ann.one_hidden_FC()[1]
Y_pre_pre = []
for x1 in range(len(X_train)):
y_pre_pre = np.sum(ann.hidden_output_output(Wih, Whp, X_train[x1])[1]) / print_node_num
Y_pre_pre.append(y_pre_pre)
Y_pre_pre = np.array(Y_pre_pre)
print(f'进行BP算法之前的目标预测值:{Y_pre_pre}')
Loss_pre = 0
for s1 in range(len(y_train)):
loss_pre = (1 / 2) * (y_train[s1] - Y_pre_pre[s1]) ** 2
Loss_pre += loss_pre
Loss_pre_aver = Loss_pre / len(y_train)
print(f'进行BP算法之前的平均损失:{Loss_pre_aver}')
# 进入BP更新所有的权重,这里采用随机梯度下降法
count, train_data = 1, np.append(X_train, np.array(y_train)[:, np.newaxis], axis=1)
while count < 200:
lr = ann.lr / count # 缩小学习率
np.random.shuffle(train_data)
X_Train, y_Train = train_data[:, 0:-1], train_data[:, -1]
Wih_zero, Whp_zero = np.zeros_like(Wih), np.zeros_like(Whp)
for i in range(len(X_train)):
Wihna, Whpna = ann.one_hidden_BP(Wih, Whp, X_Train[i], lr, y_Train[i])[0], \
ann.one_hidden_BP(Wih, Whp, X_Train[i], lr, y_Train[i])[1]
Wih_zero += Wihna
Whp_zero += Whpna
Wih, Whp = (Wih_zero / len(X_Train)), (Whp_zero / len(X_Train))
count += 1
# BP之后的预测值与损失
Wih_new, Whp_new, Y_behind_pre = Wih, Whp, []
for x2 in X_train:
y_behind_pre = np.sum(ann.hidden_output_output(Wih_new, Whp_new, x2)[1]) / print_node_num
Y_behind_pre.append(y_behind_pre)
Y_behind_pre = np.array(Y_behind_pre)
print(f'进行BP算法之后的目标预测值:{Y_behind_pre}')
Loss_behind = 0
for s2 in range(len(y_train)):
loss_behind = (1 / 2) * (y_train[s2] - Y_behind_pre[s2]) ** 2
Loss_behind += loss_behind
Loss_behind_aver = Loss_behind / len(y_train)
print(f'进行BP算法之后的平均损失:{Loss_behind_aver}')
# 测试与评估
Y_test_pre = []
for x3 in X_test:
y_test_pre = np.sum(ann.hidden_output_output(Wih_new, Whp_new, x3)[1]) / print_node_num
Y_test_pre.append(y_test_pre)
Y_test_pre = np.array(Y_test_pre)
print(f'测试集的目标预测值:{Y_test_pre}')
test_Loss = 0
for s3 in range(len(y_test)):
test_loss = (1 / 2) * (y_test[s3] - Y_test_pre[s3]) ** 2
test_Loss += test_loss
test_Loss_aver = test_Loss / len(y_test)
print(f'测试集的平均损失:{test_Loss_aver}')
运行结果
数据集目标真实值:
[2 0 2 1 0 1 1 1 1 1 1 2 2 2 0 0 0 0 2 1 0 2 2 0 1 1 0 2 0 1 0 2 2 0 0 2 0
1 1 2 0 1 1 2 2 1 2 2 0 0 2 2 2 2 2 1 1 2 0 2 1 0 2 2 0 2 2 2 2 0 0 2 2 2
1 0 1 0 0 1 0 2 1 0 2 1 1 1 1 2 2 1 1 1 2 0 2 1 1 0 0 0 0 0 0 0 2 0 2 1 1
0 0 1 1 2 2 2 0 2]
测试集目标真实值:
[1 0 0 1 0 2 0 2 1 1 1 2 2 0 1 0 1 2 1 0 1 1 1 0 0 1 1 1 0 0]
进行BP算法之前的目标预测值:
[0.95358437 0.95293946 0.95357806 0.95357565 0.95215735 0.95349937
0.9535721 0.95339961 0.95336813 0.9535622 0.95338073 0.95358438
0.95357384 0.95358075 0.95275053 0.95326864 0.95313547 0.95317084
0.95357977 0.95355051 0.95247272 0.95358561 0.95357275 0.95246984
0.95353004 0.95346372 0.95308123 0.95358242 0.95328152 0.95355286
0.95297633 0.95357651 0.95358199 0.95309172 0.95276685 0.95357728
0.95303167 0.9535244 0.95351466 0.95358512 0.95303296 0.95347749
0.95357594 0.95358712 0.95357949 0.95354221 0.95355623 0.95358596
0.95277082 0.95284331 0.95358211 0.9535851 0.95358672 0.95358851
0.95358242 0.95351957 0.95353949 0.95358442 0.95333449 0.95358136
0.95351939 0.95317617 0.95358138 0.95358546 0.95297613 0.95358165
0.95358455 0.95356967 0.95357384 0.95291504 0.95284283 0.95358587
0.95358291 0.95356937 0.95352678 0.95262859 0.95355589 0.95275914
0.95258763 0.95356351 0.95293526 0.95358712 0.95356571 0.95319253
0.95358763 0.95356097 0.95356243 0.95353473 0.95356407 0.95358475
0.95356977 0.95357479 0.95357103 0.95356173 0.95358679 0.95330895
0.95357682 0.95356577 0.95353947 0.95272987 0.95338016 0.95303203
0.9529777 0.95313444 0.95258472 0.95147526 0.95357467 0.95291091
0.95356875 0.95356211 0.95354819 0.9525402 0.95316707 0.95355547
0.95336063 0.95358038 0.95358808 0.9535511 0.9530053 0.95357283]
进行BP算法之前的平均损失:0.353186539001048
进行BP算法之后的目标预测值:
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
进行BP算法之后的平均损失:0.35000000003129444
测试集的目标预测值:
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
1. 1. 1. 1. 1. 1.]
测试集的平均损失:0.2666666665414888
评估与总结
-
评估
结合上面的运行结果,在训练阶段,将训练数据输入到BP之前和BP之后的神经网络中去,得到的预测值与真实值分别取平均误差,后者比前者略小,说明一开始的神经网络模型在经过BP算法和数据的训练之后取得了成效。但成效甚微,究其原因我认为有一下几点:
(1)代码本身可能存在逻辑上的问题(究竟是什么逻辑问题,我也不知道。我恳请各路大神指点一二,在此评论留言,本人将不胜感激)。
(2)训练次数较少(代码中仅训练了200次,但200次就运行了将近半小时,实在是有点慢,幸好最后的结果满足我的预期)。
(3)因为采用了sigmoid函数,运行过程中可能出现了梯度消失。 -
总结
在进行全连接神经网络的编程之前,通过任老师的讲解,我对神经网络有了一个初步的认识,大致的实现流程也有了。但是,在编程的过程中,许多问题开始浮出水面。
(1)神经网络的输入层的一个节点对应的到底是训练数据的一条数据[x1, x2, x3, x4,…]还是这一条数据[x1, x2, x3, x4,…]中的x1呢? 我的答案是后者
(2)神经网络的输出层为什么会有怎么多节点呢?我之所以有这个疑问,是因为无论是回归还是分类,对应的输出结果都只有一个,而输出层却又怎么多节点,难道输出有多个吗? 我的答案:输出层的所有结点得出的结果求和取平均。
(3)其他问题:权重的表示以及与隐藏层有关的问题。(我遇到的问题实在是太多了,每一个多十分费神,现在想起来都令我头大,这里就不一一列举了。如果有遇到和我类似或者不同的问题的小伙伴欢迎在评论区留言,我们共同探讨,加深理解。)总之,解决完这些问题后,编程也慢慢地步入了正轨。虽然编程问题依旧很多,但好在有惊无险的将程序编出来了。但这仅仅是进军深度学习的第一步,后面还要跟着任老师继续学习卷积神经网络CNN、循环神经网络RNN、长短时记忆网络LSTM等等等等。往后的道路艰难且漫长,正所谓“长路漫漫,唯剑作伴”,深度学习的第一把“剑”便是赢取其他“宝剑”的开始。