使用NumPy从头开始实现神经网络

  • Post category:Python

以下是使用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 是神经网络的输出。sigmoidtanh 是两种常用的激活函数,可以根据需要在代码中选择使用。

  • 计算损失

计算损失是最小化训练数据和机器学习模型之间误差的方法。对深度神经网络而言,均方误差是最常用的损失函数之一,它计算了预测输出和实际标签之间的平方误差。

# 计算损失函数
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