什么是感知机
公式文字我也就不复现了,网上简直多如牛毛
(1)推荐看李航博士的《统计学习方法》
(2)或:https://www.jianshu.com/p/c91087e6e1ea(篇幅略小,简单了解)
第二篇文章篇幅较小,但基本介绍清楚了感知机的原始形式,想要进一步了解感知机的对偶形式,可自行搜索资料
以上两个推荐中,都是以两特征数据来分类的,也就是数据点和超平面可以在二维坐标系中呈现
于是我的代码也是如此,代码的可扩展性还是很大的,想要更高维度的数据可以自行修改
但是超过三维特征,将无法结合所有维度同时可视化
——————————————————————————————————————————————————
强烈建议仔细看书学懂了再来看代码!!!不要囫囵吞枣,之前我也是觉得自己看懂了,实际写代码就傻眼
好好想想参数怎么更新,怎么储存数据,怎么利用numpy矩阵…
- 全程都采用矩阵来计算,初始化函数中,参数矩阵是一个三维矩阵,其中最后一个是偏置b
- 创建numpy数组必须声明浮点数类型,否则当学习率不为1时,会导致参数更新自动取整,进而会出现迭代无法完成的情况。
- 包括收录数据时,也要声明为浮点类型,否则就导致数据自动变为取整型int32
def __init__(self):
self.learn_rate = 0.5 # 随意设置
self.w_b = np.array([[0],
[0],
[0]], dtype='float32') # 把三个参数放在一个矩阵中(w1, w2, b)
self.t_data = None
self.t_data_c = None # 因为要三个参数一起更新,观察书上的公式,其实就是y乘x1,x2,1,然后加到参数矩阵就行了所以又建立一个y值全是1的矩阵
- 数据收录,每个数据最后一个是1或-1,作为分类标记,然后每个数据以一行存入矩阵
- 理解参数如何更新后会发现,两个w1,w2是乘以y,然后+=
- b是直接加y
- 为了方便更新,我们建立两个train_data矩阵,第二个把y全部改为1,当是误分类点时,用于更新数据
def collect_data(self):
collect_1 = []
collect_2 = []
while True:
try: # 利用异常处理结束输入,别的没想到啥好办法。。
data = map(float, input("输入数据x1 x2 y(空格隔开)\
输入任意字母敲回车以结束:").split(' '))
data = list(data)
collect_1.append(copy.copy(data)) # 测试发现,下一行的更改会影响上一行,两个指向的变量地址一样,所以得copy一下
data[2] = 1
collect_2.append(data)
except ValueError:
print("数据收集完毕!")
break
self.t_data = np.array(collect_1, dtype='float32')
self.t_data_c = np.array(collect_2, dtype='float32')
- 参数更新迭代
- 终极目的是误分类点数为0,于是每次检测到一个误分类,便mistake += 1,直到某次更新后为0,跳出while
def gradient_descent(self):
print("开始迭代...")
number = 0
while True:
number += 1
# 每次都统计误分类的点数,直到没有为止,跳出循环,迭代结束
mistake = 0
for line, line_c in zip(self.t_data, self.t_data_c): # 一个用来判断是否误分类,一个用来更新参数矩阵
if line[2] * np.dot(line_c, self.w_b)[0] <= 0:
line_c = line_c * line[2] * self.learn_rate
print(line_c)
mistake += 1
self.w_b[0][0] += line_c[0]
self.w_b[1][0] += line_c[1]
self.w_b[2][0] += line_c[2]
if mistake == 0:
break
print('第{}次迭代\n参数w:{}'.format(number, self.w_b))
print('-----------------')
print("迭代完成!")
print('本次学习率:{},迭代次数:{}'.format(self.learn_rate, number))
- 数据可视化
- 这里注意,在写直线公式的时候,y(其实是x2),可能会出现系数为0的情况,而显然除数不能为0,所以这种情况要单独判断一下
if not self.w_b[1][0]: # 测试中发现,出现了x_2的系数为0的情况,这样的话绘图时就相当于除数为0了
x = -1 * self.w_b[2][0] / self.w_b[0][0]
plt.axvline(x, color='g')
else:
x = np.linspace(-10, 10, 10)
y = -1 * self.w_b[0][0] / self.w_b[1][0] * x + -1 * self.w_b[2][0] / self.w_b[1][0]
plt.plot(x, y, color='g')
具体分析差不多就是这样
——————————————————————————————————————————————————
完整代码如下
import numpy as np
import copy
import matplotlib.pyplot as plt
# 创建numpy数组必须声明浮点数类型,否则当学习率不为1时,会导致参数更新自动取整,进而会出现迭代无法完成的情况
# 包括收录数据时,也要声明为浮点类型,否则就导致数据自动变为取整型int32
class Perceptron:
def __init__(self):
self.learn_rate = 0.5 # 随意设置
self.w_b = np.array([[0],
[0],
[0]], dtype='float32') # 把三个参数放在一个矩阵中(w1, w2, b)
self.t_data = None
self.t_data_c = None # 因为要三个参数一起更新,观察书上的公式,其实就是y乘x1,x2,1,然后加到参数矩阵就行了所以又建立一个y值全是1的矩阵
def collect_data(self):
collect_1 = []
collect_2 = []
while True:
try: # 利用异常处理结束输入,别的没想到啥好办法。。
data = map(float, input("输入数据x1 x2 y(空格隔开)\
输入任意字母敲回车以结束:").split(' '))
data = list(data)
collect_1.append(copy.copy(data)) # 测试发现,下一行的更改会影响上一行,两个指向的变量地址一样,所以得copy一下
data[2] = 1
collect_2.append(data)
except ValueError:
print("数据收集完毕!")
break
self.t_data = np.array(collect_1, dtype='float32')
self.t_data_c = np.array(collect_2, dtype='float32')
def gradient_descent(self):
print("开始迭代...")
number = 0
while True:
number += 1
# 每次都统计误分类的点数,直到没有为止,跳出循环,迭代结束
mistake = 0
for line, line_c in zip(self.t_data, self.t_data_c): # 一个用来判断是否误分类,一个用来更新参数矩阵
if line[2] * np.dot(line_c, self.w_b)[0] <= 0:
line_c = line_c * line[2] * self.learn_rate # 更新方法对应着上面第二条注释
print(line_c)
mistake += 1
self.w_b[0][0] += line_c[0]
self.w_b[1][0] += line_c[1]
self.w_b[2][0] += line_c[2]
if mistake == 0:
break
print('第{}次迭代\n参数w:{}'.format(number, self.w_b))
print('-----------------')
print("迭代完成!")
print('本次学习率:{},迭代次数:{}'.format(self.learn_rate, number))
def visualize(self): # 以下绘图中的y并不是之前的那个y,其实是所谓的x_2。。
plt.figure(figsize=(8, 4))
x = 0
y = 0
if not self.w_b[1][0]: # 测试中发现,出现了x_2的系数为0的情况,这样的话绘图时就相当于除数为0了
x = -1 * self.w_b[2][0] / self.w_b[0][0]
plt.axvline(x, color='g')
else:
x = np.linspace(-10, 10, 10)
y = -1 * self.w_b[0][0] / self.w_b[1][0] * x + -1 * self.w_b[2][0] / self.w_b[1][0]
plt.plot(x, y, color='g')
for i in self.t_data:
if i[2] == 1:
plt.scatter(i[0], i[1], c='r', s=5)
else:
plt.scatter(i[0], i[1], c='b', s=5)
plt.xlim(-10, 10)
plt.ylim(-10, 10)
plt.xlabel('x(1)')
plt.ylabel('x(2)')
plt.show()
p = Perceptron()
p.collect_data()
p.gradient_descent()
p.visualize()
CxsGhost
发布了30 篇原创文章 · 获赞 76 · 访问量 2220
私信
关注