From 198b3dd2a99374b305b2e5f500a601d71cf997ac Mon Sep 17 00:00:00 2001 From: benjas <909336740@qq.com> Date: Sun, 22 Aug 2021 09:43:02 +0800 Subject: [PATCH] Add. stacking method --- .../Stacking-checkpoint.ipynb | 677 ++++++++++++++++++ 竞赛优胜技巧/Stacking.ipynb | 677 ++++++++++++++++++ 竞赛优胜技巧/assets/stacking.jpg | Bin 0 -> 69814 bytes 3 files changed, 1354 insertions(+) create mode 100644 竞赛优胜技巧/.ipynb_checkpoints/Stacking-checkpoint.ipynb create mode 100644 竞赛优胜技巧/Stacking.ipynb create mode 100644 竞赛优胜技巧/assets/stacking.jpg diff --git a/竞赛优胜技巧/.ipynb_checkpoints/Stacking-checkpoint.ipynb b/竞赛优胜技巧/.ipynb_checkpoints/Stacking-checkpoint.ipynb new file mode 100644 index 0000000..ef7f238 --- /dev/null +++ b/竞赛优胜技巧/.ipynb_checkpoints/Stacking-checkpoint.ipynb @@ -0,0 +1,677 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "720f2b62", + "metadata": {}, + "source": [ + "# Stacking" + ] + }, + { + "cell_type": "markdown", + "id": "0b365a02", + "metadata": {}, + "source": [ + "## 先说结论,该数据集(fetch_covtype)Stacking的方法比线性加权更好\n", + "比赛中我们常用线性加权作为最终的融合方式,我们同样也会好奇怎样的线性加权权重更好,下面也会举例子\n", + "参考:https://github.com/rushter/heamy/tree/master/examples" + ] + }, + { + "cell_type": "markdown", + "id": "cc8fecb1", + "metadata": {}, + "source": [ + "通过对训练集进行五折验证,将验证结果作为第二层的训练和测试集合\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "18a12000", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple\n", + "Collecting heamy\n", + " Downloading https://pypi.tuna.tsinghua.edu.cn/packages/20/32/2f3e1efa38a8e34f790d90b6d49ef06ab812181ae896c50e89b8750fa5a0/heamy-0.0.7.tar.gz (30 kB)\n", + "Requirement already satisfied: scikit-learn>=0.17.0 in d:\\programdata\\anaconda3\\lib\\site-packages (from heamy) (0.24.1)\n", + "Requirement already satisfied: pandas>=0.17.0 in d:\\programdata\\anaconda3\\lib\\site-packages (from heamy) (1.2.4)\n", + "Requirement already satisfied: six>=1.10.0 in d:\\programdata\\anaconda3\\lib\\site-packages (from heamy) (1.15.0)\n", + "Requirement already satisfied: scipy>=0.16.0 in d:\\programdata\\anaconda3\\lib\\site-packages (from heamy) (1.6.2)\n", + "Requirement already satisfied: numpy>=1.7.0 in d:\\programdata\\anaconda3\\lib\\site-packages (from heamy) (1.19.5)\n", + "Requirement already satisfied: pytz>=2017.3 in d:\\programdata\\anaconda3\\lib\\site-packages (from pandas>=0.17.0->heamy) (2021.1)\n", + "Requirement already satisfied: python-dateutil>=2.7.3 in d:\\programdata\\anaconda3\\lib\\site-packages (from pandas>=0.17.0->heamy) (2.8.1)\n", + "Requirement already satisfied: threadpoolctl>=2.0.0 in d:\\programdata\\anaconda3\\lib\\site-packages (from scikit-learn>=0.17.0->heamy) (2.1.0)\n", + "Requirement already satisfied: joblib>=0.11 in d:\\programdata\\anaconda3\\lib\\site-packages (from scikit-learn>=0.17.0->heamy) (1.0.1)\n", + "Building wheels for collected packages: heamy\n", + " Building wheel for heamy (setup.py): started\n", + " Building wheel for heamy (setup.py): finished with status 'done'\n", + " Created wheel for heamy: filename=heamy-0.0.7-py2.py3-none-any.whl size=15353 sha256=e3ba65b34e2bdee3b90b45b637e28836afdbdb0c9547f76b36fe10d17f8aba8f\n", + " Stored in directory: c:\\users\\administrator\\appdata\\local\\pip\\cache\\wheels\\6e\\f1\\7d\\048e558da94f495a0ed0d9c09d312e73eb176a092e36774ec2\n", + "Successfully built heamy\n", + "Installing collected packages: heamy\n", + "Successfully installed heamy-0.0.7\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "pip install heamy # 安装相关包" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "69632c6a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.8.8 (default, Apr 13 2021, 15:08:03) [MSC v.1916 64 bit (AMD64)]\n" + ] + } + ], + "source": [ + "import sys\n", + "print(sys.version) # 版本信息" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "ca421279", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import time\n", + "\n", + "from heamy.dataset import Dataset\n", + "from heamy.estimator import Classifier \n", + "from heamy.pipeline import ModelsPipeline\n", + "# 导入相关模型,没有的pip install xxx 即可\n", + "from sklearn.ensemble import RandomForestClassifier\n", + "from sklearn.linear_model import LogisticRegression\n", + "import xgboost as xgb \n", + "import lightgbm as lgb \n", + "\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.metrics import accuracy_score\n", + "from sklearn.preprocessing import OrdinalEncoder\n", + "from sklearn.metrics import log_loss" + ] + }, + { + "cell_type": "markdown", + "id": "2592fbbd", + "metadata": {}, + "source": [ + "## 准备数据集" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "9a0fabe1", + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.datasets import fetch_covtype\n", + "data = fetch_covtype()" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "5bd75178", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "七分类任务,处理前: [1 2 3 4 5 6 7]\n", + "[5 5 2 ... 3 3 3]\n", + "七分类任务,处理后: [0. 1. 2. 3. 4. 5. 6.]\n", + "[4. 4. 1. ... 2. 2. 2.]\n" + ] + } + ], + "source": [ + "# 预处理\n", + "X, y = data['data'], data['target']\n", + "# 由于模型标签需要从0开始,所以数字需要全部减1\n", + "print('七分类任务,处理前:',np.unique(y))\n", + "print(y)\n", + "ord = OrdinalEncoder()\n", + "y = ord.fit_transform(y.reshape(-1, 1))\n", + "y = y_enc.reshape(-1, )\n", + "print('七分类任务,处理后:',np.unique(y))\n", + "print(y)" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "23d9778c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(435759, 54)\n", + "(145253, 54)\n" + ] + } + ], + "source": [ + "# 切分训练和测试集\n", + "X_train, X_test, y_train, y_test = train_test_split(X, y,random_state=42)\n", + "print(X_train.shape)\n", + "print(X_test.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "eac48668", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dataset(5c3ccfb5c81451d098565ef5e7e36ac5)\n" + ] + } + ], + "source": [ + "# 创建数据集\n", + "'''use_cache : bool, default True\n", + " If use_cache=True then preprocessing step will be cached until function codeis changed.'''\n", + "dataset = Dataset(X_train=X_train, y_train=y_train, X_test=X_test, y_test=None,use_cache=True) # 注意这里的y_test=None,即不存在数据泄露\n", + "print(dataset)" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "fba3f975", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[2.833e+03, 2.580e+02, 2.600e+01, ..., 0.000e+00, 0.000e+00,\n", + " 0.000e+00],\n", + " [3.008e+03, 4.500e+01, 2.000e+00, ..., 0.000e+00, 0.000e+00,\n", + " 0.000e+00],\n", + " [2.949e+03, 0.000e+00, 1.100e+01, ..., 0.000e+00, 0.000e+00,\n", + " 0.000e+00],\n", + " ...,\n", + " [3.153e+03, 2.870e+02, 1.700e+01, ..., 0.000e+00, 0.000e+00,\n", + " 0.000e+00],\n", + " [3.065e+03, 3.480e+02, 2.100e+01, ..., 0.000e+00, 0.000e+00,\n", + " 0.000e+00],\n", + " [3.021e+03, 2.600e+01, 1.600e+01, ..., 0.000e+00, 0.000e+00,\n", + " 0.000e+00]])" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 处理后的数据集\n", + "dataset.X_train" + ] + }, + { + "cell_type": "markdown", + "id": "d4517ea1", + "metadata": {}, + "source": [ + "## 定义模型" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "e8393e73", + "metadata": {}, + "outputs": [], + "source": [ + "def xgb_model(X_train, y_train, X_test, y_test):\n", + " \"\"\"参数必须为X_train,y_train,X_test,y_test\"\"\"\n", + " # 可以内置参数\n", + " params = {'objective': 'multi:softprob',\n", + " \"eval_metric\": 'mlogloss',\n", + " \"verbosity\": 0,\n", + " 'num_class': 7,\n", + " 'nthread': -1}\n", + " dtrain = xgb.DMatrix(X_train, y_train)\n", + " dtest = xgb.DMatrix(X_test)\n", + " model = xgb.train(params, dtrain, num_boost_round=300)\n", + " predict = model.predict(dtest)\n", + " return predict # 返回值必须为X_test的预测\n", + "\n", + "\n", + "def lgb_model(X_train, y_train, X_test, y_test,**parameters):\n", + " # 也可以开放参数接口\n", + " if parameters is None:\n", + " parameters = {}\n", + " lgb_train = lgb.Dataset(X_train, y_train)\n", + " model = lgb.train(params=parameters, train_set=lgb_train,num_boost_round=300)\n", + " predict = model.predict(X_test)\n", + " return predict\n", + "\n", + "\n", + "def rf_model(X_train, y_train, X_test, y_test):\n", + " params = {\"n_estimators\": 100, \"n_jobs\": -1}\n", + " model = RandomForestClassifier(**params).fit(X_train, y_train)\n", + " predict = model.predict_proba(X_test)\n", + " return predict" + ] + }, + { + "cell_type": "markdown", + "id": "0715cf6e", + "metadata": {}, + "source": [ + "## 构建和训练模型" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "78ab0083", + "metadata": {}, + "outputs": [], + "source": [ + "params = {\"objective\": \"multiclass\",\n", + " \"num_class\": 7,\n", + " \"n_jobs\": -1,\n", + " \"verbose\": -4, \n", + " \"metric\": (\"multi_logloss\",)}\n", + "\n", + "model_xgb = Classifier(dataset=dataset, estimator=xgb_model, name='xgb',use_cache=False)\n", + "model_lgb = Classifier(dataset=dataset, estimator=lgb_model, name='lgb',parameters=params,use_cache=False)\n", + "model_rf = Classifier(dataset=dataset, estimator=rf_model,name='rf',use_cache=False)\n", + "\n", + "pipeline = ModelsPipeline(model_xgb, model_lgb, model_rf)" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "173ef0f0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Best Score (log_loss): 0.18744137777851164\n", + "Best Weights: [0.36556831 0.00303401 0.63139768]\n" + ] + }, + { + "data": { + "text/plain": [ + "array([0.36556831, 0.00303401, 0.63139768])" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pipeline.find_weights(scorer=log_loss, ) # 输出最优权重组合" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "80726d19", + "metadata": {}, + "outputs": [], + "source": [ + "# 5折训练构建5折模型特征集,这里比较耗时\n", + "stack_ds = pipeline.stack(k=5,stratify=False,seed=42,full_test=False) # full_test指明预测全部还是预测当前折的验证集" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "b25bba3c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " xgb_0 xgb_1 xgb_2 xgb_3 xgb_4 \\\n", + "0 0.177179 0.818728 2.185222e-07 9.264143e-09 4.090067e-03 \n", + "1 0.005155 0.994845 7.055579e-10 1.326343e-08 6.331572e-09 \n", + "2 0.293492 0.706508 3.650662e-10 1.017633e-09 8.823530e-09 \n", + "3 0.478112 0.521816 3.207779e-06 2.878019e-08 1.076500e-08 \n", + "4 0.992430 0.006652 1.233117e-05 1.887496e-07 1.569583e-06 \n", + "... ... ... ... ... ... \n", + "435754 0.988518 0.011477 3.190797e-09 5.645121e-08 2.940739e-09 \n", + "435755 0.969212 0.030723 2.142020e-08 1.572054e-05 4.321913e-07 \n", + "435756 0.415850 0.584142 4.283793e-08 7.367601e-08 6.148067e-07 \n", + "435757 0.602601 0.397399 6.606462e-10 1.015894e-09 7.221973e-08 \n", + "435758 0.834587 0.165411 3.267833e-09 2.057172e-08 2.078704e-08 \n", + "\n", + " xgb_5 xgb_6 lgb_0 lgb_1 lgb_2 ... \\\n", + "0 1.725062e-06 1.048052e-06 0.172406 0.812678 1.416886e-06 ... \n", + "1 1.435787e-09 1.603579e-10 0.008114 0.991886 0.000000e+00 ... \n", + "2 6.384080e-10 2.823794e-08 0.817627 0.182372 0.000000e+00 ... \n", + "3 2.230641e-06 6.630235e-05 0.465733 0.534184 0.000000e+00 ... \n", + "4 5.604260e-07 9.037877e-04 0.932050 0.043451 0.000000e+00 ... \n", + "... ... ... ... ... ... ... \n", + "435754 1.530261e-08 4.466830e-06 0.970593 0.029399 0.000000e+00 ... \n", + "435755 5.574208e-10 4.977021e-05 0.862591 0.136644 0.000000e+00 ... \n", + "435756 2.371389e-06 5.185283e-06 0.466886 0.533039 0.000000e+00 ... \n", + "435757 2.326313e-09 9.193871e-09 0.674250 0.325750 4.092880e-211 ... \n", + "435758 5.976972e-08 2.204258e-06 0.709320 0.290680 0.000000e+00 ... \n", + "\n", + " lgb_4 lgb_5 lgb_6 rf_0 rf_1 rf_2 rf_3 \\\n", + "0 1.486358e-02 5.093522e-05 6.805300e-08 0.06 0.92 0.0 0.0 \n", + "1 0.000000e+00 0.000000e+00 0.000000e+00 0.12 0.88 0.0 0.0 \n", + "2 4.452850e-07 7.825338e-09 1.012052e-07 0.63 0.37 0.0 0.0 \n", + "3 0.000000e+00 0.000000e+00 8.245405e-05 0.56 0.44 0.0 0.0 \n", + "4 0.000000e+00 0.000000e+00 2.449972e-02 0.95 0.04 0.0 0.0 \n", + "... ... ... ... ... ... ... ... \n", + "435754 0.000000e+00 0.000000e+00 7.871809e-06 0.97 0.03 0.0 0.0 \n", + "435755 0.000000e+00 0.000000e+00 7.647430e-04 0.93 0.06 0.0 0.0 \n", + "435756 0.000000e+00 0.000000e+00 7.493861e-05 0.45 0.55 0.0 0.0 \n", + "435757 0.000000e+00 0.000000e+00 0.000000e+00 0.52 0.48 0.0 0.0 \n", + "435758 0.000000e+00 0.000000e+00 0.000000e+00 0.87 0.13 0.0 0.0 \n", + "\n", + " rf_4 rf_5 rf_6 \n", + "0 0.02 0.0 0.00 \n", + "1 0.00 0.0 0.00 \n", + "2 0.00 0.0 0.00 \n", + "3 0.00 0.0 0.00 \n", + "4 0.00 0.0 0.01 \n", + "... ... ... ... \n", + "435754 0.00 0.0 0.00 \n", + "435755 0.00 0.0 0.01 \n", + "435756 0.00 0.0 0.00 \n", + "435757 0.00 0.0 0.00 \n", + "435758 0.00 0.0 0.00 \n", + "\n", + "[435759 rows x 21 columns]\n" + ] + } + ], + "source": [ + "# 模型输出的训练集,7个特征对应7个标签的预测概率\n", + "print(stack_ds.X_train)" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "835205e9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " xgb_0 xgb_1 xgb_2 xgb_3 xgb_4 \\\n", + "0 9.876224e-01 0.000789 2.774616e-06 4.129093e-07 1.311387e-06 \n", + "1 5.139124e-02 0.929659 1.852793e-03 1.518293e-07 1.692924e-02 \n", + "2 7.695035e-04 0.973729 6.878623e-04 1.573823e-07 2.408167e-02 \n", + "3 3.376913e-02 0.966229 2.024872e-07 7.321523e-08 1.071163e-06 \n", + "4 1.013981e-03 0.998553 3.794874e-06 8.755425e-08 4.243054e-04 \n", + "... ... ... ... ... ... \n", + "145248 9.615189e-01 0.038480 6.486028e-08 1.744931e-08 1.069370e-06 \n", + "145249 3.055384e-02 0.969440 2.475371e-07 5.530033e-08 4.299908e-06 \n", + "145250 8.224608e-06 0.058361 9.212288e-01 9.705171e-08 5.440121e-05 \n", + "145251 9.183387e-01 0.081601 5.612090e-08 1.088283e-08 5.225256e-07 \n", + "145252 9.203915e-07 0.003578 2.372825e-01 1.582836e-06 3.307252e-07 \n", + "\n", + " xgb_5 xgb_6 lgb_0 lgb_1 lgb_2 ... \\\n", + "0 7.851924e-09 1.158422e-02 0.962538 0.004222 9.599869e-23 ... \n", + "1 1.613036e-04 6.763449e-06 0.070947 0.882463 2.232464e-03 ... \n", + "2 7.296442e-04 1.884172e-06 0.004029 0.945838 1.014722e-02 ... \n", + "3 7.227585e-08 1.448203e-09 0.066538 0.933450 1.206630e-06 ... \n", + "4 4.374311e-06 4.566837e-09 0.001334 0.997391 1.580417e-06 ... \n", + "... ... ... ... ... ... ... \n", + "145248 5.049759e-08 4.010809e-07 0.917842 0.082153 2.154302e-17 ... \n", + "145249 1.255851e-08 1.224208e-06 0.058622 0.941370 1.332795e-12 ... \n", + "145250 2.034389e-02 3.132630e-06 0.000268 0.083680 8.789707e-01 ... \n", + "145251 2.566383e-07 5.976933e-05 0.875834 0.123030 2.631276e-12 ... \n", + "145252 7.591362e-01 6.988637e-08 0.000032 0.037757 2.462795e-01 ... \n", + "\n", + " lgb_4 lgb_5 lgb_6 rf_0 rf_1 rf_2 rf_3 \\\n", + "0 1.726260e-240 0.000000e+00 3.324009e-02 0.984 0.000 0.000 0.0 \n", + "1 4.396802e-02 3.888647e-04 1.077146e-08 0.106 0.816 0.038 0.0 \n", + "2 3.329283e-02 6.693269e-03 3.213404e-09 0.008 0.950 0.002 0.0 \n", + "3 9.908823e-06 1.371448e-07 1.280235e-09 0.078 0.922 0.000 0.0 \n", + "4 1.273086e-03 7.811306e-07 5.594472e-10 0.004 0.988 0.000 0.0 \n", + "... ... ... ... ... ... ... ... \n", + "145248 6.191352e-08 0.000000e+00 4.958509e-06 0.968 0.032 0.000 0.0 \n", + "145249 7.656931e-06 3.415083e-47 2.271880e-07 0.018 0.972 0.000 0.0 \n", + "145250 2.052535e-04 3.687570e-02 1.393421e-09 0.000 0.040 0.946 0.0 \n", + "145251 2.521124e-07 5.749375e-08 1.135236e-03 0.992 0.008 0.000 0.0 \n", + "145252 2.927400e-06 7.159244e-01 8.608624e-140 0.000 0.018 0.110 0.0 \n", + "\n", + " rf_4 rf_5 rf_6 \n", + "0 0.000 0.000 0.016 \n", + "1 0.034 0.006 0.000 \n", + "2 0.032 0.008 0.000 \n", + "3 0.000 0.000 0.000 \n", + "4 0.008 0.000 0.000 \n", + "... ... ... ... \n", + "145248 0.000 0.000 0.000 \n", + "145249 0.010 0.000 0.000 \n", + "145250 0.000 0.014 0.000 \n", + "145251 0.000 0.000 0.000 \n", + "145252 0.000 0.872 0.000 \n", + "\n", + "[145253 rows x 21 columns]\n" + ] + } + ], + "source": [ + "# 模型输出的测试集,7个特征对应7个标签的预测概率\n", + "print(stack_ds.X_test) " + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "b9db35dc", + "metadata": {}, + "outputs": [], + "source": [ + "# 用lr做最后一层\n", + "stacker = Classifier(dataset=stack_ds, estimator=LogisticRegression, parameters={\"solver\": 'lbfgs', \"max_iter\": 1000},use_cache=False)\n", + "predict_stack = stacker.predict()" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "id": "a4a48219", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[9.95173402e-01 2.67623709e-03 4.23846755e-08 ... 3.15435935e-05\n", + " 5.66194220e-06 2.11044140e-03]\n", + " [2.23612439e-02 9.70927685e-01 1.23929922e-03 ... 4.49727904e-03\n", + " 8.73983383e-04 9.97020226e-05]\n", + " [6.22588197e-03 9.89402233e-01 9.81655972e-04 ... 2.83331258e-03\n", + " 5.22139184e-04 3.45071569e-05]\n", + " ...\n", + " [5.36335125e-06 2.06267200e-03 9.90604140e-01 ... 8.55252386e-04\n", + " 4.18405061e-03 1.64678945e-05]\n", + " [9.96602824e-01 2.15991442e-03 7.27481581e-08 ... 3.63552051e-05\n", + " 6.80942632e-06 1.19199377e-03]\n", + " [5.89156494e-05 1.15333400e-03 1.09178439e-02 ... 3.09244417e-04\n", + " 9.85167196e-01 2.21261408e-05]]\n" + ] + } + ], + "source": [ + "print(predict_stack) # stacking后的结果" + ] + }, + { + "cell_type": "markdown", + "id": "1372d4f8", + "metadata": {}, + "source": [ + "## 验证结果" + ] + }, + { + "cell_type": "markdown", + "id": "52ef71d4", + "metadata": {}, + "source": [ + "### 单模分数" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "id": "a28806a0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.9284696357390209\n", + "0.8890005714167694\n", + "0.9511404239499356\n" + ] + } + ], + "source": [ + "print(accuracy_score(np.argmax(stack_ds.X_test.iloc[:, :7].values, axis=1),y_test)) # XGB\n", + "print(accuracy_score(np.argmax(stack_ds.X_test.iloc[:, 7:14].values, axis=1),y_test)) # LGB\n", + "print(accuracy_score(np.argmax(stack_ds.X_test.iloc[:, 14:].values, axis=1),y_test)) # RF" + ] + }, + { + "cell_type": "markdown", + "id": "2e9423ce", + "metadata": {}, + "source": [ + "### 线性加权分数" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "d2b50ba4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "主观根据结果blending: 0.9425209806337908\n", + "根据最优权重的blending: 0.9488616414118813\n" + ] + } + ], + "source": [ + "# blending的分数\n", + "xgb_t = stack_ds.X_test.iloc[:, :7].values\n", + "lgb_t = stack_ds.X_test.iloc[:, 7:14].values\n", + "rf_t = stack_ds.X_test.iloc[:, 14:].values\n", + "\n", + "# 根据分数好坏随机定\n", + "result = 0.3*xgb_t+0.2*lgb_t+0.5*rf_t\n", + "print('主观根据结果blending:', accuracy_score(np.argmax(result, axis=1), y_test))\n", + "# 根据上面提供的最优权重 Best Weights: [0.36556831 0.00303401 0.63139768]\n", + "result = 0.36556831*xgb_t+0.00303401*lgb_t+0.63139768*rf_t\n", + "print('根据最优权重的blending:',accuracy_score(np.argmax(result, axis=1), y_test))" + ] + }, + { + "cell_type": "markdown", + "id": "dfec8968", + "metadata": {}, + "source": [ + "可以观察到最优权重比我们主观选权重更优" + ] + }, + { + "cell_type": "markdown", + "id": "e8daf1e3", + "metadata": {}, + "source": [ + "### stacking的分数" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "id": "4930b407", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.957439777491687\n" + ] + } + ], + "source": [ + "print(accuracy_score(np.argmax(predict_stack, axis=1), y_test))" + ] + }, + { + "cell_type": "markdown", + "id": "6311fbd7", + "metadata": {}, + "source": [ + "## 再说结论,该数据集(fetch_covtype)Stacking的方法更好" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/竞赛优胜技巧/Stacking.ipynb b/竞赛优胜技巧/Stacking.ipynb new file mode 100644 index 0000000..ef7f238 --- /dev/null +++ b/竞赛优胜技巧/Stacking.ipynb @@ -0,0 +1,677 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "720f2b62", + "metadata": {}, + "source": [ + "# Stacking" + ] + }, + { + "cell_type": "markdown", + "id": "0b365a02", + "metadata": {}, + "source": [ + "## 先说结论,该数据集(fetch_covtype)Stacking的方法比线性加权更好\n", + "比赛中我们常用线性加权作为最终的融合方式,我们同样也会好奇怎样的线性加权权重更好,下面也会举例子\n", + "参考:https://github.com/rushter/heamy/tree/master/examples" + ] + }, + { + "cell_type": "markdown", + "id": "cc8fecb1", + "metadata": {}, + "source": [ + "通过对训练集进行五折验证,将验证结果作为第二层的训练和测试集合\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "18a12000", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple\n", + "Collecting heamy\n", + " Downloading https://pypi.tuna.tsinghua.edu.cn/packages/20/32/2f3e1efa38a8e34f790d90b6d49ef06ab812181ae896c50e89b8750fa5a0/heamy-0.0.7.tar.gz (30 kB)\n", + "Requirement already satisfied: scikit-learn>=0.17.0 in d:\\programdata\\anaconda3\\lib\\site-packages (from heamy) (0.24.1)\n", + "Requirement already satisfied: pandas>=0.17.0 in d:\\programdata\\anaconda3\\lib\\site-packages (from heamy) (1.2.4)\n", + "Requirement already satisfied: six>=1.10.0 in d:\\programdata\\anaconda3\\lib\\site-packages (from heamy) (1.15.0)\n", + "Requirement already satisfied: scipy>=0.16.0 in d:\\programdata\\anaconda3\\lib\\site-packages (from heamy) (1.6.2)\n", + "Requirement already satisfied: numpy>=1.7.0 in d:\\programdata\\anaconda3\\lib\\site-packages (from heamy) (1.19.5)\n", + "Requirement already satisfied: pytz>=2017.3 in d:\\programdata\\anaconda3\\lib\\site-packages (from pandas>=0.17.0->heamy) (2021.1)\n", + "Requirement already satisfied: python-dateutil>=2.7.3 in d:\\programdata\\anaconda3\\lib\\site-packages (from pandas>=0.17.0->heamy) (2.8.1)\n", + "Requirement already satisfied: threadpoolctl>=2.0.0 in d:\\programdata\\anaconda3\\lib\\site-packages (from scikit-learn>=0.17.0->heamy) (2.1.0)\n", + "Requirement already satisfied: joblib>=0.11 in d:\\programdata\\anaconda3\\lib\\site-packages (from scikit-learn>=0.17.0->heamy) (1.0.1)\n", + "Building wheels for collected packages: heamy\n", + " Building wheel for heamy (setup.py): started\n", + " Building wheel for heamy (setup.py): finished with status 'done'\n", + " Created wheel for heamy: filename=heamy-0.0.7-py2.py3-none-any.whl size=15353 sha256=e3ba65b34e2bdee3b90b45b637e28836afdbdb0c9547f76b36fe10d17f8aba8f\n", + " Stored in directory: c:\\users\\administrator\\appdata\\local\\pip\\cache\\wheels\\6e\\f1\\7d\\048e558da94f495a0ed0d9c09d312e73eb176a092e36774ec2\n", + "Successfully built heamy\n", + "Installing collected packages: heamy\n", + "Successfully installed heamy-0.0.7\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "pip install heamy # 安装相关包" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "69632c6a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.8.8 (default, Apr 13 2021, 15:08:03) [MSC v.1916 64 bit (AMD64)]\n" + ] + } + ], + "source": [ + "import sys\n", + "print(sys.version) # 版本信息" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "ca421279", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import time\n", + "\n", + "from heamy.dataset import Dataset\n", + "from heamy.estimator import Classifier \n", + "from heamy.pipeline import ModelsPipeline\n", + "# 导入相关模型,没有的pip install xxx 即可\n", + "from sklearn.ensemble import RandomForestClassifier\n", + "from sklearn.linear_model import LogisticRegression\n", + "import xgboost as xgb \n", + "import lightgbm as lgb \n", + "\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.metrics import accuracy_score\n", + "from sklearn.preprocessing import OrdinalEncoder\n", + "from sklearn.metrics import log_loss" + ] + }, + { + "cell_type": "markdown", + "id": "2592fbbd", + "metadata": {}, + "source": [ + "## 准备数据集" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "9a0fabe1", + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.datasets import fetch_covtype\n", + "data = fetch_covtype()" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "5bd75178", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "七分类任务,处理前: [1 2 3 4 5 6 7]\n", + "[5 5 2 ... 3 3 3]\n", + "七分类任务,处理后: [0. 1. 2. 3. 4. 5. 6.]\n", + "[4. 4. 1. ... 2. 2. 2.]\n" + ] + } + ], + "source": [ + "# 预处理\n", + "X, y = data['data'], data['target']\n", + "# 由于模型标签需要从0开始,所以数字需要全部减1\n", + "print('七分类任务,处理前:',np.unique(y))\n", + "print(y)\n", + "ord = OrdinalEncoder()\n", + "y = ord.fit_transform(y.reshape(-1, 1))\n", + "y = y_enc.reshape(-1, )\n", + "print('七分类任务,处理后:',np.unique(y))\n", + "print(y)" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "23d9778c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(435759, 54)\n", + "(145253, 54)\n" + ] + } + ], + "source": [ + "# 切分训练和测试集\n", + "X_train, X_test, y_train, y_test = train_test_split(X, y,random_state=42)\n", + "print(X_train.shape)\n", + "print(X_test.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "eac48668", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dataset(5c3ccfb5c81451d098565ef5e7e36ac5)\n" + ] + } + ], + "source": [ + "# 创建数据集\n", + "'''use_cache : bool, default True\n", + " If use_cache=True then preprocessing step will be cached until function codeis changed.'''\n", + "dataset = Dataset(X_train=X_train, y_train=y_train, X_test=X_test, y_test=None,use_cache=True) # 注意这里的y_test=None,即不存在数据泄露\n", + "print(dataset)" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "fba3f975", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[2.833e+03, 2.580e+02, 2.600e+01, ..., 0.000e+00, 0.000e+00,\n", + " 0.000e+00],\n", + " [3.008e+03, 4.500e+01, 2.000e+00, ..., 0.000e+00, 0.000e+00,\n", + " 0.000e+00],\n", + " [2.949e+03, 0.000e+00, 1.100e+01, ..., 0.000e+00, 0.000e+00,\n", + " 0.000e+00],\n", + " ...,\n", + " [3.153e+03, 2.870e+02, 1.700e+01, ..., 0.000e+00, 0.000e+00,\n", + " 0.000e+00],\n", + " [3.065e+03, 3.480e+02, 2.100e+01, ..., 0.000e+00, 0.000e+00,\n", + " 0.000e+00],\n", + " [3.021e+03, 2.600e+01, 1.600e+01, ..., 0.000e+00, 0.000e+00,\n", + " 0.000e+00]])" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 处理后的数据集\n", + "dataset.X_train" + ] + }, + { + "cell_type": "markdown", + "id": "d4517ea1", + "metadata": {}, + "source": [ + "## 定义模型" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "e8393e73", + "metadata": {}, + "outputs": [], + "source": [ + "def xgb_model(X_train, y_train, X_test, y_test):\n", + " \"\"\"参数必须为X_train,y_train,X_test,y_test\"\"\"\n", + " # 可以内置参数\n", + " params = {'objective': 'multi:softprob',\n", + " \"eval_metric\": 'mlogloss',\n", + " \"verbosity\": 0,\n", + " 'num_class': 7,\n", + " 'nthread': -1}\n", + " dtrain = xgb.DMatrix(X_train, y_train)\n", + " dtest = xgb.DMatrix(X_test)\n", + " model = xgb.train(params, dtrain, num_boost_round=300)\n", + " predict = model.predict(dtest)\n", + " return predict # 返回值必须为X_test的预测\n", + "\n", + "\n", + "def lgb_model(X_train, y_train, X_test, y_test,**parameters):\n", + " # 也可以开放参数接口\n", + " if parameters is None:\n", + " parameters = {}\n", + " lgb_train = lgb.Dataset(X_train, y_train)\n", + " model = lgb.train(params=parameters, train_set=lgb_train,num_boost_round=300)\n", + " predict = model.predict(X_test)\n", + " return predict\n", + "\n", + "\n", + "def rf_model(X_train, y_train, X_test, y_test):\n", + " params = {\"n_estimators\": 100, \"n_jobs\": -1}\n", + " model = RandomForestClassifier(**params).fit(X_train, y_train)\n", + " predict = model.predict_proba(X_test)\n", + " return predict" + ] + }, + { + "cell_type": "markdown", + "id": "0715cf6e", + "metadata": {}, + "source": [ + "## 构建和训练模型" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "78ab0083", + "metadata": {}, + "outputs": [], + "source": [ + "params = {\"objective\": \"multiclass\",\n", + " \"num_class\": 7,\n", + " \"n_jobs\": -1,\n", + " \"verbose\": -4, \n", + " \"metric\": (\"multi_logloss\",)}\n", + "\n", + "model_xgb = Classifier(dataset=dataset, estimator=xgb_model, name='xgb',use_cache=False)\n", + "model_lgb = Classifier(dataset=dataset, estimator=lgb_model, name='lgb',parameters=params,use_cache=False)\n", + "model_rf = Classifier(dataset=dataset, estimator=rf_model,name='rf',use_cache=False)\n", + "\n", + "pipeline = ModelsPipeline(model_xgb, model_lgb, model_rf)" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "173ef0f0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Best Score (log_loss): 0.18744137777851164\n", + "Best Weights: [0.36556831 0.00303401 0.63139768]\n" + ] + }, + { + "data": { + "text/plain": [ + "array([0.36556831, 0.00303401, 0.63139768])" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pipeline.find_weights(scorer=log_loss, ) # 输出最优权重组合" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "80726d19", + "metadata": {}, + "outputs": [], + "source": [ + "# 5折训练构建5折模型特征集,这里比较耗时\n", + "stack_ds = pipeline.stack(k=5,stratify=False,seed=42,full_test=False) # full_test指明预测全部还是预测当前折的验证集" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "b25bba3c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " xgb_0 xgb_1 xgb_2 xgb_3 xgb_4 \\\n", + "0 0.177179 0.818728 2.185222e-07 9.264143e-09 4.090067e-03 \n", + "1 0.005155 0.994845 7.055579e-10 1.326343e-08 6.331572e-09 \n", + "2 0.293492 0.706508 3.650662e-10 1.017633e-09 8.823530e-09 \n", + "3 0.478112 0.521816 3.207779e-06 2.878019e-08 1.076500e-08 \n", + "4 0.992430 0.006652 1.233117e-05 1.887496e-07 1.569583e-06 \n", + "... ... ... ... ... ... \n", + "435754 0.988518 0.011477 3.190797e-09 5.645121e-08 2.940739e-09 \n", + "435755 0.969212 0.030723 2.142020e-08 1.572054e-05 4.321913e-07 \n", + "435756 0.415850 0.584142 4.283793e-08 7.367601e-08 6.148067e-07 \n", + "435757 0.602601 0.397399 6.606462e-10 1.015894e-09 7.221973e-08 \n", + "435758 0.834587 0.165411 3.267833e-09 2.057172e-08 2.078704e-08 \n", + "\n", + " xgb_5 xgb_6 lgb_0 lgb_1 lgb_2 ... \\\n", + "0 1.725062e-06 1.048052e-06 0.172406 0.812678 1.416886e-06 ... \n", + "1 1.435787e-09 1.603579e-10 0.008114 0.991886 0.000000e+00 ... \n", + "2 6.384080e-10 2.823794e-08 0.817627 0.182372 0.000000e+00 ... \n", + "3 2.230641e-06 6.630235e-05 0.465733 0.534184 0.000000e+00 ... \n", + "4 5.604260e-07 9.037877e-04 0.932050 0.043451 0.000000e+00 ... \n", + "... ... ... ... ... ... ... \n", + "435754 1.530261e-08 4.466830e-06 0.970593 0.029399 0.000000e+00 ... \n", + "435755 5.574208e-10 4.977021e-05 0.862591 0.136644 0.000000e+00 ... \n", + "435756 2.371389e-06 5.185283e-06 0.466886 0.533039 0.000000e+00 ... \n", + "435757 2.326313e-09 9.193871e-09 0.674250 0.325750 4.092880e-211 ... \n", + "435758 5.976972e-08 2.204258e-06 0.709320 0.290680 0.000000e+00 ... \n", + "\n", + " lgb_4 lgb_5 lgb_6 rf_0 rf_1 rf_2 rf_3 \\\n", + "0 1.486358e-02 5.093522e-05 6.805300e-08 0.06 0.92 0.0 0.0 \n", + "1 0.000000e+00 0.000000e+00 0.000000e+00 0.12 0.88 0.0 0.0 \n", + "2 4.452850e-07 7.825338e-09 1.012052e-07 0.63 0.37 0.0 0.0 \n", + "3 0.000000e+00 0.000000e+00 8.245405e-05 0.56 0.44 0.0 0.0 \n", + "4 0.000000e+00 0.000000e+00 2.449972e-02 0.95 0.04 0.0 0.0 \n", + "... ... ... ... ... ... ... ... \n", + "435754 0.000000e+00 0.000000e+00 7.871809e-06 0.97 0.03 0.0 0.0 \n", + "435755 0.000000e+00 0.000000e+00 7.647430e-04 0.93 0.06 0.0 0.0 \n", + "435756 0.000000e+00 0.000000e+00 7.493861e-05 0.45 0.55 0.0 0.0 \n", + "435757 0.000000e+00 0.000000e+00 0.000000e+00 0.52 0.48 0.0 0.0 \n", + "435758 0.000000e+00 0.000000e+00 0.000000e+00 0.87 0.13 0.0 0.0 \n", + "\n", + " rf_4 rf_5 rf_6 \n", + "0 0.02 0.0 0.00 \n", + "1 0.00 0.0 0.00 \n", + "2 0.00 0.0 0.00 \n", + "3 0.00 0.0 0.00 \n", + "4 0.00 0.0 0.01 \n", + "... ... ... ... \n", + "435754 0.00 0.0 0.00 \n", + "435755 0.00 0.0 0.01 \n", + "435756 0.00 0.0 0.00 \n", + "435757 0.00 0.0 0.00 \n", + "435758 0.00 0.0 0.00 \n", + "\n", + "[435759 rows x 21 columns]\n" + ] + } + ], + "source": [ + "# 模型输出的训练集,7个特征对应7个标签的预测概率\n", + "print(stack_ds.X_train)" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "835205e9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " xgb_0 xgb_1 xgb_2 xgb_3 xgb_4 \\\n", + "0 9.876224e-01 0.000789 2.774616e-06 4.129093e-07 1.311387e-06 \n", + "1 5.139124e-02 0.929659 1.852793e-03 1.518293e-07 1.692924e-02 \n", + "2 7.695035e-04 0.973729 6.878623e-04 1.573823e-07 2.408167e-02 \n", + "3 3.376913e-02 0.966229 2.024872e-07 7.321523e-08 1.071163e-06 \n", + "4 1.013981e-03 0.998553 3.794874e-06 8.755425e-08 4.243054e-04 \n", + "... ... ... ... ... ... \n", + "145248 9.615189e-01 0.038480 6.486028e-08 1.744931e-08 1.069370e-06 \n", + "145249 3.055384e-02 0.969440 2.475371e-07 5.530033e-08 4.299908e-06 \n", + "145250 8.224608e-06 0.058361 9.212288e-01 9.705171e-08 5.440121e-05 \n", + "145251 9.183387e-01 0.081601 5.612090e-08 1.088283e-08 5.225256e-07 \n", + "145252 9.203915e-07 0.003578 2.372825e-01 1.582836e-06 3.307252e-07 \n", + "\n", + " xgb_5 xgb_6 lgb_0 lgb_1 lgb_2 ... \\\n", + "0 7.851924e-09 1.158422e-02 0.962538 0.004222 9.599869e-23 ... \n", + "1 1.613036e-04 6.763449e-06 0.070947 0.882463 2.232464e-03 ... \n", + "2 7.296442e-04 1.884172e-06 0.004029 0.945838 1.014722e-02 ... \n", + "3 7.227585e-08 1.448203e-09 0.066538 0.933450 1.206630e-06 ... \n", + "4 4.374311e-06 4.566837e-09 0.001334 0.997391 1.580417e-06 ... \n", + "... ... ... ... ... ... ... \n", + "145248 5.049759e-08 4.010809e-07 0.917842 0.082153 2.154302e-17 ... \n", + "145249 1.255851e-08 1.224208e-06 0.058622 0.941370 1.332795e-12 ... \n", + "145250 2.034389e-02 3.132630e-06 0.000268 0.083680 8.789707e-01 ... \n", + "145251 2.566383e-07 5.976933e-05 0.875834 0.123030 2.631276e-12 ... \n", + "145252 7.591362e-01 6.988637e-08 0.000032 0.037757 2.462795e-01 ... \n", + "\n", + " lgb_4 lgb_5 lgb_6 rf_0 rf_1 rf_2 rf_3 \\\n", + "0 1.726260e-240 0.000000e+00 3.324009e-02 0.984 0.000 0.000 0.0 \n", + "1 4.396802e-02 3.888647e-04 1.077146e-08 0.106 0.816 0.038 0.0 \n", + "2 3.329283e-02 6.693269e-03 3.213404e-09 0.008 0.950 0.002 0.0 \n", + "3 9.908823e-06 1.371448e-07 1.280235e-09 0.078 0.922 0.000 0.0 \n", + "4 1.273086e-03 7.811306e-07 5.594472e-10 0.004 0.988 0.000 0.0 \n", + "... ... ... ... ... ... ... ... \n", + "145248 6.191352e-08 0.000000e+00 4.958509e-06 0.968 0.032 0.000 0.0 \n", + "145249 7.656931e-06 3.415083e-47 2.271880e-07 0.018 0.972 0.000 0.0 \n", + "145250 2.052535e-04 3.687570e-02 1.393421e-09 0.000 0.040 0.946 0.0 \n", + "145251 2.521124e-07 5.749375e-08 1.135236e-03 0.992 0.008 0.000 0.0 \n", + "145252 2.927400e-06 7.159244e-01 8.608624e-140 0.000 0.018 0.110 0.0 \n", + "\n", + " rf_4 rf_5 rf_6 \n", + "0 0.000 0.000 0.016 \n", + "1 0.034 0.006 0.000 \n", + "2 0.032 0.008 0.000 \n", + "3 0.000 0.000 0.000 \n", + "4 0.008 0.000 0.000 \n", + "... ... ... ... \n", + "145248 0.000 0.000 0.000 \n", + "145249 0.010 0.000 0.000 \n", + "145250 0.000 0.014 0.000 \n", + "145251 0.000 0.000 0.000 \n", + "145252 0.000 0.872 0.000 \n", + "\n", + "[145253 rows x 21 columns]\n" + ] + } + ], + "source": [ + "# 模型输出的测试集,7个特征对应7个标签的预测概率\n", + "print(stack_ds.X_test) " + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "b9db35dc", + "metadata": {}, + "outputs": [], + "source": [ + "# 用lr做最后一层\n", + "stacker = Classifier(dataset=stack_ds, estimator=LogisticRegression, parameters={\"solver\": 'lbfgs', \"max_iter\": 1000},use_cache=False)\n", + "predict_stack = stacker.predict()" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "id": "a4a48219", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[9.95173402e-01 2.67623709e-03 4.23846755e-08 ... 3.15435935e-05\n", + " 5.66194220e-06 2.11044140e-03]\n", + " [2.23612439e-02 9.70927685e-01 1.23929922e-03 ... 4.49727904e-03\n", + " 8.73983383e-04 9.97020226e-05]\n", + " [6.22588197e-03 9.89402233e-01 9.81655972e-04 ... 2.83331258e-03\n", + " 5.22139184e-04 3.45071569e-05]\n", + " ...\n", + " [5.36335125e-06 2.06267200e-03 9.90604140e-01 ... 8.55252386e-04\n", + " 4.18405061e-03 1.64678945e-05]\n", + " [9.96602824e-01 2.15991442e-03 7.27481581e-08 ... 3.63552051e-05\n", + " 6.80942632e-06 1.19199377e-03]\n", + " [5.89156494e-05 1.15333400e-03 1.09178439e-02 ... 3.09244417e-04\n", + " 9.85167196e-01 2.21261408e-05]]\n" + ] + } + ], + "source": [ + "print(predict_stack) # stacking后的结果" + ] + }, + { + "cell_type": "markdown", + "id": "1372d4f8", + "metadata": {}, + "source": [ + "## 验证结果" + ] + }, + { + "cell_type": "markdown", + "id": "52ef71d4", + "metadata": {}, + "source": [ + "### 单模分数" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "id": "a28806a0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.9284696357390209\n", + "0.8890005714167694\n", + "0.9511404239499356\n" + ] + } + ], + "source": [ + "print(accuracy_score(np.argmax(stack_ds.X_test.iloc[:, :7].values, axis=1),y_test)) # XGB\n", + "print(accuracy_score(np.argmax(stack_ds.X_test.iloc[:, 7:14].values, axis=1),y_test)) # LGB\n", + "print(accuracy_score(np.argmax(stack_ds.X_test.iloc[:, 14:].values, axis=1),y_test)) # RF" + ] + }, + { + "cell_type": "markdown", + "id": "2e9423ce", + "metadata": {}, + "source": [ + "### 线性加权分数" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "d2b50ba4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "主观根据结果blending: 0.9425209806337908\n", + "根据最优权重的blending: 0.9488616414118813\n" + ] + } + ], + "source": [ + "# blending的分数\n", + "xgb_t = stack_ds.X_test.iloc[:, :7].values\n", + "lgb_t = stack_ds.X_test.iloc[:, 7:14].values\n", + "rf_t = stack_ds.X_test.iloc[:, 14:].values\n", + "\n", + "# 根据分数好坏随机定\n", + "result = 0.3*xgb_t+0.2*lgb_t+0.5*rf_t\n", + "print('主观根据结果blending:', accuracy_score(np.argmax(result, axis=1), y_test))\n", + "# 根据上面提供的最优权重 Best Weights: [0.36556831 0.00303401 0.63139768]\n", + "result = 0.36556831*xgb_t+0.00303401*lgb_t+0.63139768*rf_t\n", + "print('根据最优权重的blending:',accuracy_score(np.argmax(result, axis=1), y_test))" + ] + }, + { + "cell_type": "markdown", + "id": "dfec8968", + "metadata": {}, + "source": [ + "可以观察到最优权重比我们主观选权重更优" + ] + }, + { + "cell_type": "markdown", + "id": "e8daf1e3", + "metadata": {}, + "source": [ + "### stacking的分数" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "id": "4930b407", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.957439777491687\n" + ] + } + ], + "source": [ + "print(accuracy_score(np.argmax(predict_stack, axis=1), y_test))" + ] + }, + { + "cell_type": "markdown", + "id": "6311fbd7", + "metadata": {}, + "source": [ + "## 再说结论,该数据集(fetch_covtype)Stacking的方法更好" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/竞赛优胜技巧/assets/stacking.jpg b/竞赛优胜技巧/assets/stacking.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fead3064cc8b8495e4b5e08e148239b5d911d30c GIT binary patch literal 69814 zcmeFYbyS>5v@hC7AP^*Y@Zc68(6|O@q#H})PJ(-ImrR0t<4&-~A-Dudf)m^=5G=U6 zUuH64a%SeN^UhuCzIESQr}=~Ws_LuS->zMI*Z%EYHxoDC01qKjU?~6s0s=th_789~ z4UhobM?pbFxpyBG6%`HbJ~{?ICdPvY7{s`E*!bimloaG7WMq$N*%%&EKcOKbW8`Ie z@|1&%i;I$hUzm?mhz-ca`THOUXlQ5{4=^5KVm{)eBBSE`Z~tyS0&wmlULl?!Ay5Ml zaS)Jj5N=ulWVfUOkZ#FE0Q~%+AR*sFMMSuN+xqe$0B{cx5dj?s4Fw4W5$zrV00Hr~ z83zTI3J;%`a1!VIKR(`h;k1N z1^FH_BGPRq1Vo%$3aB{5pu4y)VP%6Tc=()3cE4qOA|Rwzj+zH@*$>f(XO`~~ajU#R zrIj%Fd@}{WK)Rg-2MGrt3OLy%v(h;zdv9%iOr~v9gSJd%CdfoDBS=iK7sg}jBy7c# zBUeP6PiuNT3Y)n2#`H|liZl7EQ#d!QAJbpiqol08oc|l+|J42uAO1%k{(rSENLZDj zlIUwjeKR1P-qmQXoGriWUr;pWkp#_UE8pe!Qv?EOZWIrInHv z5$IJPtTqDqBp$Qjl;pi}0(}__p@c>}V!^ibN0YHxkIKJKOBzo-5~2`mE==mq6JUM* zmHtU<3rxj#B!@ieOVq>I#M3BKyQS#01cTVYys7*So-Cm$O49u`t@@2|_3=PDC-p+PDMI!CmP(#Zoje+xYF1Y%|B#mWXAZ15Za znNYf89jIvwIbf+FW}zpoY8X68YU~Cj(&fnl`OLPR2UYP>x!1a-*OUbC>|9YXIj+!d z1&jRHA!p9@lqnpAx+_tBd9AFPWo!q797MUAOb#Akx#l$sYke$hb3>G1{1GMA7?Ty) z9yrQq-D!=oFr~dlM~AG=O-wu!JiXWx8}RLAc&H5#g z;0@qiaBdp0v!AZ2gB}ZEazwK8sDnQHnR{x(WW_5T$s_#&kIiJunh)QPr;Z>+j;q{H zoX0!;~gV%*)c| zjAZ3a@I?X=NNN`!*vpxRWrtM&p*5)%Tv4wKft-&^y9Gk!d<37{MQJJzWwTp9U@;Lp z?L2(cJyu}o+dlw*g#&E*y1`?Y9Y`Q&aHJr{M!HM3))1d}>3KJpv>L1eF z>j&|g=Vm^9s?B<+1|`mqG200DHG(M_PvQ@{5FX9urGgDA@eJ7 zy;aQ7eM>$!U?8z5Asa$ zb+eL&kKS({Mia276!`n; z^mKOd3c)>&Ym97llYtMP+p==zEvScR7=Q5j}Q}%X=4iM^U&%A;W}~8 zk82yGOhcHv@ej;7)dq28`bEYMYlA}Mq|p*KeL=JA?d%6G#)f1X@hrx&&H+5+nEs>G z%DuHx`$vMMY?8)}U0Ph)&l*~eoC{+Z9j4s^R5rl8PWN;f^^~|7tTv5IE8V4I)HyKq z5}Dr(m9fCIqPmU5!;?rMpw^uWB#F^jTk%}8SGmvan-fqd+ygB_)JwFhh68zJFu zm4h}JdrFLt!(RhN+~XBt_nzv#C7v*&h8Pyd*VpV}z2s8e+*W%%iC5~%Ox(+yWx3p?mc*-j5pOe;u<)LT$09Fq zHA1Vo>p7{k+%x5iq_YoC+fM;w_UUMoz4G>CPG5||SexLTGh!5zxW)F7x|9noNf2@ti$;-U^>6F%2-%iEEshg0NlrouvVplUGN;-o zZYm=w%`cJ_=D-VS3Q>t}Lsu;)v`M8I&`R$at^;kVRt0Vd7#6ojIBs;(=6dgFCQPk* z3HHK>IZG$1pt3z$MH&4Da6ikqy^1!;Iwf>l+DqWRAzOgTYB+wqRh^ZJHY6;Fz1AMB z1|OuAk>QT1#^R&@UgI^I06pSchyita)M4G;a^2HqAB?d@KVzMcGr8i-ix$=JfQQqc zR>XB$7~&o4`2Cfp3$13j*G)arbm`TaYqbGn2UAI@Zzo{STO!&#vp7K97FKv}71=`| zNsriK>kp5nO&=(0wC=NPWk|9fgJZLUXv02yA5iaItla`QOgYhFXTXWwCfOCjs6F;E z4ushtGg^=nYVe%o2SINRG3o3INqEh@fTe~hjQs*x_(EXlafwG;C5ZPU4_?uKA!q-w z8Lx_bcHoq@NzL!!otPsA0syvU@cE4fgq`Od2E1AoCl4a+_WrDB->iSDl7uu$G*rb+ z$k9+P<|GA{@0%3{2ml!Hf-D|@nfMgm8yVXR?OijTPm;@iV(v(%TzH*p->WaUW4fjLC#yXteg>s|Dy!{`h?eS5F0CH z#23SyJsddCwNZqp2_#V}Bs9H$9Hag5cTYFY<&9JG<*f)s_3bLu+yDYj-~D&1jV zUI@Z|mj^O7ohC3%6b<`#8_oJI2u<>PCm9qvQ_Ws?1nHJkoBGe z%NkW@JbAt?L({O&X8!3L>WL@Sve_0^ah1nPIXG)m!sXNV$8DNP@d`{+$MsTeqExmM z$dw4odgFCAd@WoWi<6EU53#(wt7E>M#cBzS>2`)xUNFlBH5t&9tZ8>pQm&vnS?4V%%{}-f+?gzKT0dD@|55ckRwVK7yzO7GA6-^1YK0I0Jhkw$fuyW>919! zA4KPE=vO^TTW=9I z(D);K1BXXPISk#qSNVXfUxf_eG=~nYCkI!ire{+WG%H|Uvpc2fgr=>a5a^d7X|JRp zVPxdZ0@&Gz6Lj#lL#Z>Y;az#SXhrkDRaoxO=;H0mN7|p(O|uwgkSa+?~_w zqoBZ#*BJxb5+txdpNrMLs2nyk0s>{{l}I-_7A)$)9TLP9D46n#&GUemOZkCgQV#N47g58pAeSLAYv+|z z<0D-oL?EGJ{>vU9Z9*!KPJorxu{a4>QY@blSg$noYSfHI_(NhM&ZB5GfqZVYH*xFl zgWwZ(%rq)abk5z9X$PU9C)*Zu)|Z)$u%ekmIpf;kljyl)3vvxFW6Is(PMYjdBQ`KHDnoQim4KxBE>{n%!*N?=+8}+p;>Xc0<9|WR2e^m8_ysnw=?ayaQMlCXy z3^3GLW?NLQGq9(xRomo+=re;KGh+Ujp7`f8rHoSzR;L}h$S1~7)CoY_56MPi#L)Uf zI>mlIWBUpX2N?1BQND0Dk6>Gik)B#l+?uDr;tdK4*zQ=rJ0|a1$UuaH1b{1Hct6$A4oJP=Bd-3cHF#7vm3gABtXf z^&atil+rG24&HANwRpz(AC^$jP%$BK-&b^-6rb9I#Iv)Wm*}|E+FqBULb&tged>*h-QAcQiL+Ya-LLF5GoZw@rV!QUW z)8Ghh``MQ{#Umn+sB)u$xENzcebyaop4uheoOSok?N|fTt*eK7_Ap=Tz!fX+rOm_@ zqyE9UNyK*g{to;d2sX(j;8L+MPBm>y09^3fU!-m&IUslyuhm%mvK= zjZ>khZ5@g-AxM7m7cKa!Mx}Xp0G4ZHoO`mMtwk5Nrz<`yvEjr-c^}o4VGv25_yut=*!EA2B7j)XA_eMvkkXh#m?< z&Hm!*E=1zUY>3i&-)@1^qR0CEkhc5<5A`%DwV}#f>g@f528ZzlzaqJo@PT?&3GYBS zxno@3?n?9nZaT20g}HH6J_HCh1M8P%+p6?F-w5>5@OI0=kq6VX0_Dxc3T1sYowUgV z%Z+>AmB2Otq&t1B+vNyrtb)IB2!id3RUeWoO)?iAklAb%iVy88RrgDGac+hr{7}36 z8ei-Pb{LvBM+DxVcZ<&FM$(%6L$@N_9p9j6Q{g*ip1Arf&lq0=P#=)kP<;4EafD&m zmM&ctLH}X1$EZCypkx=dQR2~_>EM=OSIGqsVJ2*>=<4$lvD6RSCl>Q#2D>s{`E?QE zWFFz5!zgXD(MnmZAY`9`5h+1x{Y;95W!u2!86G5?5I^%nCtlhvdkcOaHy-g`nYr#sM%eZg hb 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 N*cdCxccj0H{sY$%T6q8f literal 0 HcmV?d00001