机器学习入门学习笔记:(2.2)线性回归python程序实现

  上一篇博客中,推导了线性回归的公式,这次试着编程来实现它。(机器学习入门学习笔记:(2.1)线性回归理论推导
  我们求解线性回归的思路有两个:一个是直接套用上一篇博客最后推导出来的公式;另一个是使用梯度下降法来求解极值。如果数据量很大不建议采用第一个,采用后者能更有效地减小计算量。这篇博客后面的程序也采用的是后者。
  事先声明,这篇博客是我自己的学习笔记,代码参考了《机器学习实战》一书,小部分代码有所修改。代码以及数据集我打包上传好了,感兴趣的可以下载下来看看:
线性逻辑回归代码
(今天才发现csdn下载居然没有了0分资源,最少都要1分,有点坑啊)

  代码不难,简要介绍下,程序中有注释。
  按照顺序将代码加入logRegres.py。

导入数据集

# 导入数据集
def loadDataSet():
    dataMat = []
    labelMat = []
    fr = open('testSet.txt')
    for line in fr.readlines():
        lineArr = line.strip().split()
        dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
        labelMat.append(int(lineArr[2]))
    return dataMat, labelMat
  • 创建了列表dataMat和labelMat,分别用于存储特征变量对应的值和标签对应的值;
  • 数据集存放在testSet.txt,从中读取数据;
  • 逐行读取,遍历所有行;读取的每一行做一下分割,每行的最后一个数据是标签,前面的都是特征向量的数据。将其分开后,分别加入列表dataMat和labelMat中。

激励函数

# sigmoid函数
def sigmoid(inX):
    return 1.0 / (1 + exp(-inX))

  我在以前的博客中介绍了激励函数的概念。(机器学习入门学习笔记:(1)BP神经网络原理推导及程序实现
  如果只考虑两个类的情况下,我们的结果可以用0或1来表示,这种函数叫做阶跃函数。但是由于阶跃函数存在一个0到1的跳变的过程,这会导致不可导、不光滑,有时在数学上难以处理。所以我们采用sigmoid函数来代替激励函数,以便能在数学上更易处理。
  直接给出sigmoid的公式,不做赘述了:
  这里写图片描述

梯度上升法

  通常我们更多地接触到的是梯度下降法,这两种方法本质都是一样的。由于梯度是变化最快的方向,梯度下降法就是向最快的方向减小,而梯度上升法就是增大,最后两者分别收敛于局部最小值和局部最大值。
  什么时候用梯度下降法或者是梯度上升法呢?具体问题具体分析,如果我们用的是一个误差函数,我们希望这个误差尽可能小,那么就选择梯度下降法了,很简单的逻辑。

# 梯度上升法
def gradAscent(dataMatIn, classLabels):
    # 转换为numpy矩阵数据类型
    dataMatrix = mat(dataMatIn)
    labelMat = mat(classLabels).transpose()

    # 预设初始参数
    m, n = shape(dataMatrix)# 求出矩阵大小
    alpha = 0.001   # 步长
    maxCycles = 500 # 迭代500次

    # 使用梯度上升找到最优值
    weights = ones((n, 1))  # 权值
    for k in range(maxCycles):
        h = sigmoid(dataMatrix*weights)
        error = labelMat - h
        weights = weights + alpha * dataMatrix.transpose() * error
    return weights

  首先,输入的两个参数dataMatIn和classLabels默认是列表类型,后面要使用numpy进行矩阵运算,所以调用numpy的mat()函数将其转换为numpy下的矩阵数据类型;
  然后预设一些相关的参数,步长不宜太大,迭代次数设为500次;
  接着就是迭代了,不断更新权值,使用如下公式:
这里写图片描述
  上面这个是梯度上升的公式,如果是梯度下降的公式把加号改成减号就行。
  关于算法推导这里不做赘述,我以前在其他博客中也有有关梯度下降法的介绍。梯度下降法和梯度上升法本质上是一样的。(四旋翼姿态解算——梯度下降法理论推导
  算法不难,也可以自行查阅相关资料。
  只是看公式的话还是有点懵,梯度上升法的伪代码如下:

初始化权值,使所有回归系数为1 
重复n次(n表示迭代次数): 
    计算整个数据集的梯度
    使用alpha(步长)*gradient(梯度)套上面公式更新回归系数的向量
返回回归系数

  跟给出的代码中对一对,就很容易搞懂了。在命令行测试一下看看:
这里写图片描述

  另外还有一点,我们看看代码不难发现,可以很轻易地将这段程序改成梯度下降法,也就是将程序中的梯度更新代码的几个符号改一下,结果还是一样的。

画出边界

  只是得到了参数还不够直观,我们接着使用matplotlib来是数据可视化。

# 画出数据集和logistic回归的最佳你和直线的函数
def plotBestFit(weights):
    import matplotlib.pyplot as plt
    dataMat, labelMat = loadDataSet()
    dataArr = array(dataMat)
    n = shape(dataArr)[0]
    xcord1 = []
    ycord1 = []
    xcord2 = []
    ycord2 = []
    for i in range(n):
        if int(labelMat[i]) == 1:
            xcord1.append(dataArr[i, 1])
            ycord1.append(dataArr[i, 2])
        else:
            xcord2.append(dataArr[i, 1])
            ycord2.append(dataArr[i, 2])
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(xcord1, ycord1, s=30, c='red', marker='s')
    ax.scatter(xcord2, ycord2, s=30, c='green')
    x = arange(-3.0, 3.0, 0.1)
    y = (-weights[0] - weights[1] * x) / weights[2]
    ax.plot(x, y)
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.show()

  调用库,没什么好说的。无非就是把点全部画出来,最后又画了一条边界。
这里写图片描述
  权值返回来是一个numpy的矩阵类型,我们要先调用getA()方法将其转换为数组类型。

这里写图片描述
  分类结果挺不错的,只有很少的点被分错了。

随机梯度上升

  前面使用的梯度上升法也叫作批量梯度上升法(batch gradient ascent),它每一次更新回归系数时都要遍历整个数据集,如果数据集很大,计算量也会非常大。
那么一个改进方法就是一次仅仅使用一个样本点来更新回归系数,这个方法叫随机梯度上升法。
  随机梯度上升法的伪代码:

所有回归系数初始化为1
遍历数据中的每个样本:
    计算样本的梯度
    使用alpha(步长)*gradient(梯度)套上面公式更新回归系数的向量
返回回归系数值

  编写代码:

# 随机梯度上升算法
def stocGradAscent(dataMatIn, classLabels):
    # 转换为numpy矩阵数据类型
    dataMatrix = mat(dataMatIn)
    labelMat = mat(classLabels).transpose()
    m,n = shape(dataMatrix)
    alpha = 0.01
    weights = ones((n, 1))
    for i in range(m):
        h = sigmoid(sum(dataMatrix[i] * weights))
        error = labelMat[i] - h
        weights = weights + alpha *dataMatrix[i].transpose() * error  
    return weights

  这部分代码大体上跟前面梯度上升法是一样的,不做赘述。唯一的区别就是,没有了迭代次数,这里是遍历了整个数据集,使用每个样本来对参数进行更新而不是每个样本都计算一遍。

  测试一下:
这里写图片描述

这里写图片描述
  结果不太好,我们还需要修改程序。

改进算法

# 改进的随机梯度上升算法
def stocGradAscent_optimized(dataMatIn, classLabels, numIter=150):
    # 转换为numpy矩阵数据类型
    dataMatrix = mat(dataMatIn)
    labelMat = mat(classLabels).transpose()
    m, n = shape(dataMatrix)
    weights = ones((n, 1))
    for j in range(numIter):
        dataIndex = range(m)
        for i in range(n):
            alpha = 4 / (1.0 + j + i) + 0.01
            randIndex = int(random.uniform(0, len(dataIndex)))
            h = sigmoid(sum(dataMatrix[randIndex] * weights))
            error = classLabels[randIndex] - h
            weights = weights + alpha * dataMatrix[randIndex].transpose() * error
            del(dataIndex[randIndex])
    return weights
  • 首先,步长alpha不再是一个常数,随着迭代次数的增大不断减小。步长alpha的值后面还加了一个0.01,因为不能让它趋近于0,如果趋近于0了新数据的更新也几乎没有作用,乘积都是0。
  • 然后,这里样本选取不再是按照默认顺序选取了。随机选取数据集进行更新, 反复迭代多次。

试试看结果:
这里写图片描述

这里写图片描述
  效果跟批量梯度下降差不多了,但是随机梯度下降的计算量要少很多。这里使用的默认迭代次数只有150次。

测试算法

从疝气病症预测病马死亡的概率:

分析特征变量

def classifyVector(inX, weights):
    prob = sigmoid(sum(inX * weights))
    if prob > 0.5:
        return 1
    else:
        return 0

  以回归系数和输入的特征变量计算对应的sigmoid函数的值,如果大于0.5,就预测为1;如果小于0.5, 就预测为0。

测试结果

def colicTest():
    # 打开测试集和数据集
    frTrain = open('horseColicTraining.txt')
    frTest = open('horseColicTest.txt')

    # 导入训练集的特征变量和标签
    trainingSet = []
    trainingLabels = []
    for line in frTrain.readlines():
        currentLine = line.strip().split('\t')
        lineArr = []
        for i in range(21):
            lineArr.append(float(currentLine[i]))
        trainingSet.append(lineArr)
        trainingLabels.append(float(currentLine[21]))

    # 使用优化过后的随机梯度上升法计算权重
    trainWeights = stocGradAscent_optimized(trainingSet, trainingLabels, 1200)
    # 导入测试集,并检验效果
    errorCount = 0
    numTestVec = 0
    for line in frTest.readlines():
        numTestVec += 1
        currentLine = line.strip().split('\t')
        lineArr = []
        for i in range(21):
            lineArr.append(float(currentLine[i]))
        if int(classifyVector(array(lineArr), trainWeights)) != int(currentLine[21]):
            errorCount += 1
    errorRate = float(errorCount) / numTestVec
    print('The error rate of this test is : %f'%errorRate)
    return errorRate

  直接调用前面写好的函数了,不多说了。

  调用colicTest()10次,比较结果:

def multiTest():
    numTests = 10
    errorSum = 0
    for k in range(numTests):
        errorSum += colicTest()
    print('After %d iterations the average error rate is: %f'%(numTests, errorSum/float(numTests)))

这里写图片描述

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值