当前位置:   article > 正文

逻辑斯谛回归(Logistic Regression,LR)及python实现_逻辑斯谛回归 1024维 python

逻辑斯谛回归 1024维 python

0. 简介

逻辑回归中,因变量取值是一个二元分布,模型学习 E [ y ∣ x ; θ ] E [y | x; \theta] E[yx;θ], 给定自变量和超参数后,得到因变量的期望,并基于期望来处理预测分类。

LR模型中,
log ⁡ p 1 − p = θ T x \log \frac{p}{1-p}=\theta^{\mathrm{T}} x log1pp=θTx
其中, p 是预测输入为正样本的概率。
定义 事件的几率(odds)为,该事件发生的概率与该事件不发生的概率的比值 p 1 − p \frac{p}{1-p} 1pp.

逻辑斯谛回归与线性回归的区别与相同点

逻辑斯谛回归是线性回归在分类问题的推广,使用sigmoid函数作为 link 函数。逻辑回归可以看作广义线性模型在因变量服从二元分布是的一个特殊情况。

线性回归使用最小二乘法进行参数估计,逻辑斯谛回归使用极大似然法,最大化预测属于实际的概率。其实,最小二乘法是在自变量x 与超参数 θ \theta θ 确定,因变量y服从正态分布的假设下,使用极大似然估计的一个化简。

LR和SVM(线性核)的联系与区别

LR和SVM的不同

1、损失函数的不同
LR是cross entropy(对数似然损失函数),SVM的损失函数是最大化间隔距离(合页损失);
在这里插入图片描述

2、SVM不能产生概率,LR可以产生概率;
LR 拟合的是概率分布。

3、SVM自带结构风险最小化,LR则是经验风险最小化

在假设空间、损失函数和训练集确定的情况下,经验风险最小化即最小化损失函数;
结构最小化是为了防止过拟合,在经验风险的基础上加上表示模型复杂度的正则项

4、LR和SVM在实际应用的区别

根据经验来看,对于小规模数据集,SVM的效果要好于LR,但是大数据中,SVM的计算复杂度受到限制,而LR因为训练简单,可以在线训练,所以经常会被大量采用。

n 是特征数量, m是样本数:
(1)如果 n 相对于 m 很大,(比如,n=1万, m = 10-1000) 使用LR算法或者 不带核的SVM;
(2) 如果 n 很小,m的数量适中(比如, n = 1-1000, m = 10-10000)
使用带有核函数的SVM算法
(3)如果n 很小, m很大, (n = 1-1000, m = 50000+)
增加更多的feature,使用LR或者不带核函数的SVM。
LR和不带核的SVM类似。

5、SVM的处理方法是只考虑support vectors,也就是和分类最相关的少数点,去学习分类器。
而逻辑回归通过非线性映射,大大减小了离分类平面较远的点的权重,相对提升了与分类最相关的数据点的权重。

1. 为什么LR是线性模型

逻辑斯蒂回归是假设因变量服从伯努利分布
在这里插入图片描述
也就是说,经过sigmoid函数,将 x 映射为 ϕ \phi ϕ 后,x与y的概率分布仍然是指数分布簇,即模型仍是广义线性模型。

2. LR模型定义

LR模型定义:
在这里插入图片描述
线性二分类模型:
f ( x ) = θ T x f(x)=\theta^{T} x f(x)=θTx
其中, f ( x ) = 0 f(x)=0 f(x)=0是决策平面。

逻辑回归决策函数是将此线性二分类嵌套一个sigmoid函数:
h θ ( x ) = sigmoid ⁡ ( f ( x ) ) = 1 1 + e − θ T x h_{\theta}(x)=\operatorname{sigmoid}(f(x))=\frac{1}{1+e^{-\theta^{T} x}} hθ(x)=sigmoid(f(x))=1+eθTx1
该式子取值在0~1,它的含义是,二分类中,类别y 为正类(即1)的概率。

(为什么 h θ ( x ) h_{\theta}(x) hθ(x)可以看做是正类的概率?
因为,在线性二分类中, θ T x > 0 \theta^{T} x>0 θTx>0属于正类,因此,这里 h θ ( x ) > 0.5 h_{\theta}(x)>0.5 hθ(x)>0.5属于正类。 h θ ( x ) h_{\theta}(x) hθ(x)取值又在0~1之间,因此,在二分类中,把 h θ ( x ) h_{\theta}(x) hθ(x)当作正类的概率没问题。
这里,有点本末倒置,因为构建LogisticRegression时,就是引入概率 p 来解释 y,通过 log[ p/(1-p)]=y来进行连接 p 和 y 的关系。

因此,在二分类中,
P ( Y = 1 ∣ x ) = exp ⁡ ( θ T x ) 1 + exp ⁡ ( θ T x ) P ( Y = 0 ∣ x ) = 1 1 + exp ⁡ ( θ T x ) P(Y=1 | x)=\frac{\exp (\theta^{T} x)}{1+\exp (\theta^{T} x)}\\ P(Y=0 | x)=\frac{1}{1+\exp (\theta^{T} x)} P(Y=1x)=1+exp(θTx)exp(θTx)P(Y=0x)=1+exp(θTx)1
其中, θ T x = w ⋅ x + b \theta^{T} x = w \cdot x+b θTx=wx+b
此外, 如果取Y的取值为{+1, -1 }; 最终梯度计算会不一样。

最大似然函数:
L ( θ ) = p ( y ^ ∣ X ; θ ) = ∏ i = 1 m p ( y ( i ) ∣ x ( i ) ; θ ) = ∏ i = 1 m ( h θ ( x ( i ) ) ) y ( i ) ( 1 − h θ ( x ( i ) ) ) ( 1 − y ( i ) )

L(θ)=p(y^|X;θ)=i=1mp(y(i)|x(i);θ)=i=1m(hθ(x(i)))y(i)(1hθ(x(i)))(1y(i))
L(θ)=p(y^X;θ)=i=1mp(y(i)x(i);θ)=i=1m(hθ(x(i)))y(i)(1hθ(x(i)))(1y(i))
(这里也体现了LR满足伯努利分布。)
令损失函数为负对数似然函数:

loss ⁡ ( θ ) = − log ⁡ ( L ( θ ) ) = − { ∑ i = 1 m y ( i ) log ⁡ ( h ( x ( i ) ) ) + ( 1 − y ( i ) ) log ⁡ ( 1 − h ( x ( i ) ) ) } \operatorname{loss}(\theta)=-\log (L(\theta))=- \left\{ \sum_{i=1}^{m} y^{(i)} \log \left(h\left(x^{(i)}\right)\right) + \left(1-y^{(i)}\right) \log \left(1-h\left(x^{(i)}\right)\right) \right\} loss(θ)=log(L(θ))={i=1my(i)log(h(x(i)))+(1y(i))log(1h(x(i)))}
化简得到:
l o s s ( θ ) = − ∑ i = 1 m [ ( y − 1 ) θ T x − l o g ( 1 + e − θ T x ) ] = − ∑ i = 1 m [ y θ T x − l o g ( 1 + e θ T x ) ] loss(\theta) = - \sum_{i=1}^{m} [ (y-1)\theta^Tx - log(1+e^{-\theta^Tx})]\\ = - \sum_{i=1}^{m} [ y\theta^Tx - log(1+e^{\theta^Tx})] loss(θ)=i=1m[(y1)θTxlog(1+eθTx)]=i=1m[yθTxlog(1+eθTx)]
l o s s ( θ ) loss(\theta) loss(θ) 求极小值。

∂ ∂ θ J ( θ ) = − ∑ i = 1 m ( y ( i ) − h θ ( x ( i ) ) ) ⋅ x i \frac{\partial}{\partial \theta} J(\theta) = -\sum_{i=1}^{m}\left(y^{(i) }-h_{\theta}\left(x^{(i)}\right)\right) \cdot x_{i} θJ(θ)=i=1m(y(i)hθ(x(i)))xi

这也是梯度下降法计算的参数梯度。
参数更新:
沿着负梯度方向进行更新。
w ← w − α ⋅ ∂ ∂ w J ( w , b ) w \leftarrow w-\alpha \cdot \frac{\partial}{\partial w} J(w,b) wwαwJ(w,b)
b ← b − α ⋅ ∂ ∂ b J ( w , b ) b \leftarrow b-\alpha \cdot \frac{\partial}{\partial b} J(w,b) bbαbJ(w,b)

3. 多分类LR

假如有y 有K类,使用多项逻辑回归(softmax regression)来分类。
p ( x = k ) = e w k ⋅ x ∑ i = 1 K e w k ⋅ x p(x = k) = \frac{e^{w_k\cdot x} } {\sum_{i = 1}^{K}{e^{w_k\cdot x}}} p(x=k)=i=1Kewkxewkx
损失函数:
L o s s ( w ) = ∑ i = 1 N ∑ j = 1 k [ s i g n ( y i = c ) l o g ( P ( Y = c ∣ x ) ] Loss( w ) = \sum\limits_{i = 1}^{N} \sum\limits_{j = 1}^{k} [ sign(y_i = c) log(P(Y = c | x) ] Loss(w)=i=1Nj=1k[sign(yi=c)log(P(Y=cx)]
或者采用 one-vs-rest 方法,构建 K-1 个逻辑斯蒂回归模型。

4. 防止过拟合方法

1.减少特征数量;
2.正则化

损失函数:
L o s s ( w ) = − 1 N ∑ i = 1 N [ y i l o g ( P ( Y = 1 ∣ x ) + ( 1 − y i ) l o g ( P ( Y = 0 ∣ x ) ] + λ 2 ∗ N ∑ j = 1 N θ j 2 = − 1 N ∑ i = 1 N [ y i ( w x + b ) − l o g ( 1 + l o g ( 1 + e w x + b ) ] + λ 2 ∗ N ∑ j = 1 N θ j 2 Loss( w ) = -\frac{1}{N}\sum\limits_{i = 1}^{N} [y_i log(P(Y = 1 | x) + (1 - y_i) log( P( Y = 0 | x )] + \frac{\lambda}{2*N}\sum_{j = 1}^{N} {\theta_j^2 }\\ = -\frac{1}{N} \sum\limits_{i = 1}^{N} [y_i (wx + b) - log(1 + log( 1 + e ^{wx + b})] + \frac{\lambda}{2*N}\sum_{j = 1}^{N} {\theta_j^2 } Loss(w)=N1i=1N[yilog(P(Y=1x)+(1yi)log(P(Y=0x)]+2Nλj=1Nθj2=N1i=1N[yi(wx+b)log(1+log(1+ewx+b)]+2Nλj=1Nθj2
λ \lambda λ为正则化系数。

model.LogisticRegression(penalty=‘l2’, *, dual=False, tol=0.0001, C=1.0, fit_intercept=True, intercept_scaling=1, class_weight=None, random_state=None, solver=‘lbfgs’, max_iter=100, multi_class=‘auto’, verbose=0, warm_start=False, n_jobs=None, l1_ratio=None)

在sklearn 中, 可以设置 超参数C(正则项惩罚系数的倒数)

L1,L2惩罚项的区别

L1容易使得LR系数 w 更加稀疏;
L2 相当于weight decay,目标函数变为凸函数,梯度下降法和L-BFGS都能收敛到全局最优解。
正则化项实际上是给了模型一个先验知识,L2正则相当于添加了一个均值为0,协方差为 1/λ 的高斯分布先验。

5. 实践

LogisticRegression二分类的python实现,由于LogisticRegression没有解释解,使用梯度下降法进行求解。

注:以下参数更新部分代码未必正确。

import sklearn.datasets as datasets
import numpy as np
from sklearn.linear_model import LogisticRegression as LR
class LogisticRegression():
    def __init__(self,alpha=0.01,epochs=3):
        self.W = None 
        self.b = None
        self.alpha = alpha
        self.epochs = epochs
    def fit(self,X,y): 
        # 设定种子
        np.random.seed(10)
        self.W = np.random.normal(size=(X.shape[1]))
        self.b = 0
        for epoch in range(self.epochs):
            if epoch%50 == 0:
                print("epoch",epoch)
            w_derivate = np.zeros_like(self.W)
            b_derivate = 0
            for i in range(len(y)):
            	# 这里是加上负梯度
                w_derivate += (y[i] - 1/(1+np.exp(-np.dot(X[i],self.W.T)-self.b)))*X[i]
                b_derivate += (y[i] - 1/(1+np.exp(-np.dot(X[i],self.W.T)-self.b)))
            # ?这里没必要就行求平均值,把向量转变成了标量!
            self.W = self.W + self.alpha*np.mean(w_derivate,axis=0)
            self.b = self.b + self.alpha*np.mean(b_derivate)
        return self
    def predict(self,X):
        p_1 = 1/(1 + np.exp(-np.dot(X,self.W) - self.b))
        return np.where(p_1>0.5, 1, 0)
def accuracy(pred, true):
    count = 0
    for i in range(len(pred)):
        if(pred[i] == true[i]):
            count += 1
    return count/len(pred)
def normalize(x):
    return (x - np.min(x))/(np.max(x) - np.min(x))

if __name__ == "__main__":
    # input datasets 
    digits = datasets.load_breast_cancer()
    X = digits.data
    y = digits.target
    # 归一化
    X_norm = normalize(X)
    X_train = X_norm[:int(len(X_norm)*0.8)]
    X_test = X_norm[int(len(X_norm)*0.8):]
    y_train = y[:int(len(X_norm)*0.8)]
    y_test = y[int(len(X_norm)*0.8):]
    # model 1
    lr = LogisticRegression(epochs=500,alpha=0.03)
    lr.fit(X_train,y_train)
    y_pred = lr.predict(X_test)
    # 评估准确率
    acc = accuracy(y_pred, y_test)
    print("acc", acc)
    # model 2
    clf_lr = LR()
    clf_lr.fit(X_train, y_train)
    y_pred2 = clf_lr.predict(X_test)
    print("acc2", accuracy(y_pred2, y_test))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

运行结果:

acc 0.9473684210526315
acc2 0.9298245614035088
  • 1
  • 2

可以看到自己写的LR模型比sklearn里边实现的效果更好,原因,可能是我们设定的超参数(如alpha,epoch)更加适合这个小数据集吧。


2020-10-16更新:
以上的LR模型代码实现应该是有点问题的,如果按照理论,应该是:

class LogisticRegression():
	def __init__(self,alpha=0.01,epochs=3):
		self.W = None 
		self.b = None
		self.alpha = alpha
		self.epochs = epochs
	def fit(self,X,y): 
		# 设定种子
		np.random.seed(10)
		self.W = np.random.normal(size=(X.shape[1]))
		self.b = 0
		
		for epoch in range(self.epochs):
			loss = 0.0
			if epoch%100 == 0:
				print("epoch",epoch)
			w_derivate = np.zeros_like(self.W)
			b_derivate = 0
			for i in range(len(y)):
			   # 这里是加上负梯度
				w_derivate += (y[i] - 1/(1+np.exp(-np.dot(X[i],self.W.T)-self.b)))*X[i]
				b_derivate += (y[i] - 1/(1+np.exp(-np.dot(X[i],self.W.T)-self.b)))
				loss += (y[i]*(np.dot(X[i],self.W.T)+self.b) - 1/(1+np.exp(-np.dot(X[i],self.W.T)-self.b)))
			loss /= len(y)
			loss *= -1
			if epoch % 100 == 0:
				print("loss", loss)
			#print("w", w_derivate)
			w_derivate /= len(y)
			b_derivate /= len(y)
			self.W = self.W + self.alpha*w_derivate
			self.b = self.b + self.alpha*b_derivate
		return self
	def predict(self,X):
		p_1 = 1/(1 + np.exp(-np.dot(X,self.W) - self.b))
		return np.where(p_1>0.5, 1, 0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

同样的数据集,使用1000 epochs, 0.1的学习率:

epoch 0
loss 0.4458747565746601
epoch 100
loss 0.46556179062935027
epoch 200
loss 0.43805751597460546
epoch 300
loss 0.40752544455838813
epoch 400
loss 0.3775484524293621
epoch 500
loss 0.3486239389088694
epoch 600
loss 0.32095136138793146
epoch 700
loss 0.2946335832030799
epoch 800
loss 0.2697025483110918
epoch 900
loss 0.2461376580506989
acc 0.9210526315789473 # my implement
acc2 0.9298245614035088 # sklearn LR model
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

从损失看,这个实现应该是准确的。之前实现,把参数更新量求平均值变成一个标量的方式应该是错误的,只是这种平滑的方式居然给模型学习带来意想不到的效果,所以,上述的实现就不覆盖了。

6. 其他问题

LR特征离散化原因:
1.离散特征容易增加和减少特征,快速迭代模型;
2.稀疏向量内积乘法运算速度更快,计算易于存储、扩展;
3.离散特征对异常值不敏感,鲁棒性强;
4.简化了逻辑斯蒂模型,降低模型过拟合。
5.离散化的特征方便进行特征交叉,使得模型的表达能力更强。

把高度相关的特征去掉原因:

使得模型的可解释性更强;提高训练速度。
如果模型很多特征高度相关,损失函数本身收敛了,参数仍然没有收敛,训练时间变长。

避免局部极小方法:

1.以多组不同参数值进行初始化;
2.使用随机梯度下降,加入随机的因素。

特征系数绝对值能认为是特征重要性吗?
1.特征系数绝对值越大,对分类效果的影响越显著,但不能认为特征系数更大的特征更重要。
2.改变变量尺度会改变系数绝对值;
3.特征相关,系数可以认为从一个特征转移到另一个特征;

逻辑斯谛回归需要标准化?
理论上不需要,因为特征尺寸发生改变,并不影响到最优决策面;但是,为了加快模型的收敛速度,一般也进行标准化。

但是,像Lasso回归,岭回归,是需要对数据进行标准化的,因为在输入的缩放比例下不是等变的。

特征维度很高,对LR模型造成的影响

特征多,很可能噪声维度灾难,也就是数据稀疏,LR模型容易欠拟合,效果会下降。


最近开通了个公众号,主要分享推荐系统,风控等算法相关的内容,感兴趣的伙伴可以关注下。
在这里插入图片描述


参考:

  1. zhihu 为什么机器学习的分类器用logistic模型?;
  2. 机器学习面试题汇总(逻辑斯蒂回归相关);
  3. Logistic 回归的三个视角(极大似然估计/熵/形式化损失函数);
  4. 机器学习之逻辑回归(Logistic Regression);
  5. 浅析机器学习:线性回归 & 逻辑回归;
  6. 在进行数据分析的时候,什么情况下需要对数据进行标准化处理?;
  7. 在进行逻辑回归之前需要标准化吗?;
  8. sklearn lr;
  9. 逻辑回归L1与L2正则,L1稀疏,L2全局最优(凸函数梯度下降)
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/600239
推荐阅读
相关标签
  

闽ICP备14008679号