[自然语言处理先修]四、感知机实现

四、感知机实现

学习路线参考:

https://blog.51cto.com/u_15298598/3121189

https://github.com/Ailln/nlp-roadmap

https://juejin.cn/post/7113066539053482021

本节学习使用工具&阅读文章:

https://blog.csdn.net/gracejpw/article/details/102289684

1. 数据集准备

使用sklearn库的make_blobs函数,该函数可以用来随机生成聚类数据集。X保存生成的点的坐标,y保存点的类别(此处为2聚类,因此类别为0或1)。

from sklearn.datasets import make_blobs
X, y = make_blobs(n_samples=20, centers=2, random_state=0)

重要参数:

  • n_samples:生成的点的数量,默认值为100。支持int或数组类,如果为int,则为在簇之间平均分配的点总数。 如果是数组,则序列中的每个元素表示每个簇的样本数。
  • n_features:每个样本的特征数量。默认值为2,int属性。
  • centers:要生成的中心数或固定的中心位置,默认为None。int或形状数组[n_centers,n_features], 如果n_samples是一个int且center为None,则将生成3个中心。 如果n_samples是数组类,则中心必须为None或长度等于n_samples长度的数组。
  • shuffle:样本洗牌。默认为Ture。
  • random_state:选择用于创建数据集的随机数生成。默认为None,int属性。

展示生成的散点图:

import matplotlib.pyplot as plt
plt.scatter(X[:, 0], X[:, 1], c=y)

其中X[:,0]表示X中的第一维度,即横坐标;X[:,1]表示X中的第二维度,即纵坐标。参数c表示着色方案。生成的散点图如下图所示:

img

2. 网络定义

  1. 前向传播

    已知感知机的计算公式为:\(y = \theta(WX+b)\)\(\theta\)为激活函数。

    此处取激活函数为sigmoid函数,计算公式为\(\theta(x)=\frac{1}{1+e^{-x}}\)

    因此定义前向传播函数:

    import numpy as np
    
    def forward(W, b, X):
      z = np.matmul(W, X) + b
      a = 1/(1 + np.exp(-z))
      return a
    
  2. 反向传播(梯度更新)

    1. 损失函数与代价函数

      这里选用L2范数作为损失函数,也被称为最小平方误差(LSE)。

      定义为:\(L=\sum^n_i(y_i-pred_i)^2 = \sum(Y-pred)^2\)

      def loss(Y, pred):
        return np.sum(np.square(Y-pred))
      

      损失函数是算的是单个样本的误差之和;而代价函数是所有样本误差的平均,也就是损失函数的平均。这里的代价函数即为:\(J(W, b)={1\over 2n}\sum(Y-pred)^2\)

      因此定义代价函数:

      def cost(loss, n):
        return 1/(2*n)*loss
      
    2. 梯度计算

      W和b的梯度即代价函数分别对W和b求偏导的结果,以下为推导过程:

      \(dW = {\partial J(W, b)\over\partial W} = {\partial {1\over 2n}\sum(Y-pred)^2\over\partial W}=(Y-pred)*{\partial pred\over\partial W}\)

      由于sigmoid函数有性质\(\theta'(x)=\theta(x)[1-\theta(x)]\),因此:

      \({\partial pred\over\partial W}={\partial \theta(WX+b)\over\partial W}=X*pred*(1-pred)\)

      \(dW = (Y-pred)*X*pred*(1-pred)\)

      同理有\(db = (Y-pred)*pred*(1-pred)\)

      因此定义梯度计算函数:

      def grad(X, pred, Y):
        dw = (Y-pred)*X*(1-pred)*pred
        db = (Y-pred)*(1-pred)*pred
        return dw,db
      
  3. 参数定义

    # 随机初始化W和b的值
    W = np.random.randn(1,X.shape[1])
    b = np.random.randn(X.shape[1])
    
    # 学习率
    lr = 0.1
    
    # 梯度下降迭代次数
    epoch = 1000
    
    # 数据集大小
    n = X.shape[0]
    
    # 停止迭代条件
    eps = 0.000001
    
  4. 模型训练

    c_old = 0
    for i in range(epoch): #
      c=0 # cost值
      dW=0 # W梯度
      db=0 # b梯度
      for j in range(n): # 对整个数据集进行遍历
        X_j = X[[j],:].T # 取当前数据,求转置是为了方便计算
        y_j = y[j] # and its label # 取当前标签
        
        pred = forward(W, b, X_j) # 计算前向传播结果,即当前预测结果
        l = loss(y_j, pred)
        c += cost(l, n) # 计算cost值
        dW_j,db_j = grad(X_j, pred, y_j) # 计算当前位置梯度
        dW += 1/n*dW_j # 加入W梯度
        db += 1/n*db_j # 加入b梯度
      
      # 更新W和b的值
      W = W-lr*dW.T
      b = b-lr*db.T           
      
      if i%100==0: # 每迭代100次输出当前的迭代次数与loss
        print("learning for epoch",i,"loss",c)
    
      if (abs(c_old - c) < eps): # 当loss更新值已经低于阈值时结束迭代
        break
      c_old = c
    
    # 输出最终结果
    print("learnt W:", W, "at epoch", i)
    print("learnt b:", b, "at epoch", i)
    
  5. 结果

    learning for epoch 0 loss 0.05921214575551626
    learning for epoch 100 loss 0.033531747514652946
    learning for epoch 200 loss 0.03255085316160974
    learning for epoch 300 loss 0.031832217528882635
    learning for epoch 400 loss 0.03127198384976106
    learning for epoch 500 loss 0.030819719755428104
    learning for epoch 600 loss 0.03044470565747375
    learning for epoch 700 loss 0.030127106982650278
    learning for epoch 800 loss 0.029853510549783416
    learning for epoch 900 loss 0.029614496293245248
    learnt W: [[ 1.06595926 -1.41520381]] at epoch 999
    learnt b: [[1.08729336]] at epoch 999
    

    最终的Loss值:0.029405230322307418

posted @ 2023-03-07 21:44  无机呱子  阅读(7)  评论(0编辑  收藏  举报