程序员机器学习与数据挖掘

逻辑回归

2020-03-08  本文已影响0人  抹茶不加茶

原理

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

简单实现

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

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

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

#三分类
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)

结果和前面应该是一样的

总结

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

上一篇下一篇

猜你喜欢

热点阅读