讲完线性回归本来应该乘热打铁讲讲逻辑回归的,但是我觉得应该先讲讲感知机的原理,它不仅可以帮助理解分类和回归,也能帮助理解逻辑回归。
引言
上一篇博客讲解了线性回归,我们知道线性模型可以表示为
,由于线性模型的输出y值是连续的数值类型,所以它用来做回归分析,那么线性模型能否用来做分类任务呢?当然可以!只需要将线性模型的输出y离散化就可以了。
感知机原理
感知机就实现了用线性模型来完成分类任务,那么它是怎么实现这个功能的?通过一个sign()函数将线性模型的输出y进行处理,使得最终的输出变成[1,-1],1和-1各代表一个类别,这样就相当于一个二分类的模型了
- 数学模型
感知机的数学模型也比较简单,可以表示为如下形式
注:sign()函数也可以理解为神经网络中的激活函数
- 感知机思想
我们知道感知机是为了实现分类任务而设计的,那么它是如何进行二分类任务的?它在特征空间(特征向量x的集合)寻找一个将正负样本完全正确分离的超平面,超平面的两侧即为分类的两个类别。下图直观的表现了感知机的思想
超平面是指在n维空间中的一个n-1维平面,例如三维空间的超平面是一个二维平面,二维空间的超平面是一条直线。为什么是平面而不是曲面呢?因为在进入激活函数之前的模型是线性模型。
模型求解
感知机的模型求解就是找到一个超平面将两个类别分离开,那么我们期望错误分类的样本到超平面的距离之和最小,最好小到为0,那么就没有错误分类的样本点了。
- 点到直线距离
错分样本点到超平面的距离可以根据数学中点到平面距离公式求得,如下
简化后的公式如下
- 损失函数
根据感知机的数学公式可知,wx+b>0则y=1,wx+b<0则y=-1,那么y(wx+b)<0即可表示样本错分,因此只需通过y(wx+b)的正负就可以判断样本是否错分。我们的目标是期望错误分类的样本到超平面的距离之和最小,损失函数可以设计如下
注:损失函数设计时将距离公式中的
简化掉了,损失函数只计算错分的样本点,由于错分样本存在y(wx+b)<0,所以在前面添加一个负号,整个优化目标就变成最小化损失函数了,其实不加负号也可以,那么优化目标就是最大化损失函数
- 最优化方法
求解模型即需要最小化损失函数L(w,b),这里主要采用梯度下降法来实现,因为损失函数定义的是只有误分类样本才能参与,所以不能使用批量梯度下降法(BGD),只能使用随机梯度下降法(SGD)。
这里梯度下降法的实现有两种形式,不同形式是为了应对不同的情况,下面会详细介绍
1)原始形式
注:最小化损失函数时,更新梯度本来是w=w-梯度,但是损失函数前有负号,求导后的梯度前也有负号,最终就变成加号了
2)对偶形式
当特征空间的维度很高(x的维度很高)时,原始形式会带来很大的计算量,为了减少计算量将原始形式进行改进得到了对偶形式
代码实现
def Perceptron(data, label, lr=0.001, iter=50):
'''
因为是随机梯度下降,所以是单样本进行梯度更新
:param data: 样本 (m,n) m个样本,每个样本n维
:param label: 标签 (m,1) m个样本的标签
:param lr: 学习率
:param iter: 迭代次数
:return: w,b
'''
m = data.shape[0]
n = data.shape[1]
W = np.zeros((n, 1)) # 权重 (n,1)
b = 0.0 # 偏置bias (1)
for k in range(iter):
for i in range(m):
x = np.array([data[i]]) # (1,n)
y = label[i]
d = -y*(np.dot(x, W) + b)
if d >= 0:
W = W + lr*y*np.transpose(x) # (n,1)
b = b + lr*y
return W, b
def test(data, label, W, b):
m = data.shape[0]
errorcount = 0.0
for i in range(m):
x = data[i]
y = label[i]
result = -y * (np.dot(x, W) + b)
if result >= 0:
errorcount += 1
accuracy = 1 - errorcount/m
return accuracy
if __name__ == '__main__':
#加载训练集和验证集
traindata, trainlabel = loadData('../Mnist/mnist_train.csv')
evaldata, evallabel = loadData('../Mnist/mnist_test.csv')
W, b = Perceptron(traindata, trainlabel)
accuracy = test(evaldata, evallabel, W, b)
print('accuracy rate is:', accuracy)
利用mnist数据进行训练(0-4为一类,5-9为另一类),最终测试结果如下图