天天看点

【机器学习三】逻辑回归模型-LogisticRegression

一、回归划分

​ 广义线性模型家族中,依据因变量的不同,可以有如下的划分:

(1)如果是连续的,就是多重线性回归。

(2)如果是二项分布,就是逻辑回归。

(3)如果是泊松(Poisson)分布,就是泊松回归。

(4)如果是负二项分布,就是负二项回归。

​ 逻辑回归名字中虽有“回归”二字,但它是分类算法。逻辑回归的因变量可以是二分类的,也可以是多分类的,但是二分类的更为常用,也更加容易解释。所以实际中最常用的就是二分类的逻辑回归。

二、逻辑回归模型及优化

2.1 逻辑回归模型

​ 逻辑回归模型函数是我们后面常用的激活函数“sigmoid”函数,如果我们的输入为 X θ X\theta Xθ,输出为0和1(两类就可以),则可以用下式表示:

h θ ( X ) = 1 1 + e − X θ h_\theta(X) = \frac{1}{1+e^{-X\theta}} hθ​(X)=1+e−Xθ1​

​ 此函数的图像表现:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m761ll1v-1585545189355)(D:\Haibo\TnoteBook\images\machine_learn\logistic_reg_pic.JPG)]

2.2 逻辑回归参数估计

​ 我们接下来使用极大似然估计优化逻辑回归的参数

​ 由于逻辑回归是而分类,则有概念分布:

P ( y = 1 ∣ x ; θ ) = h θ ( x ) P ( y = 0 ∣ x ; θ ) = 1 − h θ ( x ) \begin{aligned} &P(y=1|x;\theta) = h_\theta(x)\\ &P(y=0|x;\theta) = 1-h_\theta(x) \end{aligned} ​P(y=1∣x;θ)=hθ​(x)P(y=0∣x;θ)=1−hθ​(x)​

​ 其联合概率分布为:

P ( y ∣ x ; θ ) = ( h θ ( x ) ) y ( 1 − h θ ( x ) ) 1 − y P(y|x;\theta) = (h_\theta(x))^y(1-h_\theta(x))^{1-y} P(y∣x;θ)=(hθ​(x))y(1−hθ​(x))1−y

​ 对应的似然函数为:

L ( θ ) = ∏ i = 1 m ( h θ ( x ( i ) ) y ( i ) ( 1 − h θ ( x ( i ) ) ) 1 − y ( i ) L(\theta) =\prod\limits^{m}_{i=1}(h_\theta(x^{(i)})^{y^{(i)}}(1-h_\theta(x^{(i)}))^{1-y^{(i)}} L(θ)=i=1∏m​(hθ​(x(i))y(i)(1−hθ​(x(i)))1−y(i)

​ 对数似然函数为:

J ( θ ) = l o g L ( θ ) = ∑ i = 1 m ( y ( i ) l o g h ( x ( i ) ) + ( 1 − y ( i ) ) ( 1 − h ( x ( i ) ) ) \begin{aligned} J(\theta) &= logL(\theta) \\ &=\sum\limits^{m}_{i=1}(y^{(i)}log h(x^{(i)})+(1-y^{(i)})(1-h(x^{(i)})) \end{aligned} J(θ)​=logL(θ)=i=1∑m​(y(i)logh(x(i))+(1−y(i))(1−h(x(i)))​

​ 对数似然函数求梯度(已省略计算过程):

∂ ∂ θ j J ( θ ) = ( y − h θ ( x ) ) x j \frac{\partial}{\partial\theta_j}J(\theta)=(y-h_\theta(x))x_j ∂θj​∂​J(θ)=(y−hθ​(x))xj​

​ 由上面的推导可以得到参数的迭代规则:

θ j = θ j − α ( h θ ( x ) − y ) x j \theta_j = \theta_j-\alpha(h_\theta(x)-y)x_j θj​=θj​−α(hθ​(x)−y)xj​

三、手撕逻辑回归代码

​ 本节我们使用python写出逻辑回归代码优化sklearn带有的癌症数据集

'''
手写Logistic回归代码预测sklearn自带的癌症数据集
'''
import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

class LogisticRegression(object):
    def __init__(self,learning_rate=0.1,max_iter=1500,stop_error=1e-4):
        '''构造函数'''
        self.coef = None
        self.intercept = None
        self.lr = learning_rate
        self.mi = max_iter
        self.se = stop_error

    def sigmoid(self,X):
        '''sigmoid函数'''
        return 1./(1.+np.exp(-X))

    def der_sigmoid(self,X):
        '''sigmoid函数导数'''
        return self.sigmoid(X)*(1-self.sigmoid(X))

    def logistic_model(self,theta,x):
        '''模型计算'''
        z = np.sum(theta.T*x)
        return self.sigmoid(z)

    def fit(self,X,Y):
        '''使用梯度下降训练LR模型'''
        #得到训练数据
        self.x_train = X
        self.y_train = Y
        self.n_features = X.shape[1]
        #初始化参数,全为1
        self.theta = np.ones(self.n_features)
        for step in range(self.mi):
            self.theta = self._step_gradient()
            loss = self.Loss_CrossEntropy(self.theta,self.x_train,self.y_train)
            if step % 20 == 0:
                print(f"iteration:{step},loss:{loss},theta:{self.theta}")
            if loss<self.se:
                print(f"iteration:{step},loss:{loss},theta:{self.theta}")
                break
        return self.theta

    def _cross_entropy(self,y,y_hat):
        '''交叉熵计算函数'''
        n_samples = y.shape[0]
        res = 0.
        for i in range(n_samples):
            res += (-y[i]*np.log(y_hat[i])-(1-y[i])*np.log(1-y_hat[i]))/n_samples
        return res

    def Loss_CrossEntropy(self,theta,x,y):
        '''损失函数计算'''
        y_hat = []
        for i in range(x.shape[0]):
            y_hat.append(self.logistic_model(theta,x[i]))
        return self._cross_entropy(y,y_hat)

    def _step_gradient(self):
        '''梯度下降'''
        M = float(len(self.y_train))
        for i in range(self.x_train.shape[0]):
            self.theta -= (1. / M)*self.lr*(self.logistic_model(self.theta,self.x_train[i])-self.y_train[i])*self.x_train[i]
        return self.theta

    def _predict(self,X, theta):
        '''预测函数'''
        y_hat = []
        for i in range(X.shape[0]):
            y_hat.append((self.logistic_model(theta, X[i]) > 0.5)*1)
        return y_hat

    def accuracy(self,theta, X, y):
        '''准确率评估函数'''
        y_hard = self._predict(X, theta)
        count_right = sum(y_hard == y)
        return count_right * 1.0 / len(y)

def get_data():
    dataset = load_breast_cancer()
    #data = pd.DataFrame(data=dataset.data,columns=dataset.feature_names)
    #data['cancer'] = [dataset.target_names[t] for t in dataset.target]
    X = dataset.data
    y = dataset.target
    #X_test = dataset.data
    #数据归一化
    std = X.std(axis=0)
    mean = X.mean(axis=0)
    X_norm = (X-mean)/std
    #为减少截距参数,全优化为theta,在后面加1
    ones = np.ones((X.shape[0],1))
    X_ones = np.hstack((ones,X_norm))
    #分割训练数据和测试数据
    X_train,X_test,Y_train,Y_test = train_test_split(X_ones,y,test_size=0.3,random_state=12345)
    return [X_train,Y_train,X_test,Y_test]

if __name__ == "__main__":
    X_train, Y_train, X_test, Y_test = get_data()
    LR = LogisticRegression()
    theta  = LR.fit(X_train, Y_train)
    print(LR.accuracy(theta,X_test,Y_test))
#[out]:
'''iteration:1480,loss:0.04912826826649911,theta:[ 0.66067831 -0.41699501 -0.68479606 -0.46465251 -0.41594285 -0.50296146
 -0.09195785 -0.94823898 -1.06558475 -0.20327686  0.69524534 -1.09357091
  0.14172414 -0.83429623 -0.61169349  0.50944495  0.85765425  0.21714991
 -0.37680249  0.22253374  0.86564795 -0.77599478 -0.93407023 -0.74392032
 -0.672051   -0.87079857 -0.01495728 -0.80462611 -0.97748235 -0.5603786
 -0.11103954]
0.9766081871345029'''
           

四、逻辑回归的正则化

​ 逻辑回归也会存在过拟合问题,所以我们也需要考虑正则化,正则化包括L1正则化和L2正则化

​ 二元逻辑回归的L1正则化损失函数表达式如下:

J ( θ ) = − Y T ∗ l o g h θ ( X ) − ( E − Y ) T ∗ l o g ( E − h θ ( X ) ) + α ∣ ∣ θ ∣ ∣ 1 J(\theta) = -Y^T*logh_\theta(X)-(E-Y)^T*log(E-h_\theta(X))+\alpha||\theta||_1 J(θ)=−YT∗loghθ​(X)−(E−Y)T∗log(E−hθ​(X))+α∣∣θ∣∣1​

​ 二元逻辑回归的L2正则化损失函数表达式如下:

J ( θ ) = − Y T ∗ l o g h θ ( X ) − ( E − Y ) T ∗ l o g ( E − h θ ( X ) ) + 1 2 α ∣ ∣ θ ∣ ∣ 2 2 J(\theta) = -Y^T*logh_\theta(X)-(E-Y)^T*log(E-h_\theta(X))+\frac{1}{2}\alpha||\theta||_2^2 J(θ)=−YT∗loghθ​(X)−(E−Y)T∗log(E−hθ​(X))+21​α∣∣θ∣∣22​

​ 逻辑回归的正则化和线性回归的正则化有相同的性质。我们可以使用sklearn带有的logistic回归进行正则化优化

from sklearn.linear_model import LogisticRegression as SkLr
'''LogisticRegression的参数介绍:
	penalty:惩罚项,可以选'l1','l2',对应L1,L2
	dual:选择目标函数为原始形式还是对偶形式。
	tol:优化算法停止的条件。当迭代前后的函数差值小于等于tol时就停止。
	C:正则化系数。其越小,正则化越强。
	fit_intercept:选择逻辑回归模型中是否会有常数项b。
	class_weight:用于标示分类模型中各种类型的权重
	random_state:随机数种子
	solver:逻辑回归损失函数的优化方法。
	max_iter:优化算法的迭代次数
'''
lr = SkLr(penalty='l1',C=1.0)
lr.fit(X_train, Y_train)
print(lr.score(X_test, Y_test))
           

五、逻辑回归的适用性

逻辑回归可用于以下几个方面:

(1)用于概率预测。用于可能性预测时,得到的结果有可比性。比如根据模型进而预测在不同的自变量情况下,发生某病或某种情况的概率有多大。

(2)用于分类。实际上跟预测有些类似,也是根据模型,判断某人属于某病或属于某种情况的概率有多大,也就是看一下这个人有多大的可能性是属于某病。进行分类时,仅需要设定一个阈值即可,可能性高于阈值是一类,低于阈值是另一类。

(3)寻找危险因素。寻找某一疾病的危险因素等。

(4)仅能用于线性问题。只有当目标和特征是线性关系时,才能用逻辑回归。在应用逻辑回归时注意两点:一是当知道模型是非线性时,不适用逻辑回归;二是当使用逻辑回归时,应注意选择和目标为线性关系的特征。

(5)各特征之间不需要满足条件独立假设,但各个特征的贡献独立计算。

继续阅读