机器学习(超详细讲解):利用Logistic Regression(逻辑回归)实现多分类

机器学习:利用Logistic Regression(逻辑回归)实现多分类


本文章利用逻辑回归来实现多分类问题,对于大部分数据集,其准确率可以达到90%以上。
源代码和数据集点此

1.Logistic Regression的引入

​ 在介绍Logistic Regression(对数几率回归)之前,我们先来介绍Liner Regression(线性回归),简单的来说,线性回归的目标就是要寻找合适的W和B使得式1.1的误差最小。对于误差,我们可以使用损失函数来衡量。对于W和B的寻找,我们可以使用牛顿迭代法,梯度下降法等,后面我们将重点介绍梯度下降法。
Z = W T X + B Z=W^TX+B Z=WTX+B
​ 对于Logistic Regression而言,我们要完成二分类任务,实际上就是要在线性模型输出的基础上,将输出Z映射到两个区域内,比如我们可以将输出映射为数字 0 或 1,那么我们就可以将输出为 0 的作为一类,输出为 1 的作为另一类,这样就完成了二分类任务。那么我们应该选择什么样的函数作为Z的映射函数呢?最简单的我们可以选择单调越阶函数:
单调越阶函数

​ 当线性模型输出Z<0时,将其映射为 0 ;当输出Z>0时,将其映射为 1;输出Z=0时不做处理。但是单调越阶函数的数学性质并不好(譬如单调性,连续性,可导性等)。故我们选择Sigmoid函数作为映射函数,Sigmoid函数具有非常好的数学性质。

Sigmoid函数

2.损失函数

​ 谈到机器学习,首要目标是要确定损失函数,对于Logistic Regression,我们选用如下式子作为损失函数( y ^ \hat{y} y^为推测值, y {y} y为实际值):
损失函数

​ 我们不难发现当实际值 y {y} y 为 0 时,推测值 y ^ \hat{y} y^ 越接近真实值 0 ,函数L的值越小;当实际值 y {y} y为 1 时,推测值 y ^ \hat{y} y^越接近真实值1,函数L的值越小。由此可见,函数L可以作为损失函数。

3.梯度下降法

​ 梯度下降法的目标是寻找函数的最小值。其本质为不断调整参数 x 使得代价函数 f (x) 取得最小值。从直观上理解,就是在碗状结构的凸函数上取一个初始值,然后挪动这个值一步步靠近最低点的过程。从数学上理解,为了找到最小值点,就应该朝着下降速度最快的方向(导函数/偏导方向)迈进,每次迈进一小步,再看看此时的下降最快方向是哪,再朝着这个方向迈进,直至最低点。

梯度下降

​ 对于函数上的某一点而言,导函数/偏导数的方向为其下降最快的方向,故我们每次向导函数的方向迈出一小步,至于这一步有多小取决于我们设定的学习率 α \alpha α

4.参数更新

​ 确定损失函数后,我们就要思考如何利用损失函数来更行优化参数值W和B,损失函数本质上是随着W和B变化的函数:

在这里插入图片描述

​ 现在我们的问题转化为寻找合适的w和b,使得函数L(w,b)的值最小,这就将问题转化为一个二元函数的最小值问题了,对于二元函数的最小值问题,我们采用已经介绍过的梯度下降法,于是我们可以得到参数更新公式:

在这里插入图片描述

​ 对于上诉式子作进一步推导:

在这里插入图片描述

​ 于是参数更新公式变成:

在这里插入图片描述

即下一次的参数 = 上一次的参数 减去 学习率 x 输入 x 误差

​ 至此,我们可以利用上诉结论来实战操作

5 多分类器介绍

​ 已经介绍了如何通过Logistic Regression进行二分类,但我们的目标是解决多分类问题,自然而然,想到用多个二分类器去构建多分类器。在构建多分类模型时,我们介绍两种方法,一对一策略(OvO)和一对其余策略(OvR),在Python实战中,我们将用到OvO。
在这里插入图片描述

5.1 一对一分类器(OvO)

​ 对于OvO策略,我们将训练样本中的 N 个类别两两配对,从而产生 N(N-1)/2 个分类任务(如上图左所示)。我们每次拿其中的两类去训练一个分类器,最终将训练出 N(N-1)/2 个分类器。当预测一组数据时,分别用这 N(N-1)/2个分类器进行预测,最终的结果为所有预测结果中最多的那一个(即投票选出结果)。

5.1 一对其余分类器(OvR)

​ 对于OvR策略,枚举每一种类别,将枚举到的类别作为正例而其他的统一作为反例,这样只需要训练 N N N 个分类器(如上图右所示)。当预测一组数据时,分别用这 N N N 个分类器进行预测,选取结果为正例 (只可能有一个为正例) 的类别作为最终结果。

6 Python实战(Iris数据集准确率93%)

源代码和数据集点此

6.1 读取数据集(划分训练集和测试集)

def getdata():
    # 读取数据集 并且把数据集分为测试集和训练集,奇数项为测试集,偶数项为训练集
    dataSet = []
    labels = []
    f = open('watermelon_3a.csv')
    flag = 1  # 设置flag来跳过第一行
    for line in f.readlines():
        if(flag == 1):
            flag = 0
            continue
        str_data = line.strip().split(",")[1:]
        labels.append(str_data[-1])
        for k in range(len(str_data)-1):
            str_data[k] = float(str_data[k])
        str_data.insert(0, 1.0)
        dataSet.append(str_data)
    train_data = []
    test_data = []
    train_label = []
    dataset = dataSet
    # 归一化处理优点:1.加快求解速度 2.可能提高精度
    for j in range(1, len(dataset[0])-1):
        l_sum = 0
        all_sum = 0
        for i in range(len(dataset)):
            l_sum += dataset[i][j]
        # 计算期望
        average = l_sum/len(dataset)
        # 计算标准差
        for i in range(len(dataset)):
            all_sum += pow(dataset[i][j]-average, 2)
        standard_deviation = math.sqrt(all_sum/(len(dataset)-1))
        # 对每列数据进行归一化,减均值,除方差
        for i in range(len(dataset)):
            dataset[i][j] = (dataset[i][j]-average)/standard_deviation
    for i in range(len(dataSet)):
        if i % 2 == 0:
            train_data.append(dataSet[i])
            train_label.append(labels[i])
        else:
            test_data.append(dataSet[i])
       #返回参数中,dataSet为全部数据,便于西瓜数据集画图,train_data为训练集,test_data为数据集
    return dataSet, labels,  train_data, test_data, train_label

6.2 二分类任务

def get_weight(data, label):
    matrix_data = mat(data)  # m行n列
    matrix_label = mat(label).transpose()  # m行
    m, n = shape(matrix_data)
    learn_rate = 0.00001  # 学习率
    max_cycles = 60000  # 迭代次数
    weights = ones((n, 1))  # 初始化权值矩阵
    # 利用梯度下降法进行权重更新
    for k in range(max_cycles):
        estimate_result = sigmoid(matrix_data*weights)
        error = matrix_label-estimate_result
        weights = weights+learn_rate*matrix_data.transpose()*error
    return weights

6.3 采用OVO方式训练多分类器

最 终 的 训 练 结 果 形 式 : [ [ 分 类 器 1 ] , [ 分 类 器 2 ] , [ 分 类 器 3 ] . . . ] 最终的训练结果形式:[ [分类器1],[分类器2],[分类器3]...] [[1],[2],[3]...]

def train_model(data, labels):
    # 最终的训练结果形式:[ [分类器1],[分类器2],[分类器3]...]
    # 把第i类作为正类,其他作为负类
    result_weight = []
    uniqueVals = list(set(labels))
    for i in range(len(uniqueVals)):
        for k in range(i+1, len(uniqueVals)):
            train_data = []
            train_label = []
            for j in range(len(data)):
                if(data[j][-1] == uniqueVals[i]):
                    train_label.append(1.0)
                    train_data.append(data[j][:-1])
                elif(data[j][-1] == uniqueVals[k]):
                    train_label.append(0.0)
                    train_data.append(data[j][:-1])
            re = get_weight(train_data, train_label).tolist()
            re.append(uniqueVals[i])
            re.append(uniqueVals[k])
            result_weight.append(re)
    return result_weight

6.4 测试模型准确率

def predict(pre_data, result_weight, label):
    pre_data = mat(pre_data)
    uniqueVals = list(set(label))
    result = []
    for i in range(len(result_weight)):
        estimate_result = sigmoid(pre_data*mat(result_weight[i][:-2]))
        if(estimate_result >= 0.5):
            # 正例
            result.append(result_weight[i][-2])
        else:
            # 反例
            result.append(result_weight[i][-1])
    return get_mostfeature(result)
def test_rate(test_data):
    postive = 0
    total = len(test_data)
    for i in range(total):
        pre_data = test_data[i][:-1]
        result = predict(pre_data, result_weight, train_label)
        if(result == test_data[i][-1]):
            postive = postive+1
    return postive/total

7.完整代码

import matplotlib.pyplot as plt
import numpy as np
import time
from numpy import *
# 定义sigmoid函数
def sigmoid(n):
    return 1.0/(1+exp(-n))

def getdata():
    # 读取数据集 并且把数据集分为测试集和训练集,奇数项为测试集,偶数项为训练集
    dataSet = []
    labels = []
    f = open('watermelon_3a.csv')
    flag = 1  # 设置flag来跳过第一行
    for line in f.readlines():
        if(flag == 1):
            flag = 0
            continue
        str_data = line.strip().split(",")[1:]
        labels.append(str_data[-1])
        for k in range(len(str_data)-1):
            str_data[k] = float(str_data[k])
        str_data.insert(0, 1.0)
        dataSet.append(str_data)
    train_data = []
    test_data = []
    train_label = []
    dataset = dataSet
    # 归一化处理优点:1.加快求解速度 2.可能提高精度
    for j in range(1, len(dataset[0])-1):
        l_sum = 0
        all_sum = 0
        for i in range(len(dataset)):
            l_sum += dataset[i][j]
        # 计算期望
        average = l_sum/len(dataset)
        # 计算标准差
        for i in range(len(dataset)):
            all_sum += pow(dataset[i][j]-average, 2)
        standard_deviation = math.sqrt(all_sum/(len(dataset)-1))
        # 对每列数据进行归一化,减均值,除方差
        for i in range(len(dataset)):
            dataset[i][j] = (dataset[i][j]-average)/standard_deviation
    for i in range(len(dataSet)):
        if i % 2 == 0:
            train_data.append(dataSet[i])
            train_label.append(labels[i])
        else:
            test_data.append(dataSet[i])
       #返回参数中,dataSet为全部数据,便于西瓜数据集画图,train_data为训练集,test_data为数据集
    return dataSet, labels,  train_data, test_data, train_label


def get_mostfeature(lt):
    index1 = 0  # 记录出现次数最多的元素下标
    max = 0  # 记录最大的元素出现次数
    for i in range(len(lt)):
        flag = 0  # 记录每一个元素出现的次数
        for j in range(i+1, len(lt)):  # 遍历i之后的元素下标
            if lt[j] == lt[i]:
                flag += 1  # 每当发现与自己相同的元素,flag+1
        if flag > max:  # 如果此时元素出现的次数大于最大值,记录此时元素的下标
            max = flag
            index1 = i
    return lt[index1]


def get_weight(data, label):
    matrix_data = mat(data)  # m行n列
    matrix_label = mat(label).transpose()  # m行
    m, n = shape(matrix_data)
    learn_rate = 0.00001  # 学习率
    max_cycles = 60000  # 迭代次数
    weights = ones((n, 1))  # 初始化权值矩阵
    # 利用梯度下降法进行权重更新
    for k in range(max_cycles):
        estimate_result = sigmoid(matrix_data*weights)
        error = matrix_label-estimate_result
        weights = weights+learn_rate*matrix_data.transpose()*error
    return weights


# 训练分类器
def train_model(data, labels):
    # 把第i类作为正类,其他作为负类
    result_weight = []
    uniqueVals = list(set(labels))
    for i in range(len(uniqueVals)):
        for k in range(i+1, len(uniqueVals)):
            train_data = []
            train_label = []
            for j in range(len(data)):
                if(data[j][-1] == uniqueVals[i]):
                    train_label.append(1.0)
                    train_data.append(data[j][:-1])
                elif(data[j][-1] == uniqueVals[k]):
                    train_label.append(0.0)
                    train_data.append(data[j][:-1])
            re = get_weight(train_data, train_label).tolist()

            re.append(uniqueVals[i])
            re.append(uniqueVals[k])

            result_weight.append(re)

    return result_weight


def predict(pre_data, result_weight, label):
    pre_data = mat(pre_data)
    uniqueVals = list(set(label))
    result = []
    for i in range(len(result_weight)):
        estimate_result = sigmoid(pre_data*mat(result_weight[i][:-2]))
        if(estimate_result >= 0.5):
            # 正例
            result.append(result_weight[i][-2])
        else:
            # 反例
            result.append(result_weight[i][-1])
    return get_mostfeature(result)


def test_rate(test_data):
    postive = 0
    total = len(test_data)
    for i in range(total):
        pre_data = test_data[i][:-1]
        result = predict(pre_data, result_weight, train_label)
        if(result == test_data[i][-1]):
            postive = postive+1
    return postive/total

print("开始时间:", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
# 获取训练集
all_data, all_labels, train_data, test_data, train_label = getdata()
print("训练中...")
# 训练模型
result_weight = train_model(train_data, train_label)
print("训练成功!训练结果为:", result_weight)
print("正在测试准确率...")
# 预测
rate = test_rate(test_data)
print("准确率:", rate)
ticks2 = time.time()
print("结束时间:", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))


'''
#画图,只有西瓜数据集(二维)才能画图,大于二维的数据集不能画图
weights = train_model(all_data, all_labels)
data_arr=array(all_data)
p=shape(data_arr)[0]
#print(p)
x1=[]
y1=[]
x2=[]
y2=[]
for i in range(p):
    if int(all_labels[i])==1:
        x1.append(float(data_arr[i,1]))
        y1.append(float(data_arr[i,2]))
    else:
        x2.append(float(data_arr[i,1]))
        y2.append(float(data_arr[i,2]))        
fig=plt.figure()
ax=fig.add_subplot(111)
ax.scatter(x1,y1,s=30,c='red',marker='s')
ax.scatter(x2,y2,s=30,c='green')
x=arange(-1.5,1.5,0.1)
# print(x)
y=(-weights[0][0][0]-weights[0][1][0]*x)/weights[0][2][0]
ax.plot(x,y)
plt.show()
'''

### 回答1: 机器学习中的逻辑回归Logistic Regression)是一种用于分类问题的监督学习算法。它被广泛应用于各种领域,如金融、医疗、社交网络等。 逻辑回归的基本原理是利用一个线性模型,将输入特征与输出结果之间的关系进行建模。然后通过在线性模型的基础上引入一个逻辑函数(sigmoid函数),将输出结果映射到一个概率值。 具体来说,逻辑回归算法通过对训练数据进行学习,估计出模型的参数,使得模型能够对新的输入样本进行分类预测。在训练过程中,逻辑回归通过最大化似然函数的方法来估计模型的参数。常用的优化算法有梯度下降法、牛顿法等。 逻辑回归的一个重要应用是二分类问题,其中输出结果只有两个类别。通过设置一个阈值,将模型输出的概率值映射为两个类别中的一个。 逻辑回归的优点包括简单、可解释性强、计算效率高。同时,逻辑回归对于处理大规模数据集和高维数据也具有较好的适应性。然而,逻辑回归也有一些不足之处,例如无法处理非线性关系以及对异常值比较敏感。 总之,逻辑回归是一种经典的机器学习算法,既简单又有效。它在各种分类问题中得到了广泛应用,并且在实际应用中表现良好。 ### 回答2: 机器学习中的逻辑回归Logistic Regression)是一种常用的分类算法。它通过建立一个逻辑回归模型,将输入的特征与相应的类别进行关联。 逻辑回归可以处理二分类问题,也可以通过修改为多类别问题进行处理。它的核心思想是使用逻辑函数(也称为Sigmoid函数)将线性回归模型的输出映射到[0,1]之间,从而得到样本属于某个类别的概率。 逻辑回归的训练过程主要包括两个步骤:参数初始化和优化。参数初始化就是设置模型的初始权重和偏置,然后通过最优化算法(如梯度下降法)来迭代地更新参数,使得模型的损失函数最小化。 逻辑回归的优势在于它计算速度快、实现简单,且对大规模数据集的处理效果较好。它能够处理线性可分问题,并且可以通过引入正则化技术来防止过拟合。 然而,逻辑回归也有一些限制。由于它是基于线性模型的,对于非线性关系的分类问题,逻辑回归可能无法很好地适应。此外,逻辑回归对特征的选择和预处理较为敏感,需要进行适当的特征工程。 总的来说,逻辑回归是一种简单且有效的分类算法,特别适用于二分类问题。尽管有其局限性,但在实际应用中,逻辑回归仍然被广泛使用,并且可以作为其他更复杂模型的基础。 ### 回答3: 机器学习中的逻辑回归logistic regression)是一种用于分类问题的机器学习算法。逻辑回归被广泛应用于各个领域,如医学诊断、金融风险评估、文本分类等。 逻辑回归的基本思想是通过对输入变量和输出变量之间的关系进行建模来进行分类。它用到了一个逻辑函数(logistic function),将输入变量的线性组合映射到0和1之间的概率值。逻辑函数通常是sigmoid函数,常用的形式是1 / (1 + exp(-z)),其中z是输入变量的线性组合。 训练逻辑回归模型的过程是通过最大似然估计来拟合模型参数。最大似然估计的目标是找到能最大化观测到的样本的条件概率的参数。为了实现这一点,通常使用梯度下降法来最小化损失函数。损失函数可以是似然函数的负对数,即对数损失函数。 逻辑回归有一些优点。首先,它是一种简单而直观的模型,易于理解和实现。其次,逻辑回归模型的参数可以通过梯度下降等优化算法进行有效求解。此外,逻辑回归模型具有较好的解释性,可以通过参数的符号和大小了解自变量与因变量的关系。 然而,逻辑回归也有一些限制。首先,它通常只适用于处理线性可分的问题。其次,逻辑回归模型对于特征之间的相关性比较敏感,如果特征之间具有高度相关性,可能导致模型出现过拟合现象。此外,逻辑回归模型的输出是概率值,对于某些任务可能不够精确。 总之,逻辑回归是一种常用的机器学习算法,用于解决分类问题。其简单而直观的思想和容易求解的特点使其在实际应用中非常有用。但需要注意逻辑回归的局限性,并结合具体问题选择合适的模型。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值