最近网上看到一篇质量很不错的翻译文《小白都能看懂的神经网络教程:从原理到优化如此简单》,如获至宝。细读后发现里面有些翻译不到位。然后翻到原文读了一遍,又感觉原文有些虎头蛇尾,前半部分讲解深入浅出,后半部分讲解不够细致,匆匆收尾,作者似乎着急忙慌要睡觉似的。于是决定对原文再翻译一遍,以飨读者。
译文如下:
一篇简单介绍神经网络如何工作,以及如何用Python从头实现的文章
如果说“神经网络”其实不复杂,这可能会让你感到惊讶。事实上“神经网络”往往比你想象的要简单。
这篇文章是面向初学者的,并假定你机器学习基础为0。 我们将从头了解如何用 Python 实现神经网络。
1. 基本构建模块:神经元(Neurons)
首先,我们必须讨论下神经元,这是神经网络的基本单位。一个神经元接受多个输入,并对它们进行一些数学运算,然后产生一个输出。下图展示的是一个接收2个输入的神经元:
这里发生了三件事:
首先,每个 输入 (input) 都乘以一个 权重 w (weight):
$$ \begin{array}{l} x_{1} \rightarrow x_{1} * w_{1} \\ x_{2} \rightarrow x_{2} * w_{2} \end{array} $$
然后,所有的加权输入求和后又加上一个 偏移 b (bias):
$$ \left(x_{1} * w_{1}\right)+\left(x_{2} * w_{2}\right)+b $$
最后,加他们的 和 传递给一个 激活函数 (activation function):
$$ y=f\left(x_{1} * w_{1}+x_{2} * w_{2}+b\right) $$
这里的激活函数作用是将无限制的输入转换为可预测的输出。一种常见的激活函数是 sigmoid函数:
$$ \begin{aligned} \frac{1}{1+e^{-x}} \end{aligned} $$
激活函数的输出数值只会落在 \((0,1)\) 这个区间。你可以把它想象成将 \((−∞,+∞)\) 压缩成 \((0,1)\):
- 把无限小的负数转化为无限接近于0
- 把无限大的正数转化为无限接近于1
一个简单的例子
假设我们有一个使用\(sigmoid\)激活函数的神经元,它可以接收两个输入,下面是他的权重和偏移:\(w = [0,1]\) ,\(b = 4\)
\(w = [0 ,1]\) 是 \(w_{1} = 0,w_{2} = 1\)
的矢量表示法。
现在我们输入 \(x = [2, 3]\)。我们将用 点积 来简明的阐述:
$$ \begin{aligned} (w \cdot x)+b &=\left(\left(w_{1} * x_{1}\right)+\left(w_{2} * x_{2}\right)\right)+b \\ &=0 * 2+1 * 3+4 \\ &=7 \end{aligned} $$
$$ y=f(w \cdot x+b)=f(7)=\bm{0.999} $$
这个神经元给定输入 [2,3],给定输出 0.999。就是这样!这种向前传递输入以获得输出的过程成为前馈(feedforward) 。
编写一个神经元
是时候实现一个神经元了!我们将利用 Numpy (一个流行而强大的科学计数库) 来帮我们实现数学公式:
import numpy as np
def sigmoid(x):
# Our activation function: 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):
# Weight inputs, add bias, then use the activation function
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.999
2. 将神经元组合成神经网络
神经网络是什么?其实无非就是一束连接在一起的神经元。这是一个简单的神经网络的样子:
该网络有2个输入,一个包含2个神经元的隐藏层(\(h_1\) 和 \(h_2\)),以及一个带有1个神经元的输出层(\(o_1\))(请注意,\(o_1\) 的输入来自 \(h_1\) 和 \(h_2\)的输出 -这就是神经网络)。
举个栗子:前馈
以上面图中的神经网络为例,并假定所有神经元都有相同的权重 \(w=[0,1]\), 相同的偏移 \(b=0\),以及相同的 \(sigmoid\) 激活函数。用\(h_1,h_2,o_1\)表示他们代表的神经元输出。
如果我们输入 \(x = [2,3]\),会发生什么?
$$ \begin{aligned} h_{1}=h_{2} &=f(w \cdot x+b) \\ &=f((0 * 2)+(1 * 3)+0) \\ &=f(3) \\ &=0.9526 \end{aligned} $$
$$ \begin{aligned} o_{1} &=f\left(w \cdot\left[h_{1}, h_{2}\right]+b\right) \\ &=f\left(\left(0 * h_{1}\right)+\left(1 * h_{2}\right)+0\right) \\ &=f(0.9526) \\ &=\bm{0.7216} \end{aligned} $$
当输入为 \(x = [2,3]\),这个神经网络输出为 \(0.7216\), 是不是很简单?
一个神经网络可以有任意数量的层,每层可以有任意数据的神经元。基本思想一致:向前给神经元传递输入,并在后面得到结果。
方便起见,本文的其余部分将继续使用上图所示的神经网络
编写一个神经网络:前馈
让我们实现一个前馈神经网络。这是该网络的图片,仅供参考:
import numpy as np
# ... code from previous section here
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
# The Neuron class here is from the previous section
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)
# The inputs for o1 are the outputs from h1 and h2
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.7216\),完美!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。