统计学习——感知机
一、什么是感知机
感知机1957年由Rosenblatt提出,是神经网络的起源算法。
什么是感知机呢?对神经网络来说,感知机就是最基础,最普遍的一种逻辑模型,为什么说是最基础的逻辑模型呢,就是得解决网络中基本逻辑,就比如说与或这种,感知机刚好就能解决与或这样基本问题,但唯独不能解决异或问题,也算是很大缺陷吧。用书上话来讲感知机(perceptron)是一种解决二分类的线性分类模型,将特征空间的实例特征向量划分为正负两类,本质是一种分离超平面,是一种判别模型。
二、感知机详解
1.函数模型
假设输入空间(特征空间) 是
其中,
感知机模型的假设空间是定义在特征空间中的所有线性分类模型(linear classificatio model)或线性分类器(linear classifier),即函数集合
2.几何解释
感知机有如下几何解释:线性方程
对应于特征空间
感知机学习,由训练数据集(实例的特征向量及类别)
感知机预测,通过学习得到的感知机模型,对于新的输入实例给出其对应的输出类别。
3.感知机学习策略
3.1数据集的线性可分性
定义:给定一个数据集
能够将数据集的正实例点和负实例点完全正确地划分到超平面的两侧,即对所有
3.2感知机学习策略
假设训练数据集是线性可分的,感知机学习的目标是求得一个能够将训练集正实例点和负实例点完全正确分开的分离超平面。为了找出这样的超平面,即确定感知机模型参数w,b,需要确定一个学习策略,即定义(经验)损失函数并将损失函数极小化。
首先损失函数容易想到采用误分类点的数量作为损失函数,但是是离散的,
3.2.1损失函数
首先引入输入空间
这里,
对于误分类的数据
显然成立。
因此,一个误分类点到超平面
假设误分类点集合为
最后,不考虑
思考:为什么不考虑
1.
2.感知机算法的终止条件是所有输入输出都被正确分类,即不存在误分类点,最终损失为0,让分子为0即可。
4.感知机学习算法
采用随机梯度下降(SGD),目标函数如下:
4.1原始形式算法
求函数极小化问题的解,在随机梯度下降过程中,每一次仅用一个误分类点样本来使其梯度下降。
首先,求解梯度,分别对
然后,随机选取一个误分类点对
其中,
算法步骤
输入:训练数据
输出:
步骤:
(1)赋初值
(2)在训练集中选取数据点
(3)判断该数据点是否为当前模型的误分类点,即若
(4) 转到(2),直到训练集中没有误分类点
这种学习算法直观上有如下解释:当一个实例点被误分类,即位于分离超平面的错误一侧时,则调整
代码实战:
拿出iris数据集中两个数据[sepal length,sepal width]作为输入特征,进行分类:
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
iris = load_iris()
df = pd.DataFrame(iris.data,columns=iris.feature_names)
df['label'] = iris.target
df.columns = [
'sepal length', 'sepal width', 'petal length', 'petal width', 'label'
]
df.label.value_counts()
plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], label='0')
plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
plt.show()
data = np.array(df.iloc[:100, [0, 1, -1]])
X, y = data[:,:-1], data[:,-1]
y = np.array([1 if i == 1 else -1 for i in y])
# 数据线性可分,二分类数据
# 此处为一元一次线性方程
class Model:
def __init__(self):
self.w = np.ones(len(data[0]) - 1, dtype=np.float32)
self.b = 0
self.l_rate = 0.1
# self.data = data
def sign(self, x, w, b):
y = np.dot(x, w) + b
return y
# 随机梯度下降法
def fit(self, X_train, y_train):
is_wrong = False
while not is_wrong:
wrong_count = 0
for d in range(len(X_train)):
X = X_train[d]
y = y_train[d]
if y * self.sign(X, self.w, self.b) <= 0:
self.w = self.w + self.l_rate * np.dot(y, X)
self.b = self.b + self.l_rate * y
wrong_count += 1
if wrong_count == 0:
is_wrong = True
return 'Perceptron Model!'
perceptron = Model()
perceptron.fit(X, y)
x_points = np.linspace(4, 7, 10)
y_ = -(perceptron.w[0] * x_points + perceptron.b) / perceptron.w[1]
plt.plot(x_points, y_)
plt.plot(data[:50, 0], data[:50, 1], 'bo', color='blue', label='0')
plt.plot(data[50:100, 0], data[50:100, 1], 'bo', color='orange', label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
plt.show()
将蓝点,橙点进行分类
可见感知机模型完美地将两类实例点进行二分。
4.2算法的收敛性
现在证明,对于线性可分数据集感知机学习算法原始形式收敛,即经过有限次迭代可以得到一个将训练数据集完全正确划分的分离超平面及感知机模型。
为了方便叙述与推导,将偏置
Novikoff定理:设训练数据集
(1) 存在满足条件
(2) 令
Proof:
(1)由于训练数据集是线性可分的,按照定义,存在超平面可将训练数据集完全正确分开,取此超平面为
那么自然能在上述非负数集合中找到一个最小的非负数
使
(2)感知机算法从
若
即
下面推导两个不等式:
①
证明:
由此递推可得不等式
②
证明:
所以有不等式:
于是
定理表明,误分类的次数
4.3感知机算法的对偶形式
原始形式感知机每次梯度下降都是随机选取一个误分类点数据来更新
注意,为什么是
此时的参数不再是
这很容易理解,当该数据点误分类,那么让它继续分类次数
其中
其实就是对偶形式算法本质就是将
算法步骤
输入:训练数据
输出:
(1) 赋初值
(2) 选取数据点
(3) 判断该数据点是否为当前模型的误分类点,即判断若
(4) 转(2),直到训练集中没有误分类点
def fit( self,X,y):
#权重和偏置初始化
self.alpha = np.zeros ( X.shape[0])
self.b =0
train_complete_flag = False
Gram = np.dot(X, X.T) #存放样本两两内积的Gram矩阵(特点)
while not train_complete_flag:
error_count = 0
for i in range ( X.shape[0]):
x_, y_ = X[i],y[i]
#有—个点当分类错误时,更新alpha_i和偏置
tmp= np.dot(np.multiply(self.alpha,y),Gram[ :, i]) +self.b
#进行误分类点判断
if y_* tmp <= 0:
self.alpha[i] += self.lr
self.b += self.lr * y_
error_count += 1
if not error_count:
train_complete_flag = True#训练完成后计算权重
self.w = np.dot(np.multiply (self.alpha,y),X)
4.4原始形式与对偶形式
在向量维数(特征数)过高时(特征数n
在向量个数(样本数)过多时(特征数n
5.例子
如图所示,正实例点是
这里我们取初值
原始形式代码如下:
train = [[(3, 3), 1], [(4, 3), 1], [(1, 1), -1]]
w = [0, 0]
b = 0
# 使用梯度下降法更新权重
def update(data):
global w, b
w[0] = w[0] + 1 * data[1] * data[0][0]
w[1] = w[1] + 1 * data[1] * data[0][1]
b = b + 1 * data[1]
print(w, b)
# 计算到超平面的距离
def cal(data):
global w, b
res = 0
for i in range(len(data[0])):
res += data[0][i] * w[i]
res += b
res *= data[1]
return res
# 检查是否可以正确分类
def check():
flag = False
for data in train:
if cal(data) <= 0:
flag = True
update(data)
if not flag:
print("The result: w: " + str(w) + ", b: "+ str(b))
return False
flag = False
for i in range(1000):
check()
if check() == False:
break
[3, 3] 1
[2, 2] 0
[1, 1] -1
[0, 0] -2
[3, 3] -1
[2, 2] -2
[1, 1] -3
The result: w: [1, 1], b: -3
对偶形式代码如下:
import numpy as np
train = np.array([[[3, 3], 1], [[4, 3], 1], [[1, 1], -1]])
a = np.array([0, 0, 0])##为什么是三维的,三维对应三个数据点,这里代表每一个数据点误分类次数,实际是ki*学习率
b = 0
lr = 1
Gram = np.array([])
y = np.array(range(len(train))).reshape(1, 3)# 标签
x = np.array(range(len(train) * 2)).reshape(3, 2)# 特征
# 计算Gram矩阵
def gram():
g = np.array([[0, 0, 0], [0, 0, 0], [0, 0, 0]])
for i in range(len(train)):
for j in range(len(train)):
g[i][j] = np.dot(train[i][0], train[j][0])
return g
# 更新权重
def update(i):##更新第i个误分类点
global a, b
a[i] = a[i] + lr
b = b + lr * train[i][1]
print(a, b)
# 计算到超平面的距离
def cal(key):
global a, b, x, y
i = 0
for data in train:
y[0][i] = data[1]
i = i + 1
temp = a * y
res = np.dot(temp, Gram[key])
res = (res + b) * train[key][1]
return res
# 检查是否可以正确分类
def check():
global a, b, x, y
flag = False
for i in range(len(train)):
if cal(i) <= 0:
flag = True
update(i)
if not flag:
i = 0
for data in train:
y[0][i] = data[1]
x[i] = data[0]
i = i + 1
temp = a * y
w = np.dot(temp, x)
print("The result: w: " + str(w) + ", b: "+ str(b))
return False
flag = False
Gram = gram()# 初始化Gram矩阵
for i in range(1000):
check()
if check() == False:
break
[1 0 0] 1
[1 0 1] 0
[1 0 2] -1
[1 0 3] -2
[2 0 3] -1
[2 0 4] -2
[2 0 5] -3
The result: w: [[1 1]], b: -3
6.总结
本章介绍了统计学习中最简单的一种算法——感知机,对现在的机器学习理论来说,这个算法的确是太简单了,但这样简单的东西却是很多现在流行算法的基础,比如神经网络,比如支持向量机,需要好好掌握。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)