统计学习方法——5.逻辑斯蒂回归与最大熵模型

一、简介

  • 感知机模型的缺点:非黑即白的分类方法,在超平面附近的样本与距离较远的样本都被分为同一类,预测的标签比较“硬”,如果以概率的形式判断其属于哪一类,然后通过这支阈值概率进一步预测其属于哪一类比较合理,不会显得那样“粗暴”。即使用Sigmoid函数将其转换为预测的概率值。
  1. 二项逻辑斯蒂回归模型:将等于1和0的二分类情况的模型y=wx+b带入到sigmoid函数中。
  2. 参数估计。将上述模型的式子转换成一个式子后取对数并最大化,采用梯度下降法求出参数w。

二、代码实现

1.手动实现

代码来自:https://www.pkudodo.com/2018/12/05/1-7/

import time
import numpy as np


def loadData(fileName):
    '''
    加载Mnist数据集
    :param fileName:要加载的数据集路径
    :return: list形式的数据集及标记
    '''
    # 存放数据及标记的list
    dataList = []; labelList = []
    # 打开文件
    fr = open(fileName, 'r')
    # 将文件按行读取
    for line in fr.readlines():
        # 对每一行数据按切割福','进行切割,返回字段列表
        curLine = line.strip().split(',')

        # Mnsit有0-9是个标记,由于是二分类任务,所以将标记0的作为1,其余为0
        # 验证过<5为1 >5为0时正确率在90%左右,猜测是因为数多了以后,可能不同数的特征较乱,不能有效地计算出一个合理的超平面
        # 查看了一下之前感知机的结果,以5为分界时正确率81,重新修改为0和其余数时正确率98.91%
        # 看来如果样本标签比较杂的话,对于是否能有效地划分超平面确实存在很大影响
        if int(curLine[0]) == 0:
            labelList.append(1)
        else:
            labelList.append(0)
        # 存放标记
        # [int(num) for num in curLine[1:]] -> 遍历每一行中除了以第一哥元素(标记)外将所有元素转换成int类型
        # [int(num)/255 for num in curLine[1:]] -> 将所有数据除255归一化(非必须步骤,可以不归一化)
        dataList.append([int(num)/255 for num in curLine[1:]])
        # dataList.append([int(num) for num in curLine[1:]])

    # 返回data和label
    return dataList, labelList

def predict(w, x):
    '''
    预测标签
    :param w:训练过程中学到的w
    :param x: 要预测的样本
    :return: 预测结果
    '''
    # dot为两个向量的点积操作,计算得到w * x
    wx = np.dot(w, x)
    # 计算标签为1的概率
    # 该公式参考“6.1.2 二项逻辑斯蒂回归模型”中的式6.5
    P1 = np.exp(wx) / (1 + np.exp(wx))
    # 如果为1的概率大于0.5,返回1
    if P1 >= 0.5:
        return 1
    # 否则返回0
    return 0

def logisticRegression(trainDataList, trainLabelList, iter=200):
    '''
    逻辑斯蒂回归训练过程
    :param trainDataList:训练集
    :param trainLabelList: 标签集
    :param iter: 迭代次数
    :return: 习得的w
    '''
    # 按照书本“6.1.2 二项逻辑斯蒂回归模型”中式6.5的规则,将w与b合在一起,
    # 此时x也需要添加一维,数值为1
    # 循环遍历每一个样本,并在其最后添加一个1
    for i in range(len(trainDataList)):
        trainDataList[i].append(1)

    # 将数据集由列表转换为数组形式,主要是后期涉及到向量的运算,统一转换成数组形式比较方便
    trainDataList = np.array(trainDataList)
    # 初始化w,维数为样本x维数+1,+1的那一位是b,初始为0
    w = np.zeros(trainDataList.shape[1])

    # 设置步长
    h = 0.001

    # 迭代iter次进行随机梯度下降
    for i in range(iter):
        # 每次迭代冲遍历一次所有样本,进行随机梯度下降
        for j in range(trainDataList.shape[0]):
            # 随机梯度上升部分
            # 在“6.1.3 模型参数估计”一章中给出了似然函数,我们需要极大化似然函数
            # 但是似然函数由于有求和项,并不能直接对w求导得出最优w,所以针对似然函数求和
            # 部分中每一项进行单独地求导w,得到针对该样本的梯度,并进行梯度上升(因为是
            # 要求似然函数的极大值,所以是梯度上升,如果是极小值就梯度下降。梯度上升是
            # 加号,下降是减号)
            # 求和式中每一项单独对w求导结果为:xi * yi - (exp(w * xi) * xi) / (1 + exp(w * xi))
            # 如果对于该求导式有疑问可查看我的博客 www.pkudodo.com

            # 计算w * xi,因为后式中要计算两次该值,为了节约时间这里提前算出
            # 其实也可直接算出exp(wx),为了读者能看得方便一点就这么写了,包括yi和xi都提前列出了
            wx = np.dot(w, trainDataList[j])
            yi = trainLabelList[j]
            xi = trainDataList[j]
            # 梯度上升
            w += h * (xi * yi - (np.exp(wx) * xi) / ( 1 + np.exp(wx)))

    # 返回学到的w
    return w

def model_test(testDataList, testLabelList, w):
    '''
    验证
    :param testDataList:测试集
    :param testLabelList: 测试集标签
    :param w: 训练过程中学到的w
    :return: 正确率
    '''

    # 与训练过程一致,先将所有的样本添加一维,值为1,理由请查看训练函数
    for i in range(len(testDataList)):
        testDataList[i].append(1)

    # 错误值计数
    errorCnt = 0
    # 对于测试集中每一个测试样本进行验证
    for i in range(len(testDataList)):
        # 如果标记与预测不一致,错误值加1
        if testLabelList[i] != predict(w, testDataList[i]):
            errorCnt += 1
    # 返回准确率
    return 1 - errorCnt / len(testDataList)



if __name__ == '__main__':
    start = time.time()

    # 获取训练集及标签
    print('start read transSet')
    trainData, trainLabel = loadData('../Mnist/mnist_train.csv')

    # 获取测试集及标签
    print('start read testSet')
    testData, testLabel = loadData('../Mnist/mnist_test.csv')

    # 开始训练,学习w
    print('start to train')
    w = logisticRegression(trainData, trainLabel)

    # 验证正确率
    print('start to test')
    accuracy = model_test(testData, testLabel, w)

    # 打印准确率
    print('the accuracy is:', accuracy)
    # 打印时间
    print('time span:', time.time() - start)

2.库实现

from sklearn.linear_model import LogisticRegression

#coding=utf-8
# coding=utf-8
import time
import numpy as np
from sklearn.linear_model import LogisticRegression as LR
from sklearn import preprocessing


# 1. 加载数据
def loadData(fileName):
    '''
    加载Mnist数据集
    :param fileName:要加载的数据集路径
    :return: list形式的数据集及标记
    '''
    print('start to read data')
    # 存放数据及标记的list
    dataArr = []
    labelArr = []
    # 打开文件
    fr = open(fileName, 'r')
    # 将文件按行读取
    for line in fr.readlines():
        # 对每一行数据按切割福','进行切割,返回字段列表
        curLine = line.strip().split(',')

        # Mnsit有0-9是个标记,由于是二分类任务,所以将>=5的作为1,<5为-1
        # if int(curLine[0]) >= 5:
        #     labelArr.append(1)
        # else:
        #     labelArr.append(-1)
        labelArr.append(int(curLine[0]))

        # 存放标记
        # [int(num) for num in curLine[1:]] -> 遍历每一行中除了以第一哥元素(标记)外将所有元素转换成int类型
        # [int(num)/255 for num in curLine[1:]] -> 将所有数据除255归一化(非必须步骤,可以不归一化)
        # dataArr.append([int(num)/255 for num in curLine[1:]])
        dataArr.append([num for num in curLine[1:]])

    labelArr = np.ravel(labelArr)
    dataArr = np.array(dataArr)
    # dataArr = preprocessing.StandardScaler().fit(dataArr)
    #返回data和label
    return dataArr, labelArr

def LR_model(trainDataList, trainLabelList, iter=2000):
    model = LR(max_iter=iter)
    model.fit(trainDataList, trainLabelList)
    return model


def model_test(model, testDataList, testLabelList):

    accuracy = model.score(testDataList, testLabelList)

    return accuracy

if __name__ == '__main__':
    start = time.time()

    # 获取训练集及标签
    print('start read transSet')
    trainData, trainLabel = loadData('../Mnist/mnist_train.csv')

    # 获取测试集及标签
    print('start read testSet')
    testData, testLabel = loadData('../Mnist/mnist_test.csv')

    # 开始训练,学习w
    print('start to train')
    model = LR_model(trainData, trainLabel)

    # 验证正确率
    print('start to test')
    accuracy = model_test(model, testData, testLabel)

    # 打印准确率
    print('the accuracy is:', accuracy)
    # 打印时间
    print('time span:', time.time() - start)



ps:本博客仅供自己复习理解,不具其他人可参考,本博客参考了大量的优质资源,侵删。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值