Numpy 实现简单神经网络

Numpy 实现简单神经网络

在本文中,我们将介绍使用Numpy库实现一个简单的全连接神经网络模型。神经网络是一个十分强大的机器学习方法,它可以用于分类、预测等任务,而全连接神经网络是最为基础、也是最常用的一种神经网络。使用Numpy来实现神经网络可以帮助我们更好地理解神经网络的原理,提高对神经网络的掌握。

阅读更多:Numpy 教程

1. 神经网络基本概念

神经网络结构由一个或多个用于处理输入的神经元层组成,以及一个或多个输出层给出网络的结果。单层神经网络由输入层和输出层组成,多层神经网络由多个隐藏层组成。

神经元计算由 线性函数 和 非线性函数组成:
z = \sum_{i=1}^n w_ix_i + b
h = f(z)
其中,z 为线性函数,由输入的 x_i 和其对应的权重 w_i 构成,再加上常量项 b,最后经过非线性函数 f 得到激活值 h。注意到 b 做了一次广播,使得输出结果得到了偏差值的修正。

在我们的网络中,激活函数使用 sigmoid,其公式为:
sigmoid(z) = \frac{1}{1+e^{-z}}
它的导数为:
sigmoid'(x) = sigmoid(x)(1-sigmoid(x))

如果 h 表示网络输出,那么网络的预测结果为 y = softmax(h),其中softmax 是一个常用的归一化函数,可以将一个向量的值转换为概率分布。其公式为:
softmax(x) = \frac{e^{x}}{\sum_{i=1}^ne^{x_i}}

2. 神经网络的前向传播

神经网络前向传播的过程是计算前一层的输出值,通过当前层的权重矩阵和偏置值进一步计算当前层的输出值,从而获得最后一层的输出值。
前向传播的过程中需要分别计算正向的线性计算和激活函数计算结果。

本文我们将实现一个两层神经网络,隐藏层的激活函数为sigmoid,输出层的激活函数为softmax

我们将数据集中的每一行视为一个样本,将其标签对应为一个10维的 One-Hot 向量,将该样本输入神经网络中进行正向计算,获取网络的输出值,然后计算其损失值。

def forward(X, W1, b1, W2, b2):
    Z1 = np.dot(X, W1) + b1
    a1 = sigmoid(Z1)
    Z2 = np.dot(a1, W2) + b2
    y_hat = softmax(Z2)
    return y_hat

其中,X为神经网络的输入数据,W1为输入层到隐藏层的权重矩阵,b1为隐藏层的偏差值,W2为隐藏层到输出层的权重矩阵,b2为输出层的偏差值, y_hat 为神经网络的输出结果。

3. 神经网络的反向传播

神经网络反向传播的过程是通过计算损失函数的导数,然后利用链式法则逐层传递并更新权重和偏置的过程。

对于多分类问题,我们选择交叉熵作为损失函数,使用交叉熵函数的目的是为了最小化预测值与真实值之间的距离。交叉熵损失函数的公式如下:
L = -\sum_{i=1}^N\sum_{j=1}^Ky_{ij}\log{\hat{y}_{ij}}

其中,y_{ij} 是样本 i 的真实标签的第 j 维,\hat{y}_{ij} 是对应的网络输出值的第 j 维。这里我们使用\hat{y}来表示网络的预测输出值。

在反向传播的过程中,我们需要计算每个权重和偏置项对于损失函数的导数。以权重矩阵 W_2 为例,其对于损失函数的导数可以通过以下方式计算得到:

\frac{\partial L}{\partial W_2} = \frac{\partial L}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial Z_2} \cdot \frac{\partial Z_2}{\partial W_2}

其中,\frac{\partial L}{\partial \hat{y}} 为输出层的误差项,\frac{\partial \hat{y}}{\partial Z_2} 为输出层的激活函数对于线性输入的导数,\frac{\partial Z_2}{\partial W_2} 为线性计算中权重和输入的偏置项对于输出层的导数。

将每层的误差项逐层反向传递,可以得到每个参数的梯度。通过梯度下降等优化算法,可以迭代优化网络的权重和偏置,提高神经网络的预测性能。

4. Numpy 实现神经网络

接下来,我们将使用Numpy库实现一个简单的两层神经网络。我们以手写数字识别问题为例,使用MNIST数据集进行训练和验证。

4.1 数据预处理

首先,我们需要对数据进行预处理。我们使用keras库中的mnist模块来载入MNIST数据集,然后将其精简为二元分类任务,将数字5的标签设为正例,将其他数字的标签设为负例。

from keras.datasets import mnist

(X_train, y_train), (X_test, y_test) = mnist.load_data()

# Flatten the images
X_train = X_train.reshape(-1, 784)
X_test = X_test.reshape(-1, 784)

# Normalize pixel values
X_train = X_train.astype('float32') / 255.
X_test = X_test.astype('float32') / 255.

# One hot encode labels
y_train = (y_train == 5).astype(int)
y_test = (y_test == 5).astype(int)

我们将每一个图像展平为一维向量,然后对其进行像素值归一化处理,最后将标签进行二分类处理。

4.2 神经网络模型构建

我们将构建一个两层神经网络,其中输入层为784维,输出层为1维。隐藏层的维度设为32。其结构如下图所示:

Input(784) -> Hidden Layer(32) -> Output Layer(1)

我们使用Numpy构建神经网络模型,先定义一些必要的超参数:

input_size = 784
hidden_size = 32
output_size = 1
learning_rate = 0.1
epochs = 100

然后,我们初始化权重矩阵和偏置向量:

W1 = np.random.randn(input_size, hidden_size) * 0.01
b1 = np.zeros((1, hidden_size))
W2 = np.random.randn(hidden_size, output_size) * 0.01
b2 = np.zeros((1, output_size))

接着编写前向传播和反向传播函数:

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def forward(X, W1, b1, W2, b2):
    Z1 = np.dot(X, W1) + b1
    A1 = sigmoid(Z1)
    Z2 = np.dot(A1, W2) + b2
    y_hat = sigmoid(Z2)
    return Z1, A1, Z2, y_hat

def sigmoid_derivative(x):
    return sigmoid(x) * (1 - sigmoid(x))

def backward(X, y, Z1, A1, Z2, y_hat, W2):
    m = X.shape[0]

    dZ2 = y_hat - y
    dW2 = np.dot(A1.T, dZ2)
    db2 = np.sum(dZ2, axis=0, keepdims=True)
    dZ1 = np.dot(dZ2, W2.T) * sigmoid_derivative(Z1)
    dW1 = np.dot(X.T, dZ1)
    db1 = np.sum(dZ1, axis=0)

    W2 -= learning_rate * dW2 / m
    b2 -= learning_rate * db2 / m
    W1 -= learning_rate * dW1 / m
    b1 -= learning_rate * db1 / m

    return W1, b1, W2, b2

其中,forward() 实现了前向传播的整个过程,并返回计算结果。backward() 实现了反向传播的计算,并更新神经网络参数。

4.3 模型训练和验证

在编写完神经网络的模型和参数更新的函数后,我们可以开始进行模型的训练和验证。我们使用交叉熵损失函数、随机梯度下降(SGD)优化算法,进行100次迭代。

for i in range(epochs):
    for j in range(X_train.shape[0]):
        X = X_train[j]
        y = y_train[j]

        Z1, A1, Z2, y_hat = forward(X, W1, b1, W2, b2)
        W1, b1, W2, b2 = backward(X, y, Z1, A1, Z2, y_hat, W2)

    _, _, _, y_hat = forward(X_test, W1, b1, W2, b2)
    loss = -np.mean(y_test * np.log(y_hat) + (1 - y_test) * np.log(1 - y_hat))
    print('Epoch:', i, 'Loss:', loss)

在完成100次迭代后,我们可以通过测试集评估模型的性能。这里我们使用准确率作为评价指标,公式为:

Accuracy = \frac{TruePositive + TrueNegative}{TruePositive + FalsePositive + TrueNegative + FalseNegative}

其中,TruePositive 表示模型正确地将正例预测为正例的数量,FalsePositive 表示模型将负例预测为正例的数量,TrueNegative 表示模型正确地将负例预测为负例的数量,FalseNegative 表示模型将正例预测为负例的数量。

pred_test = (y_hat > 0.5).astype(int)
accuracy = np.sum(pred_test == y_test) / y_test.shape[0]
print('Accuracy:', accuracy)

通过运行代码,我们可以得到模型在测试集上的准确率大约为96%左右。

总结

本文介绍了使用Numpy实现一个简单的两层全连接神经网络的方法。我们从神经网络的基本概念开始,介绍了神经元计算、神经网络的前向传播和反向传播的过程以及梯度下降法等相关知识,并给出了Numpy实现代码。通过在MNIST数据集上进行训练和验证,我们也验证了Numpy实现的神经网络的可行性和有效性。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程