{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在上一节中我们学到，为了避免模型在数据集上过度训练导致过拟合，通常将数据集划分为训练集和测试集。\n",
    "\n",
    "### 数据集划分方法\n",
    "\n",
    "假设我们有一个包含 m 个样例的数据集 $ D = \\{(\\bf{x_1}, y_1), (\\bf{x_2}, y_2), ..., (\\bf{x_m}, y_m)\\} $，得想办法对数据集进行划分，分成训练和测试两类，训练集记为 $ S $，测试集记为 $ T $。这样的划分方法有很多，常见的有：**留出法**（hold-out）、**交叉验证法**（cross validation）、**自助法**（bootstrapping）等。\n",
    "\n",
    "留出法非常简单，直接将数据集划分成两个互斥的集合，分别作为训练集和测试集，$ D = S \\cup T, S \\cap T = \\emptyset $，常见的做法是将大约 2/3 ~ 4/5 的数据用于训练，剩余的数据用于测试，注意划分的时候尽可能保持数据分布的一致性。\n",
    "\n",
    "交叉验证法先将数据集划分成 k 个大小相似的互斥子集，$ D = D_1 \\cup D_2 \\cup \\dots \\cup D_k, D_i \\cap D_j = \\emptyset(i \\ne j) $，同样划分时尽可能保持数据分布的一致性。然后，用其中的 k-1 个子集的并集作为训练集，剩下的那个子集作为测试集，这样可以进行 k 次训练和测试，最终计算这 k 次测试结果的均值。交叉验证法又叫 **k 折交叉验证**（k-fold cross validation）。\n",
    "\n",
    "自助法是以 **自助采样**（bootstrap sampling，也称为 **可重复采样**）为基础的样本划分方法，所谓自助采样，指的是从数据集 $ D $ 中随机挑选一个样本，将其拷贝放入一个新集合 $ D' $ 中，然后将样本重新放回集合 $ D $ 中，重复 m 次，得到一个包含 m 个样本的数据集 $ D' $。很显然，$ D $ 中有些样本在 $ D' $ 中会出现多次，还有一些样本不会出现，样本不会出现的概率是：\n",
    "\n",
    "$$\n",
    "\\lim\\limits_{m \\to +\\infty}(1-\\frac{1}{m})^m = \\frac{1}{e} \\approx 0.368\n",
    "$$\n",
    "\n",
    "我们将 $ D' $ 用作训练集，$ D' $ 中没有出现的样本 $ D \\backslash D'$ 用作测试集，这样的测试结果也叫做 **包外估计**（out-of-bag estimate）。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 模型评估方法\n",
    "\n",
    "通过上面的方法，把样本数据分成训练集和测试集之后，就可以使用你的机器学习算法对训练集进行训练生成一个模型，然后再拿着这个模型在测试集上进行验证，评估这个模型的效果。\n",
    "\n",
    "其实，在前面介绍损失函数时提到了两种损失函数，一种是 **绝对损失（Absolute Loss）**，一种是 **平方损失（Squared Loss）**。\n",
    "\n",
    "绝对损失函数：$$ loss = |y - \\hat{y}| $$\n",
    "\n",
    "平方损失函数：$$ loss = (y - \\hat{y})^2 $$\n",
    "\n",
    "这实际上也是两种评估模型的指标，分别是：**平均绝对误差**（Mean Absolute Error，MAE） 和 **均方误差**（Mean Squared Error，MSE）。\n",
    "\n",
    "$$\n",
    "MAE = \\frac{1}{N} \\sum_{i=1}^{N} \\left| y_i - \\hat{y_i} \\right|\n",
    "$$\n",
    "\n",
    "$$\n",
    "MSE = \\frac{1}{N} \\sum_{i=1}^{N} (y_i - \\hat{y_i})^2\n",
    "$$\n",
    "\n",
    "除此之外，从 MAE 还可以演变成一个新的指标，**平均绝对百分误差**（Mean Absolute Percentage Error，MAPE）：\n",
    "\n",
    "$$\n",
    "MAPE = \\frac{100}{N} \\sum_{i=1}^{N} \\left| \\frac{y_i - \\hat{y_i}}{y_i} \\right|, y_i \\ne 0\n",
    "$$\n",
    "\n",
    "MAPE 通过计算绝对误差的百分比来表示预测效果，其取值越小越好，如果 MAPE = 10，这表明预测平均偏离真实值 10%。\n",
    "\n",
    "从 MSE 也可以演变成一个新的指标，**均方根误差**（Root Mean Squared Error，RMSE），它就是对 MSE 求开方：\n",
    "\n",
    "$$\n",
    "RMSE = \\sqrt{\\frac{1}{N} \\sum_{i=1}^{N} (y_i - \\hat{y_i})^2} = \\sqrt{MSE}\n",
    "$$\n",
    "\n",
    "基于 RMSE 还有一个变种指标，叫**均方根对数误差**（Root Mean Squared Logarithmic Error，RMSLE），将上面公式中的 $y_i$ 和 $\\hat{y_i}$ 换成对数形式：\n",
    "\n",
    "$$\n",
    "RMSLE = \\sqrt{\\frac{1}{N} \\sum_{i=1}^{N} (log(y_i+1) - log(\\hat{y_i}+1))^2}\n",
    "$$\n",
    "\n",
    "最后，**$R^2$**（R-Square）也是一个常见的评估指标，它的公式如下：\n",
    "\n",
    "$$\n",
    "R^2 = 1 - \\frac{\\sum_{i=1}^{N} (y_i - \\hat{y_i})^2}{\\sum_{i=1}^{N} (y_i - \\bar{y_i})^2}\n",
    "$$\n",
    "\n",
    "$R^2$ 用于度量因变量的变异中可由自变量解释部分所占的比例，一般取值范围是 0~1，$R^2$ 越接近 1，表明回归平方和占总平方和的比例越大，回归线与各观测点越接近，用 x 的变化来解释 y 值变差的部分就越多，回归的拟合程度就越好。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 使用 sklearn 评估模型\n",
    "\n",
    "sklearn 中的 metrics 模块提供了几个方法来计算模型评估的指标值，譬如 `mean_absolute_error()` 用于计算 MAE，`mean_squared_error()` 用于计算 MSE，`r2_score()` 用于计算 $R^2$。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MSE =  4.5\n",
      "MAE =  1.5\n",
      "R-Square =  0.9945454545454545\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "from sklearn import metrics\n",
    "\n",
    "expected = np.array([10,20,30,40,50,60,70,80,90,100])\n",
    "predicted = np.array([10,21,33,41,49,55,70,82,90,102])\n",
    "\n",
    "print('MAE = ', metrics.mean_absolute_error(expected, predicted))\n",
    "print('MSE = ', metrics.mean_squared_error(expected, predicted))\n",
    "print('R-Square = ', metrics.r2_score(expected, predicted))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 模型选择\n",
    "\n",
    "上面介绍了多种数据集划分的方法和模型评估的方法，看起来我们只要挑一个评估结果最好的模型就可以了，但是实际上，模型选择可能要比想象中复杂的多。这里面涉及几个重要因素：\n",
    "\n",
    "* 首先，我们希望比较的是泛化性能，而我们通过评估方法得到的是测试集上的性能，两者的对比结果未必相同；\n",
    "* 第二，测试集上的性能和测试集本身的选择有很大关系，使用大小不同的测试集，或者使用大小相同但数据不同的测试集，测试结果也会有不同；\n",
    "* 第三，很多机器学习算法本身具有一定的随机性，即便相同的参数在相同的测试集上运行多次，测试结果也可能不同；\n",
    "\n",
    "那么，有没有适当的方法对学习器的性能进行比较呢？统计学中的 **假设检验**（hypothesis test）为此提供了重要依据，根据假设检验，我们可以推断出，若在测试集上观察到学习器 A 比 B 好，则 A 的泛化性能是否在统计意义上优于 B，以及这个结论的把握有多大。\n",
    "\n",
    "**二项检验**（binomial test）和 **t 检验**（t-test）是两种最基本的假设检验方法，可以对单个学习器泛化性能的假设进行检验；如果需要对多个学习器性能进行比较，可以使用 **交叉验证 t 检验** 和 **McNemar 检验**，这两个方法都是在同一个数据集上进行检验，**Fredman 检验** 和 **Nemenyi 后续检验** 可以在一组数据集上对学习器的性能进行检验。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 参考\n",
    "\n",
    "* 机器学习, 周志华\n",
    "* [一份非常全面的机器学习分类与回归算法的评估指标汇总](https://juejin.im/post/5bbbc1d6f265da0af40726fb)\n",
    "\n",
    "#### TODO\n",
    "\n",
    "* 为什么需要这么多的评估指标？\n",
    "* 每一种评估指标的优缺点，举例说明\n",
    "* 还有没有其他的评估指标？\n",
    "* RMSE 代表的是预测值和真实值差值的样本标准差？"
   ]
  }
 ],
 "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.7.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
