感知机
一、引言
感知机的概念是在20世纪50年代末期由美国心理学家和计算机科学家Frank Rosenblatt提出的,标志着人工神经网络研究的开端之一。Rosenblatt受到了生物神经系统的启发,特别是人脑中神经元之间的信息处理方式。他试图创建一个能够学习和做出决策的机器模型,这个模型就是感知机。
1、感知机的发展
- 1957年,Rosenblatt在康奈尔航空实验室(Cornell Aeronautical Laboratory)发明了感知机,并提出了它作为一种计算模型,用以执行自动学习和识别的任务。感知机最初被设想为一种模拟人脑处理信息的机器。
- 1958年,Rosenblatt发表了关于感知机的研究论文,引起了广泛关注,特别是在模式识别和自动学习领域。感知机被认为是人工智能研究的一个重要进展。
- 1960年代,感知机作为早期的人工神经网络模型之一,其研究获得了显著的进展。研究者们对其潜力寄予厚望,认为它能够解决广泛的自动化任务和智能问题。
2、遇到的挑战
- 1969年,Marvin Minsky和Seymour Papert在他们的著作《Perceptrons》中指出了感知机的一些主要限制,特别是它无法解决非线性可分问题,如异或(XOR)问题。这一发现对感知机的研究产生了重大影响,导致了神经网络领域的第一次低潮。
- Minsky和Papert的批评促使研究者们探索更复杂的神经网络结构,包括多层网络和非线性激活函数,这些后来成为深度学习的基础。
3、后续发展
尽管早期遭遇了挑战,感知机模型的基本思想仍然对后来的神经网络和深度学习研究有重要影响。1970年代末到1980年代,随着反向传播算法的发展和计算能力的提升,多层神经网络的研究再次兴起,开启了神经网络研究的新纪元。今天,感知机作为深度学习和人工智能历史上的一个重要里程碑,其思想和原理仍然是理解更复杂神经网络架构的基础。
二、原理
感知器是一种最简单的人工神经网络,用于二分类问题,其核心原理基于线性分类器。感知器模型试图找到一个能够将两类数据分开的决策边界。这个决策边界是数据特征空间中的一个超平面。在最简单的形式中,如果我们处理的是二维数据,这个决策边界就是一条直线。
1、感知器模型
感知器模型由输入向量、权重向量、偏置项和激活函数组成。设输入向量为 X = [x_1, x_2, ..., x_n]
,权重向量为 W = [w_1, w_2, ..., w_n]
,偏置项为 b
,则模型的输出由下面的方程决定:
y = f ( W ∗ X + b ) y = f(W * X + b) y=f(W∗X+b)
其中,W * X
表示权重向量和输入向量的点积,f
是激活函数,通常是一个阶跃函数,用于将加权和转换为一个明确的类别输出(例如,0或1)。
2、工作原理
- 初始化:首先,权重和偏置被初始化。它们可以被随机设置或初始化为零。
- 计算加权和:对于每一个输入样本,计算权重向量与输入向量的点积,再加上偏置项。
- 应用激活函数:将加权和传递给激活函数。在经典的感知器模型中,如果加权和大于零,输出类别1;否则,输出类别0。这实际上是一个二分类任务。
- 权重更新:模型通过比较预测输出和实际输出来更新权重。如果预测错误,模型会调整权重和偏置,以便在下次遇到相似的输入时能做出更准确的预测。权重的更新规则如下:
W
n
e
w
=
W
o
l
d
+
η
∗
(
y
−
y
^
)
∗
X
W_new = W_old + η * (y - ŷ) * X
Wnew=Wold+η∗(y−y^)∗X
b
n
e
w
=
b
o
l
d
+
η
∗
(
y
−
y
^
)
b_new = b_old + η * (y - ŷ)
bnew=bold+η∗(y−y^)
其中,y
是真实标签,ŷ
是预测标签,η
是学习率,一个小的正数,用于控制学习的步长。
3、学习过程
感知器的学习过程是迭代的。在每次迭代中,模型通过上述规则逐步调整权重和偏置,直到找到能够正确分隔两类数据的决策边界,或直到达到某个预设的迭代次数。由于感知器是一个线性模型,它只能解决线性可分的问题。对于非线性问题,单层感知器无法找到一个有效的解决方案,这就需要使用更复杂的网络结构,比如多层感知机。多层感知机(MLP)是一种前馈人工神经网络,其包含一个输入层、至少一个隐藏层以及一个输出层。每一层都由多个神经元组成,这些神经元在层与层之间全连接。MLP能够解决单层感知器无法处理的非线性问题,这得益于其多层结构和非线性激活函数的使用。
4、MLP的工作原理
- 输入层:接收原始输入数据。
- 隐藏层:每个隐藏层的神经元将上一层的输出作为输入,通过加权和(权重与输入的乘积之和加上偏置项)计算,然后通过一个非线性激活函数转换。这些隐藏层使MLP能够捕捉复杂的数据特征。
- 输出层:最后一个隐藏层的输出被送入输出层,输出层决定了网络的最终输出。对于分类任务,输出层通常通过softmax函数实现多类别的概率分布;对于回归任务,则可能不使用激活函数或使用恒等函数(identity function)。
5、激活函数
激活函数的作用是引入非线性,使得网络能够学习和模拟任何非线性函数。常用的激活函数包括ReLU、sigmoid和tanh等。
6、参数更新原理:反向传播算法
多层感知机的训练包括正向传播和反向传播两个阶段。
- 正向传播:输入数据在网络中前向传播,通过每一层的加权和和激活函数,直到产生输出。
- 计算损失:使用损失函数(如均方误差或交叉熵)计算网络输出与真实标签之间的差距。
- 反向传播:这一过程的目的是计算损失函数关于网络参数(权重和偏置)的梯度。这是通过链式法则逐层反向执行的,从输出层开始,通过每个隐藏层,直到输入层。对于每一层,我们计算损失对于该层权重的梯度,并使用这些梯度来更新权重。
- 权重更新:一旦计算出梯度,就使用优化算法(如SGD、Adam等)来更新网络的权重和偏置。这通常涉及到梯度方向上的小步移动,步长由学习率控制。
7、数学表示
步骤 1: 正向传播
对于每一层 l l l,网络的输出 z [ l ] z^{[l]} z[l]和激活 a [ l ] a^{[l]} a[l]计算如下:
- z [ l ] = W [ l ] a [ l − 1 ] + b [ l ] z^{[l]} = W^{[l]}a^{[l-1]} + b^{[l]} z[l]=W[l]a[l−1]+b[l]
- a [ l ] = g [ l ] ( z [ l ] ) a^{[l]} = g^{[l]}(z^{[l]}) a[l]=g[l](z[l])
其中, W [ l ] W^{[l]} W[l]和 b [ l ] b^{[l]} b[l]分别是第 l l l层的权重和偏置, a [ l − 1 ] a^{[l-1]} a[l−1]是前一层的激活输出, g [ l ] g^{[l]} g[l]是激活函数。
步骤 2: 计算损失
损失函数 L L L评估模型输出 a [ L ] a^{[L]} a[L]( L L L是最后一层)与真实标签 y y y之间的差异。例如,对于二分类问题,损失函数可以是交叉熵损失:
- L ( a [ L ] , y ) = − [ y log ( a [ L ] ) + ( 1 − y ) log ( 1 − a [ L ] ) ] L(a^{[L]}, y) = -\left[y \log(a^{[L]}) + (1 - y) \log(1 - a^{[L]})\right] L(a[L],y)=−[ylog(a[L])+(1−y)log(1−a[L])]
步骤 3: 反向传播
反向传播的目的是计算损失函数相对于每个参数的梯度。这些梯度用于更新参数,以最小化损失函数。
- 对于输出层 L L L,计算损失函数相对于激活的梯度 ∂ L ∂ a [ L ] \frac{\partial L}{\partial a^{[L]}} ∂a[L]∂L,然后基于激活函数的导数计算 ∂ L ∂ z [ L ] \frac{\partial L}{\partial z^{[L]}} ∂z[L]∂L:
- ∂ L ∂ z [ L ] = ∂ L ∂ a [ L ] ⋅ g ′ [ L ] ( z [ L ] ) \frac{\partial L}{\partial z^{[L]}} = \frac{\partial L}{\partial a^{[L]}} \cdot g'^{[L]}(z^{[L]}) ∂z[L]∂L=∂a[L]∂L⋅g′[L](z[L])
- 接下来,对于每一层 l = L − 1 , L − 2 , . . . , 2 , 1 l = L-1, L-2, ..., 2, 1 l=L−1,L−2,...,2,1,执行以下步骤来计算梯度:
- ∂ L ∂ W [ l ] = ∂ L ∂ z [ l ] a [ l − 1 ] T \frac{\partial L}{\partial W^{[l]}} = \frac{\partial L}{\partial z^{[l]}} a^{[l-1]T} ∂W[l]∂L=∂z[l]∂La[l−1]T
- ∂ L ∂ b [ l ] = ∂ L ∂ z [ l ] \frac{\partial L}{\partial b^{[l]}} = \frac{\partial L}{\partial z^{[l]}} ∂b[l]∂L=∂z[l]∂L
- 如果
l
>
1
l > 1
l>1,为了计算更早层的梯度,需要传播
∂
L
∂
z
[
l
]
\frac{\partial L}{\partial z^{[l]}}
∂z[l]∂L:
- ∂ L ∂ a [ l − 1 ] = W [ l ] T ∂ L ∂ z [ l ] \frac{\partial L}{\partial a^{[l-1]}} = W^{[l]T} \frac{\partial L}{\partial z^{[l]}} ∂a[l−1]∂L=W[l]T∂z[l]∂L
- ∂ L ∂ z [ l − 1 ] = ∂ L ∂ a [ l − 1 ] ⋅ g ′ [ l − 1 ] ( z [ l − 1 ] ) \frac{\partial L}{\partial z^{[l-1]}} = \frac{\partial L}{\partial a^{[l-1]}} \cdot g'^{[l-1]}(z^{[l-1]}) ∂z[l−1]∂L=∂a[l−1]∂L⋅g′[l−1](z[l−1])
步骤 4: 更新参数
使用计算出的梯度更新网络中的权重和偏置。对于每一层 l l l,更新规则为:
- W [ l ] = W [ l ] − η ∂ L ∂ W [ l ] W^{[l]} = W^{[l]} - \eta \frac{\partial L}{\partial W^{[l]}} W[l]=W[l]−η∂W[l]∂L
- b [ l ] = b [ l ] − η ∂ L ∂ b [ l ] b^{[l]} = b^{[l]} - \eta \frac{\partial L}{\partial b^{[l]}} b[l]=b[l]−η∂b[l]∂L
其中, η \eta η是学习率,控制着学习的步长。
这个过程在多个迭代(或“epoch”)中重复,直到损失函数足够小或达到某个预定的停止条件。
三、python实现
创建一个简单的多层感知机(MLP)涉及定义网络结构、前向传播逻辑、损失函数、反向传播以及参数(权重和偏置)更新机制。这里,我们将构建一个MLP。为简化,我们使用ReLU函数作为激活函数,均方误差(MSE)作为损失函数。
import numpy as np
# ReLU函数及其导数
def relu(x):
return np.maximum(0, x)
def relu_derivative(x):
return np.where(x > 0, 1, 0)
# 初始化权重的函数
def initialize_weights(size):
return np.random.normal(0, 1, size)
class ReLUMLP:
def __init__(self, input_neurons, hidden_neurons, output_neurons):
# 初始化权重和偏置
self.weights_input_hidden = initialize_weights((input_neurons, hidden_neurons))
self.bias_hidden = np.zeros(hidden_neurons)
self.weights_hidden_output = initialize_weights((hidden_neurons, output_neurons))
self.bias_output = np.zeros(output_neurons)
def forward_propagation(self, inputs):
# 输入层到隐藏层
self.hidden_layer_input = np.dot(inputs, self.weights_input_hidden) + self.bias_hidden
self.hidden_layer_output = relu(self.hidden_layer_input)
# 隐藏层到输出层
self.output_layer_input = np.dot(self.hidden_layer_output, self.weights_hidden_output) + self.bias_output
self.output_layer_output = sigmoid(self.output_layer_input) # 输出层仍然使用Sigmoid以保持输出在[0,1]范围
return self.output_layer_output
def train(self, inputs, y_true, epochs, learning_rate):
for epoch in range(epochs):
# 前向传播
y_pred = self.forward_propagation(inputs)
# 计算损失
error_output = mse_loss_derivative(y_true, y_pred)
d_output = error_output * sigmoid_derivative(y_pred) # 输出层的激活函数导数
# 反向传播误差
error_hidden = d_output.dot(self.weights_hidden_output.T)
d_hidden = error_hidden * relu_derivative(self.hidden_layer_output)
# 更新权重和偏置
self.weights_hidden_output -= self.hidden_layer_output.T.dot(d_output) * learning_rate
self.bias_output -= np.sum(d_output, axis=0) * learning_rate
self.weights_input_hidden -= inputs.T.dot(d_hidden) * learning_rate
self.bias_hidden -= np.sum(d_hidden, axis=0) * learning_rate
if epoch % 1000 == 0:
loss = mse_loss(y_true, y_pred)
print(f"Epoch {epoch}, Loss: {loss}")
# 使用和之前相同的输入和输出进行训练
input_neurons = 2
hidden_neurons = 4 # 可以尝试不同的数量来看效果
output_neurons = 1
relu_mlp = ReLUMLP(input_neurons, hidden_neurons, output_neurons)
# 训练模型
relu_mlp.train(inputs, y_true, epochs=5000, learning_rate=0.1)
当然,在实际操作中,我们可以利用主流的编程框架来节约时间。然而,上述原始代码有助于深入理解感知机的工作原理。
假设我们面临的具体问题是解决一个二维空间中的二分类问题,类似于XOR问题,其中输入数据点位于二维空间中,根据它们的坐标位置,我们需要分类这些点属于两个不同的类别。这个问题对于单层感知器来说是不可解的,因为它不是线性可分的,但使用具有至少一个隐藏层的多层感知机可以解决这个问题。下面的完整代码包括生成符合这个问题的数据集、构建模型、训练模型以及测试模型性能的步骤:
from keras.models import Sequential
from keras.layers import Dense
import numpy as np
from sklearn.datasets import make_circles
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# 生成数据集
X, y = make_circles(n_samples=1000, factor=0.5, noise=0.05, random_state=42)
# 数据预处理
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42)
# 定义模型
model = Sequential()
model.add(Dense(4, input_dim=2, activation='relu')) # 输入层和隐藏层
model.add(Dense(1, activation='sigmoid')) # 输出层
# 编译模型
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
# 训练模型
model.fit(X_train, y_train, epochs=100, batch_size=10)
# 评估模型
_, accuracy = model.evaluate(X_test, y_test)
print('Test Accuracy: %.2f' % (accuracy*100))
这段代码首先使用make_circles
函数生成一个简单的二维非线性可分数据集,该数据集常用于演示分类算法的能力。之后,我们对数据进行标准化处理,以确保模型训练效果更佳。接着,我们使用train_test_split
方法划分训练集和测试集,以便在独立的数据上评估模型性能。最后,我们定义了一个多层感知机模型,训练它,并在测试集上评估其准确率。通过运行这段代码,你将能够看到模型在处理二分类问题时的性能表现,特别是在解决非线性问题方面的能力。
四、总结
在本篇博客中,我们深入探讨了多层感知机(MLP)的原理和实践应用。通过从基础的神经网络概念出发,我们回顾了感知机的历史背景,解释了为什么单层感知器无法解决非线性问题,而多层感知机却能够有效地处理这类问题。我们进一步介绍了多层感知机的工作原理,包括其网络架构、激活函数的选择及其在解决复杂问题中的关键作用。
通过一个具体的编程示例,我们展示了如何使用编程框架来实现和训练一个多层感知机模型,以解决二维空间的二分类问题。此外,我们还讨论了如何通过调整网络架构和训练策略来进一步提升模型性能,以及多层感知机在解决实际问题中的广泛应用。
总的来说,多层感知机是深度学习领域的一个基石,理解其工作原理和应用范围对于任何希望深入探索人工智能的人来说都是非常宝贵的。希望通过本篇博客,读者能够获得启发,掌握构建和训练自己的神经网络模型的能力。
五、参考文献
- Rosenblatt, F. (1958). The Perceptron: A Probabilistic Model for Information Storage and Organization in the Brain. Psychological Review, 65(6), 386–408. 这篇论文介绍了感知机的早期概念,是理解MLP基础的关键文献。
- Rumelhart, D. E., Hinton, G. E., & Williams, R. J. (1986). Learning representations by back-propagating errors. Nature, 323(6088), 533–536. 这篇文章介绍了反向传播算法,为训练多层网络提供了基础。
- Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep Learning. MIT Press. 《深度学习》一书中有一章专门讲述了多层感知机,提供了理论背景和实践建议。
- Nair, V., & Hinton, G. E. (2010). Rectified Linear Units Improve Restricted Boltzmann Machines. In Proceedings of the 27th International Conference on Machine Learning (ICML-10), 807–814. 这篇论文讨论了ReLU激活函数对改进网络性能的作用。
- Kingma, D. P., & Ba, J. (2014). Adam: A Method for Stochastic Optimization. arXiv preprint arXiv:1412.6980. Adam优化器的原始论文,解释了其在神经网络训练中的应用。
- LeCun, Y., Bengio, Y., & Hinton, G. (2015). Deep learning. Nature, 521(7553), 436–444. 这篇综述文章概述了深度学习的发展,包括多层感知机在内的各种网络架构在多个领域的应用。
限于笔者水平,对于本博客存在的纰漏和错误,欢迎大家留言指正,我将不断更新。