感知机(perceptron)作为机器学习的基础,理解了感知机的原理以及实现,就基本知道机器学习的本质了:
“通过对错误数据集的学习,不断调整更新自身的参数,使得模型参数对当前系统的输入数据集,得到最佳输出”
上面是自己个人的理解。
本文主要是参考了李航的《统计学习方法》,然后使用python实现了感知机,并对二维数据集进行分类,验证了算法的有效性。
本文主要内容如下:
感知机基本原理
算法步骤
代码实现
下面先贴出最终的运行结果,数据集红色点集合,以及蓝色点集合是二分类数据集,红线为最终的分类平面,运行结果如下:
输出console中
epoch代表每行的错误率,
array,代表学习的参数权重 w1, w2, 偏置 b
代码结构图如下,数据集为2维如图,最后一列为其类别,(为了方便,最终训练中把 0 转化为了 -1):
感知机基本原理
感知机是二分类的线性分类模型,其输入为实例的特征向量,输出为实例的类别取+1、-1。感知机将输入空间分为正负两类的超平面,称为判别模型。感知机的学习目的在于求出最佳超平面,由此导入基于误分类的损失函数。利用随机梯度下降法(
**不是严格上的随机梯度下降法**
)对损失函数进行最小化,求得感知机模型。
感知机是神经网络与支持向量机的基础。
定义如下(截图来自本人的笔记):
具体模型可见下图:
具体解释如下(截图来自本人笔记):
假设数据是线性可分的,那如何找到这样一个超平面呢?即确定感知机模型参数w、b。由此我们需要一个学习策略,即定义(经验)损失函数并将损失函数最小化。
loss损失函数的选取要考虑到w,b的连续可导,因此我们选择误分类点到超平面的距离。
误分类数据(本来y=1结果却分类为了-1,而-1分类为了1)因此y(w*x+b)<0
算法步骤
前面提到:
机器学习通过对错误数据集的学习,不断调整更新自身的参数,使得模型参数对当前系统的输入数据集,得到最佳输出
当有误分类数据的时候,对于感知机
误分类数据(本来y=1结果却分类为了-1,而-1分类为了1)因此
y(w*x+b)<0
对于w,b计算梯度:
至于为何加上学习率,主要是更好的调整w,b的变化,一般取0.01
(图片来自李航博士《统计学习方法》)
至于具体的神经网络的学习与理解,建议看下,这个英文教材,也有中文版,自己网上搜索一下。
http://neuralnetworksanddeeplearning.com/chap1.html
本bolg的代码风格,就是模仿这个教程。
代码实现
接下来是代码的实现了,本文的数据集是二维特征(见上面代码结构图),对应公式:
y=w2∗x2+w1∗x1+b
代码的总体思路:
从dataset.txt读取数据,生成训练数据形式(将类别0转化为了-1,主要是方便运算)
生成感知机类,以及随机梯度下降法(不是严格上的随机梯度下降法)参数,并训练网络
画出训练数据集的散点图,以及最终生成的分类平面
# _*_ coding:utf-8 _*_
import numpy as np
from utils.FileUtil import get_line_lst
import matplotlib.pyplot as plt
class Perception(object):
def __init__(self, var_num):
"""var_num为特征的个数,本例中设为2"""
# self.w = np.random.randn(1, var_num)
# 参数初始化
self.w = np.ones(var_num)
self.b = 1
self.var_num = var_num
# 错误率低于0.02则终止
self.min_error_rate = 0.02
def train(self, train_data, eta):
"""
training model
:param train_data: array like [[1, 2, -1], [1.1, 0.8, 1]]
:param eta: learning rate:
:return none:
"""
for item in train_data:
output = (np.dot(self.w, item[0:-1]) + self.b)*item[-1]
if output <= 0:
self.w += eta * item[-1] * item[0:-1]
self.b += eta * item[-1]
def sgd(self, train_data, epoch, eta, batch_size):
"""
Training perception model by stochastic gradient descent
:param train_data: 2D array like [[1.1, 2.3, -1]] the last
item -1 train_date[0][-1] means label
:param epoch:
:param eta:learning rate
:return:none
"""
for i in xrange(epoch):
np.random.shuffle(train_data)
batch_lst = [train_data[k:k+batch_size] for k in xrange(0, len(train_data), batch_size)]
for mini_batch in batch_lst:
self.train(mini_batch, eta)
current_error_rate = self.get_error_rate(train_data)
print 'epoch {0} current_error_rate: {1}'.format(i+1, current_error_rate)
print self.get_current_para()
if current_error_rate <= self.min_error_rate:
break
def get_error_rate(self, validate_data):
all_len = validate_data.shape[0]
error_len = 0
for item in validate_data:
output = np.dot(self.w, item[0:-1]) + self.b
output = 1 if output >= 0 else -1
error = True if output != item[-1] else False
if error:
error_len += 1
return float(error_len) / all_len
def get_current_para(self):
return self.w, self.b
def get_weight(self):
return self.w
def get_bias(self):
return self.b
def generate_data(data_path):
lst_data = get_line_lst(data_path)
# lst_ret = []
# for item in lst_data:
# lst_ret.append([float(s) for s in item.split()])
# the following one line is equivalent to the above for loop
lst_ret = [[float(s) for s in item.split()] for item in lst_data]
ret_arr = np.array(lst_ret)
# change all the label whose value is 0 to -1
for i in xrange(ret_arr.shape[0]):
if ret_arr[i][-1] == 0:
ret_arr[i][-1] = -1
return ret_arr
def plot_data_scatter(train_data, w, b):
x = np.linspace(-5, 5, 10)
plt.figure()
# 画散点图(plot scatter)
for i in range(len(train_data)):
if train_data[i][-1] == 1:
plt.scatter(train_data[i][0], train_data[i][1], c=u'b')
else:
plt.scatter(train_data[i][0], train_data[i][1], c=u'r')
# 画感知机分类,slope斜率图
plt.plot(x, -(w[0]*x+b) / w[1], c=u'r')
plt.show()
if __name__ == '__main__':
data_path = '../dataset/perception/dataset.txt'
train_data = generate_data(data_path)
# epoch迭代次数,eta学习率,var_num特征个数
epoch, eta, var_num, batch_size = 100, 0.1, 2, 20
# 创建感知机对象
p = Perception(var_num)
# 训练
p.sgd(train_data, epoch, eta, batch_size)
# 画出最终的分类模型
plot_data_scatter(train_data, p.get_weight(), p.get_bias())