Logistic回归 - ZhouXuyan/notes GitHub Wiki

Logistic回归

Logistic 回归 或者叫逻辑回归 虽然名字有回归,但是它是用来做分类的。其主要思想是: 根据现有数据对分类边界线(Decision Boundary)建立回归公式,以此进行分类。

Sigmoid 函数

我们想要的函数应该是: 能接受所有的输入然后预测出类别。例如,在两个类的情况下,上述函数输出 0 或 1.或许你之前接触过具有这种性质的函数,该函数称为 海维塞得阶跃函数(Heaviside step function),或者直接称为 单位阶跃函数。然而,海维塞得阶跃函数的问题在于: 该函数在跳跃点上从 0 瞬间跳跃到 1,这个瞬间跳跃过程有时很难处理。幸好,另一个函数也有类似的性质(可以输出 0 或者 1 的性质),且数学上更易处理,这就是 Sigmoid 函数。 Sigmoid 函数具体的计算公式如下:

在下面的链接可以对sigmoid函数有更直观的感受

https://www.desmos.com/calculator/bgontvxotm

Logistic 回归 原理

Logistic 回归 工作原理

  每个回归系数初始化为 1
  重复 R 次:
      计算整个数据集的梯度
      使用 步长 x 梯度 更新回归系数的向量
  返回回归系数

Logistic 回归 开发流程

  收集数据: 采用任意方法收集数据
  准备数据: 由于需要进行距离计算,因此要求数据类型为数值型。另外,结构化数据格式则最佳。
  分析数据: 采用任意方法对数据进行分析。
  训练算法: 大部分时间将用于训练,训练的目的是为了找到最佳的分类回归系数。
  测试算法: 一旦训练步骤完成,分类将会很快。
  使用算法: 首先,我们需要输入一些数据,并将其转换成对应的结构化数值;接着,基于训练好的回归系数就可以对这些数值进行简单的回归计      算,判定它们属于哪个类别;在这之后,我们就可以在输出的类别上做一些其他分析工作。

Logistic 回归 算法特点

  优点: 计算代价不高,易于理解和实现。
  缺点: 容易欠拟合,分类精度可能不高。
  适用数据类型: 数值型和标称型数据。

梯度上升算法个人理解

  已知:特征矩阵(m*n) 标签矩阵(m*1)        #特征矩阵共m条数据,每条数据n个特征;标签矩阵取值为0或1
  
  过程: 新建权重矩阵(n*1)  #n跟特征数相等,矩阵初始值全部为1

        定义步长alpha,迭代次数maxCycles

        for i in range(maxCycles):

           特征矩阵(m*n) * 权重矩阵(n*1) = 预测矩阵(m*1)

           Sigmoid(预测矩阵) - 标签矩阵(m*1) = 误差矩阵(m*1)

           权重矩阵(n*1)=权重矩阵(n*1) + alpha * 转置特征矩阵(n * m) * 误差矩阵(m*1)

随机梯度上升算法

梯度上升算法在每次更新回归系数时都需要遍历整个数据集,该方法在处理 100 个左右的数据集时尚可,但如果有数十亿样本和成千上万的特征,那么该方法的计算复杂度就太高了。一种改进方法是一次仅用一个样本点来更新回归系数,该方法称为 随机梯度上升算法。由于可以在新样本到来时对分类器进行增量式更新,因而随机梯度上升算法是一个在线学习(online learning)算法。与 “在线学习” 相对应,一次处理所有数据被称作是 “批处理” (batch) 。

随机梯度上升算法可以写成如下的伪代码:

  所有回归系数初始化为 1
  对数据集中每个样本
      计算该样本的梯度
      使用 alpha x gradient 更新回归系数值
  返回回归系数值

随机梯度上升代码实现

  # 随机梯度上升
  # 梯度上升优化算法在每次更新数据集时都需要遍历整个数据集,计算复杂都较高
  # 随机梯度上升一次只用一个样本点来更新回归系数
  def stocGradAscent0(dataMatrix, classLabels):
      m,n = shape(dataMatrix)
      alpha = 0.01
      # n*1的矩阵
      # 函数ones创建一个全1的数组
      weights = ones(n)   # 初始化长度为n的数组,元素全部为 1
      for i in range(m):
          # sum(dataMatrix[i]*weights)为了求 f(x)的值, f(x)=a1*x1+b2*x2+..+nn*xn,此处求出的 h 是一个具体的数值,而不是一个矩阵
          h = sigmoid(sum(dataMatrix[i]*weights))
          # print 'dataMatrix[i]===', dataMatrix[i]
          # 计算真实类别与预测类别之间的差值,然后按照该差值调整回归系数
          error = classLabels[i] - h
          # 0.01*(1*1)*(1*n)
          print weights, "*"*10 , dataMatrix[i], "*"*10 , error
          weights = weights + alpha * error * dataMatrix[i]
      return weights

NOTE:随机梯度上升算法与梯度上升算法在代码上很相似,但也有一些区别: 第一,后者的变量 h 和误差 error 都是向量,而前者则全是数值;第二,前者没有矩阵的转换过程,所有变量的数据类型都是 NumPy 数组。

针对这个问题,我们改进了之前的随机梯度上升算法,如下:

  # 随机梯度上升算法(随机化)
  def stocGradAscent1(dataMatrix, classLabels, numIter=150):
      m,n = shape(dataMatrix)
      weights = ones(n)   # 创建与列数相同的矩阵的系数矩阵,所有的元素都是1
      # 随机梯度, 循环150,观察是否收敛
      for j in range(numIter):
          # [0, 1, 2 .. m-1]
          dataIndex = range(m)
          for i in range(m):
              # i和j的不断增大,导致alpha的值不断减少,但是不为0
              alpha = 4/(1.0+j+i)+0.0001    # alpha 会随着迭代不断减小,但永远不会减小到0,因为后边还有一个常数项0.0001
              # 随机产生一个 0~len()之间的一个值
              # random.uniform(x, y) 方法将随机生成下一个实数,它在[x,y]范围内,x是这个范围内的最小值,y是这个范围内的最大值。
              randIndex = int(random.uniform(0,len(dataIndex)))
              # sum(dataMatrix[i]*weights)为了求 f(x)的值, f(x)=a1*x1+b2*x2+..+nn*xn
              h = sigmoid(sum(dataMatrix[dataIndex[randIndex]]*weights))
              error = classLabels[dataIndex[randIndex]] - h
              # print weights, '__h=%s' % h, '__'*20, alpha, '__'*20, error, '__'*20, dataMatrix[randIndex]
              weights = weights + alpha * error * dataMatrix[dataIndex[randIndex]]
              del(dataIndex[randIndex])
      return weights

上面的改进版随机梯度上升算法,我们修改了两处代码。

第一处改进为 alpha 的值。alpha 在每次迭代的时候都会调整,这回缓解上面波动图的数据波动或者高频波动。另外,虽然 alpha 会随着迭代次数不断减少,但永远不会减小到 0,因为我们在计算公式中添加了一个常数项。

第二处修改为 randIndex 更新,这里通过随机选取样本拉来更新回归系数。这种方法将减少周期性的波动。这种方法每次随机从列表中选出一个值,然后从列表中删掉该值(再进行下一次迭代)。

参考:https://github.com/apachecn/AiLearning/blob/dev/blog/ml/5.Logistic%E5%9B%9E%E5%BD%92.md#%E6%B3%A8%E6%84%8F