正如我们在机器学习教程的前一章中所展示的,仅由一个感知器组成的神经网络足以分离我们的示例类。当然,我们精心设计了这些类以使其工作。有许多类集群,对于它们不起作用。我们将查看其他一些示例,并将讨论无法分离类的情况。
我们的类是线性可分的。线性可分性在欧几里得几何中有意义。两组点(或类)称为线性可分的,如果平面中至少存在一条直线,使得一类的所有点都在直线的一侧,而另一类的所有点都在另一侧边。
更正式的:
如果两个数据簇(类)可以通过线性方程形式的决策边界分开
∑一世=1nX一世⋅瓦一世=0
它们被称为线性可分。
否则,即如果这样的决策边界不存在,则这两个类被称为线性不可分。在这种情况下,我们不能使用简单的神经网络。
AND 函数的感知器
在我们的下一个示例中,我们将用 Python 编写一个神经网络,它实现逻辑“与”函数。它按以下方式为两个输入定义:
我们在上一章中了解到,具有一个感知器和两个输入值的神经网络可以解释为决策边界,即划分两个类别的直线。我们要在示例中分类的两个类如下所示:
将 matplotlib.pyplot 导入为 plt
将 numpy 导入为 np
图, ax = plt 。子图()
xmin , xmax = - 0.2 , 1.4
X = np 。arange ( xmin , xmax , 0.1 )
ax 。scatter ( 0 , 0 , color = "r" )
ax 。scatter ( 0 , 1 , color = "r" )
ax 。分散(1 , 0 , color = "r" )
ax 。scatter ( 1 , 1 , color = "g" )
ax 。set_xlim ([ xmin , xmax ])
ax 。set_ylim ([ - 0.1 , 1.1 ])
m = - 1
#ax.plot(X, m * X + 1.2, label="decision boundary")
plt . 情节()
输出:
我们还发现,这样一个原始的神经网络只能创建穿过原点的直线。所以分割线是这样的:
将 matplotlib.pyplot 导入为 plt
将 numpy 导入为 np
图, ax = plt 。子图()
xmin , xmax = - 0.2 , 1.4
X = np 。arange ( xmin , xmax , 0.1 )
ax 。set_xlim ([ xmin , xmax ])
ax 。set_ylim ([ - 0.1 , 1.1 ])
m = - 1
for m in np 。范围(0 , 6 , 0.1 ):
ax 。绘图( X , m * X )
ax 。scatter ( 0 , 0 , color = "r" )
ax 。scatter ( 0 , 1 , color = "r" )
ax 。scatter ( 1 , 0 , color = "r" )
ax 。分散( 1, 1 , color = "g" )
plt . 情节()
输出:
我们可以看到,这些直线都不能用作决策边界,也不能用作穿过原点的任何其他直线。
我们需要一条线
是=米⋅X+C其中截距c不等于 0。
例如线
是=-X+1.2
可以用作我们问题的分隔线:
将 matplotlib.pyplot 导入为 plt
将 numpy 导入为 np
图, ax = plt 。子图()
xmin , xmax = - 0.2 , 1.4
X = np 。arange ( xmin , xmax , 0.1 )
ax 。scatter ( 0 , 0 , color = "r" )
ax 。scatter ( 0 , 1 , color = "r" )
ax 。分散(1 , 0 , color = "r" )
ax 。scatter ( 1 , 1 , color = "g" )
ax 。set_xlim ([ xmin , xmax ])
ax 。set_ylim ([ - 0.1 , 1.1 ])
m , c = - 1 , 1.2
ax 。绘图( X , m * X + c )
PLT 。情节()
输出
现在的问题是,我们能否找到对网络模型稍加修改的解决方案?或者换句话说:我们能否创建一个能够定义任意决策边界的感知器?
解决方案包括添加偏置节点。
具有偏差的单个感知器
具有两个输入值和一个偏差的感知器对应于一条一般直线。借助偏置值,b我们可以训练感知器来确定具有非零截距的决策边界c。
虽然输入值可以改变,但偏置值始终保持不变。只能调整偏置节点的权重。
现在,感知器的线性方程包含偏差:
∑一世=1n瓦一世⋅X一世+瓦n+1⋅乙=0
在我们的例子中,它看起来像这样:
瓦1⋅X1+瓦2⋅X2+瓦3⋅乙=0
这相当于
X2=-瓦1瓦2⋅X1-瓦3瓦2⋅乙
这意味着:
米=-瓦1瓦2
和
C=-瓦3瓦2⋅乙
import numpy as npfrom collections import Counter
类 感知器:
def __init__ ( self ,
weights ,
bias = 1 ,
learning_rate = 0.3 ):
"""
'weights' 可以是一个 numpy 数组、列表或具有
权重实际值的
元组。输入值的数量 由'weights'
"""
self 的长度。权重 = np 。数组(权重)
自我。偏见 = 偏见
自我。学习率 = 学习率
@staticmethod
def unit_step_function ( x ):
如果 x <= 0 :
return 0
else :
return 1
def __call__ ( self , in_data ):
in_data = np 。串连( (IN_DATA , [自。偏压]) )
结果 = 自我。weights @ in_data
返回 感知器。unit_step_function (结果)
def 调整( self ,
target_result ,
in_data ):
if type ( in_data ) != np . ndarray :
in_data = np 。阵列(IN_DATA ) #
calculated_result = 自(IN_DATA )
误差 = target_result - calculated_result
如果 错误 =! 0 :
IN_DATA = NP 。连接( (in_data , [ self . 偏差]) )
校正 = 错误 * in_data * self 。learning_rate
自我。权重 += 修正
DEF 评估(自, 数据, 标签):
评价 = 计数器()
对于 样品, 标签 在 拉链(数据, 标签):
结果 = 自(样品) #预测
如果 结果 == 标签:
评价[ “正确” ] + = 1
否则:
评估[ “错误” ] += 1
返回 评估
复制代码
我们假设上面带有 Perceptron 类的 Python 代码以“perceptrons.py”的名称存储在您当前的工作目录中。
from perceptrons import Perceptrondef labelled_samples ( n ):
for _ in range ( n ):
s = np 。随机的。randint ( 0 , 2 , ( 2 ,))
yield ( s , 1 ) if s [ 0 ] == 1 and s [ 1 ] == 1 else ( s , 0 )
p = 感知器(权重= [ 0.3 , 0.3 , 0.3 ],
learning_rate = 0.2 )
对于 IN_DATA , 标签 在 labelled_samples (30 ):
p 。调整(标签,
输入数据)
test_data , test_labels = list ( zip ( * labelled_samples ( 30 )))
评价 = p 。评估(test_data , test_labels )
打印(评估)
复制代码
输出:
计数器({'正确':30})