├── README.md ├── 实践 ├── xgboost调包 │ ├── agaricus.txt.test │ ├── agaricus.txt.train │ ├── dtest.buffer │ ├── xgb.model │ ├── xgboost调包之 early stopping.ipynb │ ├── xgboost调包之 sklearn接口.ipynb │ ├── xgboost调包之 基础篇.ipynb │ ├── xgboost调包之 模型篇.ipynb │ ├── xgboost调包之 特征重要性可视化.ipynb │ └── xgboost调包之 结合pandas.ipynb └── 聚类 │ └── KMeans.py ├── 第一章 ├── .ipynb_checkpoints │ └── 第一章 让计算机从数据中学习-checkpoint.ipynb └── 第一章 让计算机从数据中学习.ipynb ├── 第七章 ├── .ipynb_checkpoints │ └── 集成学习-checkpoint.ipynb └── 集成学习.ipynb ├── 第三章 ├── .ipynb_checkpoints │ └── 使用Scikit-learn进行分类器之旅-checkpoint.ipynb ├── ch3.md ├── ch3_section1.md ├── ch3_section10.md ├── ch3_section11.md ├── ch3_section12.md ├── ch3_section13.md ├── ch3_section2.md ├── ch3_section3.md ├── ch3_section4.md ├── ch3_section5.md ├── ch3_section6.md ├── ch3_section7.md ├── ch3_section8.md ├── ch3_section9.md ├── tree.dot └── 使用Scikit-learn进行分类器之旅.ipynb ├── 第二章 ├── .ipynb_checkpoints │ └── 机器学习分类算法-checkpoint.ipynb ├── ch2.md ├── ch2_section1.md ├── ch2_section2.md ├── ch2_section3.md ├── ch2_section4.md ├── ch2_section5.md ├── ch2_section6.md ├── ch2_section7.md └── 机器学习分类算法.ipynb ├── 第五章 ├── .ipynb_checkpoints │ └── 通过降维压缩数据-checkpoint.ipynb ├── ch.md ├── ch5_section1.md ├── ch5_section10.md ├── ch5_section2.md ├── ch5_section3.md ├── ch5_section4.md ├── ch5_section5.md ├── ch5_section6.md ├── ch5_section7.md ├── ch5_section8.md ├── ch5_section9.md └── 通过降维压缩数据.ipynb ├── 第六章 ├── .ipynb_checkpoints │ └── 模型评估和调参-checkpoint.ipynb ├── ch6.md ├── ch6_section1.md ├── ch6_section2.md ├── ch6_section3.md ├── ch6_section4.md ├── ch6_section5.md └── 模型评估和调参.ipynb └── 第四章 ├── .ipynb_checkpoints └── 构建一个好的训练集---数据预处理-checkpoint.ipynb ├── ch4.md ├── ch4_section1.md ├── ch4_section10.md ├── ch4_section2.md ├── ch4_section3.md ├── ch4_section4.md ├── ch4_section5.md ├── ch4_section6.md ├── ch4_section7.md ├── ch4_section8.md ├── ch4_section9.md └── 构建一个好的训练集---数据预处理.ipynb /README.md: -------------------------------------------------------------------------------- 1 | # Python机器学习 2 | 3 | 机器学习,如今最令人着迷的计算机领域(之一)。Google、Facebook、Apple、Amazon、BAT等大公司早已展开了一场关于机器学习的军备竞赛。从手机上的语音助手、垃圾邮件过滤到逛淘宝时的物品推荐,无一不用到机器学习技术。 4 | 5 | 如果你对机器学习感兴趣,甚至是想从事相关职业,那么这本书非常适合作为你的第一本机器学习资料。市面上大部分的机器学习书籍要么是告诉你如何推导模型公式要么就是如何代码实现模型算法,这对于零基础的新手来说,阅读起来相当困难。而这本书,在介绍必要的基础概念后,着重从如何调用机器学习算法解决实际问题入手,一步一步带你入门。即使你已经对很多机器学习算法的理论很熟悉了,这本书仍能从实践方面带给你一些帮助。 6 | 7 | 具体到编程语言层面,本书选择的是Python,因为它简单易懂。我们不必在枯燥的语法细节上耗费时间,一旦有了想法,你能够快速实现算法并在真实数据集上进行验证。在整个数据科学领域,Python都可以说是稳坐语言榜头号交椅。 8 | 9 | **注意** 这是我在阅读 [Python Machine Learning](https://www.amazon.com/Python-Machine-Learning-Sebastian-Raschka/dp/1783555130) 的笔记而不是严格的原文翻译,如果发现错误,请不惜吝教~ 10 | 11 | 12 | **最后,我没有本书的翻译版权,请勿商用。转载请注明出处,谢谢:\)** 13 | 14 | # 目录 15 | 16 | 17 | [第二章 机器学习分类算法](https://github.com/basicv8vc/Python-Machine-Learning-zh/blob/master/%E7%AC%AC%E4%BA%8C%E7%AB%A0/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E5%88%86%E7%B1%BB%E7%AE%97%E6%B3%95.ipynb) 18 | 19 | [第三章 使用scikit-learn进行分类器之旅](https://github.com/basicv8vc/Python-Machine-Learning-zh/blob/master/%E7%AC%AC%E4%B8%89%E7%AB%A0/%E4%BD%BF%E7%94%A8Scikit-learn%E8%BF%9B%E8%A1%8C%E5%88%86%E7%B1%BB%E5%99%A8%E4%B9%8B%E6%97%85.ipynb) 20 | 21 | [第四章 构建一个好的训练集---数据预处理](https://github.com/basicv8vc/Python-Machine-Learning-zh/blob/master/%E7%AC%AC%E5%9B%9B%E7%AB%A0/%E6%9E%84%E5%BB%BA%E4%B8%80%E4%B8%AA%E5%A5%BD%E7%9A%84%E8%AE%AD%E7%BB%83%E9%9B%86---%E6%95%B0%E6%8D%AE%E9%A2%84%E5%A4%84%E7%90%86.ipynb) 22 | 23 | [第五章 通过降维压缩数据](https://github.com/basicv8vc/Python-Machine-Learning-zh/blob/master/%E7%AC%AC%E4%BA%94%E7%AB%A0/%E9%80%9A%E8%BF%87%E9%99%8D%E7%BB%B4%E5%8E%8B%E7%BC%A9%E6%95%B0%E6%8D%AE.ipynb) 24 | 25 | 26 | [第六章 模型评估和调参](https://github.com/basicv8vc/Python-Machine-Learning-zh/blob/master/%E7%AC%AC%E5%85%AD%E7%AB%A0/%E6%A8%A1%E5%9E%8B%E8%AF%84%E4%BC%B0%E5%92%8C%E8%B0%83%E5%8F%82.ipynb) 27 | 28 | 29 | [第七章 集成学习](https://github.com/basicv8vc/Python-Machine-Learning-zh/blob/master/%E7%AC%AC%E4%B8%83%E7%AB%A0/%E9%9B%86%E6%88%90%E5%AD%A6%E4%B9%A0.ipynb) 30 | -------------------------------------------------------------------------------- /实践/xgboost调包/dtest.buffer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicv8vc/Python-Machine-Learning-zh/cc7bc8e1edf9734530c5cca4c917ccb901192008/实践/xgboost调包/dtest.buffer -------------------------------------------------------------------------------- /实践/xgboost调包/xgb.model: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basicv8vc/Python-Machine-Learning-zh/cc7bc8e1edf9734530c5cca4c917ccb901192008/实践/xgboost调包/xgb.model -------------------------------------------------------------------------------- /实践/xgboost调包/xgboost调包之 early stopping.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "%matplotlib inline\n", 12 | "import numpy as np\n", 13 | "import pandas as pd\n", 14 | "import xgboost as xgb" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": { 21 | "collapsed": true 22 | }, 23 | "outputs": [], 24 | "source": [] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 2, 29 | "metadata": { 30 | "collapsed": true 31 | }, 32 | "outputs": [], 33 | "source": [ 34 | "from sklearn.datasets import load_digits" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 3, 40 | "metadata": { 41 | "collapsed": true 42 | }, 43 | "outputs": [], 44 | "source": [ 45 | "from sklearn.cross_validation import train_test_split" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": 5, 51 | "metadata": { 52 | "collapsed": false 53 | }, 54 | "outputs": [], 55 | "source": [ 56 | "digits = load_digits(2)" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 6, 62 | "metadata": { 63 | "collapsed": true 64 | }, 65 | "outputs": [], 66 | "source": [ 67 | "X = digits['data']\n", 68 | "y = digits['target']" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": 7, 74 | "metadata": { 75 | "collapsed": true 76 | }, 77 | "outputs": [], 78 | "source": [ 79 | "X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)" 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": 8, 85 | "metadata": { 86 | "collapsed": true 87 | }, 88 | "outputs": [], 89 | "source": [ 90 | "clf1 = xgb.XGBClassifier()" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": 10, 96 | "metadata": { 97 | "collapsed": false 98 | }, 99 | "outputs": [ 100 | { 101 | "name": "stdout", 102 | "output_type": "stream", 103 | "text": [ 104 | "[0]\tvalidation_0-auc:0.999497\n", 105 | "Will train until validation_0-auc hasn't improved in 5 rounds.\n", 106 | "[1]\tvalidation_0-auc:0.999497\n", 107 | "[2]\tvalidation_0-auc:0.999497\n", 108 | "[3]\tvalidation_0-auc:0.999749\n", 109 | "[4]\tvalidation_0-auc:0.999749\n", 110 | "[5]\tvalidation_0-auc:0.999749\n", 111 | "[6]\tvalidation_0-auc:0.999749\n", 112 | "[7]\tvalidation_0-auc:0.999749\n", 113 | "[8]\tvalidation_0-auc:0.999749\n", 114 | "Stopping. Best iteration:\n", 115 | "[3]\tvalidation_0-auc:0.999749\n", 116 | "\n" 117 | ] 118 | }, 119 | { 120 | "data": { 121 | "text/plain": [ 122 | "XGBClassifier(base_score=0.5, colsample_bylevel=1, colsample_bytree=1,\n", 123 | " gamma=0, learning_rate=0.1, max_delta_step=0, max_depth=3,\n", 124 | " min_child_weight=1, missing=None, n_estimators=100, nthread=-1,\n", 125 | " objective='binary:logistic', reg_alpha=0, reg_lambda=1,\n", 126 | " scale_pos_weight=1, seed=0, silent=True, subsample=1)" 127 | ] 128 | }, 129 | "execution_count": 10, 130 | "metadata": {}, 131 | "output_type": "execute_result" 132 | } 133 | ], 134 | "source": [ 135 | "clf1.fit(X_train, y_train, early_stopping_rounds=5, eval_metric='auc',\n", 136 | " eval_set=[(X_test, y_test)])" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 11, 142 | "metadata": { 143 | "collapsed": false 144 | }, 145 | "outputs": [ 146 | { 147 | "data": { 148 | "text/plain": [ 149 | "0.999749" 150 | ] 151 | }, 152 | "execution_count": 11, 153 | "metadata": {}, 154 | "output_type": "execute_result" 155 | } 156 | ], 157 | "source": [ 158 | "clf1.best_score" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": 12, 164 | "metadata": { 165 | "collapsed": false 166 | }, 167 | "outputs": [ 168 | { 169 | "name": "stdout", 170 | "output_type": "stream", 171 | "text": [ 172 | "[0]\tvalidation_0-auc:0.999497\n", 173 | "Will train until validation_0-auc hasn't improved in 4 rounds.\n", 174 | "[1]\tvalidation_0-auc:0.999497\n", 175 | "[2]\tvalidation_0-auc:0.999497\n", 176 | "[3]\tvalidation_0-auc:0.999749\n", 177 | "[4]\tvalidation_0-auc:0.999749\n", 178 | "[5]\tvalidation_0-auc:0.999749\n", 179 | "[6]\tvalidation_0-auc:0.999749\n", 180 | "[7]\tvalidation_0-auc:0.999749\n", 181 | "Stopping. Best iteration:\n", 182 | "[3]\tvalidation_0-auc:0.999749\n", 183 | "\n" 184 | ] 185 | }, 186 | { 187 | "data": { 188 | "text/plain": [ 189 | "XGBClassifier(base_score=0.5, colsample_bylevel=1, colsample_bytree=1,\n", 190 | " gamma=0, learning_rate=0.1, max_delta_step=0, max_depth=3,\n", 191 | " min_child_weight=1, missing=None, n_estimators=100, nthread=-1,\n", 192 | " objective='binary:logistic', reg_alpha=0, reg_lambda=1,\n", 193 | " scale_pos_weight=1, seed=0, silent=True, subsample=1)" 194 | ] 195 | }, 196 | "execution_count": 12, 197 | "metadata": {}, 198 | "output_type": "execute_result" 199 | } 200 | ], 201 | "source": [ 202 | "clf2 = xgb.XGBClassifier()\n", 203 | "clf2.fit(X_train, y_train, early_stopping_rounds=4, eval_metric=\"auc\",\n", 204 | " eval_set=[(X_test, y_test)])" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": 13, 210 | "metadata": { 211 | "collapsed": false 212 | }, 213 | "outputs": [ 214 | { 215 | "data": { 216 | "text/plain": [ 217 | "0.999749" 218 | ] 219 | }, 220 | "execution_count": 13, 221 | "metadata": {}, 222 | "output_type": "execute_result" 223 | } 224 | ], 225 | "source": [ 226 | "clf2.best_score" 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": null, 232 | "metadata": { 233 | "collapsed": true 234 | }, 235 | "outputs": [], 236 | "source": [] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": null, 241 | "metadata": { 242 | "collapsed": true 243 | }, 244 | "outputs": [], 245 | "source": [] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": 18, 250 | "metadata": { 251 | "collapsed": false 252 | }, 253 | "outputs": [ 254 | { 255 | "name": "stdout", 256 | "output_type": "stream", 257 | "text": [ 258 | "[0]\tvalidation_0-auc:0.999497\n", 259 | "Will train until validation_0-auc hasn't improved in 10 rounds.\n", 260 | "[1]\tvalidation_0-auc:0.999497\n", 261 | "[2]\tvalidation_0-auc:0.999497\n", 262 | "[3]\tvalidation_0-auc:0.999749\n", 263 | "[4]\tvalidation_0-auc:0.999749\n", 264 | "[5]\tvalidation_0-auc:0.999749\n", 265 | "[6]\tvalidation_0-auc:0.999749\n", 266 | "[7]\tvalidation_0-auc:0.999749\n", 267 | "[8]\tvalidation_0-auc:0.999749\n", 268 | "[9]\tvalidation_0-auc:0.999749\n", 269 | "[10]\tvalidation_0-auc:1\n", 270 | "[11]\tvalidation_0-auc:1\n", 271 | "[12]\tvalidation_0-auc:1\n", 272 | "[13]\tvalidation_0-auc:1\n", 273 | "[14]\tvalidation_0-auc:1\n", 274 | "[15]\tvalidation_0-auc:1\n", 275 | "[16]\tvalidation_0-auc:1\n", 276 | "[17]\tvalidation_0-auc:1\n", 277 | "[18]\tvalidation_0-auc:1\n", 278 | "[19]\tvalidation_0-auc:1\n", 279 | "[20]\tvalidation_0-auc:1\n", 280 | "Stopping. Best iteration:\n", 281 | "[10]\tvalidation_0-auc:1\n", 282 | "\n" 283 | ] 284 | }, 285 | { 286 | "data": { 287 | "text/plain": [ 288 | "XGBClassifier(base_score=0.5, colsample_bylevel=1, colsample_bytree=1,\n", 289 | " gamma=0, learning_rate=0.1, max_delta_step=0, max_depth=3,\n", 290 | " min_child_weight=1, missing=None, n_estimators=100, nthread=-1,\n", 291 | " objective='binary:logistic', reg_alpha=0, reg_lambda=1,\n", 292 | " scale_pos_weight=1, seed=0, silent=True, subsample=1)" 293 | ] 294 | }, 295 | "execution_count": 18, 296 | "metadata": {}, 297 | "output_type": "execute_result" 298 | } 299 | ], 300 | "source": [ 301 | "# 是否过拟合\n", 302 | "clf3 = xgb.XGBClassifier()\n", 303 | "clf3.fit(X_train, y_train, early_stopping_rounds=10, eval_metric=\"auc\",\n", 304 | " eval_set=[(X_test, y_test)])" 305 | ] 306 | }, 307 | { 308 | "cell_type": "code", 309 | "execution_count": 19, 310 | "metadata": { 311 | "collapsed": false 312 | }, 313 | "outputs": [ 314 | { 315 | "data": { 316 | "text/plain": [ 317 | "1.0" 318 | ] 319 | }, 320 | "execution_count": 19, 321 | "metadata": {}, 322 | "output_type": "execute_result" 323 | } 324 | ], 325 | "source": [ 326 | "clf3.best_score" 327 | ] 328 | }, 329 | { 330 | "cell_type": "code", 331 | "execution_count": null, 332 | "metadata": { 333 | "collapsed": true 334 | }, 335 | "outputs": [], 336 | "source": [] 337 | }, 338 | { 339 | "cell_type": "code", 340 | "execution_count": null, 341 | "metadata": { 342 | "collapsed": true 343 | }, 344 | "outputs": [], 345 | "source": [] 346 | }, 347 | { 348 | "cell_type": "markdown", 349 | "metadata": {}, 350 | "source": [ 351 | "## 交叉验证和early stopping结合起来" 352 | ] 353 | }, 354 | { 355 | "cell_type": "markdown", 356 | "metadata": {}, 357 | "source": [ 358 | "### 使用xgboost提供的cv功能,而不是sklearn" 359 | ] 360 | }, 361 | { 362 | "cell_type": "code", 363 | "execution_count": 20, 364 | "metadata": { 365 | "collapsed": true 366 | }, 367 | "outputs": [], 368 | "source": [ 369 | "dm = xgb.DMatrix(X, label=y)" 370 | ] 371 | }, 372 | { 373 | "cell_type": "code", 374 | "execution_count": 21, 375 | "metadata": { 376 | "collapsed": true 377 | }, 378 | "outputs": [], 379 | "source": [ 380 | "params = {'max_depth': 2, 'eta': 1, 'silent': 1, 'objective': 'binary:logistic'}" 381 | ] 382 | }, 383 | { 384 | "cell_type": "code", 385 | "execution_count": 23, 386 | "metadata": { 387 | "collapsed": true 388 | }, 389 | "outputs": [], 390 | "source": [ 391 | "cv = xgb.cv(params, dm, num_boost_round=10, nfold=10, early_stopping_rounds=10)" 392 | ] 393 | }, 394 | { 395 | "cell_type": "code", 396 | "execution_count": 24, 397 | "metadata": { 398 | "collapsed": false 399 | }, 400 | "outputs": [ 401 | { 402 | "data": { 403 | "text/plain": [ 404 | "10" 405 | ] 406 | }, 407 | "execution_count": 24, 408 | "metadata": {}, 409 | "output_type": "execute_result" 410 | } 411 | ], 412 | "source": [ 413 | "cv.shape[0]" 414 | ] 415 | }, 416 | { 417 | "cell_type": "code", 418 | "execution_count": 25, 419 | "metadata": { 420 | "collapsed": true 421 | }, 422 | "outputs": [], 423 | "source": [ 424 | "cv = xgb.cv(params, dm, num_boost_round=10, nfold=10, early_stopping_rounds=5)" 425 | ] 426 | }, 427 | { 428 | "cell_type": "code", 429 | "execution_count": 26, 430 | "metadata": { 431 | "collapsed": true 432 | }, 433 | "outputs": [], 434 | "source": [ 435 | "def evalerror(preds, dtrain):\n", 436 | " from sklearn.metrics import mean_squared_error\n", 437 | " labels = dtrain.get_label()\n", 438 | " return 'rmse', mean_squared_error(labels, preds)" 439 | ] 440 | }, 441 | { 442 | "cell_type": "code", 443 | "execution_count": 29, 444 | "metadata": { 445 | "collapsed": true 446 | }, 447 | "outputs": [], 448 | "source": [ 449 | "# 使用自定义的评价指标\n", 450 | "cv = xgb.cv(params, dm, num_boost_round=10, nfold=10, feval=evalerror, maximize=True, early_stopping_rounds=3)" 451 | ] 452 | }, 453 | { 454 | "cell_type": "code", 455 | "execution_count": 30, 456 | "metadata": { 457 | "collapsed": false 458 | }, 459 | "outputs": [ 460 | { 461 | "data": { 462 | "text/plain": [ 463 | "1" 464 | ] 465 | }, 466 | "execution_count": 30, 467 | "metadata": {}, 468 | "output_type": "execute_result" 469 | } 470 | ], 471 | "source": [ 472 | "cv.shape[0]" 473 | ] 474 | }, 475 | { 476 | "cell_type": "code", 477 | "execution_count": null, 478 | "metadata": { 479 | "collapsed": true 480 | }, 481 | "outputs": [], 482 | "source": [] 483 | }, 484 | { 485 | "cell_type": "code", 486 | "execution_count": null, 487 | "metadata": { 488 | "collapsed": true 489 | }, 490 | "outputs": [], 491 | "source": [] 492 | } 493 | ], 494 | "metadata": { 495 | "kernelspec": { 496 | "display_name": "Python 2", 497 | "language": "python", 498 | "name": "python2" 499 | }, 500 | "language_info": { 501 | "codemirror_mode": { 502 | "name": "ipython", 503 | "version": 2 504 | }, 505 | "file_extension": ".py", 506 | "mimetype": "text/x-python", 507 | "name": "python", 508 | "nbconvert_exporter": "python", 509 | "pygments_lexer": "ipython2", 510 | "version": "2.7.11" 511 | } 512 | }, 513 | "nbformat": 4, 514 | "nbformat_minor": 0 515 | } 516 | -------------------------------------------------------------------------------- /实践/xgboost调包/xgboost调包之 基础篇.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "%matplotlib inline\n", 12 | "import numpy as np\n", 13 | "import xgboost as xgb\n", 14 | "import pandas as pd" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": { 21 | "collapsed": true 22 | }, 23 | "outputs": [], 24 | "source": [] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "# 基本的使用" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "## 二分类" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": 132, 43 | "metadata": { 44 | "collapsed": true 45 | }, 46 | "outputs": [], 47 | "source": [ 48 | "dtrain = xgb.DMatrix('agaricus.txt.train')" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 133, 54 | "metadata": { 55 | "collapsed": true 56 | }, 57 | "outputs": [], 58 | "source": [ 59 | "dtest = xgb.DMatrix('agaricus.txt.test')" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 134, 65 | "metadata": { 66 | "collapsed": true 67 | }, 68 | "outputs": [], 69 | "source": [ 70 | "param = {'max_detph': 2, 'eta': 1, 'silent': 1, 'objective': 'binary:logistic'}" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "metadata": {}, 76 | "source": [ 77 | "### 设定验证集,输出每一次迭代表现" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": 135, 83 | "metadata": { 84 | "collapsed": true 85 | }, 86 | "outputs": [], 87 | "source": [ 88 | "watchlist = [(dtest, 'eval'), (dtrain, 'train')]" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": 136, 94 | "metadata": { 95 | "collapsed": true 96 | }, 97 | "outputs": [], 98 | "source": [ 99 | "num_boost_round = 5" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": 137, 105 | "metadata": { 106 | "collapsed": false 107 | }, 108 | "outputs": [ 109 | { 110 | "name": "stdout", 111 | "output_type": "stream", 112 | "text": [ 113 | "[0]\teval-error:0\ttrain-error:0.000614\n", 114 | "[1]\teval-error:0\ttrain-error:0\n", 115 | "[2]\teval-error:0\ttrain-error:0\n", 116 | "[3]\teval-error:0\ttrain-error:0\n", 117 | "[4]\teval-error:0\ttrain-error:0\n" 118 | ] 119 | } 120 | ], 121 | "source": [ 122 | "bst = xgb.train(param, dtrain, num_boost_round, watchlist)" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "### 保存每一次迭代的训练集和验证集结果" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": 64, 135 | "metadata": { 136 | "collapsed": false 137 | }, 138 | "outputs": [ 139 | { 140 | "name": "stdout", 141 | "output_type": "stream", 142 | "text": [ 143 | "[0]\teval-error:0\ttrain-error:0.000614\n", 144 | "[1]\teval-error:0\ttrain-error:0\n", 145 | "[2]\teval-error:0\ttrain-error:0\n", 146 | "[3]\teval-error:0\ttrain-error:0\n", 147 | "[4]\teval-error:0\ttrain-error:0\n" 148 | ] 149 | }, 150 | { 151 | "data": { 152 | "text/plain": [ 153 | "" 154 | ] 155 | }, 156 | "execution_count": 64, 157 | "metadata": {}, 158 | "output_type": "execute_result" 159 | } 160 | ], 161 | "source": [ 162 | "# 使用train()中的evals_result参数\n", 163 | "res2 = {}\n", 164 | "xgb.train(param, dtrain, num_round, watchlist, evals_result=res2)" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": 65, 170 | "metadata": { 171 | "collapsed": false 172 | }, 173 | "outputs": [ 174 | { 175 | "name": "stdout", 176 | "output_type": "stream", 177 | "text": [ 178 | "train {'error': [0.000614, 0.0, 0.0, 0.0, 0.0]}\n", 179 | "eval {'error': [0.0, 0.0, 0.0, 0.0, 0.0]}\n" 180 | ] 181 | } 182 | ], 183 | "source": [ 184 | "for key, value in res2.iteritems():\n", 185 | " print key, value" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": null, 191 | "metadata": { 192 | "collapsed": true 193 | }, 194 | "outputs": [], 195 | "source": [] 196 | }, 197 | { 198 | "cell_type": "markdown", 199 | "metadata": {}, 200 | "source": [ 201 | "### 进行预测" 202 | ] 203 | }, 204 | { 205 | "cell_type": "code", 206 | "execution_count": 76, 207 | "metadata": { 208 | "collapsed": false 209 | }, 210 | "outputs": [ 211 | { 212 | "name": "stdout", 213 | "output_type": "stream", 214 | "text": [ 215 | "[ 0.0033117 0.99624288 0.0033117 ..., 0.99777812 0.00309621\n", 216 | " 0.99777812]\n" 217 | ] 218 | } 219 | ], 220 | "source": [ 221 | "# 得到概率值\n", 222 | "preds = bst.predict(dtest)\n", 223 | "print preds" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": 77, 229 | "metadata": { 230 | "collapsed": true 231 | }, 232 | "outputs": [], 233 | "source": [ 234 | "# 得到测试集中的y值\n", 235 | "labels = dtest.get_label()" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": 79, 241 | "metadata": { 242 | "collapsed": false 243 | }, 244 | "outputs": [ 245 | { 246 | "name": "stdout", 247 | "output_type": "stream", 248 | "text": [ 249 | "0.0\n" 250 | ] 251 | } 252 | ], 253 | "source": [ 254 | "print sum(1 for i in range(len(preds)) if int(preds[i] > 0.5) != labels[i]) / float(len(preds))" 255 | ] 256 | }, 257 | { 258 | "cell_type": "markdown", 259 | "metadata": {}, 260 | "source": [ 261 | "### 保存模型" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": 80, 267 | "metadata": { 268 | "collapsed": false 269 | }, 270 | "outputs": [], 271 | "source": [ 272 | "bst.save_model('xgb.model')" 273 | ] 274 | }, 275 | { 276 | "cell_type": "code", 277 | "execution_count": 81, 278 | "metadata": { 279 | "collapsed": true 280 | }, 281 | "outputs": [], 282 | "source": [ 283 | "dtest.save_binary('dtest.buffer')" 284 | ] 285 | }, 286 | { 287 | "cell_type": "markdown", 288 | "metadata": {}, 289 | "source": [ 290 | "### 读取模型和数据" 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": 82, 296 | "metadata": { 297 | "collapsed": true 298 | }, 299 | "outputs": [], 300 | "source": [ 301 | "bst2 = xgb.Booster(model_file='xgb.model')" 302 | ] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": 83, 307 | "metadata": { 308 | "collapsed": true 309 | }, 310 | "outputs": [], 311 | "source": [ 312 | "dtest2 = xgb.DMatrix('dtest.buffer')" 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": 84, 318 | "metadata": { 319 | "collapsed": true 320 | }, 321 | "outputs": [], 322 | "source": [ 323 | "preds2 = bst2.predict(dtest2)" 324 | ] 325 | }, 326 | { 327 | "cell_type": "code", 328 | "execution_count": 85, 329 | "metadata": { 330 | "collapsed": false 331 | }, 332 | "outputs": [], 333 | "source": [ 334 | "assert np.sum(np.abs(preds2 - preds)) == 0" 335 | ] 336 | }, 337 | { 338 | "cell_type": "code", 339 | "execution_count": null, 340 | "metadata": { 341 | "collapsed": true 342 | }, 343 | "outputs": [], 344 | "source": [] 345 | }, 346 | { 347 | "cell_type": "markdown", 348 | "metadata": {}, 349 | "source": [ 350 | "## 多分类" 351 | ] 352 | }, 353 | { 354 | "cell_type": "code", 355 | "execution_count": 86, 356 | "metadata": { 357 | "collapsed": true 358 | }, 359 | "outputs": [], 360 | "source": [ 361 | "dtrain = xgb.DMatrix('agaricus.txt.train')\n", 362 | "dtest = xgb.DMatrix('agaricus.txt.test')" 363 | ] 364 | }, 365 | { 366 | "cell_type": "code", 367 | "execution_count": 87, 368 | "metadata": { 369 | "collapsed": true 370 | }, 371 | "outputs": [], 372 | "source": [ 373 | "# 通过num_class参数设定多分类的类别数即可\n", 374 | "param = {'max_depth': 2, 'eta': 1, 'silent': 1, 'num_class': 2}" 375 | ] 376 | }, 377 | { 378 | "cell_type": "code", 379 | "execution_count": 88, 380 | "metadata": { 381 | "collapsed": true 382 | }, 383 | "outputs": [], 384 | "source": [ 385 | "watchlist = [(dtest, 'eval'), (dtrain, 'train')]" 386 | ] 387 | }, 388 | { 389 | "cell_type": "code", 390 | "execution_count": 89, 391 | "metadata": { 392 | "collapsed": true 393 | }, 394 | "outputs": [], 395 | "source": [ 396 | "num_boost_round = 5" 397 | ] 398 | }, 399 | { 400 | "cell_type": "code", 401 | "execution_count": 90, 402 | "metadata": { 403 | "collapsed": false 404 | }, 405 | "outputs": [ 406 | { 407 | "name": "stdout", 408 | "output_type": "stream", 409 | "text": [ 410 | "[0]\teval-merror:0.042831\ttrain-merror:0.046522\n", 411 | "[1]\teval-merror:0.021726\ttrain-merror:0.022263\n", 412 | "[2]\teval-merror:0.006207\ttrain-merror:0.007063\n", 413 | "[3]\teval-merror:0.018001\ttrain-merror:0.0152\n", 414 | "[4]\teval-merror:0.006207\ttrain-merror:0.007063\n" 415 | ] 416 | } 417 | ], 418 | "source": [ 419 | "bst = xgb.train(param, dtrain, num_boost_round, watchlist)" 420 | ] 421 | }, 422 | { 423 | "cell_type": "markdown", 424 | "metadata": {}, 425 | "source": [ 426 | "### 预测" 427 | ] 428 | }, 429 | { 430 | "cell_type": "code", 431 | "execution_count": 96, 432 | "metadata": { 433 | "collapsed": false 434 | }, 435 | "outputs": [ 436 | { 437 | "name": "stdout", 438 | "output_type": "stream", 439 | "text": [ 440 | "[ 0. 1. 0. ..., 1. 0. 1.]\n" 441 | ] 442 | } 443 | ], 444 | "source": [ 445 | "# 多分类,得到类别值\n", 446 | "preds = bst.predict(dtest)\n", 447 | "\n", 448 | "print preds" 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "execution_count": 97, 454 | "metadata": { 455 | "collapsed": false 456 | }, 457 | "outputs": [], 458 | "source": [ 459 | "labels = dtest.get_label()" 460 | ] 461 | }, 462 | { 463 | "cell_type": "code", 464 | "execution_count": 98, 465 | "metadata": { 466 | "collapsed": false 467 | }, 468 | "outputs": [], 469 | "source": [ 470 | "err = sum(1 for i in range(len(preds)) if preds[i] != labels[i]) / float(len(preds))" 471 | ] 472 | }, 473 | { 474 | "cell_type": "code", 475 | "execution_count": 99, 476 | "metadata": { 477 | "collapsed": false 478 | }, 479 | "outputs": [ 480 | { 481 | "name": "stdout", 482 | "output_type": "stream", 483 | "text": [ 484 | "0.00620732464308\n" 485 | ] 486 | } 487 | ], 488 | "source": [ 489 | "print err" 490 | ] 491 | }, 492 | { 493 | "cell_type": "markdown", 494 | "metadata": {}, 495 | "source": [ 496 | "### 保存模型和数据" 497 | ] 498 | }, 499 | { 500 | "cell_type": "code", 501 | "execution_count": 100, 502 | "metadata": { 503 | "collapsed": true 504 | }, 505 | "outputs": [], 506 | "source": [ 507 | "dtest.save_binary('dtest.buffer')\n", 508 | "bst.save_model('xgb.model')" 509 | ] 510 | }, 511 | { 512 | "cell_type": "markdown", 513 | "metadata": {}, 514 | "source": [ 515 | "### 从本地读取模型和数据" 516 | ] 517 | }, 518 | { 519 | "cell_type": "code", 520 | "execution_count": 101, 521 | "metadata": { 522 | "collapsed": true 523 | }, 524 | "outputs": [], 525 | "source": [ 526 | "bst2 = xgb.Booster(model_file='xgb.model')" 527 | ] 528 | }, 529 | { 530 | "cell_type": "code", 531 | "execution_count": 102, 532 | "metadata": { 533 | "collapsed": true 534 | }, 535 | "outputs": [], 536 | "source": [ 537 | "dtest2 = xgb.DMatrix('dtest.buffer')" 538 | ] 539 | }, 540 | { 541 | "cell_type": "code", 542 | "execution_count": 103, 543 | "metadata": { 544 | "collapsed": true 545 | }, 546 | "outputs": [], 547 | "source": [ 548 | "preds2 = bst2.predict(dtest2)" 549 | ] 550 | }, 551 | { 552 | "cell_type": "code", 553 | "execution_count": 104, 554 | "metadata": { 555 | "collapsed": true 556 | }, 557 | "outputs": [], 558 | "source": [ 559 | "assert np.sum(np.abs(preds2 - preds)) == 0" 560 | ] 561 | }, 562 | { 563 | "cell_type": "code", 564 | "execution_count": null, 565 | "metadata": { 566 | "collapsed": true 567 | }, 568 | "outputs": [], 569 | "source": [] 570 | }, 571 | { 572 | "cell_type": "markdown", 573 | "metadata": {}, 574 | "source": [ 575 | "## DMatrix基本使用" 576 | ] 577 | }, 578 | { 579 | "cell_type": "code", 580 | "execution_count": 105, 581 | "metadata": { 582 | "collapsed": true 583 | }, 584 | "outputs": [], 585 | "source": [ 586 | "data = np.random.randn(5, 5)" 587 | ] 588 | }, 589 | { 590 | "cell_type": "code", 591 | "execution_count": 106, 592 | "metadata": { 593 | "collapsed": false 594 | }, 595 | "outputs": [ 596 | { 597 | "data": { 598 | "text/plain": [ 599 | "array([[-0.1440365 , -1.53901335, 1.52256072, 0.24961937, 0.00816078],\n", 600 | " [ 1.50922483, 1.61828149, 0.48580408, 0.64529202, -0.38986845],\n", 601 | " [ 1.6335635 , -0.63496332, -1.37183114, 0.70000331, -1.37422337],\n", 602 | " [ 0.45820332, -1.45275691, -0.31891843, -0.63120163, -0.50904278],\n", 603 | " [ 1.54513591, -0.21328514, 1.57111063, -0.32387795, 1.64972363]])" 604 | ] 605 | }, 606 | "execution_count": 106, 607 | "metadata": {}, 608 | "output_type": "execute_result" 609 | } 610 | ], 611 | "source": [ 612 | "data" 613 | ] 614 | }, 615 | { 616 | "cell_type": "code", 617 | "execution_count": 107, 618 | "metadata": { 619 | "collapsed": true 620 | }, 621 | "outputs": [], 622 | "source": [ 623 | "dm = xgb.DMatrix(data)" 624 | ] 625 | }, 626 | { 627 | "cell_type": "code", 628 | "execution_count": 108, 629 | "metadata": { 630 | "collapsed": true 631 | }, 632 | "outputs": [], 633 | "source": [ 634 | "dm.feature_names = list('abcde')" 635 | ] 636 | }, 637 | { 638 | "cell_type": "code", 639 | "execution_count": 109, 640 | "metadata": { 641 | "collapsed": true 642 | }, 643 | "outputs": [], 644 | "source": [ 645 | "assert dm.feature_names == list('abcde')" 646 | ] 647 | }, 648 | { 649 | "cell_type": "code", 650 | "execution_count": 110, 651 | "metadata": { 652 | "collapsed": true 653 | }, 654 | "outputs": [], 655 | "source": [ 656 | "dm.feature_types = 'q'" 657 | ] 658 | }, 659 | { 660 | "cell_type": "code", 661 | "execution_count": 111, 662 | "metadata": { 663 | "collapsed": true 664 | }, 665 | "outputs": [], 666 | "source": [ 667 | "assert dm.feature_types == list('qqqqq')" 668 | ] 669 | }, 670 | { 671 | "cell_type": "code", 672 | "execution_count": 114, 673 | "metadata": { 674 | "collapsed": false 675 | }, 676 | "outputs": [], 677 | "source": [ 678 | "dm.feature_types = list('qiqiq')" 679 | ] 680 | }, 681 | { 682 | "cell_type": "code", 683 | "execution_count": 121, 684 | "metadata": { 685 | "collapsed": false 686 | }, 687 | "outputs": [ 688 | { 689 | "ename": "ValueError", 690 | "evalue": "All feature_names must be {int, float, i, q}", 691 | "output_type": "error", 692 | "traceback": [ 693 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 694 | "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", 695 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# All feature_names must be {int, float, i, q}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mdm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfeature_types\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'qi'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 696 | "\u001b[0;32m/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/xgboost-0.4-py2.7.egg/xgboost/core.pyc\u001b[0m in \u001b[0;36mfeature_types\u001b[0;34m(self, feature_types)\u001b[0m\n\u001b[1;32m 614\u001b[0m if not all(isinstance(f, STRING_TYPES) and f in valid\n\u001b[1;32m 615\u001b[0m for f in feature_types):\n\u001b[0;32m--> 616\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'All feature_names must be {int, float, i, q}'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 617\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_feature_types\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfeature_types\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 618\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", 697 | "\u001b[0;31mValueError\u001b[0m: All feature_names must be {int, float, i, q}" 698 | ] 699 | } 700 | ], 701 | "source": [ 702 | "# All feature_names must be {int, float, i, q}\n", 703 | "dm.feature_types = 'qi' " 704 | ] 705 | }, 706 | { 707 | "cell_type": "code", 708 | "execution_count": null, 709 | "metadata": { 710 | "collapsed": true 711 | }, 712 | "outputs": [], 713 | "source": [] 714 | }, 715 | { 716 | "cell_type": "code", 717 | "execution_count": null, 718 | "metadata": { 719 | "collapsed": true 720 | }, 721 | "outputs": [], 722 | "source": [] 723 | }, 724 | { 725 | "cell_type": "markdown", 726 | "metadata": {}, 727 | "source": [ 728 | "### 特征名字" 729 | ] 730 | }, 731 | { 732 | "cell_type": "code", 733 | "execution_count": 138, 734 | "metadata": { 735 | "collapsed": true 736 | }, 737 | "outputs": [], 738 | "source": [ 739 | "data = np.random.randn(100, 5)" 740 | ] 741 | }, 742 | { 743 | "cell_type": "code", 744 | "execution_count": 139, 745 | "metadata": { 746 | "collapsed": true 747 | }, 748 | "outputs": [], 749 | "source": [ 750 | "target = np.array([0, 1] * 50)" 751 | ] 752 | }, 753 | { 754 | "cell_type": "code", 755 | "execution_count": 140, 756 | "metadata": { 757 | "collapsed": true 758 | }, 759 | "outputs": [], 760 | "source": [ 761 | "cases = [['Feature1', 'Feature2', 'Feature3', 'Feature4', 'Feature5'],\n", 762 | " [u'特征1', u'特征2', u'特征3', u'特征4', u'特征5']]" 763 | ] 764 | }, 765 | { 766 | "cell_type": "code", 767 | "execution_count": 145, 768 | "metadata": { 769 | "collapsed": false 770 | }, 771 | "outputs": [ 772 | { 773 | "name": "stdout", 774 | "output_type": "stream", 775 | "text": [ 776 | "\n", 777 | "Feature1\n", 778 | "Feature2\n", 779 | "Feature3\n", 780 | "Feature4\n", 781 | "Feature5\n", 782 | "\n", 783 | "特征1\n", 784 | "特征2\n", 785 | "特征3\n", 786 | "特征4\n", 787 | "特征5\n" 788 | ] 789 | } 790 | ], 791 | "source": [ 792 | "for features in cases:\n", 793 | " dm = xgb.DMatrix(data, label=target,\n", 794 | " feature_names=features)\n", 795 | " \n", 796 | " params = {'objective': 'multi:softprob',\n", 797 | " 'eval_metric': 'mlogloss',\n", 798 | " 'eta': 0.3,\n", 799 | " 'num_class': 2}\n", 800 | " bst = xgb.train(params, dm, num_boost_round=10)\n", 801 | " scores = bst.get_fscore() # 得到特征的重要性\n", 802 | " print type(scores)\n", 803 | " for feature in list(sorted(k for k in scores)):\n", 804 | " print feature\n", 805 | " dummy = np.random.randn(5, 5)\n", 806 | " dm = xgb.DMatrix(dummy, feature_names=features)\n", 807 | " bst.predict(dm)\n", 808 | " \n", 809 | " \n", 810 | " " 811 | ] 812 | }, 813 | { 814 | "cell_type": "code", 815 | "execution_count": null, 816 | "metadata": { 817 | "collapsed": true 818 | }, 819 | "outputs": [], 820 | "source": [] 821 | }, 822 | { 823 | "cell_type": "markdown", 824 | "metadata": {}, 825 | "source": [ 826 | "### 特征重要性" 827 | ] 828 | }, 829 | { 830 | "cell_type": "code", 831 | "execution_count": 147, 832 | "metadata": { 833 | "collapsed": true 834 | }, 835 | "outputs": [], 836 | "source": [ 837 | "data = np.random.randn(100, 5)\n", 838 | "target = np.array([0, 1]*50)" 839 | ] 840 | }, 841 | { 842 | "cell_type": "code", 843 | "execution_count": 148, 844 | "metadata": { 845 | "collapsed": true 846 | }, 847 | "outputs": [], 848 | "source": [ 849 | "features = ['Feature1', 'Feature2', 'Feature3', 'Feature4', 'Feature5']" 850 | ] 851 | }, 852 | { 853 | "cell_type": "code", 854 | "execution_count": 149, 855 | "metadata": { 856 | "collapsed": true 857 | }, 858 | "outputs": [], 859 | "source": [ 860 | "dm = xgb.DMatrix(data, label=target,\n", 861 | " feature_names=features)" 862 | ] 863 | }, 864 | { 865 | "cell_type": "code", 866 | "execution_count": 150, 867 | "metadata": { 868 | "collapsed": true 869 | }, 870 | "outputs": [], 871 | "source": [ 872 | "params = {'objective': 'multi:softprob',\n", 873 | " 'eval_metric': 'mlogloss',\n", 874 | " 'eta': 0.3,\n", 875 | " 'num_class': 2}" 876 | ] 877 | }, 878 | { 879 | "cell_type": "code", 880 | "execution_count": 151, 881 | "metadata": { 882 | "collapsed": true 883 | }, 884 | "outputs": [], 885 | "source": [ 886 | "bst = xgb.train(params, dm, num_boost_round=20)" 887 | ] 888 | }, 889 | { 890 | "cell_type": "code", 891 | "execution_count": null, 892 | "metadata": { 893 | "collapsed": true 894 | }, 895 | "outputs": [], 896 | "source": [] 897 | }, 898 | { 899 | "cell_type": "code", 900 | "execution_count": 152, 901 | "metadata": { 902 | "collapsed": true 903 | }, 904 | "outputs": [], 905 | "source": [ 906 | "scores1 = bst.get_score()" 907 | ] 908 | }, 909 | { 910 | "cell_type": "code", 911 | "execution_count": 153, 912 | "metadata": { 913 | "collapsed": true 914 | }, 915 | "outputs": [], 916 | "source": [ 917 | "scores2 = bst.get_score(importance_type='weight')" 918 | ] 919 | }, 920 | { 921 | "cell_type": "code", 922 | "execution_count": 154, 923 | "metadata": { 924 | "collapsed": true 925 | }, 926 | "outputs": [], 927 | "source": [ 928 | "scores3 = bst.get_score(importance_type='cover')" 929 | ] 930 | }, 931 | { 932 | "cell_type": "code", 933 | "execution_count": 155, 934 | "metadata": { 935 | "collapsed": true 936 | }, 937 | "outputs": [], 938 | "source": [ 939 | "scores4 = bst.get_score(importance_type='gain')" 940 | ] 941 | }, 942 | { 943 | "cell_type": "code", 944 | "execution_count": 156, 945 | "metadata": { 946 | "collapsed": true 947 | }, 948 | "outputs": [], 949 | "source": [ 950 | "fscores = bst.get_fscore()" 951 | ] 952 | }, 953 | { 954 | "cell_type": "code", 955 | "execution_count": 157, 956 | "metadata": { 957 | "collapsed": true 958 | }, 959 | "outputs": [], 960 | "source": [ 961 | "assert scores1 == fscores" 962 | ] 963 | }, 964 | { 965 | "cell_type": "code", 966 | "execution_count": null, 967 | "metadata": { 968 | "collapsed": true 969 | }, 970 | "outputs": [], 971 | "source": [] 972 | }, 973 | { 974 | "cell_type": "markdown", 975 | "metadata": {}, 976 | "source": [ 977 | "## 交叉验证" 978 | ] 979 | }, 980 | { 981 | "cell_type": "code", 982 | "execution_count": 158, 983 | "metadata": { 984 | "collapsed": true 985 | }, 986 | "outputs": [], 987 | "source": [ 988 | "dm = xgb.DMatrix('agaricus.txt.train')" 989 | ] 990 | }, 991 | { 992 | "cell_type": "code", 993 | "execution_count": 159, 994 | "metadata": { 995 | "collapsed": true 996 | }, 997 | "outputs": [], 998 | "source": [ 999 | "params = {'max_depth': 2, 'eta': 1, 'silent': 1, 'objective': 'binary:logistic'}" 1000 | ] 1001 | }, 1002 | { 1003 | "cell_type": "code", 1004 | "execution_count": 169, 1005 | "metadata": { 1006 | "collapsed": true 1007 | }, 1008 | "outputs": [], 1009 | "source": [ 1010 | "# 只需要向cv方法提供训练集即可,内部会自动划分为训练集和验证集, 返回 dict\n", 1011 | "cv = xgb.cv(params, dm, num_boost_round=10, nfold=10, as_pandas=False)" 1012 | ] 1013 | }, 1014 | { 1015 | "cell_type": "code", 1016 | "execution_count": 170, 1017 | "metadata": { 1018 | "collapsed": false 1019 | }, 1020 | "outputs": [ 1021 | { 1022 | "data": { 1023 | "text/plain": [ 1024 | "dict" 1025 | ] 1026 | }, 1027 | "execution_count": 170, 1028 | "metadata": {}, 1029 | "output_type": "execute_result" 1030 | } 1031 | ], 1032 | "source": [ 1033 | "type(cv)" 1034 | ] 1035 | }, 1036 | { 1037 | "cell_type": "code", 1038 | "execution_count": 171, 1039 | "metadata": { 1040 | "collapsed": false 1041 | }, 1042 | "outputs": [ 1043 | { 1044 | "data": { 1045 | "text/plain": [ 1046 | "4" 1047 | ] 1048 | }, 1049 | "execution_count": 171, 1050 | "metadata": {}, 1051 | "output_type": "execute_result" 1052 | } 1053 | ], 1054 | "source": [ 1055 | "len(cv)" 1056 | ] 1057 | }, 1058 | { 1059 | "cell_type": "code", 1060 | "execution_count": 172, 1061 | "metadata": { 1062 | "collapsed": false 1063 | }, 1064 | "outputs": [ 1065 | { 1066 | "name": "stdout", 1067 | "output_type": "stream", 1068 | "text": [ 1069 | "train-error-std\n", 1070 | "test-error-mean\n", 1071 | "test-error-std\n", 1072 | "train-error-mean\n" 1073 | ] 1074 | } 1075 | ], 1076 | "source": [ 1077 | "for key, value in cv.iteritems():\n", 1078 | " print key" 1079 | ] 1080 | }, 1081 | { 1082 | "cell_type": "code", 1083 | "execution_count": null, 1084 | "metadata": { 1085 | "collapsed": false 1086 | }, 1087 | "outputs": [], 1088 | "source": [] 1089 | }, 1090 | { 1091 | "cell_type": "code", 1092 | "execution_count": null, 1093 | "metadata": { 1094 | "collapsed": true 1095 | }, 1096 | "outputs": [], 1097 | "source": [] 1098 | }, 1099 | { 1100 | "cell_type": "code", 1101 | "execution_count": null, 1102 | "metadata": { 1103 | "collapsed": true 1104 | }, 1105 | "outputs": [], 1106 | "source": [] 1107 | } 1108 | ], 1109 | "metadata": { 1110 | "kernelspec": { 1111 | "display_name": "Python 2", 1112 | "language": "python", 1113 | "name": "python2" 1114 | }, 1115 | "language_info": { 1116 | "codemirror_mode": { 1117 | "name": "ipython", 1118 | "version": 2 1119 | }, 1120 | "file_extension": ".py", 1121 | "mimetype": "text/x-python", 1122 | "name": "python", 1123 | "nbconvert_exporter": "python", 1124 | "pygments_lexer": "ipython2", 1125 | "version": "2.7.11" 1126 | } 1127 | }, 1128 | "nbformat": 4, 1129 | "nbformat_minor": 0 1130 | } 1131 | -------------------------------------------------------------------------------- /实践/xgboost调包/xgboost调包之 模型篇.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "%matplotlib inline\n", 12 | "import numpy as np\n", 13 | "import pandas as pd\n", 14 | "import xgboost as xgb" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 2, 20 | "metadata": { 21 | "collapsed": true 22 | }, 23 | "outputs": [], 24 | "source": [ 25 | "rng = np.random.RandomState(1994)" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 4, 31 | "metadata": { 32 | "collapsed": false 33 | }, 34 | "outputs": [], 35 | "source": [ 36 | "dtrain = xgb.DMatrix('agaricus.txt.train')\n", 37 | "dtest = xgb.DMatrix('agaricus.txt.test')" 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "metadata": {}, 43 | "source": [ 44 | "### 使用 GBLinear作为booster" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 11, 50 | "metadata": { 51 | "collapsed": true 52 | }, 53 | "outputs": [], 54 | "source": [ 55 | "params = {'silent': 1, 'objective': 'binary:logistic',\n", 56 | " 'booster': 'gblinear', 'alpha': 0.0001, 'lambda': 1}" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 12, 62 | "metadata": { 63 | "collapsed": true 64 | }, 65 | "outputs": [], 66 | "source": [ 67 | "watchlist = [(dtest, 'eval'), (dtrain, 'train')]" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": 13, 73 | "metadata": { 74 | "collapsed": true 75 | }, 76 | "outputs": [], 77 | "source": [ 78 | "num_boost_round = 4" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 14, 84 | "metadata": { 85 | "collapsed": false 86 | }, 87 | "outputs": [ 88 | { 89 | "name": "stdout", 90 | "output_type": "stream", 91 | "text": [ 92 | "[0]\teval-error:0.019863\ttrain-error:0.015047\n", 93 | "[1]\teval-error:0.005587\ttrain-error:0.00476\n", 94 | "[2]\teval-error:0\ttrain-error:0.001842\n", 95 | "[3]\teval-error:0\ttrain-error:0.000614\n" 96 | ] 97 | } 98 | ], 99 | "source": [ 100 | "bst = xgb.train(params, dtrain, num_boost_round, watchlist)" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 15, 106 | "metadata": { 107 | "collapsed": true 108 | }, 109 | "outputs": [], 110 | "source": [ 111 | "preds = bst.predict(dtest)" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": null, 117 | "metadata": { 118 | "collapsed": true 119 | }, 120 | "outputs": [], 121 | "source": [] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "### 使用 DART(Dropout + Gradient Boosting) 作为booster" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 16, 133 | "metadata": { 134 | "collapsed": true 135 | }, 136 | "outputs": [], 137 | "source": [ 138 | "params = {'max_depth': 5, 'objective': 'binary:logistic', 'booster': 'dart', 'silent': 1}" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": 17, 144 | "metadata": { 145 | "collapsed": true 146 | }, 147 | "outputs": [], 148 | "source": [ 149 | "watchlist = [(dtest, 'eval'), (dtrain, 'train')]" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": 18, 155 | "metadata": { 156 | "collapsed": true 157 | }, 158 | "outputs": [], 159 | "source": [ 160 | "num_boost_round = 5" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": 19, 166 | "metadata": { 167 | "collapsed": false 168 | }, 169 | "outputs": [ 170 | { 171 | "name": "stdout", 172 | "output_type": "stream", 173 | "text": [ 174 | "[0]\teval-error:0\ttrain-error:0.000614\n", 175 | "[1]\teval-error:0\ttrain-error:0.001228\n", 176 | "[2]\teval-error:0\ttrain-error:0.000614\n", 177 | "[3]\teval-error:0\ttrain-error:0.000614\n", 178 | "[4]\teval-error:0\ttrain-error:0\n" 179 | ] 180 | } 181 | ], 182 | "source": [ 183 | "bst = xgb.train(params, dtrain, num_boost_round, watchlist)" 184 | ] 185 | }, 186 | { 187 | "cell_type": "code", 188 | "execution_count": 20, 189 | "metadata": { 190 | "collapsed": true 191 | }, 192 | "outputs": [], 193 | "source": [ 194 | "preds = bst.predict(dtest, ntree_limit=num_boost_round)" 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": 21, 200 | "metadata": { 201 | "collapsed": true 202 | }, 203 | "outputs": [], 204 | "source": [ 205 | "labels = dtest.get_label()" 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": 22, 211 | "metadata": { 212 | "collapsed": false 213 | }, 214 | "outputs": [ 215 | { 216 | "name": "stdout", 217 | "output_type": "stream", 218 | "text": [ 219 | "0.0\n" 220 | ] 221 | } 222 | ], 223 | "source": [ 224 | "print sum(1 for i in range(len(preds)) if int(preds[i] > 0.5) != labels[i]) / float(len(preds))" 225 | ] 226 | }, 227 | { 228 | "cell_type": "code", 229 | "execution_count": 23, 230 | "metadata": { 231 | "collapsed": true 232 | }, 233 | "outputs": [], 234 | "source": [ 235 | "params['learning_rate'] = 0.1\n", 236 | "params['rate_drop'] = 0.1" 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": 24, 242 | "metadata": { 243 | "collapsed": true 244 | }, 245 | "outputs": [], 246 | "source": [ 247 | "preds_list = []" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": 25, 253 | "metadata": { 254 | "collapsed": false 255 | }, 256 | "outputs": [ 257 | { 258 | "name": "stdout", 259 | "output_type": "stream", 260 | "text": [ 261 | "[0]\teval-error:0\ttrain-error:0.000614\n", 262 | "[1]\teval-error:0\ttrain-error:0.001228\n", 263 | "[2]\teval-error:0\ttrain-error:0.001228\n", 264 | "[3]\teval-error:0\ttrain-error:0.001228\n", 265 | "[4]\teval-error:0\ttrain-error:0.001228\n", 266 | "[0]\teval-error:0\ttrain-error:0.000614\n", 267 | "[1]\teval-error:0\ttrain-error:0.001228\n", 268 | "[2]\teval-error:0\ttrain-error:0.001228\n", 269 | "[3]\teval-error:0\ttrain-error:0.001228\n", 270 | "[4]\teval-error:0\ttrain-error:0.001228\n", 271 | "[0]\teval-error:0\ttrain-error:0.000614\n", 272 | "[1]\teval-error:0\ttrain-error:0.001228\n", 273 | "[2]\teval-error:0\ttrain-error:0.001228\n", 274 | "[3]\teval-error:0\ttrain-error:0.001228\n", 275 | "[4]\teval-error:0\ttrain-error:0.001228\n", 276 | "[0]\teval-error:0\ttrain-error:0.000614\n", 277 | "[1]\teval-error:0\ttrain-error:0.001228\n", 278 | "[2]\teval-error:0\ttrain-error:0.001228\n", 279 | "[3]\teval-error:0\ttrain-error:0.001228\n", 280 | "[4]\teval-error:0\ttrain-error:0.001228\n" 281 | ] 282 | } 283 | ], 284 | "source": [ 285 | "for p in [[p0, p1] for p0 in ['uniform', 'weighted'] for p1 in ['tree', 'forest']]:\n", 286 | " params['sample_type'] = p[0]\n", 287 | " params['normalize_type'] = p[1]\n", 288 | " bst = xgb.train(params, dtrain, num_boost_round, watchlist)\n", 289 | " preds = bst.predict(dtest, ntree_limit=num_boost_round)\n", 290 | " err = sum(1 for i in range(len(preds)) if int(preds[i] > 0.5) != labels[i]) / float(len(preds))\n", 291 | " preds_list.append(preds)" 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": null, 297 | "metadata": { 298 | "collapsed": false 299 | }, 300 | "outputs": [], 301 | "source": [] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": null, 306 | "metadata": { 307 | "collapsed": true 308 | }, 309 | "outputs": [], 310 | "source": [] 311 | }, 312 | { 313 | "cell_type": "code", 314 | "execution_count": null, 315 | "metadata": { 316 | "collapsed": true 317 | }, 318 | "outputs": [], 319 | "source": [] 320 | }, 321 | { 322 | "cell_type": "markdown", 323 | "metadata": {}, 324 | "source": [ 325 | "## 参数eta(shrinkage)" 326 | ] 327 | }, 328 | { 329 | "cell_type": "code", 330 | "execution_count": 28, 331 | "metadata": { 332 | "collapsed": true 333 | }, 334 | "outputs": [], 335 | "source": [ 336 | "watchlist = [(dtest, 'eval'), (dtrain, 'train')]" 337 | ] 338 | }, 339 | { 340 | "cell_type": "code", 341 | "execution_count": 29, 342 | "metadata": { 343 | "collapsed": true 344 | }, 345 | "outputs": [], 346 | "source": [ 347 | "num_boost_round = 5" 348 | ] 349 | }, 350 | { 351 | "cell_type": "code", 352 | "execution_count": null, 353 | "metadata": { 354 | "collapsed": true 355 | }, 356 | "outputs": [], 357 | "source": [] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": 30, 362 | "metadata": { 363 | "collapsed": true 364 | }, 365 | "outputs": [], 366 | "source": [ 367 | "params = {'max_detph': 2, 'eta': 0, 'silent': 1, 'objective': 'binary:logistic'}" 368 | ] 369 | }, 370 | { 371 | "cell_type": "code", 372 | "execution_count": 31, 373 | "metadata": { 374 | "collapsed": true 375 | }, 376 | "outputs": [], 377 | "source": [ 378 | "evals_result ={}" 379 | ] 380 | }, 381 | { 382 | "cell_type": "code", 383 | "execution_count": 37, 384 | "metadata": { 385 | "collapsed": false 386 | }, 387 | "outputs": [ 388 | { 389 | "name": "stdout", 390 | "output_type": "stream", 391 | "text": [ 392 | "[0]\teval-error:0\ttrain-error:0.000614\n", 393 | "[1]\teval-error:0\ttrain-error:0.000614\n", 394 | "[2]\teval-error:0\ttrain-error:0\n", 395 | "[3]\teval-error:0\ttrain-error:0\n", 396 | "[4]\teval-error:0\ttrain-error:0\n" 397 | ] 398 | } 399 | ], 400 | "source": [ 401 | "# 将eta设置为0,看看学习率是否起作用, 为每一次迭代过程设置学习率\n", 402 | "bst = xgb.train(params, dtrain, num_boost_round, watchlist, learning_rates=[0.8, 0.7, 0.6, 0.5, 0.4],\n", 403 | " evals_result=evals_result) #evals_result保存每一次迭代的计算结果" 404 | ] 405 | }, 406 | { 407 | "cell_type": "code", 408 | "execution_count": 38, 409 | "metadata": { 410 | "collapsed": true 411 | }, 412 | "outputs": [], 413 | "source": [ 414 | "eval_erros = list(map(float, evals_result['eval']['error']))" 415 | ] 416 | }, 417 | { 418 | "cell_type": "code", 419 | "execution_count": 39, 420 | "metadata": { 421 | "collapsed": false 422 | }, 423 | "outputs": [ 424 | { 425 | "name": "stdout", 426 | "output_type": "stream", 427 | "text": [ 428 | "0.0\n", 429 | "0.0\n", 430 | "0.0\n", 431 | "0.0\n", 432 | "0.0\n" 433 | ] 434 | } 435 | ], 436 | "source": [ 437 | "for err in eval_erros:\n", 438 | " print err" 439 | ] 440 | }, 441 | { 442 | "cell_type": "code", 443 | "execution_count": 40, 444 | "metadata": { 445 | "collapsed": true 446 | }, 447 | "outputs": [], 448 | "source": [ 449 | "# 将learning_rate设置为0,看看learning_rate是否起作用\n", 450 | "params = {'max_depth': 2, 'learning_rate': 0, 'silent': 1, 'objective': 'binary:logistic'}" 451 | ] 452 | }, 453 | { 454 | "cell_type": "code", 455 | "execution_count": 41, 456 | "metadata": { 457 | "collapsed": true 458 | }, 459 | "outputs": [], 460 | "source": [ 461 | "evals_result = {}" 462 | ] 463 | }, 464 | { 465 | "cell_type": "code", 466 | "execution_count": 42, 467 | "metadata": { 468 | "collapsed": false 469 | }, 470 | "outputs": [ 471 | { 472 | "name": "stdout", 473 | "output_type": "stream", 474 | "text": [ 475 | "[0]\teval-error:0.042831\ttrain-error:0.046522\n", 476 | "[1]\teval-error:0.036623\ttrain-error:0.037617\n", 477 | "[2]\teval-error:0.015518\ttrain-error:0.013358\n", 478 | "[3]\teval-error:0.027312\ttrain-error:0.021495\n", 479 | "[4]\teval-error:0.021105\ttrain-error:0.015661\n" 480 | ] 481 | } 482 | ], 483 | "source": [ 484 | "bst = xgb.train(params, dtrain, num_boost_round, watchlist, learning_rates=[0.8, 0.7, 0.6, 0.5, 0.4],\n", 485 | " evals_result=evals_result)" 486 | ] 487 | }, 488 | { 489 | "cell_type": "code", 490 | "execution_count": 43, 491 | "metadata": { 492 | "collapsed": true 493 | }, 494 | "outputs": [], 495 | "source": [ 496 | "eval_erros = list(map(float, evals_result['eval']['error']))" 497 | ] 498 | }, 499 | { 500 | "cell_type": "code", 501 | "execution_count": 45, 502 | "metadata": { 503 | "collapsed": false 504 | }, 505 | "outputs": [ 506 | { 507 | "name": "stdout", 508 | "output_type": "stream", 509 | "text": [ 510 | "0.042831\n", 511 | "0.036623\n", 512 | "0.015518\n", 513 | "0.027312\n", 514 | "0.021105\n" 515 | ] 516 | } 517 | ], 518 | "source": [ 519 | "# 错误在减小,起作用了\n", 520 | "for error in eval_erros:\n", 521 | " print error" 522 | ] 523 | }, 524 | { 525 | "cell_type": "code", 526 | "execution_count": null, 527 | "metadata": { 528 | "collapsed": true 529 | }, 530 | "outputs": [], 531 | "source": [] 532 | }, 533 | { 534 | "cell_type": "code", 535 | "execution_count": 46, 536 | "metadata": { 537 | "collapsed": true 538 | }, 539 | "outputs": [], 540 | "source": [ 541 | "params = {'max_depth': 2, 'silent': 1, 'objective': 'binary:logistic'}" 542 | ] 543 | }, 544 | { 545 | "cell_type": "code", 546 | "execution_count": 47, 547 | "metadata": { 548 | "collapsed": true 549 | }, 550 | "outputs": [], 551 | "source": [ 552 | "evals_result = {}" 553 | ] 554 | }, 555 | { 556 | "cell_type": "code", 557 | "execution_count": 48, 558 | "metadata": { 559 | "collapsed": false 560 | }, 561 | "outputs": [ 562 | { 563 | "name": "stdout", 564 | "output_type": "stream", 565 | "text": [ 566 | "[0]\teval-error:0.481688\ttrain-error:0.482113\n", 567 | "[1]\teval-error:0.481688\ttrain-error:0.482113\n", 568 | "[2]\teval-error:0.481688\ttrain-error:0.482113\n", 569 | "[3]\teval-error:0.481688\ttrain-error:0.482113\n", 570 | "[4]\teval-error:0.481688\ttrain-error:0.482113\n" 571 | ] 572 | } 573 | ], 574 | "source": [ 575 | "bst = xgb.train(params, dtrain, num_boost_round, watchlist, learning_rates=[0,0,0,0,0],\n", 576 | " evals_result=evals_result)" 577 | ] 578 | }, 579 | { 580 | "cell_type": "code", 581 | "execution_count": 49, 582 | "metadata": { 583 | "collapsed": true 584 | }, 585 | "outputs": [], 586 | "source": [ 587 | "eval_errors = list(map(float, evals_result['eval']['error']))" 588 | ] 589 | }, 590 | { 591 | "cell_type": "code", 592 | "execution_count": 51, 593 | "metadata": { 594 | "collapsed": false 595 | }, 596 | "outputs": [ 597 | { 598 | "name": "stdout", 599 | "output_type": "stream", 600 | "text": [ 601 | "0.481688\n", 602 | "0.481688\n", 603 | "0.481688\n", 604 | "0.481688\n", 605 | "0.481688\n" 606 | ] 607 | } 608 | ], 609 | "source": [ 610 | "# 学习率为0, 模型不照\n", 611 | "for error in eval_errors:\n", 612 | " print error" 613 | ] 614 | }, 615 | { 616 | "cell_type": "markdown", 617 | "metadata": {}, 618 | "source": [ 619 | "#### 自定义衰减函数作为learning_rates" 620 | ] 621 | }, 622 | { 623 | "cell_type": "code", 624 | "execution_count": 53, 625 | "metadata": { 626 | "collapsed": true 627 | }, 628 | "outputs": [], 629 | "source": [ 630 | "# 使用自定义衰减函数作为learning_rates\n", 631 | "def eta_decay(ithround, num_boost_round):\n", 632 | " return num_boost_round / (ithround + 1)" 633 | ] 634 | }, 635 | { 636 | "cell_type": "code", 637 | "execution_count": 55, 638 | "metadata": { 639 | "collapsed": false 640 | }, 641 | "outputs": [ 642 | { 643 | "name": "stdout", 644 | "output_type": "stream", 645 | "text": [ 646 | "[0]\teval-error:0.042831\ttrain-error:0.046522\n", 647 | "[1]\teval-error:0.042831\ttrain-error:0.046522\n", 648 | "[2]\teval-error:0.042831\ttrain-error:0.046522\n", 649 | "[3]\teval-error:0.042831\ttrain-error:0.046522\n", 650 | "[4]\teval-error:0.042831\ttrain-error:0.046522\n" 651 | ] 652 | } 653 | ], 654 | "source": [ 655 | "bst = xgb.train(params, dtrain, num_boost_round, watchlist, learning_rates=eta_decay)" 656 | ] 657 | }, 658 | { 659 | "cell_type": "code", 660 | "execution_count": null, 661 | "metadata": { 662 | "collapsed": true 663 | }, 664 | "outputs": [], 665 | "source": [] 666 | }, 667 | { 668 | "cell_type": "code", 669 | "execution_count": null, 670 | "metadata": { 671 | "collapsed": true 672 | }, 673 | "outputs": [], 674 | "source": [] 675 | }, 676 | { 677 | "cell_type": "code", 678 | "execution_count": null, 679 | "metadata": { 680 | "collapsed": true 681 | }, 682 | "outputs": [], 683 | "source": [] 684 | }, 685 | { 686 | "cell_type": "markdown", 687 | "metadata": {}, 688 | "source": [ 689 | "## 自定义损失函数" 690 | ] 691 | }, 692 | { 693 | "cell_type": "code", 694 | "execution_count": 56, 695 | "metadata": { 696 | "collapsed": true 697 | }, 698 | "outputs": [], 699 | "source": [ 700 | "params = {'max_depth': 2, 'eta': 1, 'silent': 1}" 701 | ] 702 | }, 703 | { 704 | "cell_type": "code", 705 | "execution_count": 57, 706 | "metadata": { 707 | "collapsed": true 708 | }, 709 | "outputs": [], 710 | "source": [ 711 | "watchlist = [(dtest, 'eval'), (dtrain, 'train')]" 712 | ] 713 | }, 714 | { 715 | "cell_type": "code", 716 | "execution_count": 58, 717 | "metadata": { 718 | "collapsed": true 719 | }, 720 | "outputs": [], 721 | "source": [ 722 | "num_boost_round = 2" 723 | ] 724 | }, 725 | { 726 | "cell_type": "code", 727 | "execution_count": 71, 728 | "metadata": { 729 | "collapsed": true 730 | }, 731 | "outputs": [], 732 | "source": [ 733 | "# 返回梯度和Hess矩阵\n", 734 | "def logregobj(preds, dtrain):\n", 735 | " labels = dtrain.get_label()\n", 736 | " preds = 1.0 / (1.0 + np.exp(-preds))\n", 737 | " grad = preds - labels\n", 738 | " hess = preds * (1.0 - preds)\n", 739 | " return grad, hess" 740 | ] 741 | }, 742 | { 743 | "cell_type": "code", 744 | "execution_count": 72, 745 | "metadata": { 746 | "collapsed": true 747 | }, 748 | "outputs": [], 749 | "source": [ 750 | "def evalerror(preds, dtrain):\n", 751 | " labels = dtrain.get_label()\n", 752 | " return 'error', float(sum(labels != (preds > 0.0))) / len(labels)" 753 | ] 754 | }, 755 | { 756 | "cell_type": "code", 757 | "execution_count": 73, 758 | "metadata": { 759 | "collapsed": false 760 | }, 761 | "outputs": [ 762 | { 763 | "name": "stdout", 764 | "output_type": "stream", 765 | "text": [ 766 | "[0]\teval-error:0.042831\ttrain-error:0.046522\n", 767 | "[1]\teval-error:0.021726\ttrain-error:0.022263\n" 768 | ] 769 | } 770 | ], 771 | "source": [ 772 | "bst = xgb.train(params, dtrain, num_boost_round, watchlist, logregobj, evalerror)" 773 | ] 774 | }, 775 | { 776 | "cell_type": "code", 777 | "execution_count": 74, 778 | "metadata": { 779 | "collapsed": true 780 | }, 781 | "outputs": [], 782 | "source": [ 783 | "preds = bst.predict(dtest)" 784 | ] 785 | }, 786 | { 787 | "cell_type": "code", 788 | "execution_count": 75, 789 | "metadata": { 790 | "collapsed": true 791 | }, 792 | "outputs": [], 793 | "source": [ 794 | "labels = dtest.get_label()" 795 | ] 796 | }, 797 | { 798 | "cell_type": "code", 799 | "execution_count": null, 800 | "metadata": { 801 | "collapsed": true 802 | }, 803 | "outputs": [], 804 | "source": [] 805 | }, 806 | { 807 | "cell_type": "code", 808 | "execution_count": 76, 809 | "metadata": { 810 | "collapsed": false 811 | }, 812 | "outputs": [ 813 | { 814 | "data": { 815 | "text/html": [ 816 | "
\n", 817 | "\n", 818 | " \n", 819 | " \n", 820 | " \n", 821 | " \n", 822 | " \n", 823 | " \n", 824 | " \n", 825 | " \n", 826 | " \n", 827 | " \n", 828 | " \n", 829 | " \n", 830 | " \n", 831 | " \n", 832 | " \n", 833 | " \n", 834 | " \n", 835 | " \n", 836 | " \n", 837 | " \n", 838 | " \n", 839 | " \n", 840 | " \n", 841 | " \n", 842 | " \n", 843 | "
test-error-meantest-error-stdtrain-error-meantrain-error-std
00.0557600.0158270.0506910.009194
10.0211980.0038130.0213130.002075
\n", 844 | "
" 845 | ], 846 | "text/plain": [ 847 | " test-error-mean test-error-std train-error-mean train-error-std\n", 848 | "0 0.055760 0.015827 0.050691 0.009194\n", 849 | "1 0.021198 0.003813 0.021313 0.002075" 850 | ] 851 | }, 852 | "execution_count": 76, 853 | "metadata": {}, 854 | "output_type": "execute_result" 855 | } 856 | ], 857 | "source": [ 858 | "# 结合交叉验证\n", 859 | "xgb.cv(params, dtrain, num_boost_round, nfold=5, seed=0,\n", 860 | " obj=logregobj, feval=evalerror)" 861 | ] 862 | }, 863 | { 864 | "cell_type": "code", 865 | "execution_count": 77, 866 | "metadata": { 867 | "collapsed": true 868 | }, 869 | "outputs": [], 870 | "source": [ 871 | "# 测试train()中maximize 参数" 872 | ] 873 | }, 874 | { 875 | "cell_type": "code", 876 | "execution_count": 78, 877 | "metadata": { 878 | "collapsed": true 879 | }, 880 | "outputs": [], 881 | "source": [ 882 | "def neg_evalerror(preds, dtrain):\n", 883 | " labels = dtrain.get_label()\n", 884 | " return 'error', float(sum(labels == (preds > 0.0))) / len(labels)" 885 | ] 886 | }, 887 | { 888 | "cell_type": "code", 889 | "execution_count": 79, 890 | "metadata": { 891 | "collapsed": false 892 | }, 893 | "outputs": [ 894 | { 895 | "name": "stdout", 896 | "output_type": "stream", 897 | "text": [ 898 | "[0]\teval-error:0.957169\ttrain-error:0.953478\n", 899 | "[1]\teval-error:0.978274\ttrain-error:0.977737\n" 900 | ] 901 | } 902 | ], 903 | "source": [ 904 | "bst2 = xgb.train(params, dtrain, num_boost_round, watchlist, logregobj, neg_evalerror, maximize=True)" 905 | ] 906 | }, 907 | { 908 | "cell_type": "code", 909 | "execution_count": 80, 910 | "metadata": { 911 | "collapsed": true 912 | }, 913 | "outputs": [], 914 | "source": [ 915 | "preds = bst2.predict(dtest)" 916 | ] 917 | }, 918 | { 919 | "cell_type": "code", 920 | "execution_count": null, 921 | "metadata": { 922 | "collapsed": true 923 | }, 924 | "outputs": [], 925 | "source": [] 926 | }, 927 | { 928 | "cell_type": "markdown", 929 | "metadata": {}, 930 | "source": [ 931 | "### 同时使用多个评价指标" 932 | ] 933 | }, 934 | { 935 | "cell_type": "code", 936 | "execution_count": 87, 937 | "metadata": { 938 | "collapsed": true 939 | }, 940 | "outputs": [], 941 | "source": [ 942 | "watchlist = [(dtest, 'eval'), (dtrain, 'train')]" 943 | ] 944 | }, 945 | { 946 | "cell_type": "code", 947 | "execution_count": 88, 948 | "metadata": { 949 | "collapsed": true 950 | }, 951 | "outputs": [], 952 | "source": [ 953 | "params = {'max-depth': 2, 'eta': 0.2, 'silent': 1, 'objective': 'binary:logistic'}" 954 | ] 955 | }, 956 | { 957 | "cell_type": "code", 958 | "execution_count": 89, 959 | "metadata": { 960 | "collapsed": true 961 | }, 962 | "outputs": [], 963 | "source": [ 964 | "params['eval_metric'] = ['auc', 'logloss', 'error']" 965 | ] 966 | }, 967 | { 968 | "cell_type": "code", 969 | "execution_count": 90, 970 | "metadata": { 971 | "collapsed": true 972 | }, 973 | "outputs": [], 974 | "source": [ 975 | "evals_result = {}" 976 | ] 977 | }, 978 | { 979 | "cell_type": "code", 980 | "execution_count": 91, 981 | "metadata": { 982 | "collapsed": false 983 | }, 984 | "outputs": [ 985 | { 986 | "name": "stdout", 987 | "output_type": "stream", 988 | "text": [ 989 | "[0]\teval-auc:1\teval-logloss:0.514188\teval-error:0\ttrain-auc:0.999238\ttrain-logloss:0.514426\ttrain-error:0.000614\n", 990 | "[1]\teval-auc:1\teval-logloss:0.393842\teval-error:0\ttrain-auc:0.999238\ttrain-logloss:0.39443\ttrain-error:0.001228\n" 991 | ] 992 | } 993 | ], 994 | "source": [ 995 | "bst = xgb.train(params, dtrain, num_boost_round, watchlist, evals_result=evals_result)" 996 | ] 997 | }, 998 | { 999 | "cell_type": "code", 1000 | "execution_count": 92, 1001 | "metadata": { 1002 | "collapsed": false 1003 | }, 1004 | "outputs": [ 1005 | { 1006 | "data": { 1007 | "text/plain": [ 1008 | "3" 1009 | ] 1010 | }, 1011 | "execution_count": 92, 1012 | "metadata": {}, 1013 | "output_type": "execute_result" 1014 | } 1015 | ], 1016 | "source": [ 1017 | "len(evals_result['eval'])" 1018 | ] 1019 | }, 1020 | { 1021 | "cell_type": "code", 1022 | "execution_count": null, 1023 | "metadata": { 1024 | "collapsed": true 1025 | }, 1026 | "outputs": [], 1027 | "source": [] 1028 | }, 1029 | { 1030 | "cell_type": "code", 1031 | "execution_count": null, 1032 | "metadata": { 1033 | "collapsed": true 1034 | }, 1035 | "outputs": [], 1036 | "source": [] 1037 | }, 1038 | { 1039 | "cell_type": "code", 1040 | "execution_count": null, 1041 | "metadata": { 1042 | "collapsed": true 1043 | }, 1044 | "outputs": [], 1045 | "source": [] 1046 | } 1047 | ], 1048 | "metadata": { 1049 | "kernelspec": { 1050 | "display_name": "Python 2", 1051 | "language": "python", 1052 | "name": "python2" 1053 | }, 1054 | "language_info": { 1055 | "codemirror_mode": { 1056 | "name": "ipython", 1057 | "version": 2 1058 | }, 1059 | "file_extension": ".py", 1060 | "mimetype": "text/x-python", 1061 | "name": "python", 1062 | "nbconvert_exporter": "python", 1063 | "pygments_lexer": "ipython2", 1064 | "version": "2.7.11" 1065 | } 1066 | }, 1067 | "nbformat": 4, 1068 | "nbformat_minor": 0 1069 | } 1070 | -------------------------------------------------------------------------------- /实践/xgboost调包/xgboost调包之 结合pandas.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import numpy as np\n", 12 | "import pandas as pd\n", 13 | "import xgboost as xgb" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 2, 19 | "metadata": { 20 | "collapsed": true 21 | }, 22 | "outputs": [], 23 | "source": [ 24 | "rng = np.random.RandomState(1994)" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 3, 30 | "metadata": { 31 | "collapsed": true 32 | }, 33 | "outputs": [], 34 | "source": [ 35 | "df = pd.DataFrame([[1, 2., True], [2, 3., False]], columns=list('abc'))" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 4, 41 | "metadata": { 42 | "collapsed": true 43 | }, 44 | "outputs": [], 45 | "source": [ 46 | "dm = xgb.DMatrix(df, label=pd.Series([1, 2]))" 47 | ] 48 | }, 49 | { 50 | "cell_type": "markdown", 51 | "metadata": {}, 52 | "source": [ 53 | "### 重写特征名和特征类型" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 5, 59 | "metadata": { 60 | "collapsed": true 61 | }, 62 | "outputs": [], 63 | "source": [ 64 | "dm = xgb.DMatrix(df, label=pd.Series([1, 2]),\n", 65 | " feature_names=list('xyz'), feature_types=list('qqq'))" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": 6, 71 | "metadata": { 72 | "collapsed": false 73 | }, 74 | "outputs": [ 75 | { 76 | "data": { 77 | "text/plain": [ 78 | "['x', 'y', 'z']" 79 | ] 80 | }, 81 | "execution_count": 6, 82 | "metadata": {}, 83 | "output_type": "execute_result" 84 | } 85 | ], 86 | "source": [ 87 | "dm.feature_names" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 7, 93 | "metadata": { 94 | "collapsed": false 95 | }, 96 | "outputs": [ 97 | { 98 | "data": { 99 | "text/plain": [ 100 | "2L" 101 | ] 102 | }, 103 | "execution_count": 7, 104 | "metadata": {}, 105 | "output_type": "execute_result" 106 | } 107 | ], 108 | "source": [ 109 | "dm.num_row()" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": 8, 115 | "metadata": { 116 | "collapsed": false 117 | }, 118 | "outputs": [ 119 | { 120 | "data": { 121 | "text/plain": [ 122 | "3L" 123 | ] 124 | }, 125 | "execution_count": 8, 126 | "metadata": {}, 127 | "output_type": "execute_result" 128 | } 129 | ], 130 | "source": [ 131 | "dm.num_col()" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": null, 137 | "metadata": { 138 | "collapsed": true 139 | }, 140 | "outputs": [], 141 | "source": [] 142 | } 143 | ], 144 | "metadata": { 145 | "kernelspec": { 146 | "display_name": "Python 2", 147 | "language": "python", 148 | "name": "python2" 149 | }, 150 | "language_info": { 151 | "codemirror_mode": { 152 | "name": "ipython", 153 | "version": 2 154 | }, 155 | "file_extension": ".py", 156 | "mimetype": "text/x-python", 157 | "name": "python", 158 | "nbconvert_exporter": "python", 159 | "pygments_lexer": "ipython2", 160 | "version": "2.7.11" 161 | } 162 | }, 163 | "nbformat": 4, 164 | "nbformat_minor": 0 165 | } 166 | -------------------------------------------------------------------------------- /实践/聚类/KMeans.py: -------------------------------------------------------------------------------- 1 | #-*- coding:utf-8 2 | """ 3 | k-means algorithm for cluster, 4 | 5 | """ 6 | 7 | import numpy as np 8 | from collections import defaultdict 9 | from random import uniform 10 | 11 | class KMeans(object): 12 | """ 13 | kmeans 聚类算法, 简单 14 | """ 15 | def __init__(self): 16 | pass 17 | 18 | def cluster(self, X, k): 19 | self.X = X 20 | self.k = k 21 | self.centers = defaultdict(list) 22 | self.assignment = np.zeros(self.X.shape[0]) #记录每个样本属于哪个中心点 23 | 24 | self._initialize() #初始化得到k个点 25 | self._assign() #将数据集中每个样本分配给最近的中心点 26 | self.next_centers = None 27 | while self.centers != self.next_centers: 28 | self.next_centers = self.centers 29 | self._getcenters() 30 | self._assign() 31 | return self.assignment 32 | 33 | def _initialize(self): 34 | """ 35 | 初始化,即初始化k个中心点和每个样本属于哪个中心点 36 | 这k个样本点是随机产生的 37 | """ 38 | feature_min_max = defaultdict(list) #保存每个特征值的最小值和最大值 39 | feature_dimensions = self.X.shape[1] 40 | get_min_max = lambda x, y: (x,y) if x self.X[j+1][i] else self.X[j][i], self.X[j+1][i] 45 | if tmp_min < i_min: 46 | i_min = tmp_min 47 | if tmp_max > i_max: 48 | i_max = tmp_max 49 | tmp_min, tmp_max = get_min_max(self.X[-1][i], self.X[-2][i])#self.X[-1][i], self.X[-2][i] if self.X[-2][i] > self.X[-1][i] else self.X[-2][i], self.X[-1][i] 50 | i_min = tmp_min if tmp_min < i_min else i_min 51 | i_max = tmp_max if tmp_max > i_max else i_max 52 | feature_min_max[i] = [i_min, i_max] 53 | for i in xrange(self.k): 54 | this_k = [] 55 | for j in xrange(feature_dimensions): 56 | value = uniform(feature_min_max[j][0], feature_min_max[j][1]) 57 | this_k.append(value) 58 | self.centers[i] = this_k 59 | 60 | def _distance(self, point1, point2): 61 | """ 62 | 计算点point1 和 point2 之间的欧氏距离 63 | """ 64 | dd = 0 65 | for i in xrange(len(point1)): 66 | dd += (point1[i] - point2[i]) ** 2 67 | return np.sqrt(dd) 68 | 69 | def _assign(self): 70 | 71 | feature_dimensions = self.X.shape[1] 72 | 73 | for i in xrange(self.X.shape[0]): 74 | min_distance = float("inf") 75 | current_assignment = self.k 76 | for j in xrange(self.k): 77 | tmp_d = self._distance(self.X[i], self.centers[j]) 78 | if tmp_d < min_distance: 79 | min_distance = tmp_d 80 | current_assignment = j 81 | self.assignment[i] = current_assignment 82 | 83 | def _getcenters(self): 84 | """ 85 | 计算每个中心点的平均值,作为新的中心点 86 | """ 87 | cluster_numbers = defaultdict(int) #记录每个分类的样本数目 88 | cluster_sum = np.zeros((self.k, len(self.X[0]))) #记录每个分类的所有样本相加之和 89 | for index, center in enumerate(self.assignment): 90 | cluster_numbers[center] = cluster_numbers.get(center, 0) + 1 91 | cluster_sum[center] += self.X[index] 92 | for i in xrange(self.k): 93 | self.centers[i] = list(cluster_sum[i]/float(cluster_numbers[i])) 94 | -------------------------------------------------------------------------------- /第一章/.ipynb_checkpoints/第一章 让计算机从数据中学习-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 让计算机从数据中学习" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "我们生活在一个数据爆炸的时代,数据本身是不值钱的,但是通过合理的运用机器学习算法,我们可以从杂乱无章的数据中挖掘有价值的信息,这也是机器学习令人着迷的原因。" 15 | ] 16 | } 17 | ], 18 | "metadata": { 19 | "kernelspec": { 20 | "display_name": "Python 2", 21 | "language": "python", 22 | "name": "python2" 23 | }, 24 | "language_info": { 25 | "codemirror_mode": { 26 | "name": "ipython", 27 | "version": 2 28 | }, 29 | "file_extension": ".py", 30 | "mimetype": "text/x-python", 31 | "name": "python", 32 | "nbconvert_exporter": "python", 33 | "pygments_lexer": "ipython2", 34 | "version": "2.7.12" 35 | } 36 | }, 37 | "nbformat": 4, 38 | "nbformat_minor": 2 39 | } 40 | -------------------------------------------------------------------------------- /第一章/第一章 让计算机从数据中学习.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 让计算机从数据中学习" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "我们生活在一个数据爆炸的时代,数据本身是不值钱的,但是通过合理的运用机器学习算法,我们可以从杂乱无章的数据中挖掘有价值的信息,这也是机器学习令人着迷的原因。" 15 | ] 16 | } 17 | ], 18 | "metadata": { 19 | "kernelspec": { 20 | "display_name": "Python 2", 21 | "language": "python", 22 | "name": "python2" 23 | }, 24 | "language_info": { 25 | "codemirror_mode": { 26 | "name": "ipython", 27 | "version": 2 28 | }, 29 | "file_extension": ".py", 30 | "mimetype": "text/x-python", 31 | "name": "python", 32 | "nbconvert_exporter": "python", 33 | "pygments_lexer": "ipython2", 34 | "version": "2.7.12" 35 | } 36 | }, 37 | "nbformat": 4, 38 | "nbformat_minor": 2 39 | } 40 | -------------------------------------------------------------------------------- /第七章/.ipynb_checkpoints/集成学习-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 集成学习\n", 8 | "\n", 9 | "前一章我们主要学习了怎样调参以及对模型进行评估。这一章,我们就要实际运用这些技巧,继而探索构建集成分类器的不同方法,集成分类器得到的结果通常比单个分类器要好。\n", 10 | "\n", 11 | "这一章你将要学习:\n", 12 | "\n", 13 | "* 基于投票的预测\n", 14 | "* 通过可重复采用构建训练集,降低过拟合\n", 15 | "* 以弱学习器为基础,从错误中学习来构建强学习器\n", 16 | "\n" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "## 集成学习\n", 24 | "\n", 25 | "集成学习背后的思想是将不同的分类器进行组合得到一个元分类器,这个元分类器相对于单个分类器拥有更好的泛化性能。比如,假设我们从10位专家那里分别得到了对于某个事件的预测结果,集成学习能够对这10个预测结果进行组合,得到一个更准确的预测结果。\n", 26 | "\n", 27 | "后面我们会学到,有不同的方法来创建集成模型,这一节我们先解决一个基本的问题:为什么要用集成学习?她为什么就比单个模型效果要好呢?\n", 28 | "\n", 29 | "\n", 30 | "本书是为初学者打造的,所以集成学习这里我们也只关注最基本的集成方法:投票法(majority voting)。投票法意味着我们在得到最后的预测类别时,看看哪个类别是大多数单分类器都预测的,这里的大多数一般是大于50%。更严格来说,投票法只适用于二分类,当然他很容易就扩展到多分类情况: 多数表决(plurality voting).\n", 31 | "\n", 32 | "\n", 33 | "下图展示了一个投票法的例子,一共10个基本分类器:\n", 34 | "\n", 35 | "![](https://ooo.0o0.ooo/2016/07/04/577a60abc6aa0.png)\n", 36 | "\n", 37 | "\n", 38 | "我们先用训练集训练m个不同的分类器$(C_{1},...,C_{m})$, 这里的分类器可以是决策树、SVM或者LR等。我们当然也可以用同一种分类器,只不过在训练每一个模型时用不同的参数或者不同的训练集(比如自主采样法)。随机森林就是一个采用这种策略的例子,它由不同的决策树模型构成。这图展示了用投票策略的集成方法步骤:\n", 39 | "\n", 40 | "\n", 41 | "![](https://ooo.0o0.ooo/2016/07/04/577a61b502ccf.png)\n", 42 | "\n", 43 | "投票策略非常简单,我们收集每个单分类器$c_{j}$的预测类别$\\hat{y}$,将票数最多的$\\hat{y}$作为预测结果:\n", 44 | "\n", 45 | "![](http://odw1x7kgr.bkt.clouddn.com/QQ20161018-4.png)\n", 46 | "\n", 47 | "以二分类为例,类别class1=-1, class2=+1, 投票预测的过程如下, 把每个单分类器的预测结果相加,如果值大于0,预测结果为正类,否则为负类:\n", 48 | "\n", 49 | "![](http://odw1x7kgr.bkt.clouddn.com/QQ20161018-5.png)\n", 50 | "\n", 51 | "\n", 52 | "读到这里,我想大家都有一个疑问:凭啥集成学习就比单分类器效果好?道理很简单(一点点组合数学知识),假设对于一个二分类问题,有n个单分类器,每个单分类器有相等的错误率$\\epsilon$,并且单分类器之间相互独立,错误率也不相关。 有了这些假设,我们可以计算集成模型的错误概率:\n", 53 | "\n", 54 | "![](http://odw1x7kgr.bkt.clouddn.com/QQ20161018-6.png)\n", 55 | "\n", 56 | "\n", 57 | "如果n=11,错误率为0.25,要想集成结果预测错误,至少要有6个单分类器预测结果不正确,错误概率是:\n", 58 | "\n", 59 | "\n", 60 | "![](http://odw1x7kgr.bkt.clouddn.com/QQ20161018-8.png)\n", 61 | "\n", 62 | "\n", 63 | "集成结果错误率才0.034哦,比0.25小太多。继承结果比单分类器好,也是有前提的,就是你这个单分类器的能力不能太差,至少要比随机猜测的结果好一点,至少。\n", 64 | "\n", 65 | "从下图可以看出,只要单分类器的表现不太差,集成学习的结果总是要好于单分类器的。\n", 66 | "\n", 67 | "![](http://odw1x7kgr.bkt.clouddn.com/QQ20161018-9.png)\n" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": {}, 73 | "source": [ 74 | "## 结合不同的分类算法进行投票\n", 75 | "\n", 76 | "\n", 77 | "这一节学习使用sklearn进行投票分类,看一个具体的例子,数据集采用Iris数据集,只使用sepal width和petal length两个维度特征,类别我们也只是用两类:Iris-Versicolor和Iris-Virginica,评判标准使用ROC AUC。\n", 78 | "\n", 79 | "![](http://odw1x7kgr.bkt.clouddn.com/QQ20161018-6@2x.png)\n", 80 | "\n", 81 | "\n", 82 | "\n", 83 | "![](http://odw1x7kgr.bkt.clouddn.com/QQ20161018-7@2x.png)\n", 84 | "\n", 85 | "![](http://odw1x7kgr.bkt.clouddn.com/QQ20161018-8@2x.png)\n", 86 | "\n", 87 | "\n" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "metadata": { 94 | "collapsed": true 95 | }, 96 | "outputs": [], 97 | "source": [] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "metadata": { 103 | "collapsed": true 104 | }, 105 | "outputs": [], 106 | "source": [] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": null, 111 | "metadata": { 112 | "collapsed": true 113 | }, 114 | "outputs": [], 115 | "source": [] 116 | } 117 | ], 118 | "metadata": { 119 | "kernelspec": { 120 | "display_name": "Python 2", 121 | "language": "python", 122 | "name": "python2" 123 | }, 124 | "language_info": { 125 | "codemirror_mode": { 126 | "name": "ipython", 127 | "version": 2 128 | }, 129 | "file_extension": ".py", 130 | "mimetype": "text/x-python", 131 | "name": "python", 132 | "nbconvert_exporter": "python", 133 | "pygments_lexer": "ipython2", 134 | "version": "2.7.12" 135 | } 136 | }, 137 | "nbformat": 4, 138 | "nbformat_minor": 2 139 | } 140 | -------------------------------------------------------------------------------- /第七章/集成学习.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 集成学习\n", 8 | "\n", 9 | "前一章我们主要学习了怎样调参以及对模型进行评估。这一章,我们就要实际运用这些技巧,继而探索构建集成分类器的不同方法,集成分类器得到的结果通常比单个分类器要好。\n", 10 | "\n", 11 | "这一章你将要学习:\n", 12 | "\n", 13 | "* 基于投票的预测\n", 14 | "* 通过可重复采用构建训练集,降低过拟合\n", 15 | "* 以弱学习器为基础,从错误中学习来构建强学习器\n", 16 | "\n" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "## 集成学习\n", 24 | "\n", 25 | "集成学习背后的思想是将不同的分类器进行组合得到一个元分类器,这个元分类器相对于单个分类器拥有更好的泛化性能。比如,假设我们从10位专家那里分别得到了对于某个事件的预测结果,集成学习能够对这10个预测结果进行组合,得到一个更准确的预测结果。\n", 26 | "\n", 27 | "后面我们会学到,有不同的方法来创建集成模型,这一节我们先解决一个基本的问题:为什么要用集成学习?她为什么就比单个模型效果要好呢?\n", 28 | "\n", 29 | "\n", 30 | "本书是为初学者打造的,所以集成学习这里我们也只关注最基本的集成方法:投票法(majority voting)。投票法意味着我们在得到最后的预测类别时,看看哪个类别是大多数单分类器都预测的,这里的大多数一般是大于50%。更严格来说,投票法只适用于二分类,当然他很容易就扩展到多分类情况: 多数表决(plurality voting).\n", 31 | "\n", 32 | "\n", 33 | "下图展示了一个投票法的例子,一共10个基本分类器:\n", 34 | "\n", 35 | "![](https://ooo.0o0.ooo/2016/07/04/577a60abc6aa0.png)\n", 36 | "\n", 37 | "\n", 38 | "我们先用训练集训练m个不同的分类器$(C_{1},...,C_{m})$, 这里的分类器可以是决策树、SVM或者LR等。我们当然也可以用同一种分类器,只不过在训练每一个模型时用不同的参数或者不同的训练集(比如自主采样法)。随机森林就是一个采用这种策略的例子,它由不同的决策树模型构成。这图展示了用投票策略的集成方法步骤:\n", 39 | "\n", 40 | "\n", 41 | "![](https://ooo.0o0.ooo/2016/07/04/577a61b502ccf.png)\n", 42 | "\n", 43 | "投票策略非常简单,我们收集每个单分类器$c_{j}$的预测类别$\\hat{y}$,将票数最多的$\\hat{y}$作为预测结果:\n", 44 | "\n", 45 | "![](http://odw1x7kgr.bkt.clouddn.com/QQ20161018-4.png)\n", 46 | "\n", 47 | "以二分类为例,类别class1=-1, class2=+1, 投票预测的过程如下, 把每个单分类器的预测结果相加,如果值大于0,预测结果为正类,否则为负类:\n", 48 | "\n", 49 | "![](http://odw1x7kgr.bkt.clouddn.com/QQ20161018-5.png)\n", 50 | "\n", 51 | "\n", 52 | "读到这里,我想大家都有一个疑问:凭啥集成学习就比单分类器效果好?道理很简单(一点点组合数学知识),假设对于一个二分类问题,有n个单分类器,每个单分类器有相等的错误率$\\epsilon$,并且单分类器之间相互独立,错误率也不相关。 有了这些假设,我们可以计算集成模型的错误概率:\n", 53 | "\n", 54 | "![](http://odw1x7kgr.bkt.clouddn.com/QQ20161018-6.png)\n", 55 | "\n", 56 | "\n", 57 | "如果n=11,错误率为0.25,要想集成结果预测错误,至少要有6个单分类器预测结果不正确,错误概率是:\n", 58 | "\n", 59 | "\n", 60 | "![](http://odw1x7kgr.bkt.clouddn.com/QQ20161018-8.png)\n", 61 | "\n", 62 | "\n", 63 | "集成结果错误率才0.034哦,比0.25小太多。继承结果比单分类器好,也是有前提的,就是你这个单分类器的能力不能太差,至少要比随机猜测的结果好一点,至少。\n", 64 | "\n", 65 | "从下图可以看出,只要单分类器的表现不太差,集成学习的结果总是要好于单分类器的。\n", 66 | "\n", 67 | "![](http://odw1x7kgr.bkt.clouddn.com/QQ20161018-9.png)\n" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": {}, 73 | "source": [ 74 | "## 结合不同的分类算法进行投票\n", 75 | "\n", 76 | "\n", 77 | "这一节学习使用sklearn进行投票分类,看一个具体的例子,数据集采用Iris数据集,只使用sepal width和petal length两个维度特征,类别我们也只是用两类:Iris-Versicolor和Iris-Virginica,评判标准使用ROC AUC。\n", 78 | "\n", 79 | "![](http://odw1x7kgr.bkt.clouddn.com/QQ20161018-6@2x.png)\n", 80 | "\n", 81 | "\n", 82 | "\n", 83 | "![](http://odw1x7kgr.bkt.clouddn.com/QQ20161018-7@2x.png)\n", 84 | "\n", 85 | "![](http://odw1x7kgr.bkt.clouddn.com/QQ20161018-8@2x.png)\n", 86 | "\n", 87 | "\n" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "metadata": { 94 | "collapsed": true 95 | }, 96 | "outputs": [], 97 | "source": [] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "metadata": { 103 | "collapsed": true 104 | }, 105 | "outputs": [], 106 | "source": [] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": null, 111 | "metadata": { 112 | "collapsed": true 113 | }, 114 | "outputs": [], 115 | "source": [] 116 | } 117 | ], 118 | "metadata": { 119 | "kernelspec": { 120 | "display_name": "Python 2", 121 | "language": "python", 122 | "name": "python2" 123 | }, 124 | "language_info": { 125 | "codemirror_mode": { 126 | "name": "ipython", 127 | "version": 2 128 | }, 129 | "file_extension": ".py", 130 | "mimetype": "text/x-python", 131 | "name": "python", 132 | "nbconvert_exporter": "python", 133 | "pygments_lexer": "ipython2", 134 | "version": "2.7.12" 135 | } 136 | }, 137 | "nbformat": 4, 138 | "nbformat_minor": 2 139 | } 140 | -------------------------------------------------------------------------------- /第三章/ch3.md: -------------------------------------------------------------------------------- 1 | # 第三章 使用Scikit-learn进行分类器之旅 2 | 3 | 本章我们将要学习学术界和工业界常用的几种机器学习算法。在学习算法之间差异的同时,我们也要了解每个算法的优缺点。我们不会像上一章亲自实现各个算法,而是直接调用易用的scikit-learn库。 4 | 5 | 本章涉及到的知识点包括: 6 | * 介绍常用分类算法的概念 7 | * 学习使用scikit-learn 8 | * 如何选择机器学习算法 9 | -------------------------------------------------------------------------------- /第三章/ch3_section1.md: -------------------------------------------------------------------------------- 1 | # 如何选择合适的分类器算法 2 | 3 | 对于一个具体的分类问题,如何选择合适的分类器算法绝非纸上谈兵就能确定的:每一个算法都有其独特的数据偏好和假设。再一次强调"No Free Lunch"定理:在所有场景下都最厉害的分类器是不存在滴。实际上,常用的做法就是多选择几个分类器试试,然后挑选效果最好的那一个。对于同一个问题,样本数的不同、特征数目的多少、数据集中的噪音和数据是否线性可分 都会影响到分类器的效果。 4 | 5 | 6 | 最终,分类器的性能、计算能力和预测能力,都极大依赖训练集。我们概况一下训练一个机器学习算法通常的5大步骤: 7 | * 特征选择 8 | * 选择性能评价指标 9 | * 选择分类器和优化算法 10 | * 评估模型的性能 11 | * 模型调参 12 | 13 | 本书的内容贯穿上面的5大步骤,不要心急,本章重点关注常用算法的重要概念,至于特征选择、预处理、评价指标和如何调参将在后面章节一一介绍。 14 | -------------------------------------------------------------------------------- /第三章/ch3_section10.md: -------------------------------------------------------------------------------- 1 | # 构建一棵决策树 2 | 3 | 决策树通过将特征空间分割为矩形,所以其决策界很复杂。但是要知道过大的树深度会导致过拟合,所以决策界并不是越复杂越好。我们调用sklearn,使用熵作为度量,训练一颗最大深度为3的决策树。还有一点,**对于决策树算法来说,特征缩放并不是必须的**。代码如下: 4 | 5 | 6 | 7 | 8 | 9 | ![](https://ooo.0o0.ooo/2016/06/21/576a009aef9dd.png) 10 | 11 | 12 | 执行上面的代码,我们得到如下结果,决策界和坐标轴平行: 13 | 14 | ![](https://ooo.0o0.ooo/2016/06/21/576a00c983f06.png) 15 | 16 | 17 | sklearn的一大优点是可以将训练好的决策树模型输出,保存在.dot文件,我们可以利用GraphViz对其可视化。 18 | 19 | 先调用sklearn中export_graphviz将树模型导出: 20 | 21 | 22 | ![](https://ooo.0o0.ooo/2016/06/21/576a01910635d.png) 23 | 24 | 然后利用GraphViz程序将tree.dot转为PNG图片: 25 | 26 | ![](https://ooo.0o0.ooo/2016/06/21/576a01c465e7f.png) 27 | 28 | 29 | 30 | 现在我们可以查看决策树在构建树时的过程:根节点105个样本,使用 petal_width <=0.75分割为两个子节点。经过第一个分割,我们可以发现左节点中样本都是同一类型,所以停止此节点的分割,右节点继续分割,注意一点,**在构建决策树时两个特征各使用了两次**。 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /第三章/ch3_section11.md: -------------------------------------------------------------------------------- 1 | # 随机森林 2 | 3 | 随机森林一直是广受欢迎的模型,优点很多:优秀的分类表现、扩展性和使用简单。随机森林的思想也不复杂,一个随机森林模型就是多颗决策树的集成。集成学习(ensemble learning)的观点是将多个弱分类器结合来构建一个强分类器,它的泛化误差小且不易过拟合。 4 | 5 | 随机森林算法大致分为4个步骤: 6 | * 通过自助法(bootstrap)构建大小为n的一个训练集,即重复抽样选择n个训练样例 7 | * 对于刚才新得到的训练集,构建一棵决策树。在每个节点执行以下操作: 8 | * * 通过不重复抽样选择d个特征 9 | * * 利用上面的d个特征,选择某种度量分割节点 10 | * 重复步骤1和2,k次 11 | * 对于每一个测试样例,对k颗决策树的预测结果进行投票。票数最多的结果就是随机森林的预测结果。至于如何投票,下面会讲到。 12 | 13 | 14 | 随机森林中构建决策树的做法和原始决策树的区别是,在每次分割节点时,不是从所有特征中选择而是在一个小特征集中选择特征。 15 | 16 | 虽然随机森林模型的可解释性不如决策树,但是它的一大优点是受超参数的影响波动不是很大(译者注:几个主要参数还是需要好好调参的)。我们也不需要对随机森林进行剪枝因为集成模型的鲁棒性很强,不会过多受单棵决策树噪音的影响。 17 | 18 | 在实际运用随机森林模型时,树的数目(k)需要好好调参。一般,k越大,随机森林的性能越好,当然计算成本也越高。 19 | 20 | 21 | 样本大小n能够控制bias-variance平衡,如果n很大,我们就减小了随机性因此随机森林就容易过拟合。另一方面,如果n很小,虽然不会过拟合,但模型的性能会降低。大多数随机森林的实现,包括sklearn中的RandomForestClassifier,n的大小等于原始训练集的大小。 22 | 23 | 在每一次分割时特征集的大小d,一个最起码的要求是要小于原始特征集大小,sklearn中的默认值$$d=m^{0.5}$$,其中m是原始特征集大小,这是一个比较合理的数值。 24 | 25 | 26 | 直接调用sklearn来看一下随机森林吧: 27 | 28 | 29 | ![](https://ooo.0o0.ooo/2016/06/21/576a08e5ed032.png) 30 | 31 | 32 | ![](https://ooo.0o0.ooo/2016/06/21/576a08fc3685b.png) 33 | 34 | 运行上面的代码,我们训练了一个含有10颗树的随机森林,使用熵作为分割节点时的度量。虽然我们在一个小数据集上训练了一个非常小的模型,但我还是使用了n_jobs这个并行化参数,此处使用了计算机的两个核训练模型。 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /第三章/ch3_section12.md: -------------------------------------------------------------------------------- 1 | 2 | # k近邻--一个懒惰学习算法 3 | 4 | 本章我们要讨论的最后一个监督学习算法是k紧邻算法(k-nearest neighbor classifier, KNN), 这个算法很有意思,因为他背后的思想和本章其他算法完全不同。 5 | 6 | 7 | KNN是懒惰学习的一个典型示例。之所以称为“懒惰”并不是由于此类算法看起来很简单,而是在训练模型过程中这类算法并不去学习一个判别式函数(损失函数)而是要记住整个训练集。 8 | 9 | 10 | **Note** 参数模型VS变参模型 11 | 12 | 机器学习算法可以被分为两大类:参数模型和变参模型。对于参数模型,在训练过程中我们要学习一个函数,重点是估计函数的参数,然后对于新数据集,我们直接用学习到的函数对齐分类。典型的参数模型包括感知机、逻辑斯蒂回归和线性SVM。与之相对的,变参模型中的参数个数不是固定的,它的参数个数随着训练集增大而增多!很多书中变参(nonparametric)被翻译为无参模型,一定要记住,不是没有参数,而是参数个数是变量!变参模型的两个典型示例是决策树/随机森林和核SVM。 13 | 14 | KNN属于变参模型的一个子类:基于实例的学习(instance-based learning)。基于实例的学习的模型在训练过程中要做的是记住整个训练集,而懒惰学习是基于实例的学习的特例,在整个学习过程中不涉及损失函数的概念。 15 | 16 | 17 | KNN算法本身非常简单,步骤如下: 18 | * 1 确定k大小和距离度量。 19 | * 2 对于测试集中的一个样本,找到训练集中和它最近的k个样本。 20 | * 3 将这k个样本的投票结果作为测试样本的类别。 21 | 22 | 一图胜千言,请看下图: 23 | 24 | 25 | 26 | ![](https://ooo.0o0.ooo/2016/06/22/576a3afd1bb03.png) 27 | 28 | 29 | 30 | 对每一个测试样本,基于事先选择的距离度量,KNN算法在训练集中找到距离最近(最相似)的k个样本,然后将k个样本的类别的投票结果作为测试样本的类别。 31 | 32 | 33 | 像KNN这种基于内存的方法一大优点是:一旦训练集增加了新数据,模型能立刻改变。另一方面,缺点是分类时的最坏计算复杂度随着训练集增大而线性增加,除非特征维度非常低并且算法用诸如KD-树等数据结构实现。此外,我们要一直保存着训练集,不像参数模型训练好模型后,可以丢弃训练集。因此,存储空间也成为了KNN处理大数据的一个瓶颈。 34 | 35 | 36 | 下面我们调用sklearn训练一个KNN模型: 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | ![](https://ooo.0o0.ooo/2016/06/22/576a3fe9eaaa6.png) 53 | 54 | 55 | 我们设置k=5,得到了相对平滑的决策界: 56 | 57 | ![](https://ooo.0o0.ooo/2016/06/22/576a401c0be9b.png) 58 | 59 | 60 | k的选择对于KNN模型来说至关重要,除此之外,距离度量也是很有用的。通常,欧氏距离用于实数域的数据集,此时一定要对特征进行标准化,这样每一维度特征的重要性等同。我们在上面的代码中使用的距离度量是'minkowski',它是欧氏距离和曼哈顿距离的一般化: 61 | 62 | 63 | ![](https://ooo.0o0.ooo/2016/06/22/576a40dc59014.png) 64 | 65 | 66 | 67 | 如果p=2,则退化为欧氏距离,p=1,则退化为曼哈顿距离。使用metric参数可以选择不同的距离度量。 68 | 69 | 70 | **Note** 维度诅咒 71 | 72 | 注意,如果特征维度过大,KNN算法很容易过拟合。我们可以想象,对于一个固定大小的训练集,如果特征空间维度非常高,空间中最相似的两个点也可能距离很远(差别很大)。虽然我们在逻辑回归中讨论了正则项来防止过拟合,但是正则项却不适用于KNN和决策树模型,我们只能通过特征选择和降维手段来避免维度诅咒。下一章我们会讲到。 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /第三章/ch3_section13.md: -------------------------------------------------------------------------------- 1 | # 总结 2 | 3 | 本章,你学习了许多不同的机器学习算法,用于解决线性和非线性问题。如果我们关注模型可解释性,决策树是很好的选择。逻辑斯蒂回归不但可以在在线学习场景下大展拳脚还能预测概率。虽然SVM能解决线性和非线性问题,但是它参数个数比较多,调参挺麻烦。集成算法包括随机森林则不需要调节过多的参数,也不会像决策树一样容易过拟合,这使得它很受欢迎。KNN通过懒惰学习进行分类,他不需要模型训练的过程但是在预测时的计算成本相对比较高。 4 | 5 | 6 | 然而,比选择合适的算法更重要的是训练集数据本身。如果数据的特征不够好,再好的算法也没用。 7 | 8 | 9 | 在下一章,我们会讨论预处理涉及到的内容。 10 | 11 | 12 | -------------------------------------------------------------------------------- /第三章/ch3_section2.md: -------------------------------------------------------------------------------- 1 | # scikit-learn之旅 2 | 3 | 在第二章,你学习了两个分类相关的算法:感知机和Adaline,并且都用Python进行了实现。现在我们学习scikit-learn的API,这个库不但用户界面友好并且对常用算法的实现进行了高度优化。此外,它还包含数据预处理和调参和模型评估的很多方法,是Python进行数据挖掘的必备工具。 4 | 5 | 6 | 7 | ## 通过scikit-learn训练感知机模型 8 | 9 | 我们先看一下如何使用sklearn训练一个感知机模型,数据集还是用我们熟悉的Iris数据集。由于sklearn已经内置了Iris数据集,所以本章所有的分类算法我们通通使用Iris数据集,还是和第二章一样,为了可视化方便,我们只使用其中两维度特征,样本数则使用三个类别全部的150个样本。 10 | 11 | ![](https://ooo.0o0.ooo/2016/06/17/5764b351adf06.png) 12 | 13 | 为了评估训练好的模型对新数据的预测能力,我们先把Iris训练集分割为两部分:训练集和测试集。在第5章我们还会讨论模型评估的更多细节。 14 | 15 | ![](https://ooo.0o0.ooo/2016/06/17/5764b7e1873aa.png) 16 | 17 | 通过调用train_tset_split方法我们将数据集随机分为两部分,测试集占30%(45个样本),训练集占70%(105个样本)。 18 | 19 | 许多机器学习和优化算法都要求对特征进行缩放操作,回忆第二章中梯度下降的例子,当时我们自己实现了标准化代码,现在我们可以直接调用sklearn中的StandardScaler来对特征进行标准化: 20 | 21 | ![](https://ooo.0o0.ooo/2016/06/17/5764b8f061072.png) 22 | 23 | 上面的代码中我们先从preprocessing模块中读取StandardScaler类,然后得到一个初始化的StandardScaler新对象sc,使用fit方法,StandardScaler对训练集中**每一维度特征**计算出$$u$$(样本平均值)和$$\sigma$$(标准差),然后调用transform方法对数据集进行标准化。注意我们用相同的标准化参数对待训练集和测试集。 24 | 25 | 26 | 对数据标准化以后,我们可以训练一个感知机模型。sklearn中大多数算法都支持多类别分类,默认使用One-vs.-Rest方式实现。所以我们可以直接训练三分类的感知机模型: 27 | 28 | ![](https://ooo.0o0.ooo/2016/06/17/5764ba918ed43.png) 29 | 30 | sklearn中训练模型的接口和我们在第二章实现的一样耶,从linear_model模型读取Perceptron类,然后初始化得到ppn,接着使用fit方法训练一个模型。这里的eta0就是第二章中的学习率eta,n_iter同样表示对训练集迭代的次数。我们设置random_state参数使得shuffle结果可再现。 31 | 32 | 训练好感知机模型后,我们可以使用predict方法进行预测了: 33 | 34 | ![](https://ooo.0o0.ooo/2016/06/17/5764bbbac2d6b.png) 35 | 36 | 对于测试集中45个样本,有4个样本被错分类了,因此,错分类率是0.089。除了使用错分类率,我们也可以使用分类准确率(accuracy)评价模型,accuracy=1-miscassification error = 1-0.089=0.911。 37 | 38 | Sklearn中包含了许多评价指标,这些指标都位于metrics模块,比如,我们可以计算分类准确率: 39 | 40 | ![](https://ooo.0o0.ooo/2016/06/17/5764bc9492da7.png) 41 | 42 | **Notes** 本章我们评估模型性能的好坏仅仅依赖于其在测试集的表现。在第5章,你将会学习许多其他的技巧来评估模型,包括可视化分析来检测和预防过拟合(overfitting)。过拟合意味着模型对训练集中的模式捕捉的很好,但是其泛化能力却很差。 43 | 44 | 45 | 最后,我们使用第二章的plot_decision_regions画出分界区域。不过在使用之间,我们进行一点小修改,我们用圆圈表示测试集样本: 46 | 47 | 48 | 49 | 50 | ``` 51 | from matplotlib.colors import ListedColormap 52 | import matplotlib.pyplot as plt 53 | 54 | def plot_decision_regions(X, y, classifier, test_idx=None, resolution=0.02): 55 | # setup marker generator and color map 56 | markers = ('s', 'x', 'o', '^', 'v') 57 | colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan') 58 | cmap = ListedColormap(colors[:len(np.unique(y))]) 59 | 60 | # plot the decision surface 61 | x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1 62 | x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1 63 | xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution), 64 | np.arange(x2_min, x2_max, resolution)) 65 | Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T) 66 | Z = Z.reshape(xx1.shape) 67 | plt.contourf(xx1, xx2, Z, slpha=0.4, cmap=cmap) 68 | plt.xlim(xx1.min(), xx1.max()) 69 | plt.ylim(xx2.min(), xx2.max()) 70 | 71 | # plot all samples 72 | X_test, y_test = X[test_idx, :], y[test_idx] 73 | for idx, cl in enumerate(np.unique(y)): 74 | plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1], 75 | alpha=0.8, c=cmap(idx), 76 | marker=markers[idx], label=cl) 77 | 78 | # highlight test samples 79 | if test_idx: 80 | X_test, y_test = X[test_idx, :], y[test_idx] 81 | plt.scatter(X_test[:, 0], X_test[:, 1], c='', 82 | alpha=1.0, linewidth=1, marker='o', 83 | s=55, label='test set') 84 | 85 | 86 | ``` 87 | 88 | 经过上面的修改,现在可以区分训练集和测试集,运行如下代码: 89 | 90 | 91 | 92 | ![](https://ooo.0o0.ooo/2016/06/19/57674defe7bc3.png) 93 | 94 | 95 | 对结果可视化后,我们可以发现三个类别的花不能被线性决策界完美分类: 96 | 97 | ![](https://ooo.0o0.ooo/2016/06/19/57674e3426c17.png) 98 | 99 | 100 | 在第二章我们就说过,感知机对于不能够线性可分的数据,算法永远不会收敛,这也是为什么我们不推荐大家实际使用感知机的原因。在接下来的章节,我们将学习到其他线性分类器,即使数据不能完美线性分类,也能收敛到最小的损失值。 101 | -------------------------------------------------------------------------------- /第三章/ch3_section3.md: -------------------------------------------------------------------------------- 1 | # 逻辑斯蒂回归对类别概率建模 2 | 3 | 感知机算法为我们学习机器学习分类问题曾经立下汗马功劳,但由于其致命缺点:**如果数据不能完全线性分割,则算法永远不会收敛**。我们实际上很少真正使用感知机模型。 4 | 5 | 接下来我们学习另一个非常有效的线性二分类模型:逻辑斯蒂回归(logistic regression)。注意,尽管模型名字中有“回归”的字眼,但她确是百分百的分类模型。 6 | 7 | ## 逻辑斯蒂回归和条件概率 8 | 9 | 逻辑斯蒂回归(以下简称逻辑回归)是一个分类模型,它易于实现,并且对于线性可分的类别数据性能良好。她是工业界最常用的分类模型之一。和感知机和Adaline相似,本章的逻辑回归模型也是用于二分类的线性模型,当然可以使用OvR技巧扩展为多分类模型。 10 | 11 | 逻辑回归作为一个概率模型,为了解释其背后的原理,我们先介绍一个概念:几率(odds ratio)=$$\frac{p}{(1-p)}$$, 其中p表示样本为正例的概率。这里如何划分正例、负例要根据我们想要预测什么,比如,我们要预测一个病人有某种疾病的概率,则病人有此疾病为正例。数学上,正例表示类别y=1。有了几率的概念,我们可以定义对数几率函数(logit function,这里log odds简称logit): 12 | $$logit(p)=log\frac{p}{(1-p)}$$ 13 | 14 | 对数几率函数的自变量p取值范围[0,1],因变量值域为实数域,将上式中的p视为类后验概率估计p(y=1|$$x$$),然后定义如下线性关系: 15 | 16 | ![](https://ooo.0o0.ooo/2016/06/19/576756a749239.png) 17 | 18 | 实际上我们关心的是某个样本属于类别的概率,恰恰是对数几率函数的反函数,也被称为逻辑斯底函数(logistic function),有时简写为sigmoid函数,函数图像是S型: 19 | 20 | ![](https://ooo.0o0.ooo/2016/06/19/5767577811dc5.png) 21 | 其中z是网络输入,即权重参数和特征的线性组合$$z=w^{T}x=w_{0}+w_{1}x_{1}+...+w_{m}x_{m}$$。 22 | 23 | 24 | sigmoid(S曲线)函数很重要,我们不妨画图看一看: 25 | 26 | 27 | ![](https://ooo.0o0.ooo/2016/06/19/5767592c6d8d0.png) 28 | 29 | 我们可以看到随着z趋向正无穷![](https://ooo.0o0.ooo/2016/06/19/5767685625302.png),$$\phi(z)$$无限接近于1;z趋向负无穷,$$\phi(z)$$无限接近0.因此,对于sigmoid函数,其自变量取值范围为实数域,因变量值域为[0,1],并且sigmoid(0)=0.5。 30 | 31 | 32 | 为了直观上对逻辑回归有更好的理解,我们可以和Adaline模型联系起来,二者的唯一区别是:**Adaline模型,激活函数$$\phi(z)=z$$,在逻辑回归中,激活函数变成了sigmoid函数**。 33 | 34 | 35 | ![](https://ooo.0o0.ooo/2016/06/19/57676995058d2.png) 36 | 37 | 38 | 由于sigmoid函数的输出是在[0,1],所以可以赋予物理含义:样本属于正例的概率,$$\phi(z)=P(y=1|x;w)$$。举例来说,如果$$\phi(z)=0.8$$,意味着此样本是Iris-Versicolor花的概率是0.8,是Iris-Setosa花的概率是$$P(y=0|x;w)=1-P(y=1|x;w)=0.2$$。 39 | 40 | 有了样本的预测概率,再得到样本的类别值就很简单了,和Adaline一样,使用单位阶跃函数: 41 | 42 | ![](https://ooo.0o0.ooo/2016/06/19/57676accb1625.png) 43 | 44 | 上式等价于: 45 | 46 | 47 | ![](https://ooo.0o0.ooo/2016/06/19/57676b37b0235.png) 48 | 49 | 50 | 逻辑回归之所以应用广泛,一大优点就是它不但能预测类别,还能输出具体的概率值,概率值很很多场景往往比单纯的类别值重要的多。比如在天气预测中下雨的可能性,病人患病的可能性等等。 51 | 52 | 53 | 54 | ## 学习逻辑斯底损失函数中的权重参数 55 | 56 | 57 | 对逻辑回归模型有了基本认识后,我们回到机器学习的核心问题,怎样学习参数。还记得上一章Adaline中我们定义的差平方损失函数: 58 | 59 | 60 | 61 | ![](https://ooo.0o0.ooo/2016/06/20/57678fce36e92.png) 62 | 63 | 我们求解损失函数最小时的权重参数,同样,对于逻辑回归,我们也需要定义损失函数,在这之前,先定义似然(likelihood)L的概念,假设训练集中样本独立,似然定义: 64 | 65 | ![](https://ooo.0o0.ooo/2016/06/20/576790670edf7.png) 66 | 与损失函数费尽全力找最小值相反,对于似然函数,我们要找的是最大值。实际上,对于似然的log值,是很容易找到最大值的,也就是最大化log-likelihood函数: 67 | 68 | ![](https://ooo.0o0.ooo/2016/06/20/576790ea3b78d.png) 69 | 70 | 接下来,我们可以运用梯度下降等优化算法来求解最大化log-likelihood时的参数。最大化和最小化本质上没有区别,所以我们还是将log-likelihood写成求最小值的损失函数形式: 71 | 72 | ![](https://ooo.0o0.ooo/2016/06/20/576793b71874d.png) 73 | 74 | 75 | 为了更好地理解此损失函数,假设现在训练集只有一个样本: 76 | 77 | ![](https://ooo.0o0.ooo/2016/06/20/576794134afea.png) 78 | 79 | 上式等号右边的算式,如果y=0则第一项为0;如果y=1则第二项为0。 80 | 81 | 82 | 83 | ![](https://ooo.0o0.ooo/2016/06/20/576794efcb16d.png) 84 | 85 | 86 | 下图展示了一个训练样本时,不同$$\phi(z)$$时对应的$$J(w)$$: 87 | 88 | 89 | ![](https://ooo.0o0.ooo/2016/06/20/5767955034369.png) 90 | 91 | 对于蓝线,如果逻辑回归预测结果正确,类别为1,则损失为0;对于绿线,如果逻辑回归预测正确,类别为0,则损失为0。 92 | 如果预测错误,则损失趋向正无穷。 93 | 94 | 95 | ## 调用scikit-learn训练逻辑回归模型 96 | 97 | 98 | 如果我们自己实现逻辑回归,只需要将第二章中的Adaline中的损失函数替换掉即可,新的损失函数: 99 | 100 | 101 | ![](https://ooo.0o0.ooo/2016/06/20/576796f43b7f6.png) 102 | 103 | 104 | 不过考虑到sklearn中提供了高度优化过的逻辑回归实现,同时也支持多类别分类,我们就不自己实现了,而是直接调用sklearn.linear_model.LogisticRegression,使用标准化后的Iris数据集训练模型: 105 | 106 | 107 | ![](https://ooo.0o0.ooo/2016/06/20/576798ab8ee38.png) 108 | 109 | 110 | 训练模型后,我们画出了决策界, 111 | ![](https://ooo.0o0.ooo/2016/06/20/576799082535b.png) 112 | 113 | 114 | 细心的你一定会问在初始化LogisticRegression时那个参数C是什么意思呀?在下一节讲正则化的时候我们再好好讨论讨论。 115 | 116 | 有了逻辑回归模型,我们就可以预测了,如果你想知道输出概率,调用predict_proba方法即可, 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | ![](https://ooo.0o0.ooo/2016/06/20/576799f698147.png) 127 | 128 | 上面的array表示lr认为测试样本属于Iris-Virginica类别的概率为93.683%,属于Iris-Versicolor的概率为6.316%。 129 | 130 | 131 | 不论Adaline还是逻辑回归,使用梯度下降算法更新权重参数时,用到的算式都一样,$$\Delta w_{j} = -\eta \frac{\partial J}{\partial w_{j}}=\eta \sum_{i}(y^{(i)}-\phi(z^{(i)}))x_{j}^{(i)} $$。 132 | 133 | 我们好好推导一下这个计算过程,首先计算log-likelihood函数对权重参数的偏导数: 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | ![](https://ooo.0o0.ooo/2016/06/20/57679b382ba9d.png) 143 | 144 | 然后,计算sigmoid函数的导数: 145 | 146 | ![](https://ooo.0o0.ooo/2016/06/20/57679bd85ae8c.png) 147 | 148 | 将上式代入到损失函数偏导数: 149 | ![](https://ooo.0o0.ooo/2016/06/20/57679c5651e00.png) 150 | 151 | 我们的目标是求解使log-likelihood最大值时的参数w,因此,对于参数更新,我们按照: 152 | 153 | ![](https://ooo.0o0.ooo/2016/06/20/57679cb31605b.png) 154 | 155 | 156 | 由于每次更新权重时,对于所有参数同时更新,所以可以写成向量形式: 157 | 158 | ![](https://ooo.0o0.ooo/2016/06/20/57679cf9bbfa9.png) 159 | 160 | 我们定义$$\Delta w$$ 161 | 162 | ![](https://ooo.0o0.ooo/2016/06/20/57679d60b0989.png) 163 | 164 | 由于最大化log-likelihood等价于最小化损失函数$$J$$,我们可以将梯度下降算法的权重更新写作: 165 | 166 | 167 | ![](https://ooo.0o0.ooo/2016/06/20/57679da3c5e99.png) 168 | 169 | 这和Adaline中的权重更新算式完全相同。 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 | -------------------------------------------------------------------------------- /第三章/ch3_section4.md: -------------------------------------------------------------------------------- 1 | # 使用正则化解决过拟# 使用正则化解决过拟 # 使用正则化解决过拟# 使用正则化解决过拟 合 2 | 3 | 过拟合(overfitting)是机器学习中很常见的问题,指的是一个模型在训练集上表现很好但是泛化能力巨差(在测试集上的表现糟糕)。如果一个模型饱受过拟合困扰,我们也说此模型方差过高,造成这个结果的原因可能是模型含有太多参数导致模型过于复杂。同样,模型也可能遇到欠拟合(underfitting)问题,我们也说此模型偏差过高,原因是模型过于简单不能学习到训练集中数据存在的模式,同样对于测试集表现很差。 4 | 5 | 6 | 虽然到目前为止我们仅学习了用于分类任务的几种线性模型,过拟合和欠拟合问题可以用一个非线性决策界很好的演示: 7 | 8 | ![](https://ooo.0o0.ooo/2016/06/20/5767a7251fa08.png) 9 | 10 | 11 | 怎样找到bias-variance之间的平衡,常用的方法是正则化(regularization)。正则化是解决特征共线性、过滤数据中噪音和防止过拟合的有用方法。正则化背后的原理是引入额外的信息(偏差)来惩罚过大的权重参数。最常见的形式就是所谓的L2正则(L2 regularization,有时也被称为权重衰减,L2收缩): 12 | 13 | ![](https://ooo.0o0.ooo/2016/06/20/5767a8fe5dbb6.png) 14 | 15 | 此处的$$\lambda$$就是正则化系数。 16 | 17 | 18 | **Note** 正则化是为什么特征缩放如此重要的另一个原因。为了正则化起到作用,我们需要保证所有的特征都在可比较范围(comparable scales)。 19 | 20 | 21 | 如何应用正则化呢?我们只需要在现有损失函数基础上添加正则项即可,比如对于逻辑回归模型,带有L2正则项的的损失函数: 22 | 23 | 24 | ![](https://ooo.0o0.ooo/2016/06/20/5767a9baccf68.png) 25 | 26 | 27 | 通过正则系数$$\lambda$$,我们可以控制在训练过程中使得参数$$w$$比较小。$$\lambda$$值越大,正则化威力越强大。 28 | 29 | 现在我们可以解释LogisticRegression中的参数C: 30 | 31 | ![](https://ooo.0o0.ooo/2016/06/20/5767aa73a12d3.png) 32 | 33 | 34 | 35 | 所以,我们可以将逻辑回归 正则化的损失函数重写为 36 | 37 | ![](https://ooo.0o0.ooo/2016/06/20/5767aabb83aaf.png) 38 | 39 | 40 | 如果我们减小C的值,也就是增大正则系数$$\lambda$$的值,正则化项的威力也增强。 41 | 42 | 43 | ![](https://ooo.0o0.ooo/2016/06/20/5767abc002743.png) 44 | 45 | 46 | 执行上面的代码,我们训练了十个带有不同C值的逻辑回归模型。 47 | 48 | 49 | ![](https://ooo.0o0.ooo/2016/06/20/5767ac0cb843e.png) 50 | 51 | 我们可以看到随着C的减小,权重系数也减小。 52 | 53 | 54 | **Note** 本章只是简要介绍了逻辑斯蒂回归模型,如果你还意犹未尽,推荐阅读 Logistic Regression: From Introductory to Advanced Concepts and Applications, Sage Publications. 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /第三章/ch3_section5.md: -------------------------------------------------------------------------------- 1 | # 支持向量机 2 | 3 | 另一个经常使用的机器学习算法是支持向量机(support vector machine, SVM),SVM可以看做是感知机的扩展。在感知机算法中,我们最小化错误分类误差。在SVM中,我们的优化目标是最大化间隔(margin)。间隔定义为两个分隔超平面(决策界)的距离,那些最靠近超平面的训练样本也被称为支持向量(suppor vectors)。可以看下图: 4 | 5 | 6 | ![](https://ooo.0o0.ooo/2016/06/20/5767ae1337091.png) 7 | 8 | 9 | ## 最大化间隔 10 | 11 | 最大化决策界的间隔,这么做的原因是间隔大的决策界趋向于含有更小的泛化误差,而间隔小的决策界更容易过拟合。为了更好地理解间隔最大化,我们先认识一下那些和决策界平行的正超平面和负超平面,他们可以表示为: 12 | 13 | 14 | ![](https://ooo.0o0.ooo/2016/06/20/5767b4c0a36f8.png) 15 | 16 | 17 | 用(1)减去(2),得到: 18 | 19 | ![](https://ooo.0o0.ooo/2016/06/20/5767b505b8b47.png) 20 | 21 | 对上式进行归一化, 22 | 23 | ![](https://ooo.0o0.ooo/2016/06/20/5767b57928a93.png) 24 | 25 | 其中,![](https://ooo.0o0.ooo/2016/06/20/5767b59ada318.png)。 26 | 27 | 上式等号左边可以解释为正超平面和负超平面之间的距离,也就是所谓的间隔。 28 | 29 | 现在SVM的目标函数变成了最大化间隔$$\frac{2}{||w||}$$,限制条件是样本被正确分类,可以写成: 30 | 31 | ![](https://ooo.0o0.ooo/2016/06/20/5767b6164a8ac.png) 32 | 33 | 34 | 上面两个限制条件说的是所有负样本要落在负超平面那一侧,所有正样本要落在正超平面那侧。我们用更简洁的写法代替: 35 | 36 | 37 | ![](https://ooo.0o0.ooo/2016/06/20/5767b6675272a.png) 38 | 39 | 实际上,使用二次规划(quadratic programming)最小化$$\frac{1}{2}||w||^{2}$$很容易,但是二次规划显然超出了本书的内容,如果你对SVM感兴趣,推荐阅读Vladimir Vapnik写的 The Nature of Statistical Learning Theory, Springer Science&Business Media或Chris J.C. Burges写很棒的解释A Tutorial on Support Vector Machines for Pattern Recognition. 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /第三章/ch3_section6.md: -------------------------------------------------------------------------------- 1 | # 使用松弛变量解决非线性可分的情况 2 | 3 | 虽然我们不想深挖SVM背后的数学概念,但还是有必要简短介绍一下松弛变量(slack variable) $$\xi$$,它是由Vladimir Vapnik在1995年引入的,借此提出了软间隔分类(soft-margin)。引入松弛变量的动机是原来的线性限制条件在面对非线性可分数据时需要松弛,这样才能保证算法收敛。 4 | 5 | 6 | 松弛变量值为正,添加到线性限制条件即可: 7 | 8 | ![](https://ooo.0o0.ooo/2016/06/20/5767d4c235d44.png) 9 | 10 | 新的目标函数变成了: 11 | 12 | 13 | ![](https://ooo.0o0.ooo/2016/06/20/5767d54416998.png) 14 | 15 | 使用变量C,我们可以控制错分类的惩罚量。和逻辑斯蒂回归不同,这里C越大,对于错分类的惩罚越大。可以通过C控制间隔的宽度,在bias-variance之间找到某种平衡: 16 | 17 | 18 | ![](https://ooo.0o0.ooo/2016/06/20/5767d62aeaa9b.png) 19 | 20 | 21 | 22 | 这个概念和正则化相关,如果增大C的值会增加bias而减小模型的方差。 23 | 24 | 25 | 26 | 我们已经学会了线性SVM的基本概念,下面使用sklearn训练一个模型: 27 | 28 | ![](https://ooo.0o0.ooo/2016/06/20/5767d7a9d6921.png) 29 | 30 | ![](https://ooo.0o0.ooo/2016/06/20/5767d7d57c9ae.png) 31 | 32 | 33 | **Note** 逻辑斯蒂回归 VS SVM 34 | 35 | 在解决现实的分类问题时,线性逻辑斯蒂回归和线性SVM通常效果近似。逻辑回归目标是最大化训练集的条件似然,使得她更易受奇异值影响。SVM只关心那些离决策界最近的点(即,支持向量)。另一方面,逻辑斯蒂回归的优点是易于实现,特别是并行化实现。此外,面对流式数据,逻辑斯蒂回归的易于更新的特点也很明显。 36 | 37 | 38 | ## scikit-learn中不同的实现方式 39 | 40 | 前面我们用到的sklearn中的Perceptron和LogisticRegression类的实现都使用了LIBLINEAR库,LIBLINEAR是由国立台湾大学开发的一个高度优化过的C/C++库。同样,sklearn中的SVC类利用了国立台湾大学开发的LIBSVM库。 41 | 42 | 43 | 调用LIBLINEAR和LIBSVM而不是用Python自己实现的优点是训练模型速度很快,毕竟是优化过的代码。可是,有时候数据集很大,不能一次读入内存,针对这个问题,sklearn也实现了SGDClassifier类,使用提供的partial_fit方法能够支持在线学习。SGDClassifier利用随机梯度下降算法学习参数。我们可以调用这一个类初始化随机梯度下降版本的感知机、逻辑斯蒂回归和SVM。 44 | ![](https://ooo.0o0.ooo/2016/06/21/5768fae35a40b.png) 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /第三章/ch3_section7.md: -------------------------------------------------------------------------------- 1 | # 使用核SVM解决非线性 2 | 3 | 4 | SVM之所以受欢迎度这么高,另一个重要的原因是它很容易核化(kernelized),能够解决非线性分类问题。在讨论核SVM细节之前,我们先自己创造一个非线性数据集,看看他长什么样子。 5 | 6 | 使用下面的代码,我们将创造一个简单的数据集,其中100个样本是正类,100个样本是负类。 7 | 8 | 9 | ![](https://ooo.0o0.ooo/2016/06/21/5768fc6e0bfdc.png) 10 | 11 | 12 | 显然,如果要用线性超平面将正负类分开是不可能的,所以前面介绍的线性逻辑斯蒂回归和线性SVM都鞭长莫及。 13 | 14 | 15 | 核方法的idea是为了解决线性不可分数据,在原来特征基础上创造出非线性的组合,然后利用映射函数$$\phi(\cdot)$$将现有特征维度映射到更高维的特征空间,并且这个高维度特征空间能够使得原来线性不可分数据变成了线性可分的。 16 | 17 | 举个例子,下图中,我们将两维的数据映射到三位特征空间,数据集也有线性不可分变成了线性可分,使用的映射为: 18 | 19 | ![](https://ooo.0o0.ooo/2016/06/21/5768fdd6f1aea.png) 20 | 21 | 注意看右上角子图到右下角子图的转变,高维空间中的线性决策界实际上是低维空间的非线性决策界,这个非线性决策界是线性分类器找不到的,而核方法找到了: 22 | 23 | ![](https://ooo.0o0.ooo/2016/06/21/5768fe8f9b373.png) 24 | 25 | 26 | ## 使用核技巧在高维空间找到可分超平面 27 | 28 | 29 | 使用SVM解决非线性问题,我们通过映射函数$$\phi(\cdot)$$将训练集映射到高维特征空间,然后训练一个线性SVM模型在新特征空间将数据分类。然后,我们可以使用相同的映射函数对测试集数据分类。 30 | 31 | 上面的想法很不错,但是如何构建新特征是非常困难的,尤其是数据本身就是高维数据时。因此,我们就要介绍核技巧了。由于我们不会过多涉及在训练SVM时如何求解二次规划问题,你只需要知道用$$\phi(x^{(i)})^{T}\phi(x^{(j)})$$替换$$x^{(i)T}x^{(j)}$$就可以了。为了免去两个点的点乘计算, 32 | 我们定义所谓的核函数(kernel function): 33 | ![](https://ooo.0o0.ooo/2016/06/21/576935084b78b.png) 34 | 35 | 36 | 常用的一个核函数是Radial Basis Function kernel(RBF核),也称为高斯核: 37 | 38 | ![](https://ooo.0o0.ooo/2016/06/21/5769356d7c668.png) 39 | 40 | 通常简写为: 41 | 42 | 43 | ![](https://ooo.0o0.ooo/2016/06/21/576935b1e2fc0.png) 44 | 45 | 此处,$$\gamma=\frac{1}{2\sigma^{2}}$$,是一个要优化的自由参数。 46 | 47 | 48 | 通俗地讲,核(kernel)可以被解释为两个样本之间的相似形函数。高斯核中e的指数范围<=0,因此高斯核值域范围$$\in[0,1]$$,特别地,当两个样本完全一样时,值为1,两个样本完全不同时,值为0. 49 | 50 | 51 | 有了核函数的概念,我们就动手训练一个核SVM,看看是否能够对线性不可分数据集正确分类: 52 | 53 | 54 | 55 | 56 | ![](https://ooo.0o0.ooo/2016/06/21/5769ee8a93a35.png) 57 | 58 | 59 | 结果如下,可以发现核SVM在XOR数据集上表现相当不错: 60 | 61 | 62 | ![](https://ooo.0o0.ooo/2016/06/21/5769eebf4ddc6.png) 63 | 64 | 65 | 66 | 其中参数gamma可以被理解为高斯球面的阶段参数,如果我们增大gamma值,会产生更加柔软的决策界。为了更好地理解gamma参数,我们在Iris数据集上应用RBF核SVM: 67 | 68 | ![](https://ooo.0o0.ooo/2016/06/21/5769eff70c582.png) 69 | 70 | 71 | 我们选择的gamma值相对比较小,所以决策界比较soft: 72 | 73 | 74 | 75 | ![](https://ooo.0o0.ooo/2016/06/21/5769f06a74dc7.png) 76 | 77 | 78 | 现在我们增大gamma值,然后观察决策界: 79 | 80 | ![](https://ooo.0o0.ooo/2016/06/21/5769f0a5d2206.png) 81 | 82 | ![](https://ooo.0o0.ooo/2016/06/21/5769f0ba58422.png) 83 | 84 | 85 | 虽然gamma值较大的模型对训练集分类效果很大,但其泛化能力一般很差,所以选择适当的gamma值有助于避免过拟合。 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /第三章/ch3_section8.md: -------------------------------------------------------------------------------- 1 | # 决策树学习 2 | 3 | 4 | 如果我们在意模型的可解释性,那么决策树(decision tree)分类器绝对是上佳的选择。如同名字的字面意思,我们可以把决策树理解为基于一系列问题对数据做出的分割选择。 5 | 6 | 举一个简单的例子,我们使用决策树来决定某一天的活动: 7 | 8 | 9 | 10 | ![](https://ooo.0o0.ooo/2016/06/21/5769da1318edf.png) 11 | 12 | 基于训练集中的特征,决策树模型提出了一系列问题来推测样本的类别。虽然上图中做出的每个决策都是根据离散变量,但也可以用于连续型变量,比如,对于Iris中sepal width这一取值为实数的特征,我们可以问“sepal width是否大于2.8cm?” 13 | 14 | 15 | 训练决策树模型时,我们从根节点出发,使用信息增益(information gain, IG)最大的特征对数据分割。然后迭代此过程。显然,决策树的生成是一个递归过程,在决策树基本算法中,有三种情形会导致递归返回:(1)当前节点包含的样本全属于同一类别,无需划分;(2)当前属性集为空,或是所有样本在所有属性上取值相同,无法划分;(3)当前节点包含的样本集合为空,不能划分。 16 | 17 | 每一个节点的样本都属于同一个类,同时这也可能导致树的深度很大,节点很多,很容易引起过拟合。因此,剪枝操作是必不可少的,来控制树深度。 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /第三章/ch3_section9.md: -------------------------------------------------------------------------------- 1 | # 最大信息增益 2 | 3 | 为了使用最大信息增益的特征分割数据,我们需要定义一个在决策树学习过程中的目标函数。此处,我们的目标函数是在每一次分割时最大化信息增益,我们定义如下: 4 | 5 | ![](https://ooo.0o0.ooo/2016/06/21/5769dd8f3d6bb.png) 6 | 7 | 其中,f是具体的特征,$$D_{p}和D_{j}$$是当前数据集和用特征f分割后第j个子节点的数据集,I是某种度量,$$N_{p}$$是当前数据集样本个数,$$N_{j}$$是第j个子节点的数据集中样本个数。为了简化和减小搜索空间,大多数决策树(包括sklearn)都是用二叉树实现的。这意味着每一个父节点被分割为两个子节点,$$D_{left}和D_{right}$$: 8 | 9 | ![](https://ooo.0o0.ooo/2016/06/21/5769fb190d9c6.png) 10 | 11 | 12 | 常用的度量I包括基尼指数(Gini index, $$I_{G}$$)、熵(Entropy, $$I_{H}$$)和分类错误(classification error, $$I_{E}$$)。我们以熵为例: 13 | 14 | ![](https://ooo.0o0.ooo/2016/06/21/5769fbcbe1c78.png) 15 | 16 | 此处,$$p(i|t)$$指的是在节点t中属于类别c的样本占比。所以如果某个节点中所有样本都属于同一个类,则熵为0,如果样本的类别时均匀分布,则熵最大。比如,在二分类情况下,如果p(i=1|t)=1或p(i=0|t)=0,则熵为0.因此,我们说熵的评价标准目的是最大化树中的互信息。 17 | 18 | 19 | 基尼系数可以被理解为最小化误分类的概率: 20 | 21 | ![](https://ooo.0o0.ooo/2016/06/21/5769fcdb6fb31.png) 22 | 23 | 和熵一样,如果节点中样本的类别均匀,则基尼系数最大,比如,在二分类情况下: 24 | 25 | ![](https://ooo.0o0.ooo/2016/06/21/5769fd2f84f95.png) 26 | 27 | 28 | 通常,熵和基尼系数的结果相似,所以不需要花太多时间在选择度量上面。 29 | 30 | 31 | 另一种常用的度量是分类误差: 32 | 33 | ![](https://ooo.0o0.ooo/2016/06/21/5769fd97f352e.png) 34 | 35 | 这个度量更建议在剪枝时使用,而不是在构建决策树时使用。举个简单的例子,说明一下为什么不建议构建树时用: 36 | 37 | ![](https://ooo.0o0.ooo/2016/06/21/5769fe06df61a.png) 38 | 39 | $$D_{p}$$中包含40个正例样本和40个负例样本,然后分割为两个子节点。信息增益使用分类误差作为度量,得到的值在A、B情况下相同,都是0.25,计算过程如下: 40 | 41 | ![](https://ooo.0o0.ooo/2016/06/21/5769fea2248cb.png) 42 | 43 | 如果使用基尼系数,则会按照B情况分割: 44 | 45 | 46 | ![](https://ooo.0o0.ooo/2016/06/21/5769fedf9a5ff.png) 47 | 48 | 同样,如果用熵作为度量,也会按照B分割: 49 | 50 | 51 | ![](https://ooo.0o0.ooo/2016/06/21/5769ff05d59bb.png) 52 | 53 | ![](https://ooo.0o0.ooo/2016/06/21/5769ff167e10c.png) 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /第三章/tree.dot: -------------------------------------------------------------------------------- 1 | digraph Tree { 2 | node [shape=box] ; 3 | 0 [label="petal width <= 0.75\nentropy = 1.5799\nsamples = 105\nvalue = [34, 32, 39]"] ; 4 | 1 [label="entropy = 0.0\nsamples = 34\nvalue = [34, 0, 0]"] ; 5 | 0 -> 1 [labeldistance=2.5, labelangle=45, headlabel="True"] ; 6 | 2 [label="petal length <= 4.95\nentropy = 0.993\nsamples = 71\nvalue = [0, 32, 39]"] ; 7 | 0 -> 2 [labeldistance=2.5, labelangle=-45, headlabel="False"] ; 8 | 3 [label="petal width <= 1.65\nentropy = 0.4306\nsamples = 34\nvalue = [0, 31, 3]"] ; 9 | 2 -> 3 ; 10 | 4 [label="entropy = 0.0\nsamples = 30\nvalue = [0, 30, 0]"] ; 11 | 3 -> 4 ; 12 | 5 [label="entropy = 0.8113\nsamples = 4\nvalue = [0, 1, 3]"] ; 13 | 3 -> 5 ; 14 | 6 [label="petal length <= 5.05\nentropy = 0.1793\nsamples = 37\nvalue = [0, 1, 36]"] ; 15 | 2 -> 6 ; 16 | 7 [label="entropy = 0.8113\nsamples = 4\nvalue = [0, 1, 3]"] ; 17 | 6 -> 7 ; 18 | 8 [label="entropy = 0.0\nsamples = 33\nvalue = [0, 0, 33]"] ; 19 | 6 -> 8 ; 20 | } -------------------------------------------------------------------------------- /第二章/ch2.md: -------------------------------------------------------------------------------- 1 | # 第二章 训练机器学习分类算法 2 | 3 | 在本章,我们要学习第一个有确定性算法描述的机器学习分类算法,感知机(perceptron)和自适应线性神经元(adaptive linear neurons)。 4 | 我们首先用Python实现一个感知机算法,然后将其应用于Iris数据集。通过代码实现会帮助我们更好地理解分类的概念以及如果用Python有效地实现算法。 5 | 学习自适应线性神经元算法涉及到的优化知识,为我们第三章运用scikit-learn中更加复杂的分类器打好数学基础。 6 | 7 | 8 | 本章涉及到的知识点包括: 9 | * 对机器学习算法有直观了解 10 | * 使用pandas, NumPy和matplotlib读入数据,处理数据和可视化数据 11 | * 使用Python实现线性分类算法 12 | -------------------------------------------------------------------------------- /第二章/ch2_section1.md: -------------------------------------------------------------------------------- 1 | # 透过人工神经元一窥早期机器学习历史 2 | 3 | 在我们讨论感知机及其相关算法细节前,先让我们回顾一下机器学习早期的发展历程。为了理解大脑工作原理进而设计人工智能,Warren McCullock和Walter Pitts 在1943年首次提出了一个简化版大脑细胞的概念,即McCullock-Pitts(MCP)神经元(W.S.McCulloch and W.Pitts. *A Logical Calculus of the Ideas 4 | Immanent in Nervous Activity.*)。神经元是大脑中内部连接的神经细胞,作用是处理和传播 化学和电信号,可见下图: 5 | ![](https://ooo.0o0.ooo/2016/06/10/575a6855e1200.png) 6 | 7 | McCullock和Pitts描述了如下的神经细胞:可以看做带有两个输出的简单逻辑门;即有多个输入传递到树突,然后在神经元内部进行输入整合,如果累积的信号量超过某个阈值,会产生一个输出信号并且通过轴突进行传递。 8 | 十几年后,基于MCP神经元模型,Frank Rosenblatt发表了第一个感知机学习规则(F.Rosenblatt, *The Perceptron, a Perceiving and Recognizing Automaton.* Cornell Aeronautical Laboratory, 1957)。 9 | 基于此感知机规则,Rosenblatt提出了能够自动学习最优权重参数的算法,权重即输入特征的系数。在监督学习和分类任务语境中,上面提到的算法还能够用于预测一个样本是属于类别A还是类别B。 10 | 11 | 更准确的描述是,我们可以将上面提到的样本属于哪一个类别这个问题称之为二分类问题(binary classification task),我们将其中涉及到的两个类别记作1(表示正类)和-1(表示负类)。我们再定义一个称为激活函数(activation function) $$\phi(z)$$的东东,激活函数接收一个输入向量$$x$$和相应的权重向量$$w$$的线性组合,其中$$z$$也被称为网络输入($$z=w_{1}x_{1}+...+w_{m}x_{m}$$): 12 | 13 | ![](https://ooo.0o0.ooo/2016/06/10/575a6e93c7042.png) 14 | 15 | 此时,如果某个样本$$x^{(i)}$$的激活值,即$$\phi(z)$$大于事先设置的阈值$$\theta$$,我们就说样本$$x^{(i)}$$属于类别1,否则属于类别-1。 16 | 17 | 在感知机学习算法中,激活函数$$\phi(\cdot)$$的形式非常简单,仅仅是一个单位阶跃函数(也被称为Heaviside阶跃函数): 18 | 19 | ![](https://ooo.0o0.ooo/2016/06/10/575a6f866716d.png) 20 | 为了推导简单,我们可以将阈值$$\theta$$挪到等式左边并且额外定义一个权重参数$$w_{0}=-\theta$$, 这样我们可以对$$z$$给出更加紧凑的公式$$z=w_{0}x_{0}+w_{1}x_{1}+...+w_{m}x_{m}=w^{T}x$$,此时 21 | 22 | ![](https://ooo.0o0.ooo/2016/06/10/575a70ea7e4fb.png) 23 | 24 | 下面左图描述了感知机的激活函数怎样将网络输入$$z=w^{T}x$$压缩到二元输出(-1,1),右图描述了感知机如何区分两个线性可分的类别。 25 | 26 | ![](https://ooo.0o0.ooo/2016/06/10/575a71ba0e84b.png) 27 | 28 | 不论MCP神经元还是Rosenblatt的阈值感知机模型,他们背后的idea都是试图使用简单的方法来模拟大脑中单个神经元的工作方式:要么传递信号要么不传递。因此,Rosenblatt最初的感知机规则非常简单,步骤如下: 29 | * 1. 将权重参数初始化为0或者很小的随机数。 30 | * 2. 对于每一个训练集样本$$x^{(i)}$$,执行下面的步骤: 31 | * * 1、计数输出值 $$\hat{y}$$. 32 | * * 2、更新权重参数. 33 | 34 | 此处的输出值就是单位阶跃函数预测的类别(1,-1),参数向量$$w$$中的每个$$w_{j}$$的更新过程可以用数学语言表示为: 35 | 36 | ![](https://ooo.0o0.ooo/2016/06/10/575a75439903e.png) 37 | 38 | 其中$$\Delta w_{j}$$,用于更新权重$$w_{j}$$,在感知机算法中的计算公式为: 39 | 40 | ![](https://ooo.0o0.ooo/2016/06/10/575a75b230cb5.png) 41 | 其中$$\eta$$ 称为学习率(learning rate), 是一个介于0.0和1.0之间的常数,$$y^{(i)}$$是第i个训练样本的真实类别,$$\hat{y}^{(i)}$$是对第i个训练样本的预测类别。 **权重向量中的每一个参数w_{j}是同时被更新的**,这意味着在所有的$$\Delta w_{j}$$计算出来以前不会重新计算 $$\hat{y}^{(i)}$$(译者注:通俗地说,我们在计算出一个$$\hat{y}^{(i)}后,就能计算出样本i对应的所有的的$$$$\Delta w_{j}$$,然后同时更新w中的每一个权重参数;然后不断重复上面的步骤)。具体地,对于一个二维数据集,我们可以将更新过程写为: 42 | 43 | ![](https://ooo.0o0.ooo/2016/06/10/575a77bd2ba98.png) 44 | 45 | 46 | 在我们用Python实现感知机算法之前,我们先来草算一下这个简单的算法是多么美妙。如果感知机预测的类别正确,则权重参数不做改变,因为: 47 | 48 | ![](https://ooo.0o0.ooo/2016/06/10/575a783a847f8.png) 49 | 当预测结果不正确时,权重会朝着正确类别方向更新(译者注:如果正确类别是1,权重参数会增大;如果正确类别是-1,权重参数会减小): 50 | 51 | ![](https://ooo.0o0.ooo/2016/06/10/575a78a23fde9.png) 52 | 53 | 我们可以具体化上面的乘数$$x_{j}^{(i)}$$,比如一个简单的例子: 54 | 55 | ![](https://ooo.0o0.ooo/2016/06/10/575a78eea6851.png) 56 | 57 | 假设$$x_{j}^{(i)}=0.5$$,我们将样本误分类为-1.此时,更新过程会对权重参数加1,下一次在对样本i计算输出值时,有更大的可能输出1: 58 | 59 | ![](https://ooo.0o0.ooo/2016/06/10/575a7a17cf0a4.png) 60 | 61 | 参数更新$$\Delta w_{j}$$和样本$$x_{j}^{(i)}$$成正比。比如,如果我们有另一个样本$$x_{j}^{(i)}$$=2 被误分类为-1,在更新$$w_{j}$$时会朝着正确方法更新更多(相比较$$x_{j}^{(i)}$$=0.5的情况): 62 | 63 | ![](https://ooo.0o0.ooo/2016/06/10/575a7b3392f8e.png) 64 | 65 | **感知机算法仅在两个类别确实线性可分并且学习率充分小的情况下才能保证收敛**。如果两个类别不能被一个线性决策界分开,我们可以设置最大训练集迭代次数(epoch)或者可容忍的错误分类样本数 来停止算法的学习过程。 66 | 67 | 68 | ![](https://ooo.0o0.ooo/2016/06/10/575a7bfd8564d.png) 69 | 70 | 在进入下一节的代码实现之前,我们来总结一下感知机的要点: 71 | 72 | ![](https://ooo.0o0.ooo/2016/06/10/575a7c3faa0db.png) 73 | 74 | 感知机接收一个样本输入x,然后将其和权重w结合,计算网络输入z。z接着被传递给激活函数,产生一个二分类输出-1或1作为预测的样本类别。在整个学习阶段,输出用于计算预测错误率(y-$$\hat{y}$$)和更新权重参数。 75 | -------------------------------------------------------------------------------- /第二章/ch2_section2.md: -------------------------------------------------------------------------------- 1 | # 使用Python实现感知机算法 2 | 3 | 4 | 在前一节,我们学习了Rosenblatt的感知机如果工作;这一节我们用Python对其进行实现,并且应用于Iris数据集。关于代码的实现,我们使用面向对象的编程思想,定义一个感知机接口作为Python类,类中的方法主要有初始化方法,fit方法和predict方法。 5 | 6 | 7 | 8 | 9 | 10 | 11 | ``` 12 | import numpy as np 13 | 14 | class Perceptron(object): 15 | """Perceptron classifier. 16 | 17 | Parameters 18 | ------------ 19 | eta:float 20 | Learning rate (between 0.0 and 1.0) 21 | n_iter:int 22 | Passes over the training dataset. 23 | 24 | Attributes 25 | ------------- 26 | w_: 1d-array 27 | Weights after fitting. 28 | errors_: list 29 | Numebr of misclassifications in every epoch. 30 | 31 | """ 32 | 33 | def __init__(self, eta=0.01, n_iter=10): 34 | self.eta = eta 35 | self.n_iter = n_iter 36 | 37 | def fit(self, X, y): 38 | """Fit training data. 39 | 40 | Parameters 41 | ------------ 42 | X: {array-like}, shape=[n_samples, n_features] 43 | Training vectors, where n_samples is the number of samples 44 | and n_featuers is the number of features. 45 | y: array-like, shape=[n_smaples] 46 | Target values. 47 | 48 | Returns 49 | ---------- 50 | self: object 51 | """ 52 | 53 | self.w_ = np.zeros(1 + X.shape[1]) # Add w_0 54 | self.errors_ = [] 55 | 56 | for _ in range(self.n_iter): 57 | errors = 0 58 | for xi, target in zip(X, y): 59 | update = self.eta * (target - self.predict(xi)) 60 | self.w_[1:] += update * xi 61 | self.w_[0] += update 62 | errors += int(update != 0.0) 63 | self.errors_.append(errors) 64 | return self 65 | 66 | def net_input(self, X): 67 | """Calculate net input""" 68 | return np.dot(X, self.w_[1:]) + self.w_[0] 69 | 70 | def predict(self, X): 71 | """Return class label after unit step""" 72 | return np.where(self.net_input(X) >= 0.0, 1, -1) #analoge ? : in C++ 73 | 74 | 75 | 76 | ``` 77 | 78 | 有了以上的代码实现,我们可以初始化一个新的Perceptron对象,并且对学习率eta和迭代次数n\_iter赋值,fit方法先对权重参数初始化,然后对训练集中每一个样本循环,根据感知机算法对权重进行更新。类别通过predict方法进行预测。除此之外,self.errors\_ 还记录了每一轮中误分类的样本数,有助于接下来我们分析感知机的训练过程。 79 | -------------------------------------------------------------------------------- /第二章/ch2_section3.md: -------------------------------------------------------------------------------- 1 | # 基于Iris数据集训练感知机模型 2 | 3 | 我们使用Eirs数据集检验上面的感知机代码,由于我们实现的是一个二分类感知机算法,所以我们仅使用Iris中Setosa和Versicolor两种花的数据。为了简便,我们仅使用sepal length和petal length两维度的特征。记住,感知机模型不局限于二分类问题,可以用通过One-vs-All技巧扩展到多分类问题。 4 | 5 | **One-vs-All(OvA)**有时也被称为**One-vs-Rest(OvR)**,是一种常用的将二分类分类器扩展为多分类分类器的技巧。通过OvA技巧,我们为每一个类别训练一个分类器,此时,对应类别为正类,其余所有类别为负类。对新样本数据进行类别预测时,我们使用训练好的所有类别模型对其预测,将具有最高置信度的类别作为最后的结果。对于感知机来说,最高置信度指的是网络输入z绝对值最大的那个类别。 6 | 7 | 8 | 回到刚才的Iris数据集,我们使用pandas读取数据,然后通过pandas中的tail方法输出最后五行数据,看一下Iris数据集格式: 9 | 10 | ![](https://ooo.0o0.ooo/2016/06/11/575bb10b504f9.png) 11 | 12 | 接下来我们抽取出前100条样本,这正好是Setosa和Versicolor对应的样本,我们将Versicolor对应的数据作为类别1,Setosa对应的作为-1。对于特征,我们抽取出sepal length和petal length两维度特征,然后用散点图对数据进行可视化: 13 | 14 | ![](https://ooo.0o0.ooo/2016/06/11/575bb2b641b73.png) 15 | 16 | 现在开始训练我们的感知机模型,为了更好地了解感知机训练过程,我们将每一轮的误分类数目可视化出来,检查算法是否收敛和找到分界线: 17 | 18 | 19 | ![](https://ooo.0o0.ooo/2016/06/11/575bb8472ef5c.png) 20 | 21 | 通过上图我们可以发现,第6次迭代时,感知机算法已经收敛了,対训练集的预测准群率是100%。接下来我们将分界线画出来: 22 | 23 | ![](https://ooo.0o0.ooo/2016/06/11/575bbac9dffd9.png) 24 | 25 | 26 | 虽然对于Iris数据集,感知机算法表现的很完美,但是"收敛"一直是感知机算法中的一大问题。Frank Rosenblatt从数学上证明了只要两个类别能够被一个现行超平面分开,则感知机算法一定能够收敛。然而,如果数据并非线性可分,感知计算法则会一直运行下去,除非我们人为设置最大迭代次数n\_iter。 27 | -------------------------------------------------------------------------------- /第二章/ch2_section4.md: -------------------------------------------------------------------------------- 1 | # 自适应线性神经元及收敛问题 2 | 3 | 4 | 本节我们学习另一种单层神经网络:自适应线性神经元(ADAptive LInear NEuron, 简称Adaline)。在Frank Rosenblatt提出感知计算法不久,Bernard Widrow和他的博士生Tedd Hoff提出了Adaline算法作为感知机的改进算法(B.Widrow et al. Adaptive "Adaline" neuron using chemical "memistors".) 5 | 6 | 7 | 相对于感知机,Adaline算法有趣的多,因为在学习Adaline的过程中涉及到机器学习中一个重要的概念:定义、最小化损失函数。学习Adaline为以后学习更复杂高端的算法(比如逻辑斯蒂回归、SVM等)起到抛砖引玉的作用。 8 | 9 | Adaline和感知机的一个重要区别是Adaline算法中权重参数更新按照线性激活函数而不是单位阶跃函数。当然,Adaline中激活函数也简单的很,$$\phi(z)=\phi(w^{T}x)=w^{T}x$$。 10 | 11 | 虽然Adaline中参数更新不是使用阶跃函数,但是在对测试集样本输出预测类别时还是使用阶跃函数,毕竟要输出离散值-1,1。 12 | 13 | ![](https://ooo.0o0.ooo/2016/06/11/575bcd008be3f.png) 14 | 15 | ## 使用梯度下降算法最小化损失函数 16 | 17 | 在监督机器学习算法中,一个重要的概念就是定义目标函数(objective function),而目标函数就是机器学习算法的学习过程中要优化的目标,目标函数我们常称为损失函数(cost function),在算法学习(即,参数更新)的过程中就是要最小化损失函数。 18 | 19 | 对于Adaline算法,我们定义损失函数为样本真实值和预测值之间的误差平方和(Sum of Squared Erros, SSE): 20 | 21 | ![](https://ooo.0o0.ooo/2016/06/11/575bcdfee1640.png) 22 | 23 | 上式中的系数$$\frac{1}{2}$$完全是为了求导数方便而添加的,没有特殊的物理含义。相对于感知机中的单位阶跃函数,使用连续现行激活函数的一大优点是Adaline的损失函数是可导的。另一个很好的特性是Adaline的损失函数是凸函数,因为,我们可以使用简单而有效的优化算法:梯度下降(gradient descent)来找到使损失函数取值最小的权重参数。 24 | 25 | 26 | 27 | 如下图所示,我们可以把梯度下降算法看做"下山",直到遇到局部最小点或者全局最小点才会停止计算。在每一次迭代过程中,我们沿着梯度下降方向迈出一步,而步伐的大小由学习率和梯度大小共同决定。 28 | 29 | 30 | ![](https://ooo.0o0.ooo/2016/06/11/575cc3c888991.png) 31 | 32 | 使用梯度下降算法,实质就是运用损失函数的梯度来对参数进行更新: 33 | 34 | 35 | ![](https://ooo.0o0.ooo/2016/06/15/5762025444d3e.png) 36 | 37 | 此时,$$\Delta w$$的值由负梯度乘以学习率$$\eta$$确定: 38 | 39 | ![](https://ooo.0o0.ooo/2016/06/15/5762023ee9a12.png) 40 | 41 | 而要计算出损失函数的梯度,我们需要计算损失函数对每一个权重参数的偏导数$$w_{j}$$: 42 | 43 | 44 | 45 | $$\frac{\partial J}{\partial w_{j}}=-\sum_{i}(y^{(i)}-\phi(z^{(i)}))x_{j}^{(i)}$$ 46 | 47 | 因此,$$\Delta w_{j} = -\eta \frac{\partial J}{\partial w_{j}}=\eta \sum_{i}(y^{(i)}-\phi(z^{(i)}))x_{j}^{(i)} $$. 48 | 49 | 注意所有权重参数还是同时更新的,所以Adaline算法的学习规则可以简写: $$\bf{w}:=w+\Delta w$$. 50 | 51 | 虽然简写以后的学习规则和感知机一样,但不要忘了$$\phi(z)$$的不同。此外,还有一点很大的不同是在计算权重更新$$\Delta w$$的过程中:Adaline需要用到所有训练集样本才能一次性更新所有的w,而感知机则是每次用一个训练集样本更新所有权重参数。所以梯度下降法常被称为批量梯度下降 ("batch" gradient descent)。 52 | 53 | 54 | **福利:** 55 | 详细的损失函数对权重的偏导数计算过程为: 56 | ![](https://ooo.0o0.ooo/2016/06/15/5762022941d32.png) 57 | -------------------------------------------------------------------------------- /第二章/ch2_section5.md: -------------------------------------------------------------------------------- 1 | # Python实现自适应线性神经元 2 | 3 | 4 | 5 | 既然感知机和Adaline的学习规则非常相似,所以在实现Adaline的时候我们不需要完全重写,而是在感知机代码基础上进行修改得到Adaline,具体地,我们需要修改fit方法,实现梯度下降算法: 6 | 7 | ``` 8 | class AdalineGD(object): 9 | """ADAptive LInear NEuron classifier. 10 | 11 | Parameters 12 | ---------------- 13 | eta: float 14 | Learning rate (between 0.0 and 1.0) 15 | n_iter: int 16 | Passes over the training dataset. 17 | 18 | Attributes: 19 | ------------------ 20 | w_: 1d-array 21 | Weights after fitting. 22 | errors_: int 23 | Number of misclassification in every epoch. 24 | 25 | """ 26 | 27 | def __init__(self, eta=0.01, n_iter=50): 28 | self.eta = eta 29 | self.n_iter = n_iter 30 | 31 | def fit(self, X, y): 32 | """Fit training data. 33 | 34 | Parameters 35 | --------------- 36 | X: {array-like}, shape=[n_samples, n_features] 37 | Training vectors, 38 | y: array-like, shape=[n_samples] 39 | Target values. 40 | 41 | Returns 42 | ----------- 43 | self: object 44 | """ 45 | self.w_ = np.zeros(1 + X.shape[1]) 46 | self.cost_ = [] 47 | 48 | for i in range(self.n_iter): 49 | output = self.net_input(X) 50 | errors = (y - output) 51 | self.w_[1:] += self.eta * X.T.dot(errors) 52 | self.w_[0] += self.eta * errors.sum() 53 | cost = (errors ** 2).sum() / 2.0 54 | self.cost_.append(cost) 55 | return self 56 | 57 | def net_input(self, X): 58 | """ Calculate net input""" 59 | return np.dot(X, self.w_[1:]) + self.w_[0] 60 | 61 | def activation(self, X): 62 | """ Compute linear activation""" 63 | return self.net_input(X) 64 | 65 | def predict(self, X): 66 | """ Return class label after unit step""" 67 | return np.where(self.activation(X) >= 0.0, 1, -1) 68 | ``` 69 | 70 | 不像感知机那样每次用一个训练样本来更新权重参数,Adaline基于整个训练集的梯度来更新权重。 71 | 72 | **注意,X.T.dot(errors)是一个矩阵和向量的乘法,可见numpy做矩阵计算的方便性。** 73 | ![](https://ooo.0o0.ooo/2016/06/15/576205a8994a9.png) 74 | 75 | 在将Adaline应用到实际问题中时,通常需要先确定一个好的学习率$$\eta$$这样才能保证算法真正收敛。我们来做一个试验,设置两个不同的$$\eta$$值:$$\eta = 0.01, \eta=0.0001$$。然后将每一轮的损失函数值画出来,窥探Adaline是如何学习的. 76 | 77 | (学习率$$\eta$$,迭代轮数n_iter也被称为超参数(hyperparameters),超参数对于模型至关重要,在第四章我们将学习一些技巧,能够自动找到能使模型达到最好效果的超参数。) 78 | 79 | 80 | ![](https://ooo.0o0.ooo/2016/06/15/576209dd63fff.png) 81 | 82 | 83 | 分析上面两幅图各自的问题,左图根本不是在最小化损失函数,反而在每一轮迭代过程中,损失函数值不断在增大!这说明取值过大的学习率不但对算法毫无益处反而危害大大滴。右图虽然能够在每一轮迭代过程中一直在减小损失函数的值,但是减小的幅度太小了,估计至少上百轮迭代才能收敛,而这个时间我们是耗不起的,所以学习率值过小就会导致算法收敛的时间巨长,使得算法根本不能应用于实际问题。 84 | 85 | 下面左图展示了权重再更新过程中如何得到损失函数$$J(w)$$最小值的。右图展示了学习率过大时权重更新,每次都跳过了最小损失函数对应的权重值。 86 | 87 | ![](https://ooo.0o0.ooo/2016/06/15/57620d14cfd33.png) 88 | 89 | 90 | 许多机器学习算法都要求先对特征进行某种缩放操作,比如标准化(standardization)和归一化(normalization)。而缩放后的特征通常更有助于算法收敛,实际上,对特征缩放后在运用梯度下降算法往往会有更好的学习效果。 91 | 92 | 特征标准化的计算很简单,比如要对第j维度特征进行标准化,只需要计算所有训练集样本中第j维度的平均值$$u_{j}$$和标准差$$\sigma_{j}$$即可,然后套公式: 93 | 94 | ![](https://ooo.0o0.ooo/2016/06/15/576211e3c1588.png) 95 | 96 | 标准化后的特征 均值为0,标准差为1. 97 | 98 | 在Numpy中,调用mean和std方法很容易对特征进行标准化: 99 | 100 | ![](https://ooo.0o0.ooo/2016/06/15/576214f00315a.png) 101 | 102 | 标准化后,我们用Adaline算法来训练模型,看看如何收敛的(学习率为0.01): 103 | ![](https://ooo.0o0.ooo/2016/06/15/5762151a50ddd.png) 104 | 105 | 我们将决策界和算法学习情况可视化出来: 106 | 107 | ![](https://ooo.0o0.ooo/2016/06/15/5762156a15730.png) 108 | 109 | Wow 标准化后的数据再使用梯度下降Adaline算法竟然收敛了! 注意看Sum-squared-error(即,$$y-w^{T}x$$)最后并没有等于0,即使所有样本都正确分类。 110 | -------------------------------------------------------------------------------- /第二章/ch2_section6.md: -------------------------------------------------------------------------------- 1 | # 大规模机器学习和随机梯度下降 2 | 3 | 在上一节我们学习了如何使用梯度下降法最小化损失函数,由于梯度下降要用到所有的训练样本,因此也被称为批梯度下降(batch gradient descent)。现在想象一下我们有一个非常大的数据集,里面有几百万条样本,现在用梯度下降法来训练模型,可以想象计算量将是非常大,每一次求梯度都要用到所有的样本。能不能用少量的样本来求梯度呢? 4 | 5 | 随机梯度下降法(stochastic gradient descent)诞生啦!有时也被称为迭代(iteration)/在线(on-line)梯度下降。随机梯度下降法每次只用一个样本对权重进行更新(译者注:唔,感知机算法也如此,转了一圈,历史又回到了起点。): 6 | 7 | ![](https://ooo.0o0.ooo/2016/06/15/57621f9334d6f.png) 8 | 9 | 虽然随机梯度下降被当作是梯度下降的近似算法,但实际上她往往比梯度下降收敛更快,因为相同时间内她对权重更新的更频繁。由于单个样本得到的损失函数相对于用整个训练集得到的损失函数具有随机性,反而会有助于随机梯度下降算法避免陷入局部最小点。在实际应用随机梯度下降法时,为了得到准确结果,一定要以随机方式选择样本计算梯度,通常的做法在每一轮迭代后将训练集进行打乱重排(shuffle)。 10 | 11 | **Notes:**在随机梯度下降法中,通常用不断减小的自适应学习率替代固定学习率$$\eta$$,比如$$\eta = \frac{c_{1}}{[number of iterations] + c_{2}}$$,其中$$c_{1},c_{2}$$是常数。还要注意随机梯度下降并不能够保证使损失函数达到全局最小点,但结果会很接近全局最小。 12 | 13 | 随机梯度下降法的另一个优点是可以用于在线学习(online learning)。在线学习在解决不断累积的大规模数据时非常有用,比如,移动端的顾客数据。使用在线学习,系统可以实时更新并且如果存储空间快装不下数据了,可以将时间最久的数据删除。 14 | 15 | 16 | **Notes** 除了梯度下降算法和随机梯度下降算法之外,还有一种常用的二者折中的算法:最小批学习(mini-batch learning)。很好理解,梯度下降每一次用全部训练集计算梯度更新权重,随机梯度法每一次用一个训练样本计算梯度更新权重,最小批学习每次用部分训练样本计算梯度更新权重,比如50。相对于梯度下降,最小批收敛速度也更快因为权重参数更新更加频繁。此外,最小批相对于随机梯度中,使用向量操作替代for循环(每一次跌倒都要遍历所有样本),使得计算更快。 17 | 18 | 19 | 20 | 上一节我们已经实现了梯度下降求解Adaline,只需要做部分修改就能得到随机梯度下降法求解Adaline。第一个修改是fit方法内用每一个训练样本更新权重参数$$w$$,第二个修改是增加partial_fit方法,第三个修改是增加shuffle方法打乱训练集顺序。 21 | 22 | 23 | ``` 24 | from numpy.random import seed 25 | 26 | class AdalineSGD(object): 27 | """ADAptive LInear NEuron classifier. 28 | 29 | Parameters 30 | --------------- 31 | eta: float 32 | Learning rate (between 0.0 and 1.0) 33 | n_iter: int 34 | Passes over the training dataset. 35 | 36 | Attributes 37 | --------------- 38 | w_: 1d-array 39 | Weights after fitting. 40 | errors_: list 41 | Number of misclassification in every epoch. 42 | shuffle: bool (default: True) 43 | Shuffles training data every epoch 44 | if True to prevent cycles. 45 | random_state: int (default: None) 46 | Set random state for shuffling 47 | and initializing the weights. 48 | 49 | """ 50 | def __init__(self, eta=0.01, n_iter=10, shuffle=True, random_state=None): 51 | self.eta = eta 52 | self.n_iter = n_iter 53 | self.w_initialized = False 54 | self.shuffle = shuffle 55 | if random_state: 56 | seed(random_state) 57 | 58 | def fit(self, X, y): 59 | """Fit training data. 60 | 61 | Parameters 62 | ----------- 63 | X: {array-like}, shape=[n_samples, n_features] 64 | y: array-like, shape=[n_samples] 65 | 66 | Returns 67 | -------------- 68 | self: object 69 | """ 70 | self._initialize_weights(X.shape[1]) 71 | self.cost_ = [] 72 | for i in range(self.n_iter): 73 | if self.shuffle: 74 | X, y = self._shuffle(X, y) 75 | cost = [] 76 | for xi, target in zip(X, y): 77 | cost.append(self._update_weights(xi, target)) 78 | avg_cost = sum(cost)/len(y) 79 | self.cost_.append(avg_cost) 80 | return self 81 | 82 | def partial_fit(self, X, y): 83 | """Fit training data without reinitializing the weights.""" 84 | if not self.w_initialized: 85 | self._initialize_weights(X.shape[1]) 86 | if y.ravel().shape[0] > 1: 87 | for xi, target in zip(X, y): 88 | self._update_weights(xi, target) 89 | else: 90 | self._update_weights(X, y) 91 | return self 92 | 93 | def _shuffle(self, X, y): 94 | """Shuffle training data""" 95 | r = np.random.permutation(len(y)) 96 | return X[r], y[r] 97 | 98 | def _initialize_weights(self, m): 99 | """Initialize weights to zeros""" 100 | self.w_ = np.zeros(1 + m) 101 | self.w_initialized = True 102 | 103 | def _update_weights(self, xi, target): 104 | """Apply Adaline learning rule to update the weights""" 105 | output = self.net_input(xi) 106 | error = (target - output) 107 | self.w_[1:] += self.eta * xi.dot(error) 108 | self.w_[0] += self.eta * error 109 | cost = 0.5 * error ** 2 110 | return cost 111 | 112 | def net_input(self, X): 113 | """Calculate net input""" 114 | return np.dot(X, self.w_[1:]) + self.w_[0] 115 | 116 | def activation(self, X): 117 | """Compute linear activation""" 118 | return self.net_input(X) 119 | 120 | def predict(self, X): 121 | """Return class label after unit step""" 122 | return np.where(self.activation(X) >= 0.0, 1, -1) 123 | 124 | 125 | ``` 126 | 127 | \_shuffle方法的工作方式:调用numpy.random中的permutation函数得到0-100的一个随机序列,然后这个徐列作为特征矩阵和类别向量的下标,就起到了shuffle的功能。 128 | 129 | 我们使用fit方法训练AdalineSGD模型,使用plot_decision_regions对训练结果画图: 130 | 131 | 132 | ![](https://ooo.0o0.ooo/2016/06/17/5764aafc77a76.png) 133 | 134 | 135 | ![](https://ooo.0o0.ooo/2016/06/17/5764ab2180455.png) 136 | 137 | 138 | 139 | 我们可以发现,平均损失(average cost)下降的非常快,在第15次迭代后决策界和使用梯度下降的Adaline决策界非常相似。如果我们要在在线环境下更新模型参数,通过调用partial_fit方法即可,此时参数是一个训练样本,比如ada.partial_fit(X_std[0, :], y[0])。 140 | -------------------------------------------------------------------------------- /第二章/ch2_section7.md: -------------------------------------------------------------------------------- 1 | # 总结 2 | 3 | 本章我们学习了监督学习中关于线性分类器的一些基本概念。我们实现了感知机、带有梯度下降的Adaline和带有随机梯度下降法的Adaline。下一章我们将学习使用Python scikit-learn机器学习库,学习调用其中包含的很多更加强大的分类器。 4 | -------------------------------------------------------------------------------- /第五章/ch.md: -------------------------------------------------------------------------------- 1 | # 第五章 通过降维压缩数据 2 | 3 | 在第四章,你学习了使用不同的特征选择方法来降维,除了特征选择,另一种降维的方法是特征抽取(feature extraction)。本章你将会学到三种基本的方法,帮助我们摘要数据集的信息,即将原始的特征空间压缩到低纬度的特征子空间。数据压缩是机器学习中的重要课题,它能帮助我们存储和分析当今时代不断涌现的大数据。本章,我们主要讨论以下几个内容: 4 | * 主成分分析(principal component analysis, PCA), 用于无监督数据压缩 5 | * 线性判别分析(linear discriminant analysis, LDA), 用于监督降维作为一种监督降维 6 | * 通过核PCA进行非线性降维 7 | 8 | -------------------------------------------------------------------------------- /第五章/ch5_section1.md: -------------------------------------------------------------------------------- 1 | # PCA进行无监督降维 2 | 3 | 4 | 5 | 6 | 类似于特征选择,我们可以使用特征抽取来减小数据集中的特征维度。不过,不同于特征选择算法会保留原始特征空间,特征抽取会将原始特征转换/映射到一个新的特征空间。换句话说,特征抽取可以理解为是一种数据压缩的手段,同时保留大部分相关信息(译者注:理解为摘要)。特征抽取是用于提高计算效率的典型手段,另一个好处是也能够减小维度诅咒(curse of dimensionality),特别是对于没有正则化的模型。 7 | 8 | 9 | 10 | PCA(principal component analysis, 主成分分析)是一种被广泛使用的无监督的线性转换技术,主要用于降维。其他领域的应用还包括探索数据分析和股票交易的信号去噪,基因数据分析和基因表达。 11 | 12 | PCA根据特征之间的相关性帮助我们确定数据中存在的模式。简而言之,PCA的目标是找到高维数据中最大方差的方向,并且将高维数据映射到一个新的子空间,这个子空间的方向不大于原始特征空间。新子空间的正交轴(主成分)可以被解释为原始空间的最大方差方向。下图中$$x_{1}, x_{2}$$是原始特征轴,$$PC1和PC2$$是主成分: 13 | 14 | 15 | ![](https://ooo.0o0.ooo/2016/06/23/576c92f32c7a7.png) 16 | 17 | 18 | 19 | 如果使用PCA降维,我们需要构造一个d*k维的转换矩阵$$W$$,它能将样本向量$$x$$映射到新的k维度的特征子空间,k<0,$$意味着j和k这两维度特征值要么同时增加要么同时衰减,反之$$\sigma_{jk}<0$$,意味着这两个特征的值变化方向相反。 20 | 21 | 22 | 假设数据有三维度特征,协方差矩阵如下: 23 | 24 | 25 | ![](https://ooo.0o0.ooo/2016/06/23/576ca4622a37a.png) 26 | 27 | 协方差矩阵的特征向量代表了主成分(最大方差的方向),对应的特征值决定了特征向量绝对值的大小。在Wine数据集对应的13*13的协方差矩阵,我们会得到13个特征值。 28 | 29 | 30 | 如何得到特征值和特征向量呢?会议线性代数课上讲过的内容: 31 | 32 | 33 | ![](https://ooo.0o0.ooo/2016/06/23/576ca53ab284d.png) 34 | 35 | 其中,$$\lambda是特征值,一个常数$$。我们当然不可能手算特征值,NumPy提供了linalg.eig函数用于得到特征值和特征向量: 36 | 37 | 38 | ![](https://ooo.0o0.ooo/2016/06/23/576ca60a1be22.png) 39 | 40 | 41 | 42 | 43 | 44 | 45 | 我们先使用np.cov方法得到数据的协方差矩阵,然后利用linalg.eig方法计算出特征向量(eigen_vecs)和特征值(eigen_vals)。 46 | 47 | 48 | 49 | 由于我们的目的是数据压缩,即降维,所以我们只将那些包含最多信息(方差)的特征向量(主成分)拿出来。什么样的特征向量包含的信息最多呢?这就要看特征值了,因为特征值定义了特征向量的大小,我们先对特征值进行降序排序,前k个特征值对应的特征向量就是我们要找的主成分。 50 | 51 | 52 | 53 | 54 | 55 | 我们再学一个概念:方差解释率(variance explained ration)。一个特征值的方差解释率就是次特征值在特征值总和的占比: 56 | 57 | ![](https://ooo.0o0.ooo/2016/06/23/576ca832336b3.png) 58 | 59 | 60 | 61 | 利用NumPy提供的cumsum函数,我们可以计算累计解释方差和: 62 | 63 | ![](https://ooo.0o0.ooo/2016/06/23/576caa5672366.png) 64 | 65 | 66 | ![](https://ooo.0o0.ooo/2016/06/23/576caa772eaa7.png) 67 | 68 | 69 | 从上面的结果图我们可以看到第一个主成分占了近40%的方差(信息),前两个主成分占了60%的方差。 70 | 71 | 72 | 很多同学看到这里,可能将方差解释率和第四章讲到的用随机森林评估特征重要性联系起来,二者还是有很大区别的,PCA是一种无监督方法,在整个计算过程中我们都没有用到类别信息!随机森林是监督模型,建模时用到了类别信息。 73 | 74 | 方差的物理含义是对值沿着特征轴的传播进行度量。 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /第五章/ch5_section3.md: -------------------------------------------------------------------------------- 1 | 2 | # 特征转换 3 | 4 | 5 | 6 | 在得到特征向量后,接下来我们就可以对原始特征进行转换了。本节我们先对特征值进行降序排序,然后用特征向量构建映射矩阵,最后用映射矩阵将原始数据映射到低维度特征子空间。 7 | 8 | 9 | 10 | 先对特征值排序: 11 | 12 | ![](https://ooo.0o0.ooo/2016/06/23/576caccb7e3dd.png) 13 | 14 | 接下来,我们选择最大的两个特征值对应的特征向量,这里只用两个特征向量是为了下面画图方便,在实际运用PCA时,到底选择几个特征向量,要考虑到计算效率和分类器的表现两个方面(译者注:常用的选择方式是特征值子集要包含90%方差): 15 | 16 | ![](https://ooo.0o0.ooo/2016/06/23/576cada4e1218.png) 17 | 18 | 19 | 现在,我们创建了一个13*2的的映射矩阵W。然后对样本(1*13维度)进行映射,就能得到2维度的新样本: 20 | 21 | ![](https://ooo.0o0.ooo/2016/06/23/576cae20c2eac.png) 22 | 23 | ![](https://ooo.0o0.ooo/2016/06/23/576cae452595c.png) 24 | 25 | 26 | 直接对原始数据(124*13)映射: 27 | 28 | 29 | 30 | ![](https://ooo.0o0.ooo/2016/06/23/576caea74df5e.png) 31 | 32 | ![](https://ooo.0o0.ooo/2016/06/24/576cd63fd542e.png) 33 | 34 | 35 | 36 | 特征维度降到2维度后,我们就可以用散点图将数据可视化出来了: 37 | 38 | ![](https://ooo.0o0.ooo/2016/06/24/576cd777445bb.png) 39 | 40 | ![](https://ooo.0o0.ooo/2016/06/24/576cd79c21087.png) 41 | 42 | 43 | 从上图可以看到,数据在x轴(第一主成分)上要比y轴(第二主成分)分布更广,这也符合方差解释率的结果。数据降维后,直觉上使用线性分类器就能够将数据分类。 44 | 45 | 46 | ## scikit-learn中的PCA 47 | 48 | 上一小节我们详细讨论了PCA的步骤,在实际应用时,一般不会使用自己实现,而是直接调用sklearn中的PCA类,PCA类是另一个transformer类:我们先用训练集训练模型参数,然后统一应用于训练集和测试集。 49 | 50 | 51 | 下面我们就是用sklearn中的PCA类对Wine数据降维,然后调用逻辑斯蒂回归模型分类,最后将决策界可视化出来: 52 | 53 | 54 | 55 | ![](https://ooo.0o0.ooo/2016/06/24/576cdb747acfa.png) 56 | 57 | 58 | ![](https://ooo.0o0.ooo/2016/06/24/576cdb888be31.png) 59 | 60 | 61 | 62 | 执行上面的代码,我们应该得到如下的决策界: 63 | 64 | 65 | ![](https://ooo.0o0.ooo/2016/06/24/576cdbb2bd6f8.png) 66 | 67 | 68 | 69 | 如果你仔细观察我们自己实现的PCA得到的散点图和调用sklearn中PCA得到的散点图,两幅图看起来是镜像关系!这是由于NumPy和sklearn求解特征向量时计算的差异,如果你实在看不惯,只需要将其中一个得到的特征向量*(-1)即可。还要注意特征向量一般都要归一化。 70 | 71 | 我们再看看决策界在测试集的分类效果: 72 | 73 | 74 | ![](https://ooo.0o0.ooo/2016/06/24/576cdd9177ed5.png) 75 | 76 | 77 | 78 | 79 | 80 | 啊哈!测试集仅有一个样本被错误分类,效果很棒。 81 | 82 | 83 | 怎样用sklearn的PCA得到每个主成分(不是特征)的方差解释率呢?很简单,初始化PCA时,n_components设置为None,然后通过explained_variance_ratio_属性得到每个主成分的方差解释率: 84 | 85 | 86 | ![](https://ooo.0o0.ooo/2016/06/24/576cde8541c6e.png) 87 | 88 | 89 | 此时n_components=None, 我们并没有做降维。 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /第五章/ch5_section4.md: -------------------------------------------------------------------------------- 1 | # LDA进行监督数据压缩 2 | 3 | LDA(linear discriminant analysis, 线性判别分析)是另一种用于特征抽取的技术,它可以提高计算效率,对于非正则模型也能减小过拟合。 4 | 5 | 虽然LDA的很多概念和PCA很像,但他俩的目标不同,PCA目标是找到正交的主成分同时保持数据集的最大方差,LDA的目标是为每个类单独优化,得到各个类的最优特征子集。PCA和LDA都是线性转换技术,用于数据压缩,前者是无监算法,后者是监督算法。看到监督两个字,可能你会认为对于分类任务,LDA要比PCA效果更好,但实际却不是这样,在某些分类任务情境下,用PCA预处理数据得到的结果要比LDA号,比如,如果每个类含有的样本比较少。 6 | 7 | 8 | 9 | 下图画出了对于二分类问题,LDA的一些概念: 10 | 11 | ![](https://ooo.0o0.ooo/2016/06/24/576ce4747d450.png) 12 | 13 | 14 | 15 | x轴的线性判别(LD 1),很好地将两个正态分布的类别数据分离。虽然y轴的线性判别(LD 2)捕捉了数据集中的大量方差,但却不是一个好的线性判别,因为它没有捕捉任何与类别判别相关的信息。 16 | 17 | 18 | 19 | LDA 的一个假设是数据服从正态分布。同时,我们也假设各个类含有相同的协方差矩阵,每个特征都统计独立。即使真实数据可能不服从上面的某几个假设,但LDA依然具有很好的表现。 20 | 21 | 22 | 在学习LDA内部原理前,我们先将它的几大步骤列出来: 23 | * 1. 将d维度原始数据进行标准化. 24 | * 2. 对每一个类,计算d维度的平均向量. 25 | * 3. 构建类间(between-class)散点矩阵$$S_{B}$$和类内(within-class)散点矩阵$$S_{W}$$. 26 | * 4. 计算矩阵$$S_{W}^{-1}S_{B}$$的特征向量和特征值. 27 | * 5. 选择值最大的前k个特征值对应的特征向量,构建d*d维度的转换矩阵$$W$$,每一个特征向量是$$W$$的一列. 28 | * 6. 使用矩阵$$W$$将原始数据集映射到新的特征子空间. 29 | 30 | **Note** 我们在应用LDA时做出的假设是:特征服从正态分布并且彼此独立,每个类的协方差矩阵都相同。现实中的数据当然不可能真的全部服从这些假设,但是不用担心,即使某一个甚至多个假设不成立,LDA也有不俗的表现.(R.O.Duda, P.E. Hart, and D.G.Stork. *Pattern Classification*. 2nd.Edition. New York, 2001). 31 | 32 | 33 | 34 | 35 | # 计算散点矩阵 36 | 37 | 数据标准化前面已经说过了,这里就不再讲了,我们说一下如何计算平均向量,然后用这些平均向量分别构建类内散点矩阵和类间散点矩阵。 38 | 39 | 每一个平均向量$$m_{i}$$存储了类别i的样本的平均特征值$$u_{m}$$: 40 | 41 | 42 | 43 | ![](https://ooo.0o0.ooo/2016/06/24/576cf81b58b4d.png) 44 | 45 | Wine数据集有三个类,每一个的平均向量: 46 | 47 | 48 | 49 | 50 | ![](https://ooo.0o0.ooo/2016/06/24/576cf85702d4d.png) 51 | 52 | 53 | 54 | 55 | 56 | ![](https://ooo.0o0.ooo/2016/06/24/576cf94e759d4.png) 57 | 58 | 59 | 有了平均向量,我们就可以计算类内散点矩阵$$S_{W}$$: 60 | 61 | ![](https://ooo.0o0.ooo/2016/06/24/576cf97a62714.png) 62 | 63 | $$S_{W}$$就是每个类的散点矩阵$$S_{i}$$总和。 64 | 65 | ![](https://ooo.0o0.ooo/2016/06/24/576cf998c8d6b.png) 66 | 67 | 68 | 69 | 代码: 70 | 71 | 72 | ![](https://ooo.0o0.ooo/2016/06/24/576cfa97f09ee.png) 73 | 74 | 75 | 76 | 当我们计算散点矩阵时,我们做出的假设是训练集中的类别时均匀分布的。实际情况往往并不是这样,比如我们将Wine训练集各个类别个数打印出来: 77 | 78 | 79 | ![](https://ooo.0o0.ooo/2016/06/24/576cfb31c266f.png) 80 | 81 | 所以在得到每个类别的散点矩阵$$S_{i}$$后,我们要将其缩放,然后再相加得到$$S_{W}$$。如果我们将散点矩阵$$S_{i}$$除以各个类别内样本数$$N_{i}$$,我们实际上是在计算协方差矩阵$$\Sigma_{i}$$. **协方差矩阵是散点矩阵的归一化结果**: 82 | 83 | ![](https://ooo.0o0.ooo/2016/06/24/576cfbe871c14.png) 84 | 85 | ![](https://ooo.0o0.ooo/2016/06/24/576cfc82cd01f.png) 86 | 87 | 88 | 得到缩放后的类内散点矩阵后,我们接下来计算类间散点矩阵$$S_{B}$$: 89 | 90 | ![](https://ooo.0o0.ooo/2016/06/24/576cfcd0b45f6.png) 91 | 92 | 其中,$$m$$是整个训练集所有样本的特征平均值. 93 | 94 | 95 | ![](https://ooo.0o0.ooo/2016/06/24/576d213f4cc3b.png) 96 | 97 | 98 | 99 | 100 | 101 | 102 | # 为特征子空间选择线性判别式 103 | 104 | 105 | 剩下的LDA步骤和PCA很像了。不同点是PCA分解协方差矩阵得到特征值和特征向量,LDA分解$$S_{W}^{-1}S_{B}$$得到特征值和特征向量: 106 | 107 | 108 | ![](https://ooo.0o0.ooo/2016/06/24/576d215eac03e.png) 109 | 110 | 得到特征值后,对其降序排序: 111 | 112 | 113 | ![](https://ooo.0o0.ooo/2016/06/24/576d217898800.png) 114 | 115 | 116 | 我们可以看到只有两个特征值不为0,其余11个特征值实质是0,这里只不过由于计算机浮点表示才不为0。极端情况是特征向量共线,此时协方差矩阵秩为1,只有一个非0特征值。 117 | 118 | 119 | 为了度量线性判别式(特征向量)捕捉到了多少的类判别信息,我们画出类似方差解释率的线性判别图: 120 | 121 | ![](https://ooo.0o0.ooo/2016/06/24/576d238216c32.png) 122 | 123 | 124 | 125 | 我们可以看到前两个线性判别捕捉到了Wine训练集中100%的有用信息: 126 | 127 | ![](https://ooo.0o0.ooo/2016/06/24/576d23d1144f3.png) 128 | 129 | 然后由这两个线性判别式来创建转换矩阵$$W$$: 130 | 131 | ![](https://ooo.0o0.ooo/2016/06/24/576d245b7115d.png) 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /第五章/ch5_section5.md: -------------------------------------------------------------------------------- 1 | # 原始数据映射到新特征空间 2 | 3 | 有了转换矩阵$$W$$,我们就可以将原始数据映射到新的特征空间了: 4 | 5 | 6 | ![](https://ooo.0o0.ooo/2016/06/24/576d24cc6333f.png) 7 | 8 | 9 | ![](https://ooo.0o0.ooo/2016/06/24/576d2569395e9.png) 10 | 11 | 12 | 13 | 新数据集,明显线性可分: 14 | 15 | 16 | ![](https://ooo.0o0.ooo/2016/06/24/576d259f4809a.png) 17 | 18 | 19 | # 调用sklearn中LDA 20 | 21 | 我们通过一步步地实现LDA来加深理解,现在看看sklearn中如何使用现成的LDA: 22 | 23 | ![](https://ooo.0o0.ooo/2016/06/24/576d26907a38c.png) 24 | 25 | 26 | 27 | 有一个样本被错分类: 28 | 29 | ![](https://ooo.0o0.ooo/2016/06/24/576d26b0af864.png) 30 | 31 | 如果降低正则项的影响,完全正确分类训练集。当然了,过拟合并没有什么好处。我们看一下现在模型对测试集的分类效果: 32 | 33 | 34 | 35 | 36 | ![](https://ooo.0o0.ooo/2016/06/24/576d274c58876.png) 37 | 38 | 39 | Wow!100%的准确率。 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /第五章/ch5_section6.md: -------------------------------------------------------------------------------- 1 | # 使用核PCA进行非线性映射 2 | 3 | 许多机器学习算法都有一个假设:输入数据要是线性可分的。感知机算法必须针对完全线性可分数据才能收敛。考虑到噪音,Adalien、逻辑斯蒂回归和SVM并不会要求数据完全线性可分。 4 | 5 | 但是现实生活中有大量的非线性数据,此时用于降维的线性转换手段比如PCA和LDA效果就不会太好。这一节我们学习PCA的核化版本,核PCA。这里的"核"与核SVM相近。 运用核PCA,我们能将非线性可分的数据转换到新的、低维度的特征子空间,然后运用线性分类器解决。 6 | 7 | 8 | 9 | ![](https://ooo.0o0.ooo/2016/06/24/576d29033ad87.png) 10 | 11 | # 核函数和核技巧 12 | 13 | 14 | 15 | 16 | 17 | 18 | 还记得在核SVM那里,我们讲过解决非线性问题的手段是将他们映射到新的高维特征空间,此时数据在高维空间线性可分。为了将数据$$x\in R^{d}$$映射到高维k空间,我们定义了**非线性映射**函数$$\phi$$: 19 | 20 | 21 | ![](https://ooo.0o0.ooo/2016/06/24/576d29bda8386.png) 22 | 23 | 24 | 25 | 我们可以把核函数的功能理解为:通过创造出原始特征的一些非线性组合,然后将原来的d维度数据集映射到k维度特征空间,dL>M。而T恤颜色是无序的。 6 | 7 | 8 | 9 | 在讲解处理分类数据的技巧之前,我们先创建一个新的DataFrame对象: 10 | 11 | 12 | 13 | 14 | 15 | ![](https://ooo.0o0.ooo/2016/06/22/576a864c41ff4.png) 16 | 17 | 18 | 上面创建的数据集含有无序特征(color),有序特征(size)和数值型特征(price)。最后一列存储的是类别。在本书中类别信息都是无序的。 19 | 20 | 21 | ## 映射有序特征 22 | 23 | 24 | 25 | 为了保证学习算法能够正确解释有序特征(ordinal feature),我们需要将分类型字符串转为整型数值。不幸地是,并没有能够直接调用的方法来自动得到正确顺序的size特征。因此,我们要自己定义映射函数。在接下来的简单的示例,假设我们知道特征取值间的不同,比如 XL=L+1=M+2。 26 | 27 | 28 | 29 | 30 | ![](https://ooo.0o0.ooo/2016/06/22/576a898ee07cd.png) 31 | 32 | 33 | 34 | 35 | 如果我们还想将整型变量转换回原来的字符串表示,我们还可以定义一个反映射字典 inv_size_mapping={v: k for k, v in size_mapping.items()}。 36 | 37 | 38 | ## 对类别进行编码 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 许多机器学习库要求类别是整型数值。虽然sklearn中大部分Estimator都能自动将类别转为整型,我还是建议大家手动将类别进行转换。对类别进行编码,和上一节中转化序列特征很相似。但不同的是类别是无序的,所以我们可以从0开始赋整数值: 47 | 48 | 49 | 50 | ![](https://ooo.0o0.ooo/2016/06/22/576b37f3e8ea8.png) 51 | 52 | 53 | 接下来我们可以利用映射字典对类别进行转换: 54 | 55 | 56 | ![](https://ooo.0o0.ooo/2016/06/22/576b38356b4e1.png) 57 | 58 | 59 | 得到整型类别值,也可以用映射字典转为原始的字符串值: 60 | 61 | ![](https://ooo.0o0.ooo/2016/06/22/576b38c2b4b0e.png) 62 | 63 | 64 | 65 | 上面是我们自己手动创建的映射字典,sklearn中提供了LabelEncoder类来实现类别的转换: 66 | 67 | ![](https://ooo.0o0.ooo/2016/06/22/576b39344545c.png) 68 | 69 | 70 | 71 | fit_transform方法是fit和transform两个方法的合并。我们还可以调用inverse_transform方法得到原始的字符串类型值: 72 | 73 | 74 | 75 | ![](https://ooo.0o0.ooo/2016/06/22/576b399a0fe55.png) 76 | 77 | 78 | 79 | 80 | ## 对离散特征进行独热编码 81 | 82 | 前面一节我们使用字典映射来转化有序特征,由于sklearn中Estimator把类型信息看做无序的,我们使用LabelEncoder来进行类别的转换。而对于无序的离散特征,我们也可以使用LabelEncoder来进行转换: 83 | 84 | 85 | ![](https://ooo.0o0.ooo/2016/06/22/576b3af4ee4cc.png) 86 | 87 | 现在我们将无序离散特征转换为整型了,看起来下一步就是直接训练模型了。如果你这样想,恭喜你,你犯了一个很专业的错误。在处理分类型数据(categorical data)时,这是很常见的错误。你能发现问题所在吗?虽然“颜色”这一特征的值不含有顺序,但是由于进行了以下转换: 88 | 89 | ![](https://ooo.0o0.ooo/2016/06/22/576b3bb04ba79.png) 90 | 91 | 学习算法会认为‘green’比‘blue’大,‘red’比‘green’大。而这显然是不正确的,因为本身颜色是无序的!模型错误的使用了颜色特征信息,最后得到的结果肯定不是我们想要的。 92 | 93 | 94 | 95 | 那么如何处理无序离散特征呢?常用的做法是**独热编码(one-hot encoding)**。独热编码会为每个离散值创建一个哑特征(dummy feature)。什么是哑特征呢?举例来说,对于‘颜色’这一特征中的‘蓝色’,我们将其编码为[蓝色=1,绿色=0,红色=0],同理,对于‘绿色’,我们将其编码为[蓝色=0,绿色=1,红色=0],特点就是向量只有一个1,其余均为0,故称之为one-hot。 96 | 97 | 在sklearn中,可以调用OneHotEncoder来实现独热编码: 98 | 99 | ![](https://ooo.0o0.ooo/2016/06/22/576b3e13f04b1.png) 100 | 101 | 102 | 在初始化OneHotEncoder时,通过categorical_features参数设置要进行独热编码的列。还要注意的是OneHotEncoder的transform方法默认返回稀疏矩阵,所以我们调用toarray()方法将稀疏矩阵转为一般矩阵。我们还可以在初始化OneHotEncoder时通过参数sparse=False来设置返回一般矩阵。 103 | 104 | 105 | 除了使用sklearn中的OneHotEncoder类得到哑特征,我推荐大家使用pandas中的get_dummies方法来创建哑特征,get_dummies默认会对DataFrame中所有字符串类型的列进行独热编码: 106 | 107 | ![](https://ooo.0o0.ooo/2016/06/22/576b3f50a76fc.png) 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /第四章/ch4_section6.md: -------------------------------------------------------------------------------- 1 | # 将数据集分割为训练集和测试集 2 | 3 | 4 | 5 | 6 | 7 | 本节,我们使用一个新的数据集:Wine。Wine也属于UCI开源数据集,它包含178个样本,每个样本有13维度特征,描述了不同的化学属性。 8 | 9 | 10 | ![](https://ooo.0o0.ooo/2016/06/22/576b4766172b7.png) 11 | 12 | 13 | 14 | 15 | Wine数据集一共有三个类别:1,2和3.表示三个葡萄品种。 16 | 17 | 18 | 首先将数据集随机分割为训练集和测试集,一种简单的方法是使用sklearn.cross_validation中的train_test_split方法: 19 | ![](https://ooo.0o0.ooo/2016/06/22/576b4843f1b99.png) 20 | 21 | 22 | 解释一下上面的代码: 首先,我们将特征矩阵赋值给X,将类别向量赋值给y,然后调用train_test_split方法随机分割X和y。通过设置test_size=0.3,使得训练集占Wine样本数的70%,测试集占30%。 23 | 24 | 25 | **Note** 在分割数据集时,如果确定训练集和测试集的大小没有通用的做法,一般我们选择60:40, 70:30或者80:20。对于大数据集,90:10甚至 99:1也是比较常见的。还要注意的是,通过本地验证得到最优模型和参数时,还要在整个数据集(训练集+验证集+测试集)上训练一次,得到最终的模型。 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /第四章/ch4_section7.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 统一特征取值范围 4 | 5 | 特征缩放(feature scaling)是预处理阶段的关键步骤,但常常被遗忘。虽然存在决策树和随机森林这种是少数不需要特征缩放的机器学习算法,但对于大部分机器学习算法和优化算法来说,如果特征都在同一范围内,会获得更好的结果。比如第二章提到的梯度下降法。 6 | 7 | 8 | 9 | 特征缩放的重要性可以通过一个简单的示例解释。假设我们有两个特征,一个特征的取值范围是[1,10],另一个特征的取值范围是[1,100000]。我们使用Adaline中的平方误差函数,很明显,权重更新时会主要根据第二维度特征,这就使得在权重更新过程中第一个特征的话语权很小。另一个例子是如果kNN算法用欧氏距离作为距离度量,第二维度特征也占据了主要的话语权。 10 | 11 | 12 | 13 | 有两种方法能使不同的特征有相同的取值范围:归一化(normalization)和标准化(standardization)。两种方法还是有必要区分一下的。归一化指的是将特征范围缩放到[0,1],是最小-最大缩放(min-max scaling)的特例。为了得到归一化结果,我们对每一个特征应用最小-最大缩放,计算公式如下: 14 | 15 | 16 | 17 | ![](https://ooo.0o0.ooo/2016/06/22/576b4eaa08df1.png) 18 | 19 | 其中,$$x_{norm}^{(i)}$$是$$x^{(i)}$$归一化后的结果,$$x_{min}$$是对应的列特征最小值,$$x_{max}$$则是最大值。 20 | 21 | 22 | sklearn中实现了最小-最大缩放,调用MinMaxScaler类即可: 23 | 24 | 25 | 26 | ![](https://ooo.0o0.ooo/2016/06/22/576b4f8a6ceaf.png) 27 | 28 | 29 | 30 | 虽然归一化方法简单,但相对来说,标准化对于大部分机器学习算法更实用。原因是大部分线性模型比如逻辑斯蒂回归和线性SVM在初始化权重参数时,要么选择0要么选择一个接近0的随机数。实用标准化,我们能将特征值缩放到以0为中心,标准差为1,换句话说,标准化后的特征形式服从正态分布,这样学习权重参数更容易。此外,标准化后的数据保持了异常值中的有用信息,使得算法对异常值不太敏感,这一点归一化就无法保证。 31 | 32 | 33 | 标准化的计算公式如下: 34 | 35 | ![](https://ooo.0o0.ooo/2016/06/22/576b5114d083c.png) 36 | 37 | 此时,$$u_{x}$$是训练集对应特征列的平均值,$$\sigma_{x}$$是对应特征列的标准差。 38 | 39 | 下面一张表使用一个简单的例子,展示了标准化和归一化的区别: 40 | ![](https://ooo.0o0.ooo/2016/06/22/576b51a76b9ea.png) 41 | 42 | 43 | sklearn中提供了StandardScalar类实现列标准化: 44 | 45 | 46 | 47 | ![](https://ooo.0o0.ooo/2016/06/22/576b522ed91da.png) 48 | 49 | 50 | **再次强调,StandardScaler只使用训练集fit一次,这样保证训练集和测试集使用相同的标准进行的特征缩放**。 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /第四章/ch4_section8.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 选择有意义的特征 4 | 5 | 如果一个模型在训练集的表现比测试集好很多,那我们就要小心了,模型很可能过拟合了。过拟合意味着模型捕捉了训练集中的特例模式,但对未知数据的泛化能力比较差,我们也说模型此时具有高方差。 6 | 7 | 模型过拟合的一个原因是对于给定的训练集数据,模型过于复杂,常用的减小泛化误差的做法包括: 8 | * 收集更多的训练集数据 9 | * 正则化,即引入模型复杂度的惩罚项 10 | * 选择一个简单点的模型,参数少一点的 11 | * 降低数据的维度 12 | 13 | 14 | 在上面的一系列做法中,第一条收集更多数据通常不实用。在下一章,我们会学习一个有用的技巧来判断更多的训练集数据是否有帮助。在接下来的章节,我们学习正则化和特征选择的方法来降低过拟合。 15 | 16 | 17 | ## L1正则 18 | 19 | 会议第三章,我们运用过L2正则来降低模型的复杂度,当时我们定义的L2正则项: 20 | 21 | 22 | ![](https://ooo.0o0.ooo/2016/06/22/576b57f087fe7.png) 23 | 24 | 25 | 除了L2正则,另一个中减低模型复杂度的方法是L1正则(L1 regularization): 26 | 27 | ![](https://ooo.0o0.ooo/2016/06/22/576b582456836.png) 28 | 29 | L2正则项是权重参数的平方和,而L1正则项是权重参数的绝对值和。相对于L2, L1正则项趋向于得到稀疏特征向量,即很多特征权重参数为0.如果数据集的特征维度很高且特征不相干(极端情况是 不相干的特征维度数目比训练样本数还大),特征稀疏性是非常有用的。 由于很多特征权重为0,所以,很多人也把L1正则看做特征选择的一种方式。 30 | 31 | 32 | 为了更好地理解L1正则倾向于产生稀疏特征,让我们看一下正则化的几何解释。假设损失函数是差平方损失函数(sum of the squared errors, SSE),且只含有两个权重参数$$w_{1}, w_{2}$$,易知损失函数是凸函数。对于给定的损失函数,我们的目的是找到损失函数取最小值时对应的权重值,如下图损失函数等高线所示,当($$w_{1},w_{2}$$)取椭圆中心点时,损失函数值最小: 33 | 34 | 35 | ![](https://ooo.0o0.ooo/2016/06/23/576b8d0681fc4.png) 36 | 37 | 38 | 而正则项是对现在损失函数的惩罚项,它鼓励权重参数取小一点的值,换句话说,正则项惩罚的是大权重参数。 39 | 40 | 因此,如果增大正则系数$$\lambda$$的值,也就增大了正则项的威力,也就会导致权重参数变小(趋向于0),从而也减小了模型对训练数据的依赖。我们在下图画出L2惩罚项: 41 | 42 | ![](https://ooo.0o0.ooo/2016/06/23/576b8e1a721d3.png) 43 | 44 | 45 | L2正则项用图上阴影球形表示$$w_{1}^{2}+w_{2}^{2}<=\frac{1}{\lambda}$$。**正则化后的 新损失函数=原始损失函数+正则项,我们可以换一种思路解释这个公式,新的损失函数还是原始损失函数,我们把正则项看做权重参数的限制条件,也就是说,权重参数的取值范围必须在图中阴影球形内**。如果增大$$\lambda$$的值,会缩小阴影球形面积。比如,如果$$\lambda$$趋向正无穷,则阴影面积趋向0,权重参数也趋向0. 46 | 47 | 我们总结一下:我们的目标是最小化原始损失函数和正则项,等价于在原始损失函数基础上增加限制条件,更加偏向于简单模型来减小方差。 48 | 49 | 50 | 现在我们讨论L1正则项和稀疏性。L1正则和刚才讨论的L2正则很像。当然区别还是有的:L1正则是权重参数绝对值的和,我们用一个另行区域表示,如下图所示: 51 | 52 | 53 | 54 | ![](https://ooo.0o0.ooo/2016/06/23/576b939fa6338.png) 55 | 56 | 57 | 由于菱形的特点,在和损失函数等高线相交时,最小点很可能落在坐标轴上,这就导致了特征的稀疏性。如果你对这背后的数学感兴趣,推荐阅读 *The Elements of Statistical Learning, Trevor Hastie, Robert Tibshirani, and Friedman, Springer*。 58 | 59 | 60 | 对于sklearn中那些支持L1正则的模型,我们只需要在初始化时用penalty参数设置为L1正则即可: 61 | 62 | 63 | 64 | ![](https://ooo.0o0.ooo/2016/06/23/576b95920e99a.png) 65 | 66 | 67 | 将L1正则逻辑斯蒂回归应用到标准化后的Wine数据集: 68 | 69 | ![](https://ooo.0o0.ooo/2016/06/23/576b961255616.png) 70 | 71 | 72 | 模型在训练集和测试集上的准确率说明没有过拟合。如果我们调用lr.intercept_属性,可以发现只返回了三个值: 73 | 74 | 75 | ![](https://ooo.0o0.ooo/2016/06/23/576b9676a7e65.png) 76 | 77 | 78 | 由于Wine数据集是多类别数据,所以lr使用了One-vs-Rest(OvR)方法,所以上面三个值分别属于三个模型:第一个模型用类别1 vs 类别2和3;第二个模型用类别2 vs 类别1和3;第三个模型用类别3 vs 类别1和2。 79 | 80 | 81 | ![](https://ooo.0o0.ooo/2016/06/23/576b9726ddac5.png) 82 | 83 | 84 | 通过lr.coef_得到权重数组,共三行,每一个类对应一行。每一行有13个参数,对应13个特征。网络输入计算如下: 85 | 86 | 87 | ![](https://ooo.0o0.ooo/2016/06/23/576b9a7bc8c6c.png) 88 | 89 | 我们可以发现权重向量中有很多0值,这说明L1正则可以作为特征选择的一种手段,得到的模型具有鲁棒性。 90 | 91 | 92 | 93 | 最后,我们画出正则路径,即不同正则威力下的不同特征的权重参数: 94 | 95 | 96 | ![](https://ooo.0o0.ooo/2016/06/23/576b9c806a05f.png) 97 | 98 | 99 | ![](https://ooo.0o0.ooo/2016/06/23/576b9c9718361.png) 100 | 101 | 102 | 我们可以发现,如果C<0.1,正则项威力很大时,所有特征权重都为0,$$C=\frac{1}{\lambda}$$。 103 | 104 | 105 | 106 | ## 序列特征选择算法 107 | 108 | 另一种减小模型复杂度和避免过拟合的方法是通过特征选择进行维度降低(dimensionality reduction),这个方法尤其对非正则模型有用。维度降低有两种做法:特征选择(feature selection)和特征抽取(feature extraction)。 109 | 110 | 特征选择会从原始特征集中选择一个子集合。特征抽取是从原始特征空间抽取信息,从而构建一个新的特征子空间。本节,我们学习特征选择算法。在下一章,我们会学到不同的特征抽取方法来将数据集压缩到一个低维度特征子空间。 111 | 112 | 113 | 序列特征选择算法属于贪心搜索算法,用于将原始的d维度特征空间降低到k维度特征子空间,其中k