与大佬交流后,项目需要神经网络技术支撑。但恕本人才疏学浅,之前对这方面了解甚少,本贴就作为我的学习笔记。
1. 神经网络
神经网络是深度学习的重要算法,在图像(如图像的分类、检测)和自然语言处理(如文本分类、聊天)有很多应用。
人工神经网络(Artificial Neural Network),也简称为神经网络,是一种模仿生物神经网络(大脑)结构和功能的计算模型。经典的神经网络结构包含三个层次,即输入层、隐藏层、输出层。
其中,每层的圆圈代表一个“神经元”,“神经元”之间的连线对应不同的权重。
2. 神经元
2.1 生物神经元
一个神经元通常具有多个树突,主要用来接受传入信息;而轴突只有一条,轴突尾端有许多轴突末梢可以给其他多个神经元传递信息。轴突末梢跟其他神经元的树突产生连接,从而传递信号。这个连接的位置在生物学上叫做“突触”。
2.2 人工神经元
人工神经元是神经网络的基本单元,也经常被称为“处理单元”,是一种对生物神经元的形式化描述。它将生物神经元的信息处理传递过程抽象化,并用数学语言描述。其模型通常包含输入、计算、输出。输入可类比为树突,输出可类比为轴突,而计算可类比为细胞核。下面介绍一个例子:
在这个神经元中,输入为\(x_1,x_2,x_3, w_1,w_2,w_3\)为权重。
它将输入乘以权重,然后相加:
\(x_1 \rightarrow x_1 * w_1\)
\(x_2 \rightarrow x_2 * w_2\)
\(x_3 \rightarrow x_3 * w_3\)
\(z = x_1 * w_1 + x_2 * w_2 + x_3 * w_3 \)
最后再经过激励函数\(f\)(activation function)处理,得到最后的输出:
\(output = f(z) \)
3. 神经网络的搭建
3.1 单层神经网络
单层神经网络,也被称为“感知机”,这是最早的“人造神经元”模型。它只有两个层次:输入层、输出层。
输入层:这层的单元只负责传输数据,不做计算。
输出层:这层的单元需要对前面一层的输入进行计算。
人们把需要计算的层次称之为“计算层”,并把拥有一个计算层的神经网络称为“单层神经网络”。有的文献会按照神经网络所拥有的层数,例如把“感知机”称为两层神经网络。
对于上图我们设置权重\(w\)和偏置\(b\),最后的结果可表示为\(z=f(w*x+b)\),其中\(x=[x_1,x_2,x_3],w=[w_1,w_2,w_3], f\)为激励函数。
下面为简单的代码实现:
import numpy as np
# define activation function
def sigmoid(z):
return 1 / (1 + np.exp(-z))
# create my own netual
class my_N(object):
# set weights and biase
def __init__(self, weights, biase):
self.weights = weights
self.biase = biase
# calculate
def output(self, inputs):
y = np.dot(self.weights, inputs) + self.biase
y = sigmoid(y)
return y
weights = np.random.randint(-2, 5, 3) # set w1, w2, w3 in [-2,5) randomly
print("weights:", weights)
biase = np.random.randn(1) # set biase obeying the standard normal distribution
print("biases:", biase)
x = np.array([1, 2, 4]) # x1 = 1, x2 = 2, x3 = 4
n = my_NN(weights, biase)
result = n.output(x)
print("result:", result)
输出结果为:
weights: [-1 0 3]
biases: [1.18146466]
result: [0.99999488]
3.2 多层神经网络
由于单层神经网络的能力有限,无法解决更复杂的问题,多层神经网络就出现了。它通过增加计算层,使它可以更精准地表达抽象的逻辑或行为。如下图所示:
这个网络有3个输入,一个包含2个神经元的隐藏层,一个输出\(z\)。
我们在构建神经网络的时候,输入层和输出层往往都是固定的,而中间的隐藏层则需要我们去设计,这同时也是构建网络的关键部分。
为了方便,将第一层的权重都设置为\(w^{(1)}=[1,2,3]\),第二层权重为\(w^{(2)}=[0,1]\),偏置\(b=1\),激励函数都设置为\(sigmoid\)函数。下面为代码实现:
import numpy as np
# define activation function
def sigmoid(z):
return 1 / (1 + np.exp(-z))
class my_N(object):
# set weights and biase
def __init__(self, weights, biase):
self.weights = weights
self.biase = biase
# calculate
def output(self, inputs):
y = np.dot(self.weights, inputs) + self.biase
y = sigmoid(y)
return y
class my_NN(object):
def __init__(self, weights1, weights2, biase):
self.weights = weights1
self.biase = biase
self.h1 = my_N(weights1, biase) # one neuron in the hidden layer
self.h2 = my_N(weights1, biase) # another one
self.y = my_N(weights2, biase) # the neuron in the output layer
def output(self, inputs):
out_h1 = self.h1.output(inputs)
out_h2 = self.h2.output(inputs)
y = self.y.output([out_h1, out_h2])
return y
weights1 = np.array([1, 2, 3])
weights2 = np.array([0, 1])
biase = 1
x = np.array([1, 1, 1])
network = my_NN(weights1, weights2, biase)
result = network.output(x)
print("result:", result)
输出结果为:
result: 0.8807013902565914
3.3激励函数
在我们搭建神经网络的过程中,激励函数是一个很重要的部分,在隐藏层和输出层都会使用到。它的作用是将多个线性输入转换为非线性的关系。如果不使用激励函数的话,无论你神经网络有多少层,输出都是输入的线性组合。(还是那句话,要想神经网络表达能力更强,必须引入非线性的激励函数)。
下面简单介绍4个比较常见的激励函数:
3.3.1 Sigmoid函数
Sigmoid函数应该是神经网络中出现最频繁的一个激励函数了,它的数学表达式如下:
$$ g(z)=\frac {1} {1+ e^{-z}} $$
图像为:
Sigmoid函数能把一个实数压缩至0到1之间。当输入非常大时,输出就会接近1;当输入非常小时,输出则会接近于0。在早期的神经网络学习中使用得非常多,因为它很好地解释了神经元收到刺激后是否被激活的场景。但是它也有些缺点,例如使用Sigmoid函数容易出现梯度爆炸或梯度消失,由于本人正在学习中,这里不作详细解释。
3.3.2 tanh函数
tanh函数表达式为:
$$tanh(x)=\frac{e^x-e^{-x}} {e^x+e^{-x}}$$
图像为:
tanh函数将输入值压缩至-1到1之间。它也存在着与Sigmoid同样的缺点。
3.3.3 Relu函数
Relu函数表达式为:
$$f(x)=max(0,x), or$$
$$
f(x) =
\begin{cases}
x, & \text{if $x>0$ } \\
0, & \text{if $x \leq 0$ } \\
\end{cases}
$$
图像为:
Relu函数也称为线性整流函数。相比于Sigmoid函数和tanh函数,它更加简单,并且它避免了梯度爆炸和梯度消失问题;同时,它使得输出具有稀疏性,缓解了过拟合的问题。它的缺点是,随着训练的进行,可能会出现神经元死亡的情况,指的是某些神经元可能不会被激活,从而导致相应的参数不能被更新。如果发生这种情况,那么流经神经元的梯度从这一点开始将永远是0。
3.3.4 Leaky Relu函数
Leaky Relu函数表达式为:
$$f(x)=max(\alpha x,x),or$$
$$
f(x) =
\begin{cases}
x, & \text{if $x>0$ } \\
\alpha x, & \text{if $x \leq 0$ } \\
\end{cases}
$$
图像为:
人们为了解决Relu函数中容易出现的问题, 就将Relu的前半段设置为\(\alpha x\),而非\(0\),通常\(\alpha = 0.01\)。当神经元处于非激活状态时,允许一个非\(0\)的梯度存在。
4. 神经网络的训练
向网络输入足够的样本,通过一定算法调整网络的结构(主要是调节权重),使得网络的输出与预期值相符,这样的过程就是神经网络训练。在开始讨论之前,我们需要了解“损失函数”这个东西。
4.1 损失函数
顾名思义,损失函数就是一个用来计算“损失”的函数,那么什么叫做“损失”呢?
简单来说,损失就是真实值与预测值的误差,是我们评估网络模型好坏的一个标准。损失越小,预测结果越好,神经网络训练的过程也就是求得损失最小化的过程。
未完待续...
同时欢迎大家补充与指正。