ML(5) - LogisticRegression逻辑回归


LogisticRegression基本概念

  • 逻辑回归:解决的分类问题,是基于线性回归的衍生算法。
  • 将样本的特征和样本发生的概率联系在一起,概率是一个数。
  • 在之前的线性回归中,通过公式 y ^ = f ( x ) \hat{y}=f(x) y^=f(x)求出的 y ^ \hat{y} y^ 就是需要的值。例如预测成绩得到的就是成绩,预测价格得到的就是价格。
  • 逻辑回归中,将 y ^ \hat{y} y^再通过转换公式 p ^ = p ( y ^ ) \hat{p} = p(\hat{y}) p^=p(y^) 求出概率 p ^ \hat{p} p^,根据概率再进行分类。
    因此,逻辑回归其实可以看成回归算法也可看成分类算法,作为分类算法只可以解决二分类问题,当然可以通过改进解决多分类问题。

LogisticRegression数学模型

  • 对于线性回归公式, y ^ = θ t ⋅ x b \hat{y}= \theta^t\cdot x_b y^=θtxb 求得的可以是任意的值。
  • 逻辑回归将线性回归方式求得的值,进行一个转换,变成[0,1]区间的概率。
  • 这种转换通常采用Sigmoid函数: σ ( t ) = 1 1 + e − t σ(t)= \frac{1}{1+e^{-t}} σ(t)=1+et1 , 取值范围为[0,1]
    在这里插入图片描述
  • y ^ = θ t ⋅ x b \hat{y}= \theta^t\cdot x_b y^=θtxb 带入sigmiod转换函数中,即得:
    在这里插入图片描述
  • 通常我们定义输出概率 p ^ > 0.5 \hat p>0.5 p^>0.5 ,预测结果 y ^ \hat y y^ 归为1类, 输出概率 p ^ < 0.5 \hat p<0.5 p^<0.5 ,预测结果 y ^ \hat y y^ 归为0类 。也就是 y ^ = θ t ⋅ x b \hat{y}= \theta^t\cdot x_b y^=θtxb 大于0时分为1类,小于0时分为0类。因此 y ^ = θ t ⋅ x b = 0 \hat{y}= \theta^t\cdot x_b = 0 y^=θtxb=0 就成为了分类的边界,称为决策边界。当然并非 θ t ⋅ x b = 0 \theta^t\cdot x_b = 0 θtxb=0 才是决策边界,可定义任意threshold,即 θ t ⋅ x b = t h r e s h o l d \theta^t\cdot x_b = threshold θtxb=threshold 皆可称为决策边界。(tips:决策边界的定义目的用于预测阶段分类别,训练阶段与决策边界的定义无关)

LogisticRegression损失函数

  • 对于逻辑回归训练过程,损失函数定义应满足,真值为1类时,输出概率 p ^ \hat p p^ 若越大损失cost应该就要越小,若越小损失cost就要越大。同理当真值为0类时,输出概率 p ^ \hat p p^ 若越小对应的损失cost应该就要越小,若越大损失cost就要越大。
  • 损失函数的定义:
    1.MSE均方误差仍然有效: J ( θ ) = 1 m ∑ i = 1 m ( p i ^ − y i ) 2 J(\theta) = \frac{1}{m}\sum_{i=1}^m(\hat{p_{i}}-y_{i})^2 J(θ)=m1i=1m(pi^yi)2 , p i ^ \hat{p_{i}} pi^即概率值[0,1], y i y_{i} yi即真值0或1。
    2.二元交叉熵损失函数(binary_crossentropy): python中log()默认以e为底。
    在这里插入图片描述
     当y = 1 时,损失函数: − l o g ( p ^ ) -log(\hat p) log(p^)
     当y = 0 时,损失函数: − l o g ( 1 − p ^ ) -log(1-\hat p) log(1p^)
     对应的坐标图:x 轴即 p ^ \hat p p^ 取值,范围 [0,1]。
       在这里插入图片描述
     从坐标图可以看出:
     当样本y = 1时,预测的x(输出概率p)越大,cost就越小。
     当样本y = 0时,预测的x(输出概率p)越小,cost就越小。
     完全符合logistic损失函数的定义。
     
     利用真值 y y y 将二者结合得到真正的损失函数: 当y=1时只会用到 − l o g ( p ^ ) -log(\hat p) log(p^),当y=0时只会用到 − l o g ( 1 − p ^ ) -log(1-\hat p) log(1p^)
      在这里插入图片描述
     在这里插入图片描述

LogisticRegression梯度下降

  • 梯度下降公式推导

  • 损失函数:
    在这里插入图片描述
  • 求梯度:
    在这里插入图片描述
  1. l o g ( σ ( t ) ) log(σ(t)) log(σ(t)) 求导 :
    在这里插入图片描述
    在这里插入图片描述
  2. 对损失函数前半部分进行求偏导:
    在这里插入图片描述
  3. l o g ( 1 − σ ( t ) ) log(1-σ(t)) log(1σ(t)) 求导 :
    在这里插入图片描述
    在这里插入图片描述
  4. 对损失函数后半部分进行求偏导:
    在这里插入图片描述
  5. 前后结合化简:公式几乎和线性回归一模一样,不同处线性回归为 X b i θ X_{b}^i\theta Xbiθ,逻辑回归为 σ ( X b i θ ) σ(X_{b}^i\theta) σ(Xbiθ)
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
  • 手写LogisticRegression

import numpy as np
from sklearn.metrics import  accuracy_score

class LogisticRegression:
    def __int__(self):
        '''初始化线性回归模型'''
        self.coef_ = None
        self.interception_ = None
        self._theta = None
        
    def _sigmoid(self,t):
        return 1 / (1 + np.exp(-t))
        
    def fit_gd(self,x_train,y_train,eta = 0.001,n_iters = 1e4):
        assert x_train.shape[0] == y_train.shape[0],"size of x must equal y"
        # 损失函数
        def J(theta,X_b,y):
            p = self._sigmoid(X_b.dot(theta))
            try:
                return -np.sum(y * np.log(p) + (1-y) * np.log(1-p)) / len(y)
            except:
                return float('inf')
        # 梯度计算
        def dJ(theta,X_b,y):
            return X_b.T.dot(self.sigmoid(X_b.dot(theta))-y)/len(X_b)
        # 更新参数
        def gradient_descent(init_theta,X_b,y,eta,n_iters,epsilon=1e-8):
            theta = init_theta
            count = 0
            while count<n_iters:
                last_theta = theta
                gradient = dJ(theta,X_b,y)
                theta = theta - eta * gradient
                if(abs(J(theta,X_b,y)-J(last_theta,X_b,y)) < epsilon) :
                    break
                count += 1
            return theta       
        X_b = np.hstack([np.ones((len(x_train),1)),x_train])
        init_theta = np.zeros(X_b.shape[1])
        self._theta = gradient_descent(init_theta,X_b,y_train,eta,n_iters)
        self.coef_ = self._theta[1:]
        self.interception_ = self._theta[0]
        return self
        
# 返回预测概率
def predict_proba(self,x_test):
    assert self.interception_ is not None and self.coef_ is not None,"must fit before predict"
    assert x_test.shape[1] == len(self.coef_),"the feature number of x_test must be equal to x_ train"
    X_b = np.hstack([np.ones((len(x_test),1)),x_test])
    return self._sigmoid(X_b.dot(self._theta))
   
# 返回按概率计算的分类结果
def predict(self,x_test):
    assert self.interceptio self.predict_proba(x)n_ is not None and self.coef_ is not None,"must fit before predict"
    assert x_test.shape[1] == len(self.coef_),"the feature number of x_test must be equal to x_ train"
    proba = self.predict_proba(x_test)
    return np.array(proba >= 0.5,dtype="int")

# 评估准确率
def score(self,x_test,y_test):
    y_predict = self.predict(x_test)
    return accuracy_score(y_test,y_predict)

def __repr__(self):
    return  "Logistic Regression"

Scikit-Learn中的LogisticRegression

  • API:sklearn.linear_model.LogisticRegression

主要参数:

参数名含义
penalty选择正则化方式: ‘l1’, ‘l2’(默认), ‘elasticnet’ or ‘none’
C正则化影响强度,越小正则效果越强,默认:1.0(必须为大于0的float)
dual是否转化为对偶问题求解,默认=False。仅在solver为liblinear,penalty为l2时有效。当n_samples> n_features时,首选dual = False。
tol停止训练迭代的误差指标,默认:1e-4
fit_intercept是否存在截距,默认存在
solver优化方法:‘newton-cg’, ‘lbfgs’, ‘liblinear’(默认), ‘sag’, ‘saga’ 。‘liblinear’,坐标轴下降法,’lbfgs‘,’newton-cg’皆利用牛顿法(loss二阶导数),‘sag’(随机梯度下降)。小数据集建议使用‘liblinear’,大数据集使用‘sag’或’saga‘。1.saga:支持‘l1’, ‘l2’, ‘elasticnet’ or ‘none’。 2.liblinear:支持‘l1’, ‘l2’。 3.'newton-cg’, ‘lbfgs’, ‘sag’:支持 ‘l2’,‘none’
multi_class分类策略:‘ovr’(默认), ‘multinomial’(ovo), ‘auto’。sklearn的逻辑回归是默认支持多分类任务,默认使用‘ovr’且可搭配所有solver。若使用’multinomial‘策略,solver只能选择:‘newton-cg’, ‘lbfgs’, ‘sag’, ‘saga’。
n_jobs使用运算核心数,默认:none(1), -1代表使用全部
verbose训练资讯显示(仅于solver为liblinear,lbfgs有效),默认:0,数字越大越详细
max_iter训练最大迭代次数,默认:100。
class_weight各类别的权重:字典({0:0.9,1:0.1}),‘balanced’,默认None。如果class_weight选择balanced,会根据训练样本量来计算权重,某类样本量越多,权重越低,反之亦然。主要用于高度失衡的样本集。
  • API:sklearn.linear_model.LogisticRegressionCV

主要参数:

参数名含义
Cs正则化影响强度,float或int,默认:10。如果Cs为int,则以1e-4和1e4之间的对数标度选择Cs值网格,再生成10个C值。
cv交叉验证折叠数k,默认:None
refitbool,默认:True。若为True,则从所有交叉验证中获取对应于最佳得分的coefs和C,并且使用这些参数进行最终训练。否则,取最佳交叉验证k折的平均值。
solver优化方法:默认变成lbfgs,其它与LogisticRegression一致。
  1. 上面主要列出了LogisticRegression没有或不一致的参数。
  2. LogisticRegressionCV利用Cs设置多个C值,再通过cv交叉验证获取最佳的C值,并得到最佳参数模型。
  3. 其实就是多实现了交叉验证和仅对C参数的网格搜索。

  • 多项式逻辑回归

逻辑回归本质由线性回归发展而来,对于处理非线性的数据,同样也是使用sklearn.preprocessing. PolynomialFeatures 进行特征构造实现。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression

np.random.seed(666)
X = np.random.normal(0, 1, size=(200, 2))
y = np.array((X[:,0]**2+X[:,1]**2)<1.5, dtype='int')

# 多项式逻辑回归
def PolynomialLogisticRegression(degree,C,penalty'):
    return Pipeline([
        ('poly', PolynomialFeatures(degree=degree)),
        ('std_scaler', StandardScaler()),
        ('log_reg', LogisticRegression(C=C,penalty=penalty))
    ])

# 画决策边界(仅画两个特征,即二维)
def plot_decision_boundary(model, axis):
    x0, x1 = np.meshgrid(
        np.linspace(axis[0], axis[1], int((axis[1]-axis[0])*100)).reshape(-1, 1),
        np.linspace(axis[2], axis[3], int((axis[3]-axis[2])*100)).reshape(-1, 1),)
    X_new = np.c_[x0.ravel(), x1.ravel()]
    y_predict = model.predict(X_new)
	zz = y_predict.reshape(x0.shape)
    custom_cmap = ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])    
	plt.contourf(x0, x1, zz, linewidth=5, cmap=custom_cmap)

poly_log_reg = PolynomialLogisticRegression(degree=2, C=0.1, penalty='l1')
poly_log_reg.fit(X, y)

plot_decision_boundary(poly_log_reg, axis=[-4, 4, -4, 4])
plt.scatter(X[y==0,0], X[y==0,1])
plt.scatter(X[y==1,0], X[y==1,1])
plt.show()

在这里插入图片描述

  • LogisticRegression处理多分类任务

  1. 使用OvR(One vs Rest)和 OvO(One vs One,是MvM(many-vs-many)的特例)两种策略,这两种策略可以使所有的二分类算法适用于多分类,不仅仅是LogisticRegression。
  2. OvR:
    假设有4个类别,每次取1个类别和剩下的3个类别,就可以变成4个二分类问题,再利用LogisticRegression模型。当预测一个新样本,分别经过4种模型,得到4个概率值,取最高概率值的模型分类作为结果。
    在这里插入图片描述
    3.OvO(MvM,many-vs-many的特例):
    假设4个类别,两两组合形成二分类,组合情况有: C m n = m ! n ! ( m − n ) ! C_{m}^n= \frac{m!}{n!(m-n)!} Cmn=n!(mn)!m! 。当预测一个新样本,每种组合情况都会有一个分类结果,进行投票,多数决。比OvR 更耗时,但是更准确。
    在这里插入图片描述
    4.LogisticRegression中默认可使用多分类,可通过修改参数调整多分类使用策略:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

#使用鸢尾花的所有数据,一共有3种分类
iris = datasets.load_iris()
X = iris.data
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666)

# 默认的OvR策略
log_reg = LogisticRegression()
log_reg.fit(X_train, y_train)
log_reg.score(X_test, y_test)
>>> 0.94736842105263153

# 使用其它策略(mvm(ovo)等)
log_reg2 = LogisticRegression(multi_class="multinomial", solver="newton-cg")
log_reg2.fit(X_train, y_train)
log_reg2.score(X_test, y_test)
>>> 1.0

Scikit-learn中的OvR和OvO类

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.multiclass import OneVsRestClassifier
from sklearn.multiclass import OneVsOneClassifier

#使用鸢尾花的所有数据,一共有3种分类
iris = datasets.load_iris()
X = iris.data
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666)

# 逻辑回归模型对象
log_reg = LogisticRegression()

# OvR
ovr = OneVsRestClassifier(log_reg)
ovr.fit(X_train, y_train)
ovr.score(X_test, y_test)
>>> 0.94736842105263153

# OvO
ovo = OneVsOneClassifier(log_reg)
ovo.fit(X_train, y_train)
ovo.score(X_test, y_test)
>>> 1.0
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值