概述
在机器学习中,感知机(perceptron)是二分类的线性分类模型,属于监督学习算法。输入为实例的特征向量,输出为实例的类别(取+1和-1)。
感知机对应于输入空间中将实例划分为两类的分离超平面。感知机旨在求出该超平面,为求得超平面导入了基于误分类的损失函数,利用梯度下降法 对损失函数进行最优化(最优化)。
感知机的学习算法具有简单而易于实现的优点,分为原始形式和对偶形式。感知机预测是用学习得到的感知机模型对新的实例进行预测的,因此属于判别模型。
感知机由Rosenblatt于1957年提出的,是神经网络和支持向量机的基础。
假设输入空间(特征向量)为X=[x1,x2,x3,x…],输出空间为Y = [1,-1]。
输入 = X
表示实例的特征向量,对应于输入空间的点;
输出 = Y
表示示例的类别。
由输入空间到输出空间的函数为
称为感知机。其中,参数w叫做权值向量(weight),b称为偏置(bias)。
其中
sign为符号函数,即
感知机算法就是要找到一个超平面将我们的数据分为两部分。
超平面就是维度比我们当前维度空间小一个维度的空间, 例如:我们当前的维度是二维的空间(由数据维度确定,x有多少列就有多大的维度),那么超平面就是一维 的,即一条直线。如下图
上面的图片,其实就是初高中里学的东西,在直角坐标系中,画一条直线,把坐标系分为两部分,假设横坐标是x1,纵坐标是x2.那么直线就可以表示为w1×x1+w2×x2+b=0,这其实就是很简单的直线方程,假设f(x1,x2)表示这条直线,当把在直线上方的点的坐标带入直线中,会有f>0,那么带入直线下方的点的坐标时,会有f<0,这样就把一个空间分为两个部分了。当是三维时,可以想象一间房子,里面有一堵墙把房子分为了两部分,这里的墙就是一个超平面;当是更高维时,就不容易想象了,但是计算机能构造出一个超平面,把空间中的数据分为两个部分。
实现最简单的感知机
#引入必要的包
import paddle
print("本教程使用的paddle版本为:" + paddle.__version__)
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(0)
num=100
#生成数据集x1,x2,y0/1
#随机生成100个x1
x1=np.random.normal(6,1,size=(num))
#随机生成100个x2
x2=np.random.normal(3,1,size=(num))
#生成100个y
y=np.ones(num)
#将生成好的点放入到一个分类中
class1=np.array([x1,x2,y])
class1.shape
#接下来生成第二类点,原理跟第一类一样
x1=np.random.normal(3,1,size=(num))
x2=np.random.normal(6,1,size=(num))
y=np.ones(num)*(-1)
class2=np.array([x1,x2,y])
#看一下生成点的样子
print(class1.shape)
print(class2.shape)
print(class1)
上面数据的形状不便于我们操作数据,我们希望数据是这种类型的:[x1,x2,y],所以要转置一下
class1=class1.T
class2=class2.T
#再看一下生成点的样子
print(class1.shape)
print(class2.shape)
print(class1)
画图看一下
plt.scatter(class1[:,0],class1[:,1])
plt.scatter(class2[:,0],class2[:,1],marker='*')
接下来就要画线了,让一条直线(超平面)把上面数据分为两个部分。
先将两类数据都放到一个变量里面。
all_data = np.concatenate((class1,class2))
#将数据打乱
np.random.shuffle(all_data)
print(all_data)
print(all_data.shape)
#截取出坐标数据
train_data_x=all_data[:150,:2]
#截取出标签数据
train_data_y=all_data[:150,-1].reshape(150,1)
print(train_data_x.shape)
print(train_data_y.shape)
#将数据转化为tensor形式
x_data = paddle.to_tensor(train_data_x.astype('float32'))
y_data = paddle.to_tensor(train_data_y.astype('float32'))
要画出的直线线的公式是:w1 × x1+w2 × x2+b=0
下面初始化一个感知机
linear = paddle.nn.Linear(in_features=2, out_features=1)
#初始化一个优化函数帮助我们训练感知“鸡”
mse_loss = paddle.nn.MSELoss()
sgd_optimizer = paddle.optimizer.SGD(learning_rate=0.001, parameters = linear.parameters())
下面开始训练
total_epoch = 50000
#构建训练过程
for i in range(total_epoch):
y_predict = linear(x_data)
#获取loss
loss = mse_loss(y_predict, y_data)
#反向传播
loss.backward()
sgd_optimizer.step()
sgd_optimizer.clear_grad()
#获取w1
w1_after_opt = linear.weight.numpy()[0].item()
#获取w2
w2_after_opt = linear.weight.numpy()[1].item()
#获取b
b_after_opt = linear.bias.numpy().item()
#每1000次输出一次数据
if i%1000 == 0:
print("epoch {} loss {}".format(i, loss.numpy()))
print("w1 after optimize: {}".format(w1_after_opt))
print("w2 after optimize: {}".format(w2_after_opt))
print("b after optimize: {}".format(b_after_opt))
print("finished training, loss {}".format(loss.numpy()))
训练好后开始画直线
plt.scatter(class1[:,0],class1[:,1])
plt.scatter(class2[:,0],class2[:,1],marker='*')
x=np.arange(10)
#画线的公式
y=-(w1_after_opt*x)/w2_after_opt+b_after_opt
plt.plot(x,y)
从上图中可以看到训练出来的直线能够很好地把数据分为两部分。