学习的过程就像收集龙珠,每一颗都是困难,但是收集齐了就可以召唤出成功。
学习目标:
[1].使用类的方法封装多层神经网络
[2].使用函数实现神经网络的正向计算、反向传播
代码运行需要的库
scikit-learn
numpy
matplotlib
学习内容:
import numpy as np
from sklearn import datasets, linear_model#内置数据集,用于练习和测试机器学习模型
from sklearn.metrics import accuracy_score #线性模型的模块,包括逻辑回归
import matplotlib.pyplot as plt
def sigmoid(X):
return 1.0/(1 + np.exp(-X))#定义sigmoid激活函数
class NN_Model:#用于初始化类的实例
def __init__(self,nodes = None):
self.epsilon = 0.01 #学习率
self.n_epoch = 1000 #迭代数
if not nodes:#根据传入的 nodes 参数来初始化神经网络的结构。
self.nodes = [2, 6, 2]#如果没有提供nodes参数,则默认输入层2个节点,隐藏层6个节点,输出层2个节点
else:
self.nodes = nodes
def init_weight(self):#初始化神经网络的权重
W = []#存储权重的列表
B = []#存储偏置的列表
n_lay = len(self.nodes)#获取神经网络的层数
for i in range(n_lay-1):#遍历神经网络的每一层,除了输出层
w = np.random.randn(self.nodes[i], self.nodes[i+1])/np.sqrt(self.nodes[i])#用高斯分布随机初始化权重并缩放
b = np.random.randn(1, self.nodes[i+1])#随机初始化偏置
W.append(w)#将初始化的权重添加到列表中
B.append(b)#将初始化的偏置添加到列表中
self.W = W#将权重列表保存在类的属性中
self.B = B#将偏置列表保存在类的属性中
def forward(self,X):#向前传播方法。目的是将输入数据传递到网络中,经过每一层的计算,最终得到网络的输出。
Z = []#是一个列表,储存每一层的输出
x0 = X#当前层的输入,初始化为X
for i in range(len(self.nodes) - 1):#遍历神经网路的每一层,除了输出层
z = sigmoid(np.dot(x0, self.W[i]) + self.B[i])#向前传播计算
x0 = z#更新x0,当前层的输出,便于下一层输入
Z.append(z)#将当前层的输出添加到列表中
self.Z = Z#将所有层的输出保存到类的属性中
return Z[-1]#返回输出层的输出,即神经网络的最终输出
def backpropagation(self, X, y, n_epoch = None, epsilon = None):#反向传播方法,用于训练神经网络
if not n_epoch: n_epoch = self.n_epoch#检查n_epoch是否为 None,是则使用默认值,否则使用传入的值
if not epsilon: epsilon = self.epsilon#检查epslion是否为 None,是则使用默认值,否则使用传入的值
self.X = X #存储输入数据
self.Y = y #存储标签数据
for i in range(n_epoch): #正向计算每个神经元的输出
self.forward(X) #调用前面的函数进行计算,向前传播
self.evaluate() #调用后面的函数进行计算,计算网格的输出误差
W = self.W #获取权重
B = self.B #获取偏置
Z = self.Z #获取每一层的输出
D = [] #存储每一层的误差
d0 = y #初始误差为标签
n_layer = len(self.nodes) #神经网络的层数
for j in range(n_layer - 1, 0, -1): #从输出层到输入层,逐层计算每一层的误差
jj = j-1#用于索引权重和输出列表的索引
z = self.Z[jj]#获取当前层的输出
if j == n_layer - 1:#如果是输出层,计算输出层的误差
d = z*(1-z)*(d0 - z)#输出层误差公式
else:#如果是隐藏层,计算隐藏层的误差
d = z*(1-z)*np.dot(d0, W[j].T)#隐藏层误差公式
d0 = d#更新当前层的误差,用于下一层的计算
D.insert(0, d)#将当前层的误差插入到误差列表的开头
for j in range(n_layer - 1, 0, -1): #从输出层到输入层,逐层更新权重和偏置//采用的是梯度下降法
jj = j - 1#用于索引权重、偏置和输出列表的索引
if jj != 0:#更新隐藏层和输出层的权重
W[jj] += epsilon * np.dot(Z[jj-1].T, D[jj])
else:#更新输入层的权重
W[jj] += epsilon * np.dot(X.T, D[jj])#更新公式
B[jj] += epsilon * np.sum(D[jj], axis = 0)
def evaluate(self):#评估神经网络在当前训练轮次中的性能
z = self.Z[-1]#获取输出层的输出
L = np.sum((z - self.Y)**2)#计算均方误差
y_pred = np.argmax(z,axis = 1)#获取预测类别
y_true = np.argmax(self.Y, axis = 1)#获取真实类别
acc = accuracy_score(y_true, y_pred)#计算分类准确率
print("L = %f, acc = %f" % (L, acc))#打印均方误差和准确率
至此,多层神经网络封装为类任务结束!!!
下面生成一些数据来进行验证一下
np.random.seed(0)#设置随机种子,以确保实验的可重复性
X, y = datasets.make_moons(200, noise = 0.2)#生成包含 200 个样本的 moons 数据集,噪声为 0.2
t = np.zeros((X.shape[0], 2))#创建一个独热编码的标签矩阵
t[np.where(y==0), 0] = 1
t[np.where(y==1), 1] = 1
nn = NN_Model([2, 6, 4, 2])#创建神经网络模型,定义了输入层、两个隐藏层和输出层的节点数
nn.init_weight()#初始化神经网络的权重和偏置
nn.backpropagation(X, t, 2000)#使用反向传播算法进行训练,迭代 2000 次
y_res = nn.forward(X)#使用训练好的神经网络进行前向传播,得到预测结果
y_pred = np.argmax(y_res, axis = 1)#获取预测类别
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))#创建包含两个子图的画布
ax1.scatter(X[:, 0], X[:, 1], c = y, cmap = plt.cm.Spectral, marker='^')#在第一个子图中绘制真实值的散点图
ax1.set_title("真实值")
ax2.scatter(X[:, 0], X[:, 1], c = y_pred, cmap = plt.cm.Spectral)#在第二个子图中绘制预测值的散点图
ax2.set_title("预测值")
plt.subplots_adjust(wspace=0.4)#调整子图之间的间距
plt.show()#绘图
可以得到结果如下图:
–
总结
这段代码实现了一个简单的前馈神经网络(Feedforward Neural Network),通过反向传播算法进行训练。以下是数学原理的简要介绍:
-
Sigmoid 激活函数:
def sigmoid(X): return 1.0/(1 + np.exp(-X))
Sigmoid 函数用于引入非线性变换,将输入映射到 (0, 1) 的范围内,用于激活神经网络的每一层。
-
神经网络结构:
nn = NN_Model([2, 6, 4, 2])
这定义了神经网络的结构,包括输入层 2 个节点、两个隐藏层(分别有 6 个和 4 个节点)以及输出层 2 个节点。
-
初始化权重和偏置:
nn.init_weight()
权重和偏置是神经网络的参数,通过高斯分布随机初始化,并根据节点数量进行缩放。
-
前向传播:
y_res = nn.forward(X)
前向传播是通过神经网络从输入到输出的计算过程。对于每一层,计算输入与权重的乘积,加上偏置,并通过激活函数得到输出。
-
反向传播:
nn.backpropagation(X, t, 2000)
反向传播是通过梯度下降法更新权重和偏置,以最小化损失函数。该算法计算输出误差,然后反向传播误差,根据梯度更新权重和偏置。
-
评估性能:
nn.evaluate()
评估方法计算均方误差和准确率,用于监控神经网络在训练过程中的性能。
-
数据集生成和可视化:
X, y = datasets.make_moons(200, noise=0.2)
使用 scikit-learn 中的
make_moons
函数生成包含 200 个样本的二类数据集,其中噪声为 0.2。通过 Matplotlib 绘制真实值和预测值的散点图,用于可视化模型性能。
整个过程涉及到神经网络的前向传播和反向传播,通过不断调整权重和偏置,使得神经网络的输出更好地匹配真实标签,从而实现模型的训练。这是基本的神经网络训练流程,数学原理主要涉及到梯度下降、链式法则等基础数学知识。