当前位置:   article > 正文

逻辑回归(Logistic回归又名对数几率回归)原理及python代码实现_python对数几率回归

python对数几率回归

1. 公式推导

        为了实现Logistic回归分类器,我们可以在每个特征上都乘以一个回归系数,然后把所有的结果值相加,将这个总和代人Sigmoid函数中,进而得到一个范围在0~1之间的数值。任何大于0.5的数据被分人1类 ,小于0.5即被归人0类 ,所以Logistic回归也可以被看成是一种概率估计。逻辑回归的本质还是线性回归,母体函数是线性回归函数,只不过将结果值代入Sigmoid函数转换为0到1之间的数值用来完成分类。

线性回归方程如下所示:

\hat{y}=\theta ^{T}X_{b}                                                                        (1)

为了将预测结果值转换到0~1之间的值,将\hat{y}代入Sigmoid函数中:

\sigma (\hat{y})= \frac{1}{1+e^{-\hat{y}}}                                                                        (2)

将式(1)代入到式(2)中,得到下式概率公式(3):

\hat{p}= \frac{1}{1+e^{-\theta ^{T}X_{b}}}                                                                       (3)

        构造如下损失函数:

J(\theta ) = \left\{\begin{matrix} -log(\hat{p}) & if & y=1\\ -log(1-\hat{p}) &if& y=0\end{matrix}\right.                                                (4)

        设 \hat{p} 是当 y = 1 时的概率,那么肯定希望概率越大越好,如果偏离的100%就给一定的惩罚,如果概率为0 ,则让其损失函数无限大。

        反之,当 y = 0 时,让其1-\hat{p} 的概率最大化,如果偏离100%就给一定的惩罚,如果概率为1,则让其损失无限大。

        上式分段函数(4)可转换为下式(5):

J(\theta )=-ylog(\hat{p})-(1-y)log(1-\hat{p})                                (5)

        上式便是经典的交叉熵损失函数,又称对数损失函数,此函数可衡量两个分布之间的举例,一般用于分类模型的损失函数。

2. 决策边界

        当概率大于等于0.5时,我们认为y = 1,反之小于0.5时,认为y = 0,即如下式所示:

\hat{y} = \left\{\begin{matrix} 1, &\hat{p}\geqslant 0.5 & \theta ^{T}.X_{b}\geqslant 0\\ 0, &\hat{p}< 0.5& \theta ^{T}.X_{b}< 0\end{matrix}\right.                                        (6)

即决策边界为:

\theta ^{T}.X_{b} = 0                                                                        (7)

        在实际分类问题中,决策边界可能会是一个弯曲的曲线或平面,但以上表达式为线性回归方程,注定最后的决策边界将会是一条直线或一个平面,无法对非线性关系的样本做到精准的分类。

所以为了更好的解决这类非线性的分类问题,我们可以引入多项式回归方程,通过对特征向量增加阶数实现非线性关系的数据集分类,具体实现方法可参考笔者另一篇博文:

多项式回归(非线性回归)的python代码实现_南山十一少的博客-CSDN博客

3. 多分类问题

        逻辑回归本身只能处理二分类问题,但通过巧妙的多次运用逻辑回归,也能完成多分类任务。(sklearn封装的逻辑回归算法可通过设置超参数处理多分类任务,使用方法见本文 5.2)

方法一:OVR法(One vs Rest)

假定N分类问题,首先将其中一个类别和其他类别作为一次二分类,然后从N类别中循环执行N次,分别得到每个样本N个类别的概率,挑出概率最高的类别作为次样本的类别分类。

方法二:OVO法(One vs One)

假定N分类,首先拿出两个类别进行二次分类任务,然后从N类别中循环挑选出两个类别进行分类计算概率,共计算C_{n}^{2}次,得到每个样本N个类别的概率,挑出概率最高的类别作为次样本的类别分类。

4. 自制代码实现

首先编写Sigmoid函数、损失函数、损失函数的偏导,然后根据批量梯度下降的方法不断迭代计算参数\theta,具体编码如下:

注意:

1. 在编写Sigmoid函数时,因为代入的-t = X_b.dot(theta) 比较大,通过指数函数np.exp(-t)后容易导致数据溢出,建议更换为另一个公式代替:return .5 * (1 + np.tanh(.5 * t)),否则会有如下报错:

RuntimeWarning: overflow encountered in exp
  return 1. / (1. + np.exp(-t))

2. 计算损失函数时,由于分母过大,容易导致精度不够,建议增加浮点数的精度,在y_hat后面增加1e-6,否则会有如下报错

RuntimeWarning: divide by zero encountered in log
  return - np.sum(y * np.log(y_hat) + (1 - y) * np.log(1 - y_hat)) / len(y)

RuntimeWarning: invalid value encountered in multiply
  return - np.sum(y * np.log(y_hat) + (1 - y) * np.log(1 - y_hat)) / len(y)

RuntimeWarning: invalid value encountered in scalar subtract
  if abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon:

  1. # 自编逻辑回归函数
  2. def MyselfLogisticRegression(X_train, y_train, eta=0.01, n_iters=1e4):
  3. def sigmoid(t):
  4. # return 1. / (1. + np.exp(-t))
  5. return .5 * (1 + np.tanh(.5 * t))
  6. def J(theta, X_b, y):
  7. y_hat = sigmoid(X_b.dot(theta))
  8. try:
  9. return - np.sum(y * np.log(y_hat + 1e-6) + (1 - y) * np.log(1 - y_hat + 1e-6)) / len(y)
  10. except:
  11. return float('inf')
  12. def dJ(theta, X_b, y):
  13. return X_b.T.dot(sigmoid(X_b.dot(theta)) - y) / len(y)
  14. def gradient_descent(X_b, y, initial_theta, eta, n_iters=1e4, epsilon=1e-8):
  15. theta = initial_theta
  16. cur_iter = 0
  17. while cur_iter < n_iters:
  18. gradient = dJ(theta, X_b, y)
  19. last_theta = theta
  20. theta = theta - eta * gradient
  21. if abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon:
  22. break
  23. cur_iter += 1
  24. return theta
  25. X_b = np.hstack([np.ones((len(X_train), 1)), X_train])
  26. # initial_theta = np.zeros(X_b.shape[1])
  27. initial_theta = np.random.randn(X_b.shape[1])
  28. theta = gradient_descent(X_b, y_train, initial_theta, eta, n_iters)
  29. return theta

5 调用sklearn的逻辑回归函数

5.1 模型正则化超参数使用

在sklearn中封装的逻辑函数中,自动封装了正则化,可以通过超参数进行调节,其正则化后的损失函数如下:

C.J(\theta ) + L

如果读者对正则化感兴趣,可以参考笔者另一篇博文:

模型正则化在多项式回归中的运用:Ridge回归(岭回归)、LASSO回归、弹性网络回归的原理及python代码实现_南山十一少的博客-CSDN博客

C:正则化强度权重,大于0的浮点数,不填写默认1.0。C越小,正则化的效力越强,参数会被压缩得越小。

penalty:正则化方式, penalty = l_{1}时,损失函数中的L为系数值绝对值和,penalty = l_{2}时,损失函数的L为系数平方和

solver:优化算法选择参数,参数为liblinear时,使用了坐标轴下降法迭代损失函数。

具体实现代码如下:

  1. from sklearn import datasets
  2. from sklearn.linear_model import LogisticRegression
  3. from sklearn.pipeline import Pipeline
  4. from sklearn.preprocessing import PolynomialFeatures, StandardScaler
  5. from sklearn.model_selection import train_test_split
  6. import numpy as np
  7. if __name__ == "__main__":
  8. # 构造带随机误差的呈非线性关系的数据集
  9. np.random.seed(666)
  10. X = np.random.normal(0, 1, size=(200, 2))
  11. y = np.array((X[:, 0] ** 2 + X[:, 1]) < 1.5, dtype='int')
  12. for _ in range(20):
  13. y[np.random.randint(200)] = 1
  14. X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666)
  15. # 预处理数据进行非线性化多项式组合
  16. poly = PolynomialFeatures(degree=20)
  17. poly.fit(X_train)
  18. X_train = poly.transform(X_train)
  19. X_test = poly.transform(X_test)
  20. # 调用sklearn逻辑回归函数
  21. poly_log_reg = LogisticRegression(C=0.2,solver='liblinear',penalty="l1")
  22. poly_log_reg.fit(X_train, y_train)
  23. train_score = poly_log_reg.score(X_train, y_train)
  24. test_score = poly_log_reg.score(X_test, y_test)
  25. print(
  26. "poly_log_reg.intercept_ = {}, poly_log_reg.coef_ = {}".format(poly_log_reg.intercept_[0], poly_log_reg.coef_))
  27. print("PolynomialLogisticRegression train_score = {}, test_score = {}".format(train_score, test_score))
  28. # 调用自编逻辑回归函数对比
  29. theta = MyselfLogisticRegression(X_train, y_train)
  30. poly_log_reg.intercept_[0] = theta[0]
  31. poly_log_reg.coef_[0] = theta[1:]
  32. print("poly_log_reg.intercept_ = {}, poly_log_reg.coef_ = {}".format(poly_log_reg.intercept_, poly_log_reg.coef_))
  33. train_score = poly_log_reg.score(X_train, y_train)
  34. test_score = poly_log_reg.score(X_test, y_test)
  35. print("MyselfLogisticRegression train_score = {}, test_score = {}".format(train_score, test_score))

5.2 多分类超参数使用

如果要进行多分类任务,同样sklearn也封装了OVR和OVO的方法,只需通过超参数设置便能使用。

multi_class:多分类模式

ovr:,一对剩余。有K类,则训练K个模型,每个模型把第i类当一类,其余当一类。最后选择预测概率最高的一类作为预测类别。
multinomial:多项模式,OVR模式,两两分类,最后选择预测概率最高的一类作为预测类,值得注意的是,如果选择次模式,solver超参数需要选择newton-cg

代码如下:

  1. if __name__ == "__main__":
  2. # 鸢尾花为例调用sklearn逻辑回归函数进行多分类试验
  3. iris = datasets.load_iris()
  4. X = iris.data
  5. y = iris.target
  6. X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666)
  7. log_reg = LogisticRegression(multi_class='multinomial',solver='newton-cg')
  8. log_reg.fit(X_train, y_train)
  9. score = log_reg.score(X_test, y_test)
  10. y_predict = log_reg.predict(X_test)
  11. print("multi_class predict y_predict = {}.".format(y_predict))
  12. print("multi_class LogisticRegression score = {}".format(score))

6 结论

        本文通过讲解逻辑回归(对数几率回归)从回归模型到分类模型的演变过程以及损失函数的推导过程,然后利用梯度下降编写逻辑回归方法,最后通过调用sklearn封装的逻辑回归方法,分别演示了模型正则化和多分类的超参数使用。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/279308
推荐阅读
相关标签
  

闽ICP备14008679号