文章目录
BP神经网络
- 神经网络
每次根据训练的结果与预想的结果进行误差分析,进而修改权重和偏置,不断迭代,进而得到能和输出结果一致的模型- 梯度下降与反向传播
梯度下降用来寻找损失函数极小值
反向传播用来求解梯度
神经网络训练过程:
训练数据集的时候,神经网络经过前向传播之后,得到了预测值与真实值之间的差距,使用损失函数来表示差距,其大小反映了真实值与预测值之间的差距。经过反向传播与梯度下降,不断减小损失函数的值
一、误差逆传播公式推导
1.1 神经网络的结构
1.2 参数含义
- w为权重矩阵,矩阵乘积之后得到下一层神经元的输入
- W矩阵的特点,行的个数为输入的维数,列的个数为下一层神经元的维数
- 上一层的输出为Sigmod函数的输出
1.3 损失函数
- Y为预测的值,Y=sigmod(Z2)
- y为实际的值
- 损失函数为方差
1.4 梯度下降
- 设置更新步长alpha
- 梯度下降的难点,求偏导
1.5 链式法则求偏导
- 矩阵求导时,矩阵要求转置
- 求对w1的偏导
- 求b1的偏导
- 求w2的偏导
- 求b2的偏导
- 都含有损失函数对Z2的偏导,即对,最后的输出求偏导
- 对于sigmod函数
- 对上面进行替换
1.6 反向逆传播算法
- 设神经网络为m层,用 L 表示其中的一层
- 这一层神经元的输入
- 求权重和偏置的对
损失函数的偏导
- 可知需要求对本层Z的偏导,因此引入其下一层
- 对下一层Z的损失函数求偏导
- 因此可以知道,求出下一层的对Z的偏导, 则能递推求出上一层的偏导
- 对于最后一层,求出最后一层的偏导,能够退出上面所有层的梯度下降
- 求上层的梯度下降
二、BP神经网络
2.1 主函数
import numpy as np
# 读取数据
def load_data(file_name: "数据集的相对位置"):
"""
从txt文件中读取数据,一次读取所有的行
去掉首尾的多余空格,将数字进行分词
最后一列是标记,其他列是属性
:param file_name:
:return:
"""
# 存放数据
data_set = []
# 存放标签
labels = []
with open(file_name) as f:
for line in f.readlines():
values = line.strip().split() # 空格分割
data_set.append([float(j) for j in values[0: -1]])
labels.append(int(float(values[-1])))
return data_set, labels
pass
# 激活函数
def sigmoid(value):
"""
非线性激活函数, 使用 sigmoid = 1/(1+exp(-x))
:param value: 输入的值
:return: 返回函数值
"""
return 1 / (1 + np.exp(-value))
pass
# 初始化参数
def paramter_init(input_len, hidden_len, output_len):
"""
:param input_len: 输入神经元的个数, 即输入的维数
:param hidden_len: 隐藏层神经元的个数,
:param output_len: 输出层神经元的个数
:return:
"""
RANDOM_INT = 5
# 输入层与隐藏层的连接权重 input_len * hidden_len微,第一行为w1
w1 = np.random.randint(-RANDOM_INT, RANDOM_INT, (input_len, hidden_len)).astype(np.float64)
# 隐藏层偏置, 维数 1 * input_len
b1 = np.random.randint(-RANDOM_INT, RANDOM_INT, (1, hidden_len)).astype(np.float64)
# 隐藏层与输出层的权重 hidden_len * output_len
w2 = np.random.randint(-RANDOM_INT, RANDOM_INT, (hidden_len, output_len)).astype(np.float64)
# 输出层偏置,维数 1 * hidden_len
b2 = np.random.randint(-RANDOM_INT, RANDOM_INT, (1, output_len)).astype(np.float64)
return w1, b1, w2, b2
pass
def my_trainning(dataset, label_set, w1, b1, w2, b2):
"""
过程:
1. 输入的维数即神经元的个数,x = [1, 2, 3] 输入是三维的
2. 输入*权重 + 偏置为激活函数的输入 x*w + b
其中:
w 的维数为 m * n; m为行数, 对应输入神经元的个数,n为列数, 即第一隐藏层神经元的个数
x * w后, 向量是1 * n 维的
b 为行向量, [b1, b2, b3]
3. f = sigmoid(x*w + b) 是 1 * n 维的, 作为下一层神经网络的输出, 或者结果
基于梯度下降的反向传播算法
w = w + alpha * 损失函数对 w 的偏导
b = b + alpha * 损失函数对 b 的偏导
难点是求偏导:
:param dataset: 训练的数据集
:param label_set: 标签, 可以说是分类
:param w1: 连接层到隐藏层权重
:param b1: 连接层到隐藏层的偏置
:param w2: 隐层层到输出层的权重
:param b2: 隐藏层到输出层的偏置
:return: 返回训练后的权重和参数 w1, b1, w2, b2
"""
alpha = 0.01
# 对测试的样本进行训练
for i in range(len(dataset)): # 长度为测试集中元素的个数
# 输入矩阵
input_date = np.mat(dataset[i]).astype(np.float64) # 转化为浮点数‘
# 输出矩阵
label = np.mat(label_set[i]).astype(np.float64)
# 隐藏层的输入, 即第一层的输出
z1 = np.dot(input_date, w1).astype(np.float64) - b1
# 隐藏层的输出
n1 = sigmoid(z1)
# 最后一层的输入
z2 = np.dot(n1, w2).astype(np.float64) - b2
# 最后一层的输出
n2 = sigmoid(z2)
# 梯度下降算法的计算 更新隐藏层到输出层
pianE_div_pianZ2 = np.multiply((label - n2), np.multiply(n2, 1 - n2))
delta_w2 = alpha * np.dot(np.transpose(n1), pianE_div_pianZ2)
delta_b2 = alpha * pianE_div_pianZ2
# 更新输入层到隐藏层
pianE_div_pianZ1 = np.multiply(np.multiply(n1, 1 - n1), np.dot(pianE_div_pianZ2, np.transpose(w2)))
delta_b1 = alpha * pianE_div_pianZ1
delta_w1 = alpha * np.dot(np.transpose(input_date), pianE_div_pianZ1)
# 更新参数
w1 += delta_w1
b1 -= delta_b1
w2 += delta_w2
b2 -= delta_b2
# 返回训练的参数
return w1, b1, w2, b2
pass
# 得到一组测试数据的方差
def get_e(data_test, label, w1, b1, w2, b2):
DATA_TEST_ROW_LEN = len(data_test)
tmp = 0
for i in range(DATA_TEST_ROW_LEN):
input_test = np.mat(data_test[i]).astype(np.float64)
n1 = sigmoid(np.dot(input_test, w1) - b1)
n2 = sigmoid(np.dot(n1, w2) - b2)
tmp += np.multiply((n2 - label[i]), (n2 - label[i]).T) # .T 为matrix的转置
return tmp / DATA_TEST_ROW_LEN
pass
# 进行预测
def predict_test(data_test, label_test, w1, b1, w2, b2):
"""
对神经网络的参数进行测试,由输入层到输出层逐个转递
使用sigmod函数作为激活函数
:param data_test: 测试集
:param label_test: 测试集的标记
:param w1: 第一层到隐藏层的权重
:param b1: 隐藏层的偏置
:param w2: 隐藏层到输出层的权重
:param b2: 输出层的偏置
:return: 返回测试的正确率
"""
right_cnt = 0
# 测试的个数
TEST_NUM = len(data_test)
# 对数据集的每一个输入进行测试
for i in range(TEST_NUM):
# 第一层输入
input_date = np.mat(data_test[i]).astype(np.float64)
# 第一层输出, 第二层输入
output_1 = sigmoid(np.dot(input_date, w1) - b1)
# 第二层输出, 结果
output_2 = sigmoid(np.dot(output_1, w2) - b2)
if output_2 >= 0.5:
flag = 1
else:
flag = 0
if label_test[i] == flag:
right_cnt += 1
# 输出结果
print("预测为: {0}, 实际为: {1}".format(flag, label_test[i]))
return right_cnt / TEST_NUM
def plot_e(a_list):
import matplotlib.pyplot as plt
x = np.arange(1, len(a_list) + 1)
y = np.array(a_list) # 转化为向量
print("最后误差:", y[-1])
# 绘制
plt.plot(x, y)
plt.xlabel("practiseTime")
plt.ylabel("variance")
plt.show()
pass
def main():
file_name = "horseColicTraining.txt"
data_set, label_set = load_data(file_name)
w1, b1, w2, b2 = paramter_init(len(data_set[0]), len(data_set[0]) + 30, 1)
# 进行两千次的训练
a_list = []
for i in range(1500):
w1, b1, w2, b2 = my_trainning(data_set, label_set, w1, b1, w2, b2)
num = get_e(data_set, label_set, w1, b1, w2, b2)
a_list.append(num[0, 0])
# 绘制方差的图像
plot_e(a_list)
# 进行测试
file_name_2 = "horseColicTest.txt"
data_test, label_test = load_data(file_name_2)
rate = predict_test(data_test, label_test, w1, b1, w2, b2)
print("预测的正确率为: ", rate)
pass
if __name__ == "__main__":
main()
2.1.1 方差的图像
2.1.2 预测结果
2.2 矩阵乘积分析
- 矩阵内数的乘积
- 矩阵乘积
即:
2.2 测试集
- horseColicTraining
2.2 训练集
- horseColicTest
下载
https://gitee.com/HQHQHQ123/machine_-learning_-bp.git