1.4-机器学习分类算法-逻辑回归算法

1. Logistic Regression逻辑回归(对数几率回归)

1.1 逻辑回归算法基本原理

其实机器学习主要做的事情就是数值预测分类。数值预测一般用的都是回归模型,比如线性回归(所谓的回归就是想办法弄个函数来根据输入预测输出)什么的。但是这里的逻辑回归是一种非常特殊的回归,逻辑回归是使用回归的思路来解决分类的问题。

假设我们现在得到n个训练数据特征向量,。这n个特征向量对应了一个类别编号。这n个特征向量,每个中均包含m个特征,即第i个特征向量可以表示为,如果用回归方法建立拟合曲线,预估,则就是要想办法建立一个如下的曲线方程: 但是分类问题是一个离散的问题,因此我们还需要找一个阶跃函数来做一个映射,如单位阶跃函数: 但是直接整个这个阶跃函数在z=0处不连续不利后续计算。因此逻辑回归算法中采用了Sigmoid函数 该函数图像如下图所示

import matplotlib.pyplot as plt
import numpy as np
plt.figure(figsize=(8, 8))#设置画布大小
ax = plt.gca()
xfit = np.linspace(-20, 20)
plt.grid(ls=":",c='b',)#打开坐标网格
plt.plot(xfit, 1/(1+np.e**(-xfit)), '-k')
[<matplotlib.lines.Line2D at 0x1130882b0>]

这个函数具有一个非常棒的地方在于自变量的取值范围为,但是值域为(0,1)。因此我们可以将它当作概率值!确定了这个之后我们就可以使用最大似然函数的方法来想办法计算当中的

我们将g(z)当作该测试数据的后验概率,则 对于上面的公式,我们可以进一步统一成一个公式,即:

那么,问题转换成为了当给定一个训练样本集,现在就是要想办法让该样本集的联合后验概率(似然函数)最大。

两边同时取对数得

事实上,-l(W)就可以当作机器学习中常用的代价函数(将随机事件或其有关随机变量的取值映射为非负实数以表示该随机事件的“风险”或“损失”的函数)。这里我们可以令:

代价函数的曲线图如下图所示

import matplotlib.pyplot as plt
import numpy as np
plt.figure(figsize=(8, 8))#设置画布大小
ax = plt.gca()
xfit = np.linspace(0, 1)
plt.grid(ls=":",c='b',)#打开坐标网格
plt.plot(xfit, -np.log(xfit), '-k') #y=1时的损失函数图
plt.plot(xfit, -np.log(1-xfit), '-k') #y=0时的损失函数图
/Users/jus4fun/Library/Python/3.7/lib/python/site-packages/ipykernel_launcher.py:7: RuntimeWarning: divide by zero encountered in log
  import sys
/Users/jus4fun/Library/Python/3.7/lib/python/site-packages/ipykernel_launcher.py:8: RuntimeWarning: divide by zero encountered in log






[<matplotlib.lines.Line2D at 0x11336fb00>]

png

从上图中,我们可以发现,该损失函数当样本值是1的时候,预估值越接近1,该损失函数越小。当样本值是0时,预估函数越接近0该损失函数越小。因此,接下来我们要做的就是根据这个损失函数,使用梯度下降法求出最优的W。因此,我们应该沿着梯度负方向逐渐调整权重分量wj,直到得到最小值。梯度下降的偏导数推导过程如下:

上式推导的过程中用到了Sigmoid函数一个非常重要的性质:

至此,更新权重的公式为:

权重向量的一般选择接近于0的随机值。

参考链接: - https://www.cnblogs.com/VitoLin21/p/11395753.html 逻辑回归(Logistic Regression)详解,公式推导及代码实现 - https://blog.csdn.net/zjuPeco/article/details/77165974 逻辑回归 - https://blog.csdn.net/qq_39355550/article/details/81809467 极大似然函数解释

1.2 逻辑回归算法实例

1.2.1 使用 Logistic 回归在简单数据集上的分类

给定数据集TestSet.txt,其数据格式如下:

-0.017612   14.053064   0
-1.395634   4.662541    1
-0.752157   6.538620    0
-1.322371   7.152853    0
0.423363    11.054677   0
0.406704    7.067335    1
0.667394    12.741452   0

1.首先从文件中读取数据集,该数据集一共一百条数据,我们将80条用作训练,20条用作测试。

def getTrainData():
    with open("./attach/1.4/TestSet.txt","r") as f:
        fileContent=f.read()
    fileContent = fileContent.split("\n")
    fileContent.pop()#删除最后一个空行
    trainDataSet = []
    for item in fileContent:
        x1 = float(item.split("\t")[0])
        x2 = float(item.split("\t")[1])
        classify = int(item.split("\t")[2])
        trainDataSet.append([x1,x2,classify])
    return trainDataSet
DataSet = getTrainData()
testDataMat = np.array(DataSet[80:100])[:,0:2]
testDataLabel = np.array(DataSet[80:100])[:,2:3]
trainDataMat = np.array(DataSet[0:80])[:,0:2]
trainDataLabel = np.array(DataSet[0:80])[:,2:3]
#print(trainDataMat)

2.两种数据的分布情况如下所示

import matplotlib.pyplot as plt
plt.xlabel("x1", fontsize=16)
plt.ylabel("x2", fontsize=16)
num=0
for item in trainDataMat:
    if trainDataLabel[num]==0:
        plt.plot(item[0], item[1], 'o', color='green', markeredgewidth=2, markersize=10)
    else:
        plt.plot(item[0], item[1], 'o', color='red', markeredgewidth=2, markersize=10)
    num+=1

3.为方便后续使用,提前定义sigmoid函数

import numpy as np
def sigmoid(x):
    return 1.0/(1+np.exp(-x))
#print(sigmoid(0))

4.设计最大似然函数,使用梯度上升法求权值

def gradientAscent(trainDataMat,trainDataLabel):
    trainDataMat = np.mat(trainDataMat) #把数组转换成矩阵方便后续计算
    trainDataLabelMat = np.mat(trainDataLabel) #将标签转换成列向量
    alpha=0.001#设置训练步长
    cyclesTime = 10000 #设置循环迭代次数
    m,n = np.shape(trainDataMat) #获取特征矩阵的行/列数
    weightMat = np.mat(np.ones((n,1))) #初始化权重矩阵
    for i in range(cyclesTime):
        s = sigmoid(trainDataMat*weightMat) #使用矩阵乘法计算z,带入sigmoid函数
        error = (trainDataLabelMat-s)
        #print(np.shape(trainDataLabelMat))
        weightMat = weightMat+alpha * trainDataMat.transpose() * error
    return weightMat
weightMat=gradientAscent(trainDataMat,trainDataLabel)
#我是个憨憨,写到这里我发现我忘了算w0了,正确算法应该是要再两个特征值前面,加一个值为1的列向量。不过没关系。不影响算法理解
def testForLR(testDataMat,testDataLabel):
    num=0
    for item in testDataMat:
        z= np.dot(item,weightMat)
        res = sigmoid(z)
        if res>0.4: #本来应该是跟0.5比的,我个憨憨没算w0
            res=1
        else:
            res=0
        if res==testDataLabel[num]:
            print("预测正确")
        else:
            print("预测错误")
        #print(res,testDataLabel[num])
        num+=1
testForLR(testDataMat,testDataLabel)
预测正确
预测正确
预测正确
预测错误
预测正确
预测正确
预测错误
预测正确
预测错误
预测正确
预测正确
预测正确
预测正确
预测正确
预测正确
预测正确
预测正确
预测正确
预测正确
预测正确