用Python从零开始构建一个BP神经网络

用Python从零开始构建一个神经网络

2023年西安电子科技大学认知计算作业

下面的内容旨在帮助你完成从零构建一个神经网络,内容基础来自于victorzhou的一篇blog:Machine Learning for Beginners: An Introduction to Neural Networks
在上文基础上对内容做了优化

1. 构建基本的神经元

神经元往往是一个神经网络最基础的组成部分,一个神经元的任务是,它应该接受一个输入,然后根据内部的权重来做一个输出,本质上是一个数学模型,下面的内容会展示一个非常简单的神经元,它接受一个二维输入,然后输出一维结果

perceptron

该神经元内部发生的计算过程如下:
首先每一维的特征需要与对应的权重值相乘,这里的权重就指 w 1 w_1 w1
x 1 → x 1 ∗ w 1 x_1 \rightarrow x_1 * w_1 x1x1w1
x 2 → x 2 ∗ w 2 x_2 \rightarrow x_2 * w_2 x2x2w2

接下来当权重计算完成后,我们需要加入一个偏置值 b i a s bias bias 在这里写为 b b b :
( x 1 ∗ w 1 ) + ( x 2 ∗ w 2 ) + b (x_1 * w_1) + (x_2 * w_2) + b (x1w1)+(x2w2)+b

最后得到的结果将经过一个激活函数 f f f ,在后面的演示中,激活函数我们将采用 s i g m o d sigmod sigmod:
y = f ( x 1 ∗ w 1 + x 2 ∗ w 2 + b ) y = f(x_1 * w_1 + x_2 * w_2 + b) y=f(x1w1+x2w2+b)

sigmod 函数的图像如下,它的输出范围为 ( 0 , 1 ) (0, 1) (0,1),可以将 ( − ∞ , + ∞ ) (-\infty, +\infty) (,+) 的输出范围 缩小到 ( 0 , 1 ) (0, 1) (0,1) 非常适合完成二分类任务

一个简单示例

假设按照我们的设计存在这样一个神经元,权重为 [0, 1] ,偏置值为 4

w = [ 0 , 1 ] w = [0, 1] w=[0,1]
b = 4 b = 4 b=4

当输入为 x = [ 2 , 3 ] x = [2, 3] x=[2,3]. 它会进行如下的计算:
( w ⋅ x ) + b = ( ( w 1 ∗ x 1 ) + ( w 2 ∗ x 2 ) ) + b = 0 ∗ 2 + 1 ∗ 3 + 4 = 7 \begin{aligned} (w \cdot x) + b &= ((w_1 * x_1) + (w_2 * x_2)) + b \\ &= 0 * 2 + 1 * 3 + 4 \\ &= 7 \\ \end{aligned} (wx)+b=((w1x1)+(w2x2))+b=02+13+4=7
y = f ( w ⋅ x + b ) = f ( 7 ) = 0.999 y = f(w \cdot x + b) = f(7) = \boxed{0.999} y=f(wx+b)=f(7)=0.999

最后我们得到的结果是 0.999 0.999 0.999 ,这样的一个完整的过程,我们称它为前向计算。

### Python代码实现一个简单的神经元
import numpy as np

def sigmoid(x):
  # 激活函数的表达式: f(x) = 1 / (1 + e^(-x))
  return 1 / (1 + np.exp(-x))

class Neuron:
  def __init__(self, weights, bias):
    self.weights = weights
    self.bias = bias

  def feedforward(self, inputs):
    # 与权重相乘,再加上偏置值,最后经过激活函数
    total = np.dot(self.weights, inputs) + self.bias
    return sigmoid(total)

weights = np.array([0, 1]) # w1 = 0, w2 = 1
bias = 4                   # b = 4
n = Neuron(weights, bias)

x = np.array([2, 3])       # x1 = 2, x2 = 3
print(n.feedforward(x))    # 0.9990889488055994
0.9990889488055994

2. 将神经元组装为一个神经网络

一个复杂的神经网络往往由许多个神经元连接组成,而这些神经元往往可以看成一层一层的结构,一个简单的神经网络结构如下:

network

该网络有 2 个输入,一个带有 2 个神经元的隐藏层 ( h 1 h_1 h1 and h 2 h_2 h2), 以及一个具有 1 个神经元的输出层 ( o 1 o_1 o1).

隐藏层是输入(第一)层和输出(最后)层之间的任何层。可以有多个隐藏层

神经网络中的前向计算

让我们使用上图所示的网络并假设所有神经元具有相同的权重 w = [ 0 , 1 ] w = [0, 1] w=[0,1] , 偏置值 b = 0 b = 0 b=0 , 并且同样使用sigmod来作为激活函数.

来看看输入 x = [ 2 , 3 ] x = [2, 3] x=[2,3] 会发生什么

h 1 = h 2 = f ( w ⋅ x + b ) = f ( ( 0 ∗ 2 ) + ( 1 ∗ 3 ) + 0 ) = f ( 3 ) = 0.9526 \begin{aligned} h_1 = h_2 &= f(w \cdot x + b) \\ &= f((0 * 2) + (1 * 3) + 0) \\ &= f(3) \\ &= 0.9526 \\ \end{aligned} h1=h2=f(wx+b)=f((02)+(13)+0)=f(3)=0.9526
o 1 = f ( w ⋅ [ h 1 , h 2 ] + b ) = f ( ( 0 ∗ h 1 ) + ( 1 ∗ h 2 ) + 0 ) = f ( 0.9526 ) = 0.7216 \begin{aligned} o_1 &= f(w \cdot [h_1, h_2] + b) \\ &= f((0 * h_1) + (1 * h_2) + 0) \\ &= f(0.9526) \\ &= \boxed{0.7216} \\ \end{aligned} o1=f(w[h1,h2]+b)=f((0h1)+(1h2)+0)=f(0.9526)=0.7216

我们输入了 x = [ 2 , 3 ] x = [2, 3] x=[2,3] 得到了神经网络的计算值为 0.7216 0.7216 0.7216.

神经网络可以有任意数量的层,这些层中可以有任意数量的神经元。基本思想保持不变:通过网络中的神经元向前馈送输入,以最终获得输出。为简单起见,我们将在本文的其余部分继续使用上图所示的网络。

Python代码实现实现简单的神经网络

结构参考如下:

network2

class OurNeuralNetwork:
  '''
  A neural network with:
    - 2 inputs
    - a hidden layer with 2 neurons (h1, h2)
    - an output layer with 1 neuron (o1)
  Each neuron has the same weights and bias:
    - w = [0, 1]
    - b = 0
  '''
  def __init__(self):
    weights = np.array([0, 1])
    bias = 0

    # 这里的神经元我们使用上一小节定义的神经元
    self.h1 = Neuron(weights, bias)
    self.h2 = Neuron(weights, bias)
    self.o1 = Neuron(weights, bias)

  def feedforward(self, x):
    out_h1 = self.h1.feedforward(x)
    out_h2 = self.h2.feedforward(x)
    out_o1 = self.o1.feedforward(np.array([out_h1, out_h2]))

    return out_o1

network = OurNeuralNetwork()
x = np.array([2, 3])
print(network.feedforward(x)) # 0.7216325609518421
0.7216325609518421

3. 训练我们设计的神经网络, Part 1 Loss

现在我们有如下的数据信息:

NameWeight (lb)Height (in)Gender
Alice13365F
Bob16072M
Charlie15270M
Diana12060F

我们现在希望能够训练一个模型,根据用户输入的特征,比如 180身高80公斤的体重 来得出一个人的性别
结构大概如下图:

network3

为了方便训练,我们将女性性别表示为 1 1 1 ,将男性性别表示为 0 0 0

NameWeight (minus 135)Height (minus 66)Gender
Alice-2-11
Bob2560
Charlie1740
Diana-15-61

这里不会过多的介绍为什么这样处理训练数据,而且这里的数据处理非常简单,如果您有更多的兴趣,可以在网络上获取更多关于数据预处理操作的信息

损失

在训练网络之前,我们首先需要一种方法来量化它的程度,以便它可以尝试做得更好。这就是损失函数。

我们将使用均方误差(MSE)损失::

MSE = 1 n ∑ i = 1 n ( y t r u e − y p r e d ) 2 \text{MSE} = \frac{1}{n} \sum_{i=1}^n (y_{true} - y_{pred})^2 MSE=n1i=1n(ytrueypred)2

Let’s break this down:

  • n n n 是样本数, w为 4 4 4 (Alice, Bob, Charlie, Diana)。
  • y y y 代表被预测的变量, 也就是性别(0,1)。
  • y t r u e y_{true} ytrue 是变量的真实值(“正确答案”)。也就是说 y t r u e y_{true} ytrue 当输入 Alice 的特征时,结果应该是 1 1 1 (Female)。
  • y p r e d y_{pred} ypred 是变量的预测值。这就是我们的网络输出结果。

( y t r u e − y p r e d ) 2 (y_{true} - y_{pred})^2 (ytrueypred)2 称为均方误差。我们的损失函数只是取所有平方误差的平均值(因此称为均方误差)。我们的预测越好,我们的损失就越低

通常越小的loss值就代表模型现在的能力越强(不考虑过拟合)

训练模型的过程就是希望最小化它的损失

Loss计算的示例

假设我们的网络总是输出0 换句话说,它确信所有人类都是男性🤔。我们的损失是什么?

Name y t r u e y_{true} ytrue y p r e d y_{pred} ypred ( y t r u e − y p r e d ) 2 (y_{true} - y_{pred})^2 (ytrueypred)2
Alice101
Bob000
Charlie000
Diana101

MSE = 1 4 ( 1 + 0 + 0 + 1 ) = 0.5 \text{MSE} = \frac{1}{4} (1 + 0 + 0 + 1) = \boxed{0.5} MSE=41(1+0+0+1)=0.5

Python代码实现 MSE Loss

import numpy as np

def mse_loss(y_true, y_pred):
  # y_true and y_pred are numpy arrays of the same length.
  return ((y_true - y_pred) ** 2).mean()

y_true = np.array([1, 0, 0, 1])
y_pred = np.array([0, 0, 0, 0])

print(mse_loss(y_true, y_pred)) # 0.5

4. 训练我们设计的神经网络, Part 2 反向传播, 优化神经网络

我们现在有一个明确的目标:最小化神经网络的损失。我们知道我们可以改变网络的权重和偏差来影响其预测,但网络权重与偏差的改变不可能由我们随心所欲地来进行,我们希望整个过程损失是不断减少的,那如何做到这一点呢?

为了简单起见,我们假设数据集中只有 Alice:

NameWeight (minus 135)Height (minus 66)Gender
Alice-2-11

那么均方误差损失就是 Alice 的平方误差:

MSE = 1 1 ∑ i = 1 1 ( y t r u e − y p r e d ) 2 = ( y t r u e − y p r e d ) 2 = ( 1 − y p r e d ) 2 \begin{aligned} \text{MSE} &= \frac{1}{1} \sum_{i=1}^1 (y_{true} - y_{pred})^2 \\ &= (y_{true} - y_{pred})^2 \\ &= (1 - y_{pred})^2 \\ \end{aligned} MSE=11i=11(ytrueypred)2=(ytrueypred)2=(1ypred)2

考虑损失的另一种方法是作为权重和偏差的函数。让我们标记网络中的每个权重和偏差:

network3
然后,我们可以将损失写成多变量函数:

L ( w 1 , w 2 , w 3 , w 4 , w 5 , w 6 , b 1 , b 2 , b 3 ) L(w_1, w_2, w_3, w_4, w_5, w_6, b_1, b_2, b_3) L(w1,w2,w3,w4,w5,w6,b1,b2,b3)

想象一下我们想要调整 w 1 w_1 w1. 那当我们调整 w 1 w_1 w1时, L L L 会如何变化呢? 我们可以用偏导数来观察 L L L的变化 ∂ L ∂ w 1 \frac{\partial L}{\partial w_1} w1L

利用链式法则,我们可以将 ∂ y p r e d ∂ w 1 \frac{\partial y_{pred}}{\partial w_1} w1ypred 重写为:

∂ L ∂ w 1 = ∂ L ∂ y p r e d ∗ ∂ y p r e d ∂ w 1 \frac{\partial L}{\partial w_1} = \frac{\partial L}{\partial y_{pred}} * \frac{\partial y_{pred}}{\partial w_1} w1L=ypredLw1ypred

我们可以计算 ∂ L ∂ y p r e d \frac{\partial L}{\partial y_{pred}} ypredL 因为我们已经计算过 L = ( 1 − y p r e d ) 2 L = (1 - y_{pred})^2 L=(1ypred)2 :

∂ L ∂ y p r e d = ∂ ( 1 − y p r e d ) 2 ∂ y p r e d = − 2 ( 1 − y p r e d ) \frac{\partial L}{\partial y_{pred}} = \frac{\partial (1 - y_{pred})^2}{\partial y_{pred}} = \boxed{-2(1 - y_{pred})} ypredL=ypred(1ypred)2=2(1ypred)

接下来我们研究如何计算 ∂ y p r e d ∂ w 1 \frac{\partial y_{pred}}{\partial w_1} w1ypred. 我们用 h 1 , h 2 , o 1 h_1, h_2, o_1 h1,h2,o1 来代表神经网络每层的输出

y p r e d = o 1 = f ( w 5 h 1 + w 6 h 2 + b 3 ) y_{pred} = o_1 = f(w_5h_1 + w_6h_2 + b_3) ypred=o1=f(w5h1+w6h2+b3)

f 表示激活函数 sigmoid

参数 w 1 w_1 w1 只影响 h 1 h_1 h1 (not h 2 h_2 h2), 所以我们可以将计算式写成

∂ y p r e d ∂ w 1 = ∂ y p r e d ∂ h 1 ∗ ∂ h 1 ∂ w 1 \frac{\partial y_{pred}}{\partial w_1} = \frac{\partial y_{pred}}{\partial h_1} * \frac{\partial h_1}{\partial w_1} w1ypred=h1ypredw1h1
∂ y p r e d ∂ h 1 = w 5 ∗ f ′ ( w 5 h 1 + w 6 h 2 + b 3 ) \frac{\partial y_{pred}}{\partial h_1} = \boxed{w_5 * f'(w_5h_1 + w_6h_2 + b_3)} h1ypred=w5f(w5h1+w6h2+b3)

同样的,我们也可以对 ∂ h 1 ∂ w 1 \frac{\partial h_1}{\partial w_1} w1h1 使用链式法则:

h 1 = f ( w 1 x 1 + w 2 x 2 + b 1 ) h_1 = f(w_1x_1 + w_2x_2 + b_1) h1=f(w1x1+w2x2+b1)
∂ h 1 ∂ w 1 = x 1 ∗ f ′ ( w 1 x 1 + w 2 x 2 + b 1 ) \frac{\partial h_1}{\partial w_1} = \boxed{x_1 * f'(w_1x_1 + w_2x_2 + b_1)} w1h1=x1f(w1x1+w2x2+b1)

x 1 x_1 x1 代表体重, and x 2 x_2 x2 代表身高. 接下来我们来观察sigmod函数的导数

f ( x ) = 1 1 + e − x f(x) = \frac{1}{1 + e^{-x}} f(x)=1+ex1
f ′ ( x ) = e − x ( 1 + e − x ) 2 = f ( x ) ∗ ( 1 − f ( x ) ) f'(x) = \frac{e^{-x}}{(1 + e^{-x})^2} = f(x) * (1 - f(x)) f(x)=(1+ex)2ex=f(x)(1f(x))

现在我们已经将最开始的 ∂ L ∂ w 1 \frac{\partial L}{\partial w_1} w1L 拆解为下面这个我们可以计算的式子:

∂ L ∂ w 1 = ∂ L ∂ y p r e d ∗ ∂ y p r e d ∂ h 1 ∗ ∂ h 1 ∂ w 1 \boxed{\frac{\partial L}{\partial w_1} = \frac{\partial L}{\partial y_{pred}} * \frac{\partial y_{pred}}{\partial h_1} * \frac{\partial h_1}{\partial w_1}} w1L=ypredLh1ypredw1h1

这种通过向后计算偏导数的系统称为反向传播

如何计算偏导数

我们将继续假设只有 Alice 在我们的数据集中

NameWeight (minus 135)Height (minus 66)Gender
Alice-2-11

让我们将所有权重初始化为 1 1 1 所有的偏置设置为 0 0 0. 当我们进行前向计算,我们会得到下面的结果:

h 1 = f ( w 1 x 1 + w 2 x 2 + b 1 ) = f ( − 2 + − 1 + 0 ) = 0.0474 \begin{aligned} h_1 &= f(w_1x_1 + w_2x_2 + b_1) \\ &= f(-2 + -1 + 0) \\ &= 0.0474 \\ \end{aligned} h1=f(w1x1+w2x2+b1)=f(2+1+0)=0.0474
h 2 = f ( w 3 x 1 + w 4 x 2 + b 2 ) = 0.0474 h_2 = f(w_3x_1 + w_4x_2 + b_2) = 0.0474 h2=f(w3x1+w4x2+b2)=0.0474
o 1 = f ( w 5 h 1 + w 6 h 2 + b 3 ) = f ( 0.0474 + 0.0474 + 0 ) = 0.524 \begin{aligned} o_1 &= f(w_5h_1 + w_6h_2 + b_3) \\ &= f(0.0474 + 0.0474 + 0) \\ &= 0.524 \\ \end{aligned} o1=f(w5h1+w6h2+b3)=f(0.0474+0.0474+0)=0.524

输出的结果为 y p r e d = 0.524 y_{pred} = 0.524 ypred=0.524, 这不能够帮助我们准确的分辨出输入的是男性特征( 0 0 0)还是女性特征( 1 1 1) 。接下来让我们计算偏导 ∂ L ∂ w 1 \frac{\partial L}{\partial w_1} w1L:

∂ L ∂ w 1 = ∂ L ∂ y p r e d ∗ ∂ y p r e d ∂ h 1 ∗ ∂ h 1 ∂ w 1 \frac{\partial L}{\partial w_1} = \frac{\partial L}{\partial y_{pred}} * \frac{\partial y_{pred}}{\partial h_1} * \frac{\partial h_1}{\partial w_1} w1L=ypredLh1ypredw1h1
∂ L ∂ y p r e d = − 2 ( 1 − y p r e d ) = − 2 ( 1 − 0.524 ) = − 0.952 \begin{aligned} \frac{\partial L}{\partial y_{pred}} &= -2(1 - y_{pred}) \\ &= -2(1 - 0.524) \\ &= -0.952 \\ \end{aligned} ypredL=2(1ypred)=2(10.524)=0.952
∂ y p r e d ∂ h 1 = w 5 ∗ f ′ ( w 5 h 1 + w 6 h 2 + b 3 ) = 1 ∗ f ′ ( 0.0474 + 0.0474 + 0 ) = f ( 0.0948 ) ∗ ( 1 − f ( 0.0948 ) ) = 0.249 \begin{aligned} \frac{\partial y_{pred}}{\partial h_1} &= w_5 * f'(w_5h_1 + w_6h_2 + b_3) \\ &= 1 * f'(0.0474 + 0.0474 + 0) \\ &= f(0.0948) * (1 - f(0.0948)) \\ &= 0.249 \\ \end{aligned} h1ypred=w5f(w5h1+w6h2+b3)=1f(0.0474+0.0474+0)=f(0.0948)(1f(0.0948))=0.249
∂ h 1 ∂ w 1 = x 1 ∗ f ′ ( w 1 x 1 + w 2 x 2 + b 1 ) = − 2 ∗ f ′ ( − 2 + − 1 + 0 ) = − 2 ∗ f ( − 3 ) ∗ ( 1 − f ( − 3 ) ) = − 0.0904 \begin{aligned} \frac{\partial h_1}{\partial w_1} &= x_1 * f'(w_1x_1 + w_2x_2 + b_1) \\ &= -2 * f'(-2 + -1 + 0) \\ &= -2 * f(-3) * (1 - f(-3)) \\ &= -0.0904 \\ \end{aligned} w1h1=x1f(w1x1+w2x2+b1)=2f(2+1+0)=2f(3)(1f(3))=0.0904
∂ L ∂ w 1 = − 0.952 ∗ 0.249 ∗ − 0.0904 = 0.0214 \begin{aligned} \frac{\partial L}{\partial w_1} &= -0.952 * 0.249 * -0.0904 \\ &= \boxed{0.0214} \\ \end{aligned} w1L=0.9520.2490.0904=0.0214

计算的偏导大于0,这告诉我们 增大 w 1 w_1 w1, L L L 也会增大,所以我们需要减小 w 1 w_1 w1

随机梯度下降

我们现在基本已经完成了训练的所有准备! 我们将使用一种称为随机梯度下降(SGD)的优化算法,它告诉我们如何改变权重和偏差以最小化损失。基本上就是这个更新方程:

w 1 ← w 1 − η ∂ L ∂ w 1 w_1 \leftarrow w_1 - \eta \frac{\partial L}{\partial w_1} w1w1ηw1L

η \eta η 是一个称为学习率的常数,它控制我们训练的速度。我们所做的只是让 w 1 w_1 w1 减去 η ∂ L ∂ w 1 \eta \frac{\partial L}{\partial w_1} ηw1L :

  • 如果 ∂ L ∂ w 1 \frac{\partial L}{\partial w_1} w1L 大于0, w 1 w_1 w1 将会减小, 使得 L L L 下降.
  • 如果 ∂ L ∂ w 1 \frac{\partial L}{\partial w_1} w1L 小于0, w 1 w_1 w1 将会增大, 使得 L L L 下降.

如果我们对网络中的每个权重和偏置都这样做,损失就会慢慢减少,我们的网络也会得到改善。

我们的训练过程将如下所示:

  1. 从数据集中随机采样,这里我们只随机采取一个样本。
  2. 计算损失相对于权重或偏差的所有偏导数 (e.g. ∂ L ∂ w 1 \frac{\partial L}{\partial w_1} w1L, ∂ L ∂ w 2 \frac{\partial L}{\partial w_2} w2L, etc).
  3. 使用SGD来更新每个权重和偏差
  4. 重复步骤1

实现完整的BP神经网络

训练数据如下:

NameWeight (minus 135)Height (minus 66)Gender
Alice-2-11
Bob2560
Charlie1740
Diana-15-61

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

import numpy as np

def sigmoid(x):
  # Sigmoid activation function: f(x) = 1 / (1 + e^(-x))
  return 1 / (1 + np.exp(-x))

def deriv_sigmoid(x):
  # Derivative of sigmoid: f'(x) = f(x) * (1 - f(x))
  fx = sigmoid(x)
  return fx * (1 - fx)

def mse_loss(y_true, y_pred):
  # y_true and y_pred are numpy arrays of the same length.
  return ((y_true - y_pred) ** 2).mean()

class OurNeuralNetwork:
  '''
  A neural network with:
    - 2 inputs
    - a hidden layer with 2 neurons (h1, h2)
    - an output layer with 1 neuron (o1)

  *** DISCLAIMER ***:
  The code below is intended to be simple and educational, NOT optimal.
  Real neural net code looks nothing like this. DO NOT use this code.
  Instead, read/run it to understand how this specific network works.
  '''
  def __init__(self):
    # Weights
    self.w1 = np.random.normal()
    self.w2 = np.random.normal()
    self.w3 = np.random.normal()
    self.w4 = np.random.normal()
    self.w5 = np.random.normal()
    self.w6 = np.random.normal()

    # Biases
    self.b1 = np.random.normal()
    self.b2 = np.random.normal()
    self.b3 = np.random.normal()

  def feedforward(self, x):
    # x is a numpy array with 2 elements.
    h1 = sigmoid(self.w1 * x[0] + self.w2 * x[1] + self.b1)
    h2 = sigmoid(self.w3 * x[0] + self.w4 * x[1] + self.b2)
    o1 = sigmoid(self.w5 * h1 + self.w6 * h2 + self.b3)
    return o1

  def train(self, data, all_y_trues):
    '''
    - data is a (n x 2) numpy array, n = # of samples in the dataset.
    - all_y_trues is a numpy array with n elements.
      Elements in all_y_trues correspond to those in data.
    '''
    learn_rate = 0.1
    epochs = 1000 # number of times to loop through the entire dataset

    for epoch in range(epochs):
      for x, y_true in zip(data, all_y_trues):
        # --- Do a feedforward (we'll need these values later)
        sum_h1 = self.w1 * x[0] + self.w2 * x[1] + self.b1
        h1 = sigmoid(sum_h1)

        sum_h2 = self.w3 * x[0] + self.w4 * x[1] + self.b2
        h2 = sigmoid(sum_h2)

        sum_o1 = self.w5 * h1 + self.w6 * h2 + self.b3
        o1 = sigmoid(sum_o1)
        y_pred = o1

        # --- Calculate partial derivatives.
        # --- Naming: d_L_d_w1 represents "partial L / partial w1"
        d_L_d_ypred = -2 * (y_true - y_pred)

        # Neuron o1
        d_ypred_d_w5 = h1 * deriv_sigmoid(sum_o1)
        d_ypred_d_w6 = h2 * deriv_sigmoid(sum_o1)
        d_ypred_d_b3 = deriv_sigmoid(sum_o1)

        d_ypred_d_h1 = self.w5 * deriv_sigmoid(sum_o1)
        d_ypred_d_h2 = self.w6 * deriv_sigmoid(sum_o1)

        # Neuron h1
        d_h1_d_w1 = x[0] * deriv_sigmoid(sum_h1)
        d_h1_d_w2 = x[1] * deriv_sigmoid(sum_h1)
        d_h1_d_b1 = deriv_sigmoid(sum_h1)

        # Neuron h2
        d_h2_d_w3 = x[0] * deriv_sigmoid(sum_h2)
        d_h2_d_w4 = x[1] * deriv_sigmoid(sum_h2)
        d_h2_d_b2 = deriv_sigmoid(sum_h2)

        # --- Update weights and biases
        # Neuron h1
        self.w1 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_w1
        self.w2 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_w2
        self.b1 -= learn_rate * d_L_d_ypred * d_ypred_d_h1 * d_h1_d_b1

        # Neuron h2
        self.w3 -= learn_rate * d_L_d_ypred * d_ypred_d_h2 * d_h2_d_w3
        self.w4 -= learn_rate * d_L_d_ypred * d_ypred_d_h2 * d_h2_d_w4
        self.b2 -= learn_rate * d_L_d_ypred * d_ypred_d_h2 * d_h2_d_b2

        # Neuron o1
        self.w5 -= learn_rate * d_L_d_ypred * d_ypred_d_w5
        self.w6 -= learn_rate * d_L_d_ypred * d_ypred_d_w6
        self.b3 -= learn_rate * d_L_d_ypred * d_ypred_d_b3

      # --- Calculate total loss at the end of each epoch
      if epoch % 10 == 0:
        y_preds = np.apply_along_axis(self.feedforward, 1, data)
        loss = mse_loss(all_y_trues, y_preds)
        print("Epoch %d loss: %.3f" % (epoch, loss))

# Define dataset
data = np.array([
  [-2, -1],  # Alice
  [25, 6],   # Bob
  [17, 4],   # Charlie
  [-15, -6], # Diana
])
all_y_trues = np.array([
  1, # Alice
  0, # Bob
  0, # Charlie
  1, # Diana
])

# Train our neural network!
network = OurNeuralNetwork()
network.train(data, all_y_trues)

随着网络的不断学习,损失不断下降:


下面我们就能看到预测的结果:

emily = np.array([-7, -3]) # 128 pounds, 63 inches
frank = np.array([20, 2])  # 155 pounds, 68 inches
print("Emily: %.3f" % network.feedforward(emily)) # 0.951 - F
print("Frank: %.3f" % network.feedforward(frank)) # 0.039 - M
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是Python构建一个BP神经网络的代码: ``` import numpy as np # 构建 BP 神经网络类 class BPNeuralNetwork(object): def __init__(self, input_size, hidden_size, output_size): # 初始化权重和偏置 self.input_size = input_size # 输入层节点数 self.hidden_size = hidden_size # 隐藏层节点数 self.output_size = output_size # 输出层节点数 self.weights_input_hidden = np.random.randn(self.input_size, self.hidden_size) * 0.01 self.bias_hidden = np.zeros((1, self.hidden_size)) self.weights_hidden_output = np.random.randn(self.hidden_size, self.output_size) * 0.01 self.bias_output = np.zeros((1, self.output_size)) # 定义 sigmoid 函数 def sigmoid(self, x): return 1.0 / (1.0 + np.exp(-x)) # 定义 sigmoid 的导数函数 def sigmoid_derivative(self, x): return x * (1.0 - x) def train(self, X, y, epochs, learning_rate): for i in range(epochs): # 迭代次数 # 正向传播 hidden_layer_activation = np.dot(X, self.weights_input_hidden) + self.bias_hidden hidden_layer_output = self.sigmoid(hidden_layer_activation) output_layer_activation = np.dot(hidden_layer_output, self.weights_hidden_output) + self.bias_output predicted_output = self.sigmoid(output_layer_activation) # 反向传播 error = y - predicted_output d_predicted_output = error * self.sigmoid_derivative(predicted_output) error_hidden_layer = d_predicted_output.dot(self.weights_hidden_output.T) d_hidden_layer = error_hidden_layer * self.sigmoid_derivative(hidden_layer_output) # 更新权重和偏置 self.weights_hidden_output += hidden_layer_output.T.dot(d_predicted_output) * learning_rate self.bias_output += np.sum(d_predicted_output, axis=0, keepdims=True) * learning_rate self.weights_input_hidden += X.T.dot(d_hidden_layer) * learning_rate self.bias_hidden += np.sum(d_hidden_layer, axis=0, keepdims=True) * learning_rate # 预测 def predict(self, X): hidden_layer_activation = np.dot(X, self.weights_input_hidden) + self.bias_hidden hidden_layer_output = self.sigmoid(hidden_layer_activation) output_layer_activation = np.dot(hidden_layer_output, self.weights_hidden_output) + self.bias_output predicted_output = self.sigmoid(output_layer_activation) return predicted_output ``` 以上是构建 BP 神经网络Python 代码,可以通过该类进行训练和预测。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值