1. 写在前面: 

本篇属于实战部分,更注重于算法在实际项目中的应用。如需对逻辑回归算法本身有详细的了解,可参考以下链接,在本人学习的过程中,起到了很大的帮助:

统计学习方法 李航

逻辑回归原理小结 

 

2. 数据集:

 

数据集地址:https://www.kaggle.com/c/titanic

 

Titanic数据集是Kaggle上参与人数最多的项目之一。数据本身简单小巧,适合初学者上手,深入了解比较各个机器学习算法。

 

数据集包含11个变量:PassengerID、Pclass、Name、Sex、Age、SibSp、Parch、Ticket、Fare、Cabin、Embarked,通过这些数据来预测乘客在Titanic事故中是否幸存下来。

 

3. 算法简介:

 

这一节中简单介绍一下逻辑回归,虽然名字中有回归,但其实逻辑回归是经典的分类算法,属于对数线性模型。

3.1 逻辑回归模型:

3.1.1 二元逻辑回归模型:

假设$x\in R^{n}$,$y \in \{0,1\}$, $w$为带偏置的权值向量,$h_{w}(x)$为模型输出,则:

回归数据集 适合做回归的数据集_回归数据集

$h_{w}(x)$属于逻辑斯蒂分布函数,是一条S形曲线,以点$(0, \frac{1}{2})$为中心对称。当模型输出$h_{w}(x) > 0.5$时,分类结果为1;$h_{w}(x) < 0.5$时,分类结果为0。

 

则其条件概率为:

回归数据集 适合做回归的数据集_数据集_02

回归数据集 适合做回归的数据集_感知机_03

 

引入几率概念(几率是指该事件发生的概率与该事件不发生的概率的比值,对数几率用logit表示):

回归数据集 适合做回归的数据集_回归数据集_04

 

3.1.2 多元逻辑回归模型:

假设$x\in R^{n}$,$y \in \{1,2,...,K\}$, $w_{k}$为带偏置的权值向量,$h_{w_{k}}(x)$为模型输出,则:

回归数据集 适合做回归的数据集_损失函数_05

回归数据集 适合做回归的数据集_回归数据集_06

 

并且,其对数条件概率满足:

回归数据集 适合做回归的数据集_回归数据集_07

...

回归数据集 适合做回归的数据集_回归数据集_08

 

注:和感知机不同,感知机模型是一个由输入空间到输出空间的函数;逻辑回归的模型是条件概率分布,所以我们使用逻辑回归进行分类时,不仅可以得到预测的分类结果,也可以得到每个分类的概率。对应在sklearn的应用中,即不仅有predict()方法,同时也有predict_proba()方法。

 

3.2 逻辑回归损失函数:

逻辑回归的损失函数通过极大似然法获得,令回归数据集 适合做回归的数据集_损失函数_09

则关于参数w的似然函数为:

回归数据集 适合做回归的数据集_回归数据集_10

对数似然函数为:

回归数据集 适合做回归的数据集_损失函数_11

 逻辑回归的损失函数定义为负的对数似然函数:

回归数据集 适合做回归的数据集_数据集_12

 

3.3 逻辑回归学习过程:

逻辑回归的学习过程即求解感知机损失函数的最小化问题,通常可以采用梯度下降法即拟牛顿法求解,下面介绍一下梯度下降法的求解过程:

首先,将损失函数写成矩阵形式:

回归数据集 适合做回归的数据集_感知机_13

接着,对其求导可得: 

回归数据集 适合做回归的数据集_损失函数_14

最后,根据梯度下降公式,在每次迭代过程中,更新$w$:

回归数据集 适合做回归的数据集_回归数据集_15

 

4. 实战:

1. Sklearn中主要提供了LogsiticRegression和LogisticRegressionCV两个类来应用逻辑回归,其中LogisticRegressionCV,从名字就可以看出,其自带Cross validation。据Sklearn官方文档所说“The advantage of using a cross-validation estimator over the canonical estimator class along with grid search is that they can take advantage of warm-starting by reusing precomputed results in the previous steps of the cross-validation process. ” 即这类estiamtor可以利用之前几步的cross-validation的结果来实现热启动,而这通常会改善运算速度。但据个人使用下来发现,其自带的grid seacrh只能设定,正则项系数Cs这一个超参数。对于其他的超参数,如正则化方式penalty, 类别权重class_weight等并不能选择,因此使用过程中还是使用了 LogisticRegression 配合 GridSearchCV 进行参数的选择。

2. 在 param_grid 的设置过程中,我们将 penalty = 'l1' 和 'l2' 区分开了,这是因为两种正则化的系数'C'的最佳区间可能不在一个数量级的范围内,分开后在调参的过程中可以单独给出合适的范围。比如在本例中,penalty='l1', C<=0.01会有明显的欠拟合;而penalty='l2',C<0.0001才会有明显的欠拟合。以上可以用过grid_search.cv_results_对网格搜索的结果进行查看。

3. GridSearchCV的 cv_results_这个attribute返回的结果乍一看比较杂乱,其实是可以通过pd.DataFrame()将其转换成dataframe格式,看起来非常清晰,甚至可以对其按照感兴趣的列,如‘mean_test_score’等进行排序,非常方便。

 

下面附上简单的代码及其运行结果:

1 import pandas as pd
 2 import numpy as np
 3 import matplotlib.pyplot as plt
 4 from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder, StandardScaler
 5 from sklearn.impute import SimpleImputer
 6 from sklearn.pipeline import Pipeline, FeatureUnion
 7 from sklearn.model_selection import cross_val_score, GridSearchCV, ParameterGrid, StratifiedKFold, ShuffleSplit
 8 from sklearn.linear_model import LogisticRegression, LogisticRegressionCV
 9 from sklearn.metrics import accuracy_score, precision_score, recall_score
10 from sklearn.base import TransformerMixin, BaseEstimator
11 
12 
13 class DataFrameSelector(BaseEstimator, TransformerMixin):
14     def __init__(self, attribute_name):
15         self.attribute_name = attribute_name
16 
17     def fit(self, x, y=None):
18         return self
19 
20     def transform(self, x):
21         return x[self.attribute_name].values
22 
23 
24 # Load data
25 data_train = pd.read_csv('train.csv')
26 
27 train_x = data_train.drop('Survived', axis=1)
28 train_y = data_train['Survived']
29 
30 # Data cleaning
31 cat_attribs = ['Pclass', 'Sex', 'Embarked']
32 dis_attribs = ['SibSp', 'Parch']
33 con_attribs = ['Age', 'Fare']
34 
35 # encoder: OneHotEncoder()、OrdinalEncoder()
36 cat_pipeline = Pipeline([
37     ('selector', DataFrameSelector(cat_attribs)),
38     ('imputer', SimpleImputer(strategy='most_frequent')),
39     ('encoder', OneHotEncoder()),
40 ])
41 
42 dis_pipeline = Pipeline([
43     ('selector', DataFrameSelector(dis_attribs)),
44     ('scaler', StandardScaler()),
45     ('imputer', SimpleImputer(strategy='most_frequent')),
46 ])
47 
48 con_pipeline = Pipeline([
49     ('selector', DataFrameSelector(con_attribs)),
50     ('scaler', StandardScaler()),
51     ('imputer', SimpleImputer(strategy='mean')),
52 ])
53 
54 full_pipeline = FeatureUnion(
55     transformer_list=[
56         ('con_pipeline', con_pipeline),
57         ('dis_pipeline', dis_pipeline),
58         ('cat_pipeline', cat_pipeline),
59     ]
60 )
61 
62 train_x_cleaned = full_pipeline.fit_transform(train_x)
63 
64 cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=2)
65 clf1 = LogisticRegression(tol=1e-4, max_iter=1000, solver='liblinear')
66 
67 param_grid = [{'penalty': ['l1'],
68                'C': [1e-4, 1e-3, 1e-2, 1e-1, 1, 10],
69                'class_weight': ['balanced', None],
70                },
71               {'penalty': ['l2'],
72                'C': [1e-4, 1e-3, 1e-2, 1e-1, 1, 10],
73                'class_weight': ['balanced', None],
74                },
75               {'penalty': ['none'],
76                'class_weight': ['balanced', None],
77                'solver': ['lbfgs']
78                }
79               ]
80 
81 grid_search = GridSearchCV(clf1, param_grid=param_grid, cv=cv, scoring='accuracy', n_jobs=-1, return_train_score=True)
82 
83 grid_search.fit(train_x_cleaned, train_y)
84 predicted_y = grid_search.predict(train_x_cleaned)
85 
86 df_cv_results = pd.DataFrame(grid_search.cv_results_)
87 print(accuracy_score(train_y, predicted_y))
88 print(precision_score(train_y, predicted_y))
89 print(recall_score(train_y, predicted_y))

 

运行结果:

1 0.8069584736251403 2 0.7814569536423841 3

可以看到在相同的数据清洗步骤下,作为对数线性模型的逻辑回归和作为线性模型的感知机,在分类精度上相差无几。

 

和前几篇一样,我们将最优参数 " C = 0.1, class_weight = None, penalty = 'l2' ",用于预测集,并将结果上传kaggle,结果如下:

 

 

训练集

accuracy

训练集

precision

训练集

recall

预测集

accuracy(需上传kaggle获取结果)

朴素贝叶斯最优解

0.790

0.731

0.716

0.756

感知机

0.771

0.694

0.722

0.722

逻辑回归

0.807

0.781

0.690

0.768