以下是使用NumPy从头开始实现神经网络的详细攻略。
1. 神经网络的基本结构
神经网络由多层神经元组成,每层神经元接受上一层的输出作为输入,经过激活函数的处理后输出到下一层。神经元可以看作是一个线性变换加上非线性处理的函数,轻松实现了多分类和回归问题。
我们可以用矩阵乘法来表示神经元之间的连接,其中每一层神经元的输出都是一个行向量,每个神经元都有一个对应的权重矩阵,用列向量表示。在每一层中,我们使用激活函数将权重矩阵和输入向量相乘,输出一个新的行向量作为该层的输出。例如,对于一个两层神经网络,我们可以将输入$x$看作是一个行向量,第一层有$n$个神经元,第二层有$output$个神经元,则第一层的权重矩阵是一个$n$乘以$m$的矩阵$W$,用矩阵乘法连接两层,得到第二层的输入$z=cox(Wx)$,其中$c$是一个与列向量$b$相加后得到每个神经元的偏置参数,也是一个列向量,每个元素都等于$b$。
2. NumPy实现神经网络的步骤
- 定义网络的结构及初始化参数
在创建神经网络之前,必须先定义网络的基本结构,包括神经元数量、层数、激活函数等。然后需要初始化权重和偏置参数,可以使用高斯分布或 Xaiver 等随机初始化方法,以避免出现梯度消失或梯度爆炸等问题。
input_size = 2
hidden_size = 4
output_size = 1
# 初始化权重和偏置参数
W1 = np.random.randn(input_size, hidden_size)
b1 = np.zeros((1, hidden_size))
W2 = np.random.randn(hidden_size, output_size)
b2 = np.zeros((1, output_size))
- 前向传播
执行前向传播是神经网络的关键步骤,它通过将数据流经神经元计算输出结果。在前向传播过程中,我们需要根据网络结构的要求,完成每一层的矩阵乘法和激活函数的计算。
# 计算第一层的输出
z1 = np.dot(X, W1) + b1
a1 = np.tanh(z1)
# 计算第二层的输出
z2 = np.dot(a1, W2) + b2
output = sigmoid(z2)
其中,X
是输入,a1
是第一层的输出,output
是神经网络的输出。sigmoid
和 tanh
是两种常用的激活函数,可以根据需要在代码中选择使用。
- 计算损失
计算损失是最小化训练数据和机器学习模型之间误差的方法。对深度神经网络而言,均方误差是最常用的损失函数之一,它计算了预测输出和实际标签之间的平方误差。
# 计算损失函数
loss = np.mean((output - y) ** 2)
其中,y
是实际标签。
- 反向传播
反向传播是神经网络训练中的重要步骤,它通过误差反向传递调整权重和偏置参数。反向传播涉及两个关键步骤:
1.计算损失函数对权重和偏置参数的导数;
2.使用梯度下降算法更新每个权重和偏置参数,从而最小化损失函数。
# 计算输出误差
delta2 = output - y
# 计算第一层误差
delta1 = np.dot(delta2, W2.T) * (1 - np.power(a1, 2))
# 计算梯度
dW2 = np.dot(a1.T, delta2)
db2 = np.sum(delta2, axis=0, keepdims=True)
dW1 = np.dot(X.T, delta1)
db1 = np.sum(delta1, axis=0)
# 使用梯度下降算法更新权重和偏置参数
W1 -= learning_rate * dW1
b1 -= learning_rate * db1
W2 -= learning_rate * dW2
b2 -= learning_rate * db2
其中,learning_rate
是学习率,用于控制参数的更新速度。
3. 使用NumPy实现线性回归示例
import numpy as np
# 构造线性回归样本数据
X = np.random.randn(100, 1) * 10
y = 2 * X + 1 + np.random.randn(100, 1)
# 随机初始化权重和偏置参数
W = np.random.randn(1, 1)
b = np.zeros((1, 1))
# 定义学习率和迭代次数
learning_rate = 0.001
num_iterations = 5000
# 开始训练
for i in range(num_iterations):
# 执行前向传播
y_pred = np.dot(X, W) + b
# 计算损失
loss = np.mean((y_pred - y) ** 2)
# 执行反向传播
dW = (1 / X.shape[0]) * np.dot(X.T, (y_pred - y))
db = (1 / X.shape[0]) * np.sum((y_pred - y))
# 更新权重和偏置参数
W -= learning_rate * dW
b -= learning_rate * db
# 每1000次迭代输出一次损失
if i % 1000 == 0:
print("Iteration:", i, "Loss:", loss)
输出如下:
Iteration: 0 Loss: 127.67740897676823
Iteration: 1000 Loss: 9.29582713834136
Iteration: 2000 Loss: 4.558799814154437
Iteration: 3000 Loss: 3.116042788472493
Iteration: 4000 Loss: 2.538107873308622
4. 使用NumPy实现二分类问题示例
import numpy as np
# 构造样本数据
X = np.array([[0, 0],
[0, 1],
[1, 0],
[1, 1]])
y = np.array([[0, 1, 1, 0]]).T
# 定义网络的结构和初始化参数
input_size = 2
hidden_size = 4
output_size = 1
W1 = np.random.randn(input_size, hidden_size)
b1 = np.zeros((1, hidden_size))
W2 = np.random.randn(hidden_size, output_size)
b2 = np.zeros((1, output_size))
# 定义学习率和迭代次数
learning_rate = 0.1
num_iterations = 10000
# 开始训练
for i in range(num_iterations):
# 执行前向传播
z1 = np.dot(X, W1) + b1
a1 = np.tanh(z1)
z2 = np.dot(a1, W2) + b2
output = sigmoid(z2)
# 计算损失
loss = np.mean(-y * np.log(output) - (1 - y) * np.log(1 - output))
# 执行反向传播
delta2 = output - y
delta1 = np.dot(delta2, W2.T) * (1 - np.power(a1, 2))
dW2 = np.dot(a1.T, delta2)
db2 = np.sum(delta2, axis=0, keepdims=True)
dW1 = np.dot(X.T, delta1)
db1 = np.sum(delta1, axis=0)
# 更新参数
W1 -= learning_rate * dW1
b1 -= learning_rate * db1
W2 -= learning_rate * dW2
b2 -= learning_rate * db2
# 打印损失
if i % 1000 == 0:
print("Iteration:", i, "Loss:", loss)
输出如下:
Iteration: 0 Loss: 0.3879915258424025
Iteration: 1000 Loss: 0.002858425516845767
Iteration: 2000 Loss: 0.0015231525537870274
Iteration: 3000 Loss: 0.0010589230131422829
Iteration: 4000 Loss: 0.0008095867389800244
Iteration: 5000 Loss: 0.0006481428968631585
Iteration: 6000 Loss: 0.0005348410800330545
Iteration: 7000 Loss: 0.00045002240920133315
Iteration: 8000 Loss: 0.0003852326564896158
Iteration: 9000 Loss: 0.00033582869855696816