07.md 14.7 KB
Newer Older
W
wizardforcel 已提交
1
# 七、估计事件的可能性
W
wizardforcel 已提交
2

W
wizardforcel 已提交
3
逻辑回归是一种回归分析,可帮助您根据某些给定参数估计事件发生的可能性。 它用作具有二进制结果的分类技术。 使用逻辑函数,根据解释性变量(预测变量)对描述单个试验可能结果的概率进行建模。
W
wizardforcel 已提交
4

W
wizardforcel 已提交
5
在第 5 章“发现机器学习”中,已经向您介绍了 *Logisitc 回归*。 在本章中,您将学习:
W
wizardforcel 已提交
6 7 8 9 10

*   使用 statsmodels 建立逻辑回归模型
*   使用 SciKit 建立逻辑回归模型
*   评估和测试模型

W
wizardforcel 已提交
11
# 逻辑回归
W
wizardforcel 已提交
12

W
wizardforcel 已提交
13
我们将使用泰坦尼克数据集,该数据集在第 3 章“在干草堆中查找针”中使用,以帮助我们建立逻辑回归模型。 由于我们已经探索了数据,因此我们将不进行任何探索性数据分析,因为我们已经有了该数据的上下文。
W
wizardforcel 已提交
14 15 16

这是泰坦尼克号数据集的字段描述的回顾:

W
wizardforcel 已提交
17 18
*   `Survival`:这是指乘客的生存(`0`:否,`1`:是)
*   `Pclass`:这指的是乘客等级(`1`:一等,`2`:二等,`3`:三等)
W
wizardforcel 已提交
19 20 21
*   `Name`:这是指乘客的姓名
*   `Sex`:这是指乘客的性别
*   `Age`:这是指乘客的年龄
W
wizardforcel 已提交
22
*   `Sibsp`:指船上兄弟姐妹/配偶的数量
W
wizardforcel 已提交
23 24 25 26
*   `Parch`:这是指船上父母/子女的数量
*   `Ticket`:这是票证编号
*   `Fare`:这是指旅客票价
*   `Cabin`:这是指客舱
W
wizardforcel 已提交
27
*   `Embarked`:此是指登船港口(`C`:瑟堡,`Q`:皇后镇,`S`:南安普敦)
W
wizardforcel 已提交
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

## 数据准备

让我们从读取数据开始:

```py
>>> df = pd.read_csv('Data/titanic data.csv')

```

让我们通过照顾那些缺少很多值的列来稍微清理一下数据:

```py
>>> df.count(0)

```

![Data preparation](img/B03450_07_01.jpg)

我们可以看到`Ticket``Cabin`列不会为模型构建过程增加太多价值,因为`Ticket`列基本上是每位乘客的唯一标识符,而`Cabin`列基本上是空的。 另外,我们将删除缺少值的行。

W
wizardforcel 已提交
49
我们将从`DataFrame`中删除以下两列:
W
wizardforcel 已提交
50 51 52 53 54 55 56 57 58 59 60 61 62

```py
>>> # Applying axis as 1 to remove the  columns with the following labels
>>> df = df.drop(['Ticket','Cabin','Name'], axis=1)
>>> # Remove missing values
>>> df = df.dropna()

```

## 创建训练和测试集

在前面的代码中,我们删除了`Ticket``Cabin``Name`列,然后删除了缺少的值。

W
wizardforcel 已提交
63
我们将使用名为 Patsy 的 Python 包,该包有助于描述统计模型。 它有助于定义与 R 相似的因变量和自变量公式。在`~`左侧定义的变量是因变量,在其右侧定义的变量是自变量。 `C()`中包含的变量被视为分类变量。
W
wizardforcel 已提交
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

现在,我们将根据数据创建训练和测试集:

```py
>>> formula = 'Survived ~ C(Pclass) + C(Sex) + Age + SibSp  + C(Embarked) +  Parch' 

>>> # create a results dictionary to hold our regression results for easy >>> # analysis later
>>> df_train = df.iloc[ 0: 600, : ]
>>> df_test = df.iloc[ 600: , : ]

>>> #Splitting the data into dependent and independent variables
>>> y_train,x_train = dmatrices(formula, data=df_train, return_type='dataframe')
>>> y_test,x_test = dmatrices(formula, data=df_test, return_type='dataframe')

```

W
wizardforcel 已提交
80
在前面的代码中,我们在公式变量中定义方程,其中`survived`是因变量,其右边的是自变量。 之后,我们将前`600`行作为训练集,并将`df``DataFrame`中的其余行作为测试集。
W
wizardforcel 已提交
81

W
wizardforcel 已提交
82
最后,我们使用 Patsy 包的`dmatrices`,该包接受公式并输入一个`DataFrame`来创建一个`DataFrame`。 准备将其输入到 statsmodels 和 SciKit 的建模功能中。
W
wizardforcel 已提交
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

## 建立模型

我们将使用 statsmodels 包来构建模型:

```py
>>> # instantiate our model
>>> model = sm.Logit(y_train,x_train)
>>> res = model.fit()
>>> res.summary()

```

这是前面代码的输出:

![Building a model](img/B03450_07_02.jpg)

W
wizardforcel 已提交
100
我们可以看到,最大似然估计已用于预测系数。 伪 R 平方类似于线性回归的 R 平方,用于测量它的优度。 如果我们得到`0.33`的值,则认为`0.2``0.4`之间的伪 R 平方值是好的。
W
wizardforcel 已提交
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139

从前面的表中,我们可以看到乘船的港口和父母/子女的数量是重要的预测因子,因为它们的 p 值高于`0.05`

我们将通过使用预测变量来重建模型,例如类别,年龄,性别和同胞数目:

```py
>>> formula = 'Survived ~ C(Pclass) + C(Sex) + Age + SibSp ' 

>>> y_train,x_train = dmatrices(formula, data=df_train, return_type='dataframe')
>>> y_test,x_test = dmatrices(formula, data=df_test, return_type='dataframe')

>>> # instantiate our model
>>> model = sm.Logit(y_train,x_train)
>>> res = model.fit()
>>> res.summary()

```

![Building a model](img/B03450_07_03.jpg)

我们可以看到,所有预测变量在前面的模型中都很重要。

## 模型评估

现在,让我们用以下代码查看训练数据上预测的分布:

```py
>>> kde_res = KDEUnivariate(res.predict())
>>> kde_res.fit()
>>> plt.plot(kde_res.support,kde_res.density)
>>> plt.fill_between(kde_res.support,kde_res.density, alpha=0.2)
>>> plt.title("Distribution of our Predictions")

```

在前面的代码中,我们使用核密度估计来找到预测值的概率密度。 这有助于我们了解预测概率的哪些区域更密集。

![Model evaluation](img/B03450_07_04.jpg)

W
wizardforcel 已提交
140
从前面的图中可以看出,在`0``1`的概率附近密度较高,这是一个好兆头,表明该模型能够根据给定的数据预测某些模式。 这也表明密度是最高的,接近 0,这意味着很多人无法生存。 这证明了我们在第 3 章,“在干草堆中寻找针”中执行的分析。
W
wizardforcel 已提交
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156

让我们看看基于男性的预测分布:

```py
>>> plt.scatter(res.predict(),x_train['C(Sex)[T.male]'] , alpha=0.2)
>>> plt.grid(b=True, which='major', axis='x')
>>> plt.xlabel("Predicted chance of survival")
>>> plt.ylabel("Male Gender")
>>> plt.title("The Change of Survival Probability by Gender being Male")

```

在前面的代码中,我们在预测的生存概率和标志之间创建了一个散点图,该标志指示乘客是男性。

![Model evaluation](img/B03450_07_05.jpg)

W
wizardforcel 已提交
157
我们可以看到,模型预测表明,如果乘客是男性,那么与女性相比,生存机会要低。 我们的分析在第 3 章,“在干草堆中寻找针”中也显示了这一点,其中发现女性的成活率更高。
W
wizardforcel 已提交
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207

现在,让我们看一下基于下层乘客的预测分布:

```py
>>> plt.scatter(res.predict(),x_train['C(Pclass)[T.3]'] , alpha=0.2)
>>> plt.xlabel("Predicted chance of survival")
>>> plt.ylabel("Class Bool") # Boolean class to show if its 3rd class
>>> plt.grid(b=True, which='major', axis='x')
>>> plt.title("The Change of Survival Probability by Lower 
 Class which is 3rd class")

```

![Model evaluation](img/B03450_07_06.jpg)

我们可以看到,与其他类别的乘客相比,较低类别的乘客的生存概率较低,因为的概率更多地集中于 0。

让我们来看看关于乘客年龄的概率分布:

```py
>>> plt.scatter(res.predict(),x_train.Age , alpha=0.2)
>>> plt.grid(True, linewidth=0.15)
>>> plt.title("The Change of Survival Probability by Age")
>>> plt.xlabel("Predicted chance of survival")
>>> plt.ylabel("Age")

```

![Model evaluation](img/B03450_07_07.jpg)

如果您观察前面的图,可以看到随着乘客年龄的增加,概率朝图表的左侧倾斜,这表明老年人的存活率较低。

让我们看一下关于同胞/配偶数的概率分布:

```py
>>> plt.scatter(res.predict(),x_train.SibSp , alpha=0.2)
>>> plt.grid(True, linewidth=0.15)
>>> plt.title("The Change of Survival Probability by Number of 
 siblings/spouses")
>>> plt.xlabel("Predicted chance of survival")
>>> ylabel("No. of Siblings/Spouses")

```

![Model evaluation](img/B03450_07_08.jpg)

从前面的图表中,我们可以看到的唯一模式是,有四到五个兄弟姐妹/配偶的乘客的存活率较低。 对于其余的乘客,或多或少是随机分布的。

## 根据测试数据评估模型

W
wizardforcel 已提交
208
让我们使用测试数据上的模型通过进行预测,并通过保持`0.7`的阈值来通过精度和召回率显示模型的表现:
W
wizardforcel 已提交
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223

```py
>>> y_pred = res.predict(x_test)
>>> y_pred_flag = y_pred > 0.7
>>> print pd.crosstab(y_test.Survived
 ,y_pred_flag
 ,rownames = ['Actual']
 ,colnames = ['Predicted'])

>>> print '\n \n'

>>> print classification_report(y_test,y_pred_flag)

```

W
wizardforcel 已提交
224
在前面的代码中,我们在测试数据上获得预测的概率,然后根据`0.7`的阈值为事件分配`True``False`。 我们使用 Pandas 的交叉表功能,该功能有助于显示两个变量之间的频率分布。 我们将使用它来获取实际值和预测值之间的交叉表,然后我们将使用 SciKit 的`classification_report`函数来获取精度和调用值:
W
wizardforcel 已提交
225 226 227 228 229 230 231 232 233 234 235

![Evaluating a model based on test data](img/B03450_07_09.jpg)

下图显示了测试数据的精度和召回率:

![Evaluating a model based on test data](img/B03450_07_10.jpg)

我们可以看到所有非幸存者均已正确预测,但该模型能够基于 0.7 阈值正确预测仅一半幸存者。 请注意,精度和召回率值将随所使用的阈值而变化。

让我们了解精确度和召回率的含义。

W
wizardforcel 已提交
236
*   **精度**:精度告诉您,在 0 类或 1 类的所有预测中,有多少个已正确预测。 因此,在上述情况下,非幸存者的预测的 76% 是正确的,而幸存者的预测的 100% 是正确的。
W
wizardforcel 已提交
237
*   **召回**:召回告诉您,在实际实例中,有多少个已正确预测。 因此,在上述情况下,所有未幸存的人都可以 100% 的准确率正确预测,但是在所有幸存的人中,只有 53% 的人被正确预测了。
W
wizardforcel 已提交
238

W
wizardforcel 已提交
239
让我们绘制**受试者工作特性****ROC**)曲线,其解释如下:
W
wizardforcel 已提交
240 241 242 243 244 245 246 247 248 249 250 251 252

```py
>>> # Compute ROC curve and area the curve
>>> fpr, tpr, thresholds = roc_curve(y_test, y_pred)
>>> roc_auc = auc(fpr, tpr)
>>> print "Area under the ROC curve : %f" % roc_auc

Area under the ROC curve : 0.879934

```

曲线下面的面积是`0.87`,这是一个很好的值。 在前面的代码中,我们使用`roc_curve`函数分别获得错误率和正确率,其定义如下:

W
wizardforcel 已提交
253
假阳性率是`FP / (FP + TN)`,也称为辐射;真阳性率是`TPR = TP / P = TP / (TP + FN)`,也称为灵敏度。
W
wizardforcel 已提交
254 255 256

以下是我们的一些观察结果:

W
wizardforcel 已提交
257 258 259 260
*   `False Positive``FP`):这是一个肯定的预测,实际上是错误的。 因此,在前面的交叉表中,`0`为假正例
*   `True Positive``TP`):这是一个肯定的预测,实际上是正确的。 因此,在前面的交叉表中,`24`为真正例
*   `True Negative``TN`):这是一个负面预测,实际上是正确的。 因此,在上述交叉表中,`67`为真负例
*   `False Negative``FN`):这是一个否定预测,实际上是错误的。 因此,在前面的交叉表中,`21`为假负例
W
wizardforcel 已提交
261 262 263

因此,误报率告诉我们,在所有未幸存的人中,有百分之几被预测为幸存。 真实阳性率告诉我们,在所有幸存的人中,有百分之多少被预测为幸存了。 理想情况下,假阳性率应低,真阳性率应高。

W
wizardforcel 已提交
264
通过将 TPR 和 FPR 设置为不同的阈值,然后将它们相互绘制,可以创建`roc_curve`函数。
W
wizardforcel 已提交
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284

`roc_curve`函数在不同的阈值下给出假阳性率和真阳性率,这将用于绘制 ROC 曲线:

```py
>>> # Plot ROC curve
>>> plt.clf()
>>> plt.plot(fpr, tpr, label='ROC curve (area = %0.2f)' % roc_auc)
>>> plt.plot([0, 1], [0, 1], 'k--')
>>> plt.xlim([0.0, 1.0])
>>> plt.ylim([0.0, 1.0])
>>> plt.xlabel('False Positive Rate')
>>> plt.ylabel('True Positive Rate')
>>> plt.title('Receiver operating characteristic example')
>>> plt.legend(loc="lower right")
>>> plt.show()

```

![Evaluating a model based on test data](img/B03450_07_13.jpg)

W
wizardforcel 已提交
285
精度由 ROC 曲线下的面积衡量。 面积 1 代表完美测试; `0.5`区域表示该模型与随机猜测一样好。 对诊断测试的准确率进行分类的大致指南是传统的学术评分系统,如下所示:
W
wizardforcel 已提交
286

W
wizardforcel 已提交
287
| 范围 | 类别 |
W
wizardforcel 已提交
288
| --- | --- |
W
wizardforcel 已提交
289 290 291 292 293
| 0.90-1 | 优秀(A) |
| 0.80-0.90 | 良好(B) |
| 0.70-0.80 | 一般(C) |
| 0.60-0.70 | 差(D) |
| 0.50-0.60 | 失败(F) |
W
wizardforcel 已提交
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317

上图中的虚线的 AUC 为`0.50`,不好。 我们的模型为我们提供了`0.88`的 AUC,这确实很好,并且是图中的蓝线。

## 使用 SciKit 建立和评估模型

让我们使用 SciKit 构建先前显示的相同模型:

```py
>>> # instantiate a logistic regression model, and fit with X and y
>>> model = LogisticRegression()
>>> model = model.fit(x_train, y_train.Survived)

```

在前面的代码中,我们创建`LogisticRegression`方法的对象,然后使用我们的训练数据拟合模型:

```py
>>> # examine the coefficients
>>> pd.DataFrame(zip(x_train.columns, np.transpose(model.coef_)))

```

![Model building and evaluation with SciKit](img/B03450_07_14.jpg)

W
wizardforcel 已提交
318
第一列包含我们的因变量名称,第二列包含系数值。 我们可以看到,我们的预测变量的系数与使用 statsmodels 包构建的模型相似但不相同。
W
wizardforcel 已提交
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342

让我们看看我们的精确度和召回率是如何表现的:

```py
>>> y_pred = model.predict_proba(x_test)
>>> y_pred_flag = y_pred[:,1] > 0.7

>>> print pd.crosstab(y_test.Survived
 ,y_pred_flag
 ,rownames = ['Actual']
 ,colnames = ['Predicted'])

>>> print '\n \n'

>>> print classification_report(y_test,y_pred_flag)

```

![Model building and evaluation with SciKit](img/B03450_07_15.jpg)

以下显示了测试数据的精度和召回率:

![Model building and evaluation with SciKit](img/B03450_07_16.jpg)

W
wizardforcel 已提交
343
我们可以看到与我们创建的先前模型相比,表现略有不同。 有两种肯定的预测已转变为否定的预测。
W
wizardforcel 已提交
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376

让我们计算曲线下的 ROC 和面积:

```py
>>> # Compute ROC curve and area the curve
>>> fpr, tpr, thresholds = roc_curve(y_test, y_pred[:,1])
>>> roc_auc = auc(fpr, tpr)
>>> print "Area under the ROC curve : %f" % roc_auc
Area under the ROC curve :0.878275

```

它几乎相同,但略小于先前模型的 AUC。

让我们绘制 ROC 曲线,该曲线几乎与以前的模型相同:

```py
>>> # Plot ROC curve
>>> plt.clf()
>>> plt.plot(fpr, tpr, label='ROC curve (area = %0.2f)' % roc_auc)
>>> plt.plot([0, 1], [0, 1], 'k--')
>>> plt.xlim([0.0, 1.0])
>>> plt.ylim([0.0, 1.0])
>>> plt.xlabel('False Positive Rate')
>>> plt.ylabel('True Positive Rate')
>>> plt.title('Receiver operating characteristic example')
>>> plt.legend(loc="lower right")
>>> plt.show()

```

![Model building and evaluation with SciKit](img/B03450_07_17.jpg)

W
wizardforcel 已提交
377
# 总结
W
wizardforcel 已提交
378 379 380

在本章中,您学习了逻辑回归的目的。 您学习了如何使用 statsmodels 和 SciKit 构建逻辑回归模型,然后如何评估模型并查看其是否为好模型。

W
wizardforcel 已提交
381
在下一章中,您将学习如何生成建议,例如您在[这个页面](http://www.amazon.com/)上看到的建议,根据您的购买历史将向您推荐新商品。 也可以根据您当前正在浏览的产品向您显示类似的项目。