python计算学习,《统计学习方法》的Python实现:(1)感知机

0. 假装有一个前言

前几天看到有人转李航老师的《统计学习方法》python 3.6实现,突然发现书我是看了一半了,代码却只写过第三章的

math?formula=k近邻法。(不要问我为什么现在才看了一半,也不要问我为什么不一边看一边写)

1. 感知机原理

c59c1f19cc69

赶只鸡(划掉)

感知机(Perceptron)是二分类的线性分类模型,只适用于线性可分的二分类问题。

c59c1f19cc69

线性二分类问题

输入

输出

模型类型

参数意义

特征向量

类别(

math?formula=%5Cpm1)

判别模型

超平面参数

感知机的损失函数为所有误分类点到分类超平面的距离之和,因此算法是误分类驱动的,正确分类的点不会对算法的结果做出贡献。

2. 感知机学习算法的两种形式

2.1 原始形式

使用随机梯度下降法,针对每个误分类使其梯度下降。

算法2.1_感知机学习算法的原始形式

输入:训练数据集

math?formula=T%3D%5C%7B(x_1%2Cy_1)%2C(x_2%2Cy_2)%2C...%2C(x_N%2Cy_N)%5C%7D ;学习率

math?formula=%5Ceta(0%3C%5Ceta%5Cleq1)

输出:

math?formula=w%2Cb;感知机模型

math?formula=f(x)%3D%5Ctext%7Bsign%7D(w%5Ccdot%20x%2Bb).

1 选取初值

math?formula=w_0%2Cb_0

2 在训练集中选取数据

math?formula=(x_i%2Cy_i)

3 如果

math?formula=y_i(w%5Ccdot%20x%2Bb)%5Cleq%200

math?formula=w%20%5Cleftarrow%20w%2B%5Ceta%20y_ix_i%20%5C%5C%20b%20%5Cleftarrow%20b%2B%20%5Ceta%20y_i

4 如果训练集中存在误分类点,转至 2;否则,结束

def trainOri(self,yita = 0.1):

self.w = self.w0

self.b = self.b0

misDivision = True

self.yita = yita

self.k = 0

while misDivision:

for it in range(len(self.data)):

if self.label[it] * (np.dot(self.w, self.data[it]) + self.b) <= 0:

self.w += self.yita * self.label[it] * self.data[it]

self.b += self.yita * self.label[it]

self.k += 1

break

if it == len(self.data) - 1:

misDivision = False

原始形式这里没有问题,对1000个2维数据进行分类使用了

math?formula=0.14s,更新次数为

math?formula=7470

c59c1f19cc69

PerceptronOri

2.2 对偶形式

使用随机梯度下降法,针对每个误分类使其梯度下降。

算法2.2_感知机学习算法的对偶形式

输入:训练数据集

math?formula=T%3D%5C%7B(x_1%2Cy_1)%2C(x_2%2Cy_2)%2C...%2C(x_N%2Cy_N)%5C%7D ;学习率

math?formula=%5Ceta(0%3C%5Ceta%5Cleq1)

输出:

math?formula=%5Calpha%2C%5Cbeta;感知机模型

math?formula=f(x)%3D%5Ctext%7Bsign%7D(%5Csum_%7Bj%3D1%7D%5E%7BN%7D%5Calpha_jy_jx_j%5Ccdot%20x_i%20%2B%20%5Cbeta).

1 选取初值

math?formula=%5Calpha_0%3D%5C%7B%5Calpha_1%2C%5Calpha_2%2C...%5Calpha_N%5C%7D%3D%5C%7B0%2C...%2C0%5C%7D%2C%5Cbeta_0%3D0

2 在训练集中选取数据

math?formula=(x_i%2Cy_i)

3 如果

math?formula=y_i(%5Csum_%7Bj%3D1%7D%5E%7BN%7D%5Calpha_jy_jx_j%5Ccdot%20x_i%20%2B%20%5Cbeta)%5Cleq%200

math?formula=%5Calpha%20%5Cleftarrow%20%5Calpha%2B%5Ceta%20%5C%5C%20%5Cbeta%20%5Cleftarrow%20%5Cbeta%2B%20%5Ceta%20y_i

4 如果训练集中存在误分类点,转至 2;否则,结束

def trainDual(self, yita = 1):

self.alpha = self.alpha0

self.beta = self.beta0

gram = []

for it in self.data:

temp = []

for ot in self.data:

temp.append(np.dot(it,ot))

gram.append(temp)

misDivision = True

self.yita = yita

self.k = 0

self.kk = 0

while misDivision:

for it in range(len(self.data)):

temp = 0

self.kk +=1

if self.label[it] * (sum([self.alpha[i] * self.label[i] * gram[i][it] for i in range(len(self.data))]) + self.beta) <= 0:

self.alpha[it] += self.yita

self.beta += self.yita * self.label[it]

self.k += 1

break

if it == len(self.data) - 1:

misDivision = False

对偶形式这里问题就大了,等了一分钟还以为是条件给错进入死循环了,反复检查确认没有问题,心想,跑去吧(其实去刷知乎了)。于是就有了下面这张图:

c59c1f19cc69

PerceptrDual

等一下,说好的使用Gram矩阵可以降低运算量呢?同样更新了七千多次为什么你跑了三分钟啊?!差了2000倍有木有啊!

c59c1f19cc69

emm

2.3 问题分析

1) 从编程角度分析

冷静分析一下,Gram矩阵计算时间只需

math?formula=0.6s基本可以忽略不记,由于刚刚只统计了参数更新次数,我们重新统计一下两种算法第三步的判别步骤:

c59c1f19cc69

判别步骤

原始算法判别

math?formula=15133次,更新参数

math?formula=239次,耗时

math?formula=0.016s

对偶算法判别

math?formula=16300次,更新参数

math?formula=235次,耗时

math?formula=33.29s

由算法2.1,2.2可知,参数更新基本不消耗时间,也即大部分时间用于判别步骤。原始算法平均耗时

math?formula=1.05%5Ccdot%2010%5E%7B-6%7Ds,对偶算法平均耗时

math?formula=2ms。这中间也就差了,额,1931倍吧。

继续冷静分析,算法2.2中第三步计算量大的主要原因是有一个求积再求和的过程,这个过程也可以当作向量内积来计算,这样就实现了在一次参数更新前只计算一次

math?formula=%5Calpha_jy_j。这个部分书中没有提及,可能因为不属于算法而是计算方法的一部分吧。

更新对偶算法如下:

def trainDual(self, yita = 1):

self.alpha = self.alpha0

self.beta = self.beta0

gram = []

for it in self.data:

temp = []

for ot in self.data:

temp.append(np.dot(it,ot))

gram.append(temp)

gramA = np.array(gram)

misDivision = True

self.yita = yita

self.k = 0

self.kk = 0

while misDivision:

ay = np.array([self.alpha[x] * self.label[x] for x in range(len(self.alpha))])

for it in range(len(self.data)):

temp = 0

self.kk +=1

if self.label[it] * (np.dot(ay, gramA[it]) + self.beta) <= 0:

self.alpha[it] += self.yita

self.beta += self.yita * self.label[it]

self.k += 1

break

if it == len(self.data) - 1:

misDivision = False

同样,我们使用1000个2维数据进行测试,结果如下:

c59c1f19cc69

更新对偶算法之后

虽然对偶算法还是比原始算法慢了20倍左右,但最起码两者是接近量级的运行时间了。

2)从算法角度分析

样本包括三个属性:个数,特征向量尺寸和标签。对于感知机,标签与数据个数相同。Gram矩阵是将样本的特征向量两两做内积,当特征向量尺寸较大时对偶算法应该可以比原算法简化更多的计算量。

我们将数据由1000个2维数据提升为1000个1000维数据,此时两种算法结果对比如下:

c59c1f19cc69

1000维数据

虽然对偶算法依然慢于原始算法,但两者间的差距已经由30倍缩小到了2.5倍。

我们继续增加样本的维数,将维数提高到丧(gan)心(de)病(piao)狂(liang)的500,000维,为了硬盘着想,我们这次只生成了100条数据。

c59c1f19cc69

绝不会用记事本打开的文本文档

最终结果是,原始算法速度依然是对偶算法的两倍左右。

c59c1f19cc69

50w维数据

大概是我代码能力太烂?可能还需要进一步优化。另外1957年有500,000维的数据需要处理吗?

代码

P.S. 大概也许有可能还会更新

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值