📜  了解逻辑回归 Python实现

📅  最后修改于: 2020-04-22 05:41:49             🧑  作者: Mango

先决条件: 线性回归
本文讨论Logistic回归的基本知识及其在Python中的实现。逻辑回归基本上是一种监督分类算法。在分类问题中,目标变量(或输出)y对于给定的一组特征(或输入)X只能采用离散值。
与普遍的看法相反,逻辑回归是一种回归模型。该模型构建回归模型,以预测给定数据条目属于编号为“ 1″的类别的概率。就像线性回归假设数据遵循线性函数一样,逻辑回归也使用S型函数对数据进行建模。
g(z) = \frac{1}{1 + e^-^z}\

仅当将决策阈值引入时,逻辑回归才成为分类技术。阈值的设置是Logistic回归的一个非常重要的方面,并且取决于分类问题本身。
阈值的决定主要受精度和召回率的影响。理想情况下,我们希望精度和召回率都为1,但这很少是这种情况。如果进行精确召回权衡,我们使用以下参数来决定阈值:
1. 低精度/高召回率:在我们希望减少假阴性的数量而不必减少假阳性的数量的应用中,我们选择精度低或召回率高的决策值。例如,在癌症诊断应用程序中,我们不希望任何被误诊为癌症的患者都被分类为未受影响。这是因为,可以通过进一步的医学疾病检测到癌症的不存在,但是在已经被拒绝的候选人中不能检测到疾病的存在。
2. 高精度/低召回率:在我们希望减少误报次数而不必减少误报数的应用中,我们选择一个决策值,该决策值应具有Precision值较高或Recall值较低的情况。例如,如果我们要分类客户对个性化广告的正面还是负面反应,则我们要绝对确定客户会对广告产生正面反应,否则,负面反应可能会导致潜在的销售损失。
根据类别数,Logistic回归可分为:

  1. 二项式:目标变量只能有2种可能的类型:“ 0″或“ 1″,可以表示“获胜”与“失败”,“通过”与“失败”,“无效”与“有效”等。
  2. 多项式:目标变量可以具有3种或多种可能的类型,这些类型没有顺序(即类型没有定量意义),例如“疾病A”与“疾病B”与“疾病C”。
  3. 顺序的:它处理具有排序类别的目标变量。例如,测试分数可以分类为:“非常差”,“差”,“好”,“非常好”。在这里,可以给每个类别一个分数,例如0、1、2、3。

首先,我们探索Logistic回归的最简单形式,即二项式Logistic回归

二项式Logistic回归

考虑一个示例数据集,该数据集将学习时间与考试结果对应起来。结果只能采用两个值,即通过(1)或失败(0):

小时(X) 0.50 0.75 1.00 1.25 1.50 1.75 2.00 2.25 2.50
通过(Y) 0 0 0 0 0 0 1 0 1

 

2.75 3.00 3.25 3.50 3.75 4.00 4.25 4.50 4.75 5.00 5.50
0 1 0 1 0 1 1 1 1 1 1

因此,我们拥有

y是一个分类目标变量,它只能采用两种可能的类型:“ 0″或“ 1″。
为了概括我们的模型,我们假设:

  • 数据集具有“ p”个特征变量和“ n”个观测值。
  • 特征矩阵表示为:

    这里, x_{ij}表示 i^{th}的观察中的 j^{th}的特征值。
    在这里,我们保持  x_{i0}= 1 的惯例(请继续阅读,稍后您将了解逻辑)。
  • i^{th}观察,x_i可以被表示为:
  • h(x_i)表示第 i^{th}观察的预测响应,即 x_i。我们用于计算的公式 h(x_i)称为假设

如果您已经进行了线性回归,您应该记得在线性回归中,我们用于预测的假设是:

其中, \beta_0, \beta_1,…, \beta_p是回归系数。
令回归系数矩阵/向量 \beta为:

然后,以更紧凑的形式表达:

现在取 x_0=1 的原因非常清楚。
我们需要做一个矩阵乘积,但是在原始假设公式中并没有
实际 x_0相乘 \beta_0的结果。因此,我们定义 x_0= 1。

现在,如果我们尝试对上述问题应用线性回归,则可能会使用上面讨论的假设来获得连续值。同样, h(x_i)取大于1或小于0的值也没有意义。
因此,对分类假设进行了一些修改:

其中,

称为对数函数S形函数
这是显示g(z)的图: 我们可以从上图推断:
sigmoid

  • g(z)趋于1 随着 z\rightarrow\infty
  • g(z)趋于0,随着 z\rightarrow-\infty
  • g(z)始终在0到1之间

因此,现在,我们可以将第 i^{th}观察的两个标签(0和1)的条件概率定义为:

我们可以更紧凑地写为:

现在,我们定义另一个术语,参数的似然为:

给定一个模型和特定的参数值,似然不过是数据的概率。它针对的每个可能的 \beta值测量数据提供的支持。我们通过给定的 \beta全部相乘 P(y_i|x_i)得到它

为了简化计算,我们采用对数似然性:对数回归

的cost函数与参数似然性成反比。因此,我们可以使用对数似然方程得出cost函数J的表达式:

我们的目标是进行估算 \beta以使成本函数最小!

使用梯度下降算法

首先,我们分别使用 J(\beta)中, \beta_j \in \beta的偏导数来推导随机梯度下降规则(此处仅给出最终推导值):

在此,y和h(x)分别表示响应向量和预测响应向量。同样, x_j是表示第 x_j特征观测值的向量。
现在,为了获得最小的 J(\beta) 

这里 \alpha称为学习率,需要明确设置。

让我们在样本数据集上查看上述技术的Python实现(从此处下载):
2.25 2.50 2.75 3.00 3.25 3.50 3.75 4.00 4.25 4.50 4.75 5.00 5.50

import csv
import numpy as np
import matplotlib.pyplot as plt
def loadCSV(filename):
    '''
    加载数据集的功能
    '''
    with open(filename,"r") as csvfile:
        lines = csv.reader(csvfile)
        dataset = list(lines)
        for i in range(len(dataset)):
            dataset[i] = [float(x) for x in dataset[i]]
    return np.array(dataset)
def normalize(X):
    '''
    归一化特征矩阵X的函数
    '''
    mins = np.min(X, axis = 0)
    maxs = np.max(X, axis = 0)
    rng = maxs - mins
    norm_X = 1 - ((maxs - X)/rng)
    return norm_X
def logistic_func(beta, X):
    '''
    逻辑(S形)函数
    '''
    return 1.0/(1 + np.exp(-np.dot(X, beta.T)))
def log_gradient(beta, X, y):
    '''
    逻辑梯度函数
    '''
    first_calc = logistic_func(beta, X) - y.reshape(X.shape[0], -1)
    final_calc = np.dot(first_calc.T, X)
    return final_calc
def cost_func(beta, X, y):
    '''
    cost函数,J
    '''
    log_func_v = logistic_func(beta, X)
    y = np.squeeze(y)
    step1 = y * np.log(log_func_v)
    step2 = (1 - y) * np.log(1 - log_func_v)
    final = -step1 - step2
    return np.mean(final)
def grad_desc(X, y, beta, lr=.01, converge_change=.001):
    '''
    梯度下降函数
    '''
    cost = cost_func(beta, X, y)
    change_cost = 1
    num_iter = 1
    while(change_cost > converge_change):
        old_cost = cost
        beta = beta - (lr * log_gradient(beta, X, y))
        cost = cost_func(beta, X, y)
        change_cost = old_cost - cost
        num_iter += 1
    return beta, num_iter
def pred_values(beta, X):
    '''
    预测标签的功能
    '''
    pred_prob = logistic_func(beta, X)
    pred_value = np.where(pred_prob >= .5, 1, 0)
    return np.squeeze(pred_value)
def plot_reg(X, y, beta):
    '''
    绘制决策边界的函数
    '''
    # 标记的观察
    x_0 = X[np.where(y == 0.0)]
    x_1 = X[np.where(y == 1.0)]
    # 用差异颜色绘制差异标签的点
    plt.scatter([x_0[:, 1]], [x_0[:, 2]], c='b', label='y = 0')
    plt.scatter([x_1[:, 1]], [x_1[:, 2]], c='r', label='y = 1')
    # 绘制决策边界
    x1 = np.arange(0, 1, 0.1)
    x2 = -(beta[0,0] + beta[0,1]*x1)/beta[0,2]
    plt.plot(x1, x2, c='k', label='reg line')
    plt.xlabel('x1')
    plt.ylabel('x2')
    plt.legend()
    plt.show()
if __name__ == "__main__":
    # 加载数据集
    dataset = loadCSV('dataset1.csv')
    # 归一化特征矩阵
    X = normalize(dataset[:, :-1])
    # stacking columns wth all ones in feature matrix
    X = np.hstack((np.matrix(np.ones(X.shape[0])).T, X))
    # 响应向量
    y = dataset[:, -1]
    # 初始Beta值
    beta = np.matrix(np.zeros(X.shape[1]))
    # 运行梯度下降后的beta值
    beta, num_iter = grad_desc(X, y, beta)
    # 估计的beta值和迭代次数
    print("估计的回归系数:", beta)
    print("迭代次数:", num_iter)
    # 预测标签
    y_pred = pred_values(beta, X)
    # 正确预测的标签数
    print("正确预测的标签:", np.sum(y == y_pred))
    # plotting regression line
    plot_reg(X, y, beta)

输出:

估计的回归系数: [[  1.70474504  15.04062212 -20.47216021]]
迭代次数: 2612
正确预测的标签: 100

logistic_reg
注意:梯度下降是多种估算 \beta的方法之一
基本上,这些是更高级的算法,一旦您定义了cost函数和梯度,就可以轻松地在Python中运行。这些算法是:

  • BFGS(Broyden–Fletcher–Goldfarb–Shanno算法)
  • L-BFGS(类似于BFGS,但使用的内存有限)
  • 共轭梯度

与梯度下降相比,使用这些算法中的任何一种的优点/缺点:

  • 优点
    • 不需要选择学习率
    • 通常运行速度更快(并非总是如此)
    • 可以从数字上为您近似渐变(不一定总是很好)
  • 缺点
    • 更复杂
    • 除非您了解细节,否则更多是黑匣子

多项式Lo​​gistic回归

在多项式Lo​​gistic回归中,输出变量可以具有两个以上的可能离散输出。考虑数字数据集。在这里,输出变量是数字值,可以取不到(0、12、3、4、5、6、7、8、9)中的值。
下面给出了使用scikit-learn对数字数据集进行预测的多项式Lo​​gisitc回归的实现。

from sklearn import datasets, linear_model, metrics
# 加载数字数据集
digits = datasets.load_digits()
# 定义特征矩阵(X)和响应向量(y)
X = digits.data
y = digits.target
# 将X和y分为训练和测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4,
                                                    random_state=1)
# 创建逻辑回归对象
reg = linear_model.LogisticRegression()
# 使用训练集训练模型
reg.fit(X_train, y_train)
# 对测试集进行预测
y_pred = reg.predict(X_test)
# 比较实际响应值(y_test)与预测响应值(y_pred)
print("Logistic回归模型的准确性(%):",
metrics.accuracy_score(y_test, y_pred)*100)

输出:

Logistic回归模型的准确性(%): 95.6884561892

最后,需要考虑以下有关Logistic回归的要点:

  • 不假设因变量和自变量之间存在线性关系,但是假设解释变量对数响应之间存在线性关系。
  • 自变量甚至可以是原始自变量的幂项或其他一些非线性变换。
  • 因变量不需要是正态分布的,但是它通常假定来自指数族的分布(例如,二项式,泊松,多项式,正态等);二元逻辑回归假设响应的二项式分布。
  • 不需要满足方差的均匀性。
  • 错误必须是独立的,但不能正态分布。
  • 它使用最大似然估计(MLE)而不是普通最小二乘(OLS)来估计参数,因此依赖于大样本近似值