千里之行,始于足下。从最开始的基础“神经元”—感知机来开始深度学习的学习过程。每个机器学习模型模型不论大小,都有它的三要素:模型、策略和算法。所以主要从这三个方面的学习来认知感知机模型。
本次学习参考资料是李航老师的《统计学方法》。
模型
感知机是通过对于生物神经元的简单模拟得到的。
感知机可以看作是只含有一个神经元的神经网络:
感知机是一个简单的 二分类模型。它的输入是 输入数据的特征向量,它的输出是输入数据的类别。而它的激活函数是sign函数:
感知机的数学公式描述:
感知机的几何描述:
策略
感知器的学习算法是一种错误驱动的在线学习算法 [Rosenblatt, 1958]。
为了确定上述介绍的感知机斜体样式模型(分离超平面),也就是去确定参数 w 和 b 的值。所以我们在这里需要找一个损失函数,通过最小化损失函数来确定我们的参数具体值,这就是我们感知机模型的策略。怎么确定这个损失函数呢?从几何上面理解的话,通过上文模型理解知道,最后我们求得的模型其实就是分离超平面,这在每一次求得的超平面下,一定有误分类的点,我们计算所有的误分类点到超平面的距离,而如果希望我们的模型效果好,我们是不是应该希望这个总的距离越少越好?甚至事到0,因为这就证明我们的模型的分类结果是完全正确的!有了这个思想,我们来依次推导出数学公式:
首先,一个预知识:
所以,误分类点到分离超平面的距离:
||w|| 是指 w 的L2范数,也就是向量各元素的平方和然后求平方根。
然后,我们来想办法简化这个公式,首先,我们明确计算的距离是误分类点到分离超平面的距离,而对于误分类的数据,我们一定能得到这样的公式:
解释一下这个公式,误分类点通过当前参数计算出来的最后的值一定是和它的实际值异号的!(也就是正负不同,相同的就就是分类正确的了)那么它们的乘积自然就小于0,前面再加个负号,也就得到了上面的式子。通过这个式子,我们可以把最开始的损失函数的式子的分子的绝对值符号拿掉:
接着,我们直接去掉 ||w|| ,理由是什么呢?因为它是一个常数,对于我们这个式子的影响无非就是放大或者缩小,但是对于正负号或者对于我们最后正确分类是没有影响的。(这里这个只是我的个人理解)。最后,我们将所有数据考虑进去,取得平均值,最后我们的损失函数就得到如下:
M 是误分类点的集合。
算法
确定了策略之后,我们再来确定算法,也就是通过何种方式使得我们的损失函数达到最低值,而我们的方式就是经典的梯度下降(梯度下降也是需要好好总结的,这部分内容再另开一个博客)。使用梯度下降第一步就是求得梯度。
接着确定我们的学习率η,然后迭代进行梯度下降,就可以得到我们最后正确的分类模型了。
代码
class Perceptron:
def __init__(self, data: ndarray, result: ndarray, max_iter=10000, lr=0.01):
self.data = data
self.result = result
self.length = len(self.data[0]) # 数据量大小
self.w = np.zeros(3)
self.iter = max_iter # 最大迭代次数
self.lr = lr # 学习率
def calculate(self):
i = 0
n = 0
loss = list()
for n in range(self.iter):
while i < len(self.data):
if self.result[i] * (np.dot(self.w, self.data[i])) <= 0: # 判断该点是否是误分类点
self.w += self.lr * self.result[i] * self.data[i] # 更新参数
i = 0
n += 1 # 计算迭代次数
m = 0
all_cost = 0
while m < len(self.data): # 计算每次更新参数后的损失值
if self.result[m] * (np.dot(self.w, self.data[m])) <= 0:
cost = -self.result[m] * (np.dot(self.w, self.data[m]))
all_cost += cost
m += 1
loss.append(all_cost)
else:
i += 1
break
print('w:')
print(self.w)
print('b:')
print(self.w[2])
print(n)
for i in range(len(data)):
if result[i] == 1:
plt.plot(data[i][0], data[i][1], 'ro')
else:
plt.plot(data[i][0], data[i][1], 'bo')
plt.xlabel('X1')
plt.ylabel('X2')
plt.xlim(xmax=10, xmin=0)
plt.ylim(ymax=10, ymin=0)
line_x = np.array([0, 10])
line_y = np.array([0, 0])
for i in range(len(line_x)):
line_y[i] = (-self.w[0] * line_x[i] - self.w[2]) / self.w[1]
plt.plot(line_x, line_y, c='green')
plt.show()
return loss
data = np.array(
[[5, 6], [6, 5], [6, 4], [7, 9], [8, 6], [6, 8], [3, 5], [4, 4], [3, 3], [2, 4], [3, 4], [4, 3], [1, 2], [1, 1], ])
result = np.array([1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1])
b = np.ones(14)
data = np.c_[data, b]
perceptron = Perceptron(data, result)
loss = perceptron.calculate()
效果图:
loss值变化图:
最后得到的参数值(b放入了w参数的向量里的最后一个)
w:[ 0.19 -0.05 -0.63]
迭代轮次:369