逻辑回归

作者: 抹茶不加茶 | 来源:发表于2020-03-08 22:35 被阅读0次

    原理

    逻辑回归用于解决分类问题,但是你可能会疑惑,为何名字偏偏称为回归,嘿嘿嘿,这时因为我们在逻辑回归中将连续取值结果的函数,一分为二,连续值便变成了0、1这样的离散值,这样便成了分类,这样说实在过于抽象,下面来具体看一下

    • 我们先将样本的特征和样本的概率联系起来,而且我们定义: 通过概率进行分类
    • 我们首先计算出p这个概率值,这是一个连续值,如果我们就此打住,这便可以称为一个回归算法,如果我们按照上面的的定义,将不同概率分为两类,这样就成了分类算法
    • 我们来看看计算过程,这里注意一下sigma函数


      过程
    • 对于sigma函数,我们通常选择:


      sigma函数
    • 总结一下 1

      那么问题就变成了,对于给定的xy,寻找theta参数,使得这种方式下能获得最好的分类效果。

    • 这时候我们要考虑损失函数,这里不加推导给出:


      损失函数
      对于所有样本,我们得到损失函数

      其中,有:


      p_hat值
      所以我们得到损失函数完整表达式:
      损失函数完整表达式
    • 对于此损失函数,不像线性回归中那样,具有公式解,我们目前只能采用梯度下降法进行求解
    • 我们在这里特别看看其与线性回归的差别(在求梯度的时候)


      梯度比较

      我们注意到,其实最大的差别就是y_hat的值,是否使用了sigma函数而已
      我们进行矩阵形式化简


      矩阵形式

    简单实现

    对于逻辑回归,其重要性是不用多说的,吴恩达的课程里面占了较大的篇幅。
    所以这里的实现写的比较全面,希望读者自己也可以试一试,相信你将获益匪浅!

    import numpy as np
    from metrics import accuracy_score
    #这里采用类进行封装的形式
    class LogisticRegression:
    
        def __init__(self):
            """初始化Logistic Regression模型"""
            self.coef_ = None
            self.intercept_ = None
            self._theta = None
        def _sigmoid(self,t):
            return 1/(1+np.exp(-t))
    
    
    
        def fit(self, X_train, y_train, eta=0.01, n_iters=1e4):
            """根据训练数据集X_train, y_train, 使用梯度下降法训练Logistic Regression模型"""
            assert X_train.shape[0] == y_train.shape[0], \
                "the size of X_train must be equal to the size of y_train"
    
            def J(theta, X_b, y):
                y_hat=self._sigmoid(X_b.dot(theta))
                try:
                    return - np.sum(y*np.log(y_hat)+(1-y)*np.log(1-y_hat)) / 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(X_b, y, initial_theta, eta, n_iters=1e4, epsilon=1e-8):
    
                theta = initial_theta
                cur_iter = 0
    
                while cur_iter < n_iters:
                    gradient = dJ(theta, X_b, y)
                    last_theta = theta
                    theta = theta - eta * gradient
                    if (abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon):
                        break
    
                    cur_iter += 1
    
                return theta
            X_b = np.hstack([np.ones((len(X_train), 1)), X_train])
            initial_theta = np.zeros(X_b.shape[1])
            self._theta = gradient_descent(X_b, y_train, initial_theta, eta, n_iters)
    
            self.intercept_ = self._theta[0]
            self.coef_ = self._theta[1:]
    
            return self
           
        def predict_proba(self, X_predict):
            """给定待预测数据集X_predict_proba,返回表示X_predict的结果概率"""
            assert self.intercept_ is not None and self.coef_ is not None, \
                "must fit before predict!"
            assert X_predict.shape[1] == len(self.coef_), \
                "the feature number of X_predict must be equal to X_train"
    
            X_b = np.hstack([np.ones((len(X_predict), 1)), X_predict])
            return self._sigmoid(X_b.dot(self._theta))
     
    
    
        def predict(self, X_predict):
            """给定待预测数据集X_predict,返回表示X_predict的结果向量"""
            assert self.intercept_ is not None and self.coef_ is not None, \
                "must fit before predict!"
            assert X_predict.shape[1] == len(self.coef_), \
                "the feature number of X_predict must be equal to X_train"
    
            proba=self.predict_proba(X_predict)
            return np.array(proba>=0.5,dtype='int')
    
        def score(self, X_test, y_test):
            """根据测试数据集 X_test 和 y_test 确定当前模型的准确度"""
    
            y_predict = self.predict(X_test)
            return accuracy_score(y_test, y_predict)
    
        def __repr__(self):
            return "LinearRegression()"
    
    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn import datasets
    iris=datasets.load_iris()
    x=iris.data
    y=iris.target
    x=x[y<2,:2]#只取y=0,1的两个分类,为了方便可视化而只取两个特征
    y=y[y<2]
    

    我们先看看这堆数据的散点图


    原始数据散点图
    ###使用逻辑回归
    from sklearn.model_selection import train_test_split
    x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.2,random_state=6)
    #实际上这里size默认是0.2
    log_reg=LogisticRegression()
    log_reg.fit(x_train,y_train)
    
    
    结果
    决策边界
    决策边界
    其实在二维情况下的函数,就是初中高中的二元一次函数,使用这一直线对数据进行分割。
    上面示例的划分如图
    划分
    对于非直线划分的情况呢?——多项式特性
    圆形决策边界
    类似于我们在多项式回归中使用的技巧,将x12、x22视为一个特征进行计算即可,由此我们还可以得到其他圆形,曲线一类的......
    这里就不进行额外的实现了

    用sklearn实现逻辑回归&&正则化

    不同于前面多项式回归中的模型正则化,我们有一点点的区别:


    正则化区别

    常数参数由alpha变成了C,实际上我们可以视为1/alpha,其效果类似,但是使用c进行正则化,主要是考虑到逻辑回归时,L1,L2一般是要为1,所以便于计算,我们取了C放在J前面,其原理类似之前。

    #sklearn 中的逻辑回归
    import numpy as np
    import matplotlib.pyplot as plt
    X=np.random.normal(0,1,size=(200,2))
    y=np.array(X[:,0]**2+X[:,1]<1.5,dtype='int')#抛物线
    
    for k in range(20):
        y[np.random.randint(200)]=1#加20个噪音
    plt.scatter(X[y==0,0],X[y==0,1])
    plt.scatter(X[y==1,0],X[y==1,1])
    

    我们先看看散点图分布


    散点图
    from sklearn.model_selection import train_test_split
    x_train,x_test,y_train,y_test=train_test_split(X,y,test_size=0.2,random_state=6)
    #实际上这里size默认是0.2
    from sklearn.linear_model import LogisticRegression
    log_reg=LogisticRegression()
    log_reg.fit(x_train,y_train)
    

    这里我们先留意一下逻辑回归的参数,因为后面调参可能会用到


    参数

    我们先看看使用线性的逻辑回归进行分类的结果,可以预见的,效果并不好,因为我们的数据实际是需要二次函数进行分类的


    结果1
    from sklearn.pipeline import Pipeline
    from sklearn.preprocessing import StandardScaler
    from sklearn.preprocessing import PolynomialFeatures
    def PolyLogisticRegression(degree):
        return Pipeline([
            ("poly",PolynomialFeatures(degree=degree)),
            ("std_scacler",StandardScaler()),
            ("lin_reg",LogisticRegression())
        ])
    poly_log_reg=PolyLogisticRegression(degree=2)
    poly_log_reg.fit(x_train,y_train)
    poly_log_reg.score(x_train,y_train)
    

    这里我们使用二阶进行分类,查看结果


    结果2

    我们可以看到,结果显然好了不少
    我们可以接着改变degree进行测试,这里就不做展开了

    OVR&&OVO

    我们前面进行的都是二分类,但是二分类显然无法满足实际,作为用处最为广泛的分类算法,逻辑回归自然也具有多分类功能。

    • OVR一对剩余所有


      OVR
    • OVO一对一
      OVO
      一般来说一对一要准确度更高,但是复杂度也更高,遵循排列组合中的Cn2计算出需要进行分类的次数,这显然比一对剩余的n次要复杂的多。
      下面使用鸢尾花数据集(3分类)进行三分类
      sklearn中非常人性化地给我们自动添加了多分类的功能,查看参数得知,默认是OVR ,即我们不用担心我们现在是几分类问题
    #三分类
    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn import datasets
    iris=datasets.load_iris()
    x=iris.data
    y=iris.target
    x=x[:,:2]#为了方便可视化而只取两个特征
    from sklearn.model_selection import train_test_split
    x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.2,random_state=6)
    from sklearn.linear_model import LogisticRegression
    #sklearn中自动封装了多分类
    log_reg=LogisticRegression()
    log_reg.fit(x_train,y_train)#默认支持且使用ovr方式
    log_reg.score(x_train,y_train)
    

    (这里可视化的代码有点复杂,懒得去理解了,故不作可视化了)


    结果1

    注意这里的将ovr改成ovo的方式,一是需要改multi_class,二是由于解法不能使用ovr的默认解法,故也许更改,这里查阅手册,可以改成newton-cg

    log_reg2=LogisticRegression(multi_class="multinomial",solver="newton-cg")#ovo方式
    log_reg2.fit(x_train,y_train)
    log_reg2.score(x_train,y_train)
    

    这是不仅仅只用两个特征的例子,使用的完整数据

    #使用所有数据
    #三分类
    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn import datasets
    iris=datasets.load_iris()
    x=iris.data
    y=iris.target
    from sklearn.model_selection import train_test_split
    x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.2,random_state=6)
    from sklearn.linear_model import LogisticRegression
    #sklearn中自动封装了多分类
    log_reg=LogisticRegression()
    log_reg.fit(x_train,y_train)#默认支持且使用ovr方式
    log_reg.score(x_train,y_train)
    log_reg2=LogisticRegression(multi_class="multinomial",solver="newton-cg")#ovo方式
    log_reg2.fit(x_train,y_train)
    log_reg2.score(x_train,y_train)
    
    结果2

    sklearn中其实也封装了ovr、ovo两个类,我们调用试试

    #sklearn中特地封装了ovr、ovo两个类
    from sklearn.multiclass import OneVsRestClassifier
    ovr=OneVsRestClassifier(log_reg)#传进去一个二分类器即可
    ovr.fit(x_train,y_train)#自动使用ovr方式
    ovr.score(x_test,y_test)
    
    from sklearn.multiclass import OneVsOneClassifier
    ovo=OneVsOneClassifier(log_reg)#传进去一个二分类器即可
    ovo.fit(x_train,y_train)#自动使用ovr方式
    ovo.score(x_test,y_test)
    

    结果和前面应该是一样的

    总结

    逻辑回归的全部内容到这里就结束了,这是用的最多的一种机器学习算法,里面含括了不少我们前面学到的重要知识,能够掌握好前面整个部分的代码原理是很重要的,可算是码完字了,溜了溜了!

    相关文章

      网友评论

        本文标题:逻辑回归

        本文链接:https://www.haomeiwen.com/subject/smxsdhtx.html