【机器学习】Logistic回归详解(含源码)

作者:大松鼠
注:本文相关练习用jupyter notebook完成,源文件可到公众号”大拨鼠Code”回复“机器学习”领取,题目及数据、文章的pdf版本都已打包在一起,如果对您有帮助,希望点个关注哦,万分感谢!

专栏不断更新,欢迎订阅~
Linux
数据结构和算法
机器学习

Logistic回归

逻辑函数

我们将逻辑函数(sigmoid函数)定义为:

g%28z%29%3D%5Cfrac%7B1%7D%7B1%2Be%5E%7B-z%7D%7D

因为z%3D%5Ctheta%5ETx也可以写成:

h_%7B%5Ctheta%7D%28x%29%3D%5Cfrac%7B1%7D%7B1%2Be%5E%7B-%5Ctheta%5E%7BT%7Dx%7D%7D

逻辑函数的图像如下:

【机器学习】Logistic回归详解(含源码)

当我们有了函数h_%7B%5Ctheta%7D%28x%29%3D%5Cfrac%7B1%7D%7B1%2Be%5E%7B-%5Ctheta%5E%7BT%7Dx%7D%7D,我们要做的和前面的线性回归一样,就是用参数%5Ctheta来拟合我们的数据。

由于逻辑函数处于0-1之间,且预测结果只输出1或者0,所以当h_%7B%5Ctheta%7D%28x%29为某个值时,这个值,代表的就是预测为1的概率,即当h_%7B%5Ctheta%7D%28x%29%3D0.7时,以下为y%3D1y%3D0的概率。

P%28y%3D1%7Cx%2C%5Ctheta%29%3D0.7

P%28y%3D0%7Cx%2C%5Ctheta%29%3D1-P%28y%3D1%7Cx%2C%5Ctheta%29%3D0.3

决策边界

我们现在来看看,逻辑函数什么时候会将y预测为1,什么时候会预测为0

这时我们可以设定一个界限,例如我们设为0.5,则

h_%7B%5Ctheta%7D%28x%29%E2%89%A50.5%2Cy%3D1

h_%7B%5Ctheta%7D%28x%29%3C0.5%2Cy%3D0

同时,逻辑函数的图像显示:

%E5%BD%93z%E2%89%A50%E6%97%B6%EF%BC%8Ch_%7B%5Ctheta%7D%28x%29%E2%89%A50.5

%E5%BD%93z%3C0%E6%97%B6%EF%BC%8Ch_%7B%5Ctheta%7D%28x%29%3C0.5

【机器学习】Logistic回归详解(含源码)

现在假设我们有如下所示的训练集和假设函数

【机器学习】Logistic回归详解(含源码)

现在让%5Ctheta_0%3D-3%2C%5Ctheta_1%3D1%2C%5Ctheta_2%3D1

如果是-3%2Bx_1%2Bx_2%E2%89%A50,则预测y%3D1

我们知道,图上-3%2Bx_1%2Bx_2%E2%89%A50所代表的区域可以被一条直线分割

【机器学习】Logistic回归详解(含源码)

然后当数据落在直线的右边时,预测y%3D1,当它落在左边时,预测y%3D0

我们称这条线为决策边界,当%5Ctheta的值确定时,我们的决策边界也就确定了。

让我们看另一个例子

【机器学习】Logistic回归详解(含源码)

现在让%5Ctheta_0%3D-1%2C%5Ctheta_1%3D0%2C%5Ctheta_2%3D0%2C%5Ctheta_3%3D1%2C%5Ctheta_4%3D1

如果是-1%2Bx_1%5E2%2Bx_2%5E2%E2%89%A50,则预测y%3D1

此时的决策边界就是一个以0为原点,以1为半径的圆,但数据落在圆外,则预测y%3D1

【机器学习】Logistic回归详解(含源码)

这里需要强调的是,我们不是使用训练集来定义决策边界,而是拟合参数%5Ctheta。当%5Ctheta的值确定时,决策边界也就确定了。

成本函数

假设我们现在有n个特征值和m个训练样本的训练集

【机器学习】Logistic回归详解(含源码)

和之前的线性回归一样,我们让x_0%3D1

我们假设函数:

h_%7B%5Ctheta%7D%28x%29%3D%5Cfrac%7B1%7D%7B1%2Be%5E%7B-%5Ctheta%5E%7BT%7Dx%7D%7D

现在我们要讨论的是在给定训练集时如何拟合函数%5Ctheta

在线性回归中,我们使用成本函数J%28%5Ctheta%29%3D%5Cfrac%7B1%7D%7Bm%7D%5Csum%5Em_%7Bi%3D1%7D%5Cfrac%7B1%7D%7B2%7D%28h_%7B%5Ctheta%7D%28x%5E%7B%28i%29%7D%29-y%5E%7B%28i%29%7D%29%5E2

订单Cost%28h_%7B%5Ctheta%7D%28x%5E%7B%28i%29%7D%29%2Cy%5E%7B%28i%29%7D%29%3D%5Cfrac%7B1%7D%7B2%7D%28h_%7B%5Ctheta%7D%28x%5E%7B%28i%29%7D%29-y%5E%7B%28i%29%7D%29%5E2

原来的公式变成J%28%5Ctheta%29%3D%5Cfrac%7B1%7D%7Bm%7D%5Csum%5Em_%7Bi%3D1%7DCost%28h_%7B%5Ctheta%7D%28x%5E%7B%28i%29%7D%29%2Cy%5E%7B%28i%29%7D%29

因为逻辑函数h_%7B%5Ctheta%7D%28x%29%3D%5Cfrac%7B1%7D%7B1%2Be%5E%7B-%5Ctheta%5E%7BT%7Dx%7D%7D是一个更复杂的函数,

那么它的代价函数就是一个非凸函数。左图如下。有许多局部最优解。那么我们不能保证通过梯度下降法可以找到全局最优解,所以我们更喜欢右图的代价函数。

【机器学习】Logistic回归详解(含源码)

为了解决上述问题,我们定义Cost函数如下

【机器学习】Logistic回归详解(含源码)

我们先讨论y%3D1的情况

Cost 函数的形象是

【机器学习】Logistic回归详解(含源码)

由于该图像是在y%3D1的基础上绘制的,即cost是越接近于0,则y%3D1的概率越大

由此我们知道,若h_%7B%5Ctheta%7D%28x%29%3D1%EF%BC%8C%E5%88%99cost%3D0,也就意味着y%3D1的概率为1,若h_%7B%5Ctheta%7D%28x%29-%3E0%EF%BC%8C%E5%88%99cost-%3E%E2%88%9E,也就意味着y%3D1的概率为0。

现在讨论y%3D0的情况

Cost 函数的形象是

【机器学习】Logistic回归详解(含源码)

由于该图像是在y%3D0的基础上绘制的,即cost是越接近于0,则y%3D的0概率越大

由此我们知道,若h_%7B%5Ctheta%7D%28x%29%3D0%EF%BC%8C%E5%88%99cost%3D0,也就意味着y%3D1的概率为0,若h_%7B%5Ctheta%7D%28x%29-%3E1%EF%BC%8C%E5%88%99cost-%3E%E2%88%9E,也就意味着y%3D%200的概率为0。

梯度下降求解器

如下是我们的逻辑函数,在分类问题中,y的值总是等于0或1。

【机器学习】Logistic回归详解(含源码)

既然y值总是等于0或1,那么我们可以尝试简化代价函数,将两个表达式合二为一,这更方便我们使用梯度下降。

因为当y%3D1%E6%97%B6%EF%BC%8CCost%28h_%7B%5Ctheta%7D%28x%29%2Cy%29%3D-log%28h_%7B%5Ctheta%7D%28x%29%29

因为当y%3D0%E6%97%B6%EF%BC%8CCost%28h_%7B%5Ctheta%7D%28x%29%2Cy%29%3D-log%281-h_%7B%5Ctheta%7D%28x%29%29

所以我们可以结合

Cost%28h_%7B%5Ctheta%7D%28x%29%2Cy%29%3D-ylog%28h_%7B%5Ctheta%7D%28x%29%29-%281-y%29log%281-h_%7B%5Ctheta%7D%28x%29%29

这样我们就得到了Logistic 回归的代价函数如下

【机器学习】Logistic回归详解(含源码)

现在我们要做的就是找到最小化成本函数的参数%5Ctheta

与线性回归类似,我们使用梯度下降,直到找到最小值

【机器学习】Logistic回归详解(含源码)

计算导数项%5Cfrac%7B%E2%88%82%7D%7B%E2%88%82%5Ctheta_j%7DJ%28%5Ctheta%29,然后

%5Ctheta_j%3A%3D%5Ctheta_j-%5Calpha%5Cfrac%7B1%7D%7Bm%7D%5Csum%5Em_%7Bi%3D1%7D%28h_%7B%5Ctheta%7D%28x%5E%7B%28i%29%7D%29-y%5E%7B%28i%29%7D%29x%5E%7B%28i%29%7D_j

多变量分类(一对多)

【机器学习】Logistic回归详解(含源码)

对于上图中的三个类别的样本,我们应该如何分类呢?

我们可以将这种多元分类问题转化为三个独立的二元分类问题。

【机器学习】Logistic回归详解(含源码)

首先,假设三个类别为y%3D1%2Cy%3D2%2Cy%3D3

【机器学习】Logistic回归详解(含源码)

如图(1)我们先把正方形和红叉看成一类,从而转化为二元分类,那么第一个分类器就可以预测出y%3D1的概率,同理,图(2)和图(3)分别能预测出y%3D2%2Cy%3D3的概率。

也就是说,在分析三元问题时,我们转换为三个二元分类器,每个分类器都针对其中一种情况进行训练。

过拟合和求解

一个假设可以在训练数据上获得比其他假设更好的拟合,但不能很好地将数据拟合到训练数据以外的数据集上。此时,该假设被认为是过拟合的。造成这种现象的主要原因是训练数据中存在噪声或训练数据太少。

在单变量线性回归问题中,我们可以选择观察数据在图上的分布特征来选择合适的拟合函数,但在多元回归分析中,很难画图并观察数据分布特征。

解决方案:

  1. 部分特征向量被手动或通过选择算法模型丢弃。
  2. 正则化:保留所有特征,但减少拟合参数%5Ctheta,这种方法非常适合我们有很多特征值并且没有一个可以丢弃的情况。

现在让我们修改成本函数以减少参数%5Ctheta

【机器学习】Logistic回归详解(含源码)

%5Clambda称为归一化参数,用于控制两个目标之间的权衡

第一个目标是使拟合参数更好地拟合我们的数据

第二个目标是保持拟合参数尽可能小

线性回归的正则化

一般来说,我们只需要对参数%5Ctheta_1%2C...%2C%5Ctheta_n进行归一化,所以在进行梯度下降时,%5Ctheta_0需要单独更新。

【机器学习】Logistic回归详解(含源码)

将更新%5Ctheta_j的项转换为以下内容:

【机器学习】Logistic回归详解(含源码)

如果使用正规方程,则变为

【机器学习】Logistic回归详解(含源码)

Logistic回归的正规化

【机器学习】Logistic回归详解(含源码)

示例源代码

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.metrics import classification_report #生成报告

线性分类问题

#数据处理
data=pd.read_csv('E:\\happy\\data\\Logistic_data\\ex2data1.txt',names=['exam1','exam2','admitted'])
data

【机器学习】Logistic回归详解(含源码)

#先看看数据各个统计值
data.describe()

【机器学习】Logistic回归详解(含源码)

#绘制散点
positive = data[data['admitted'].isin([1])]
negative = data[data['admitted'].isin([0])]

fig = plt.figure(figsize=(12,8), dpi=100)
plt.scatter(positive.exam1,positive.exam2,c='b',marker='o',label='admitted')
plt.scatter(negative.exam1,negative.exam2,c='r',marker='x',label='not admitted')
plt.xlabel("exam1")
plt.ylabel("exam2")
plt.legend()

【机器学习】Logistic回归详解(含源码)

#sigmoid函数
def sigmoid(z):
    return 1/(1 + np.exp(-z))
#绘图检查一下sigmoid函数定义是否正确
nums = np.arange(-10,10,step=0.1)

fig = plt.figure(figsize=(12,8), dpi=100)
plt.plot(nums, sigmoid(nums),'r')

【机器学习】Logistic回归详解(含源码)

J%5Cleft%28%20%5Ctheta%20%5Cright%29%3D%5Cfrac%7B1%7D%7Bm%7D%5Csum%5Climits_%7Bi%3D1%7D%5E%7Bm%7D%7B%5B-%7B%7By%7D%5E%7B%28i%29%7D%7D%5Clog%20%5Cleft%28%20%7B%7Bh%7D_%7B%5Ctheta%20%7D%7D%5Cleft%28%20%7B%7Bx%7D%5E%7B%28i%29%7D%7D%20%5Cright%29%20%5Cright%29-%5Cleft%28%201-%7B%7By%7D%5E%7B%28i%29%7D%7D%20%5Cright%29%5Clog%20%5Cleft%28%201-%7B%7Bh%7D_%7B%5Ctheta%20%7D%7D%5Cleft%28%20%7B%7Bx%7D%5E%7B%28i%29%7D%7D%20%5Cright%29%20%5Cright%29%5D%7D

#添加一列1
data.insert(0,'Ones',1)

#读取特征值和目标函数
cols = data.shape[1]  #得到矩阵列数
x_data = np.array(data.iloc[:, 0:cols-1])  #取不包含最后一列的所有数据,并转化为数组,不然不能进行下面的运算
y_data = np.array(data.iloc[:,cols-1:cols])     #取最后一列数据
theta = np.zeros(3)
def cost(theta,X,y):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)
    return np.mean(np.multiply(-y, np.log(sigmoid(X * theta.T))) - np.multiply((1 - y), 	np.log(1 - sigmoid(X * theta.T))))
偏导数

%5Cfrac%7B%5Cpartial%20J%5Cleft%28%20%5Ctheta%20%5Cright%29%7D%7B%5Cpartial%20%7B%7B%5Ctheta%20%7D_%7Bj%7D%7D%7D%3D%5Cfrac%7B1%7D%7Bm%7D%5Csum%5Climits_%7Bi%3D1%7D%5E%7Bm%7D%7B%28%7B%7Bh%7D_%7B%5Ctheta%20%7D%7D%5Cleft%28%20%7B%7Bx%7D%5E%7B%28i%29%7D%7D%20%5Cright%29-%7B%7By%7D%5E%7B%28i%29%7D%7D%29x_%7B_%7Bj%7D%7D%5E%7B%28i%29%7D%7D
转换为矢量
%5Cfrac%7B1%7D%7Bm%7D%20X%5ET%28%20sigmoid%28X%5Ctheta%5ET%29%20-%20y%20%29

#求代价函数偏导
def gradient(theta, X, y):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)
    return  X.T * (sigmoid(X * theta.T) - y) / len(X)
#看看能否运行
gradient(theta ,x_data ,y_data)

【机器学习】Logistic回归详解(含源码)

现在用SciPy’s truncated newton(TNC)实现寻找最优参数。

import scipy.optimize as opt
res = opt.fmin_tnc(func=cost, x0=theta, fprime=gradient, args=(x_data, y_data))
res

【机器学习】Logistic回归详解(含源码)

cost(res[0], x_data, y_data)

【机器学习】Logistic回归详解(含源码)

构建分类器

%7B%7Bh%7D_%7B%5Ctheta%20%7D%7D小于0.5时,预测 y=0 。

def predict(theta , X):
    theta = np.matrix(theta)
    X = np.matrix(X)
    prob = sigmoid(X * theta.T)
    return (prob >= 0.5).astype(int)
#预测一下,看看精确率
final_theta = res[0];
y_pred = predict(final_theta , x_data)
#生成报告
print(classification_report(y_data,y_pred))

【机器学习】Logistic回归详解(含源码)

决策边界
coef = -(res[0] / res[0][2])   #决策边界方程系数
x = np.arange(130 ,step=0.1)
y = coef[1] * x + coef[0]
#画出决策边界
fig = plt.figure(figsize=(12,8), dpi=100)
plt.plot(x, y,'purple')
plt.scatter(positive.exam1,positive.exam2,c='b',marker='o',label='admitted')
plt.scatter(negative.exam1,negative.exam2,c='r',marker='x',label='not admitted')
plt.xlim(0,120)
plt.ylim(0,120)
plt.xlabel("exam1")
plt.ylabel("exam2")
plt.legend()
plt.title('Decision Boundary')

【机器学习】Logistic回归详解(含源码)

非线性分类问题

data2 = pd.read_csv('E:\\happy\\data\\Logistic_data\\ex2data2.txt',names=['test1', 'test2','accepted'])
data2

【机器学习】Logistic回归详解(含源码)

#绘制散点图
positive2 = data2[data2['accepted'].isin([1])]
negative2 = data2[data2['accepted'].isin([0])]

fig = plt.figure(figsize=(12,8), dpi=100)
plt.scatter(positive2['test1'], positive2['test2'], c='b',marker='o',label='accepted')
plt.scatter(negative2['test1'], negative2['test2'], c='r',marker='x',label='rejected')
plt.legend()
plt.xlabel('test1')
plt.ylabel('test2')

【机器学习】Logistic回归详解(含源码)

这些数据散点看起来是非线性的,不能简单的区分正负类,所以我们需要创建更多的项来让决策边界更好的对数据进行分类,这就需要使用特征图,但是这样容易出现过拟合的问题,所以我们还需要正则化成本函数。

特征图

【机器学习】Logistic回归详解(含源码)

def feature_mapping(x, y, power, as_ndarray=False):
    data = {"f{}{}".format(i - p, p): np.power(x, i - p) * np.power(y, p)
                for i in np.arange(power + 1)
                for p in np.arange(i + 1)
            }

    if as_ndarray:
        return pd.DataFrame(data).values
    else:
        return pd.DataFrame(data)
x1 = np.array(data2.test1)
x2 = np.array(data2.test2)
归一化成本函数

J%5Cleft%28%20%5Ctheta%20%5Cright%29%3D%5Cfrac%7B1%7D%7Bm%7D%5Csum%5Climits_%7Bi%3D1%7D%5E%7Bm%7D%7B%5B-%7B%7By%7D%5E%7B%28i%29%7D%7D%5Clog%20%5Cleft%28%20%7B%7Bh%7D_%7B%5Ctheta%20%7D%7D%5Cleft%28%20%7B%7Bx%7D%5E%7B%28i%29%7D%7D%20%5Cright%29%20%5Cright%29-%5Cleft%28%201-%7B%7By%7D%5E%7B%28i%29%7D%7D%20%5Cright%29%5Clog%20%5Cleft%28%201-%7B%7Bh%7D_%7B%5Ctheta%20%7D%7D%5Cleft%28%20%7B%7Bx%7D%5E%7B%28i%29%7D%7D%20%5Cright%29%20%5Cright%29%5D%7D%2B%5Cfrac%7B%5Clambda%20%7D%7B2m%7D%5Csum%5Climits_%7Bj%3D1%7D%5E%7Bn%7D%7B%5Ctheta%20_%7Bj%7D%5E%7B2%7D%7D

X = feature_mapping(x1, x2, power=6, as_ndarray=True)
cols = data2.shape[1]  
y = np.array(data2.iloc[:,cols-1:cols])
theta = np.zeros(28)
def regularized_cost(theta, X, y, lamb = 1):
    '''正规项不包含theta_0'''
    theta_except_0 = theta[1:]
    reg = (lamb / (2 * len(X))) * np.sum(np.power(theta_except_0, 2))
    return cost(theta,X,y) + reg
正则化梯度

%7B%7B%5Ctheta%20%7D_%7B0%7D%7D%3A%3D%7B%7B%5Ctheta%20%7D_%7B0%7D%7D-a%5Cfrac%7B1%7D%7Bm%7D%5Csum%5Climits_%7Bi%3D1%7D%5E%7Bm%7D%7B%5B%7B%7Bh%7D_%7B%5Ctheta%20%7D%7D%5Cleft%28%20%7B%7Bx%7D%5E%7B%28i%29%7D%7D%20%5Cright%29-%7B%7By%7D%5E%7B%28i%29%7D%7D%5Dx_%7B_%7B0%7D%7D%5E%7B%28i%29%7D%7D
%7B%7B%5Ctheta%20%7D_%7Bj%7D%7D%3A%3D%7B%7B%5Ctheta%20%7D_%7Bj%7D%7D-a%5Cfrac%7B1%7D%7Bm%7D%5Csum%5Climits_%7Bi%3D1%7D%5E%7Bm%7D%7B%5B%7B%7Bh%7D_%7B%5Ctheta%20%7D%7D%5Cleft%28%20%7B%7Bx%7D%5E%7B%28i%29%7D%7D%20%5Cright%29-%7B%7By%7D%5E%7B%28i%29%7D%7D%5Dx_%7Bj%7D%5E%7B%28i%29%7D%7D%2B%5Cfrac%7B%5Clambda%20%7D%7Bm%7D%7B%7B%5Ctheta%20%7D_%7Bj%7D%7D

def regularized_gradient(theta, X, y, lam=1):
    theta_except_0 = theta[1:]
    reg_theta = (lam / len(X)) * theta_except_0
    #将theta_0和theta_j(j≠0)两种情况拼接到一个数组中
    reg_term = np.concatenate([np.array([0]), reg_theta])
    reg_term = np.matrix(reg_term)
    return gradient(theta, X, y) + reg_term.T
regularized_gradient(theta, X, y, lam=1)

现在用SciPy’s truncated newton(TNC)实现寻找最优参数。

res2 = opt.fmin_tnc(func=regularized_cost, x0=theta, fprime=regularized_gradient, args=(X, y))
res2

【机器学习】Logistic回归详解(含源码)

#看看精确度
final_theta = res2[0]
y_pred = predict(final_theta, X)
print(classification_report(y, y_pred))

【机器学习】Logistic回归详解(含源码)

决策边界
#寻找决策边界
def find_decision_boundary(power, theta):
    t1 = np.linspace(-1, 1.5, 1000)
    t2 = np.linspace(-1, 1.5, 1000)  

    cordinates = [(x, y) for x in t1 for y in t2]
    x_cord, y_cord = zip(*cordinates)
    mapped_cord = feature_mapping(x_cord, y_cord, power)

    inner_product = mapped_cord @ theta
    decision = mapped_cord[np.abs(inner_product) < 0.001]  #保留接近0的数据用于绘图

    return decision.f10, decision.f01
#画出据决策边界
power = 6
x, y = find_decision_boundary(power,final_theta)
fig = plt.figure(figsize=(12,8), dpi=100)
plt.scatter(positive2['test1'], positive2['test2'], c='b',marker='o',label='accepted')
plt.scatter(negative2['test1'], negative2['test2'], c='r',marker='x',label='rejected')
plt.scatter(x, y, c = 'purple')
plt.legend()
plt.xlabel('test1')
plt.ylabel('test2')

【机器学习】Logistic回归详解(含源码)

参考:

吴恩达机器学习系列课程

机器学习笔记

版权声明:本文为博主大拨鼠原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/watermelon_c/article/details/122785071

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
xiaoxingxing的头像xiaoxingxing管理团队
上一篇 2022年2月18日 上午10:34
下一篇 2022年2月18日 上午11:37

相关推荐