自己动手写感知机


  • 自己动手写感知机
  • 1 什么是感知机?
  • 2 感知机模型
  • 3 感知机学习算法
  • 4 总结


1 什么是感知机?

感知机(perceptron)是一种二分类的线性分类模型,可以将所有输入的实例划分为True或是False两类。感知机模型的输入向量是实例的特征向量,其任务就是在N维空间中寻找一个平面,让这个平面可以正好将实例划分为正负两类,这个平面我们称其为超平面。感知机是神经网络与支持向量机的基础。

2 感知机模型

输入空间:χ⊆Rn χ ⊆ R n

输出空间:y={+1,−1} y = { + 1 , − 1 }

输入空间到输出的函数:


f(x)=sign(w⋅x+b) f ( x ) = s i g n ( w ⋅ x + b )


其中:



sign(x)={+1−1x≥0x<0 s i g n ( x ) = { + 1 x ≥ 0 − 1 x < 0


几何解释:

函数中的w w 可以理解为超平面的法向量,bb为超平面的截距,超平面将整个空间分为两部分,就是分类出来的正、负两类。

3 感知机学习算法

感知机学习算法的具体策略就是使用误分类点到超平面的距离和为损失函数,并使用梯度下降法最小化损失函数,即可求解出超平面。

  • 原始形式
    首先初始化所有参数为0,然后根据梯度下降法,用每个误分类点来更新参数,封装成类:
class PreceptronClassifier:
  def __init__(self,learning_rate):
      self.b = 0
      self.w = []
      self.rate = learning_rate

  def func(self, x):
      # 定义模型
      res = 0.0
      for i in range(len(x)):
          res += self.w[i]*x[i]
      res += self.b
      return res

  def update(self,error_x,error_y):
      # 更新参数
      for i in range(len(error_x)):
          self.w[i] += self.rate*error_x[i]*error_y
      self.b += self.rate*error_y

  def error_label(self,temp_res:list,trainY):
      # 标记误分类点
      for i in range(len(self.w)):
          if temp_res[i] == trainY[i]:
              temp_res[i] = 0
          else:

              temp_res[i] = 1
      return temp_res

  def fit(self, trainX,trainY):
      # 初始化参数
      self.w = [0]*len(trainX[0])
      self.b = 0
      temp_res = self.prediction(trainX)
      temp_res = self.error_label(temp_res, trainY)
      train_iter = 0
      while(1 in temp_res):
          # 显示迭代过程
          print('第'+str(train_iter)+'次迭代','w:',self.w,'b:',self.b)
          error_index = temp_res.index(1)
          self.update(trainX[error_index],trainY[error_index])
          temp_res = self.prediction(trainX)
          temp_res = self.error_label(temp_res, trainY)

  def prediction(self, testX):
      # 进行预测
      res  = []
      for x in testX:
          res.append(1 if self.func(x) > 0 else -1 )
      return res


if __name__ == '__main__':
  # 准备训练数据和测试数据
  trainX = [[3,3],[4,3],[1,1]]
  trainY = [1,1,-1]
  testX  = [[1,2],[3,4]]
  # 创建对象,指定学习率
  pc = PreceptronClassifier(0.5)
  pc.fit(trainX,trainY)       # 训练
  print(pc.prediction(testX)) # 测试
  • 对偶形式
    前面提到过,每次更新w w 参数,使用w=w+ηyixiw=w+ηyixi 这个式子是模型对求的偏导,η η 表示学习率。
    所以相对于每一个实例,他们的yixi y i x i 是不会变的,所以最终的的模型参数可以表示为:
    w=∑i−1Nαiyixib=∑i−1Nαiyi w = ∑ i − 1 N α i y i x i b = ∑ i − 1 N α i y i
    αi α i 表示第i i <script type="math/tex" id="MathJax-Element-16">i</script>个点被误分类用来更新模型的次数,这就是对偶形式,下面放代码:
class PreceptronClassifier:
  def __init__(self,learning_rate):
      self.b = 0
      self.w = []
      self.rate = learning_rate

  def func(self, x):
      # 定义模型
      res = 0.0
      for i in range(len(x)):
          res += self.w[i]*x[i]
      res += self.b
      return res

  def update(self,error_index, trainY):
      # 更新参数
      self.a[error_index] += 1
      for i in range(len(self.w)):
          self.w[i] += self.rate*self.matrix[error_index][i]
      self.b += self.rate*trainY[error_index]

  def error_label(self,temp_res:list,trainY):
      # 标记误分类点
      for i in range(len(self.w)):
          if temp_res[i] == trainY[i]:
              temp_res[i] = 0
          else:

              temp_res[i] = 1
      return temp_res

  def fit(self, trainX,trainY):
      # 初始化参数
      self.w = [0]*len(trainX[0])
      self.a = [0]*len(trainX)
      self.b = 0
      self.matrix = []
      for i in range(len(trainX)):
          self.matrix.append([xi*trainY[i] for xi in trainX[i]])
      temp_res = self.prediction(trainX)
      temp_res = self.error_label(temp_res, trainY)
      train_iter = 0
      while(1 in temp_res):
          train_iter += 1
          error_index = temp_res.index(1)
          self.update(error_index,trainY)
          temp_res = self.prediction(trainX)
          temp_res = self.error_label(temp_res, trainY)
          print('第'+str(train_iter)+'次迭代','w:',self.w,'b:',self.b)

  def prediction(self, testX):
      # 进行预测
      res  = []
      for x in testX:
          res.append(1 if self.func(x) > 0 else -1 )
      return res


if __name__ == '__main__':
  # 准备训练数据和测试数据
  trainX = [[3,3],[4,3],[1,2]]
  trainY = [1,1,-1]
  testX  = [[1,2],[3,4]]
  # 创建对象,指定学习率
  pc = PreceptronClassifier(0.5)
  pc.fit(trainX,trainY)       # 训练
  print(pc.prediction(testX)) # 测试

4 总结

感知机模型简单且易于实现,是入门级算法,同时又是神经网络和支持向量机的基础。比如说神经网络的全连接层就和感知机很相似,所以这个算法还是值得理解一下的。

初学乍练,请多多指正!