# 损失函数
> 原文:[https://www.bookbookmark.ds100.org/ch/10/modeling_loss_functions.html](https://www.bookbookmark.ds100.org/ch/10/modeling_loss_functions.html)
```
# HIDDEN
# Clear previously defined variables
%reset -f
# Set directory for data loading to work properly
import os
os.chdir(os.path.expanduser('~/notebooks/10'))
```
```
# HIDDEN
import warnings
# Ignore numpy dtype warnings. These warnings are caused by an interaction
# between numpy and Cython and can be safely ignored.
# Reference: https://stackoverflow.com/a/40846742
warnings.filterwarnings("ignore", message="numpy.dtype size changed")
warnings.filterwarnings("ignore", message="numpy.ufunc size changed")
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
%matplotlib inline
import ipywidgets as widgets
from ipywidgets import interact, interactive, fixed, interact_manual
import nbinteract as nbi
sns.set()
sns.set_context('talk')
np.set_printoptions(threshold=20, precision=2, suppress=True)
pd.options.display.max_rows = 7
pd.options.display.max_columns = 8
pd.set_option('precision', 2)
# This option stops scientific notation for pandas
# pd.set_option('display.float_format', '{:.2f}'.format)
```
```
# HIDDEN
tips = sns.load_dataset('tips')
tips['pcttip'] = tips['tip'] / tips['total_bill'] * 100
```
回想到目前为止我们的假设:我们假设有一个单一的总体提示百分比$\theta^*$。我们的模型估计这个参数;我们使用变量$\theta$来表示我们的估计。我们希望使用收集到的 Tips 数据来确定$\theta$应该具有的值,
为了精确地确定哪一个$theta$值是最好的,我们定义了一个**损失函数**。损失函数是一个数学函数,它接受一个估计值$\theta$和数据集$y_1,y_2,ldots,y_n$中的点。它输出一个单独的数字,即**损失**,用来衡量$\theta$是否适合我们的数据。在数学符号中,我们要创建函数:
$$ L(\theta, y_1, y_2, \ldots, y_n) =\ \ldots $$
按照惯例,损失函数输出的值越低,值越大,值越高,值越低,值越大,值越低。为了适应我们的模型,我们选择了产生比其他所有选择的损失都要低的$theta$的值,即$theta$的值,它是**将损失最小化的$theta$的值**。我们使用符号$\hat \theta$表示将指定损失函数最小化的$\theta$值。
再次考虑两个可能的值:$\theta$:$\theta=10$和$\theta=15$。
```
# HIDDEN
sns.distplot(tips['pcttip'], bins=np.arange(30), rug=True)
plt.axvline(x=10, c='darkblue', linestyle='--', label=r'$ \theta = 10$')
plt.axvline(x=15, c='darkgreen', linestyle='--', label=r'$ \theta = 15$')
plt.legend()
plt.xlim(0, 30)
plt.xlabel('Percent Tip Amount')
plt.ylabel('Proportion per Percent');
```
![](https://img.kancloud.cn/ce/55/ce552b229bfbbf38110a16b9e17fd9ce_424x290.jpg)
由于$\theta=15$接近大多数点,我们的损失函数应该输出一个小的值为$\theta=15$和一个大的值为$\theta=10$。
让我们用这个直觉来创建一个损失函数。
### 我们的第一个损失函数:均方误差[¶](#Our-First-Loss-Function:-Mean-Squared-Error)
我们希望选择的$\theta$接近数据集中的点。因此,我们可以定义一个损失函数,它输出一个更大的值,因为$\theta$远离数据集中的点。我们从一个叫做 _ 均方误差 _ 的简单损失函数开始。这里的想法是:
1. 我们选择的值是$\theta$。
2. 对于数据集中的每个值,取值和 theta 之间的平方差:$(y_i-\theta)^2$。用一种简单的方法将差异平方化,将负差异转化为正差异。我们之所以要这样做,是因为如果我们的点$y_i=14$、$\theta=10$和$\theta=18$距离该点同样远,因此也同样“差”。
3. 要计算最终损失,取每个平方差的平均值。
这给了我们一个最终的损失函数:
$$ \begin{aligned} L(\theta, y_1, y_2, \ldots, y_n) &= \text{average}\left\{ (y_1 - \theta)^2, (y_2 - \theta)^2, \ldots, (y_n - \theta)^2 \right\} \\ &= \frac{1}{n} \left((y_1 - \theta)^2 + (y_2 - \theta)^2 + \ldots + (y_n - \theta)^2 \right) \\ &= \frac{1}{n} \sum_{i = 1}^{n}(y_i - \theta)^2\\ \end{aligned} $$
创建一个 python 函数来计算损失很简单:
```
def mse_loss(theta, y_vals):
return np.mean((y_vals - theta) ** 2)
```
让我们看看这个损失函数是如何工作的。假设我们的数据集只包含一个点,$y_1=14$。我们可以尝试不同的$\theta$值,看看每个值的损失函数输出了什么。
```
# HIDDEN
def try_thetas(thetas, y_vals, xlims, loss_fn=mse_loss, figsize=(10, 7), cols=3):
if not isinstance(y_vals, np.ndarray):
y_vals = np.array(y_vals)
rows = int(np.ceil(len(thetas) / cols))
plt.figure(figsize=figsize)
for i, theta in enumerate(thetas):
ax = plt.subplot(rows, cols, i + 1)
sns.rugplot(y_vals, height=0.1, ax=ax)
plt.axvline(theta, linestyle='--',
label=rf'$ \theta = {theta} $')
plt.title(f'Loss = {loss_fn(theta, y_vals):.2f}')
plt.xlim(*xlims)
plt.yticks([])
plt.legend()
plt.tight_layout()
try_thetas(thetas=[11, 12, 13, 14, 15, 16],
y_vals=[14], xlims=(10, 17))
```
![](https://img.kancloud.cn/71/31/71318ca5cf7ec43aec2993fe1c57ad24_695x479.jpg)
您还可以交互地尝试下面的不同值$\theta$。你应该理解为什么$theta=11$的损失比$theta=13$的损失高很多倍。
```
# HIDDEN
def try_thetas_interact(theta, y_vals, xlims, loss_fn=mse_loss):
if not isinstance(y_vals, np.ndarray):
y_vals = np.array(y_vals)
plt.figure(figsize=(4, 3))
sns.rugplot(y_vals, height=0.1)
plt.axvline(theta, linestyle='--')
plt.xlim(*xlims)
plt.yticks([])
print(f'Loss for theta = {theta}: {loss_fn(theta, y_vals):.2f}')
def mse_interact(theta, y_vals, xlims):
plot = interactive(try_thetas_interact, theta=theta,
y_vals=fixed(y_vals), xlims=fixed(xlims),
loss_fn=fixed(mse_loss))
plot.children[-1].layout.height = '240px'
return plot
mse_interact(theta=(11, 16, 0.5), y_vals=[14], xlims=(10, 17))
```
<button class="js-nbinteract-widget">Loading widgets...</button>
正如我们所希望的那样,我们的损失更大,因为$\theta$离我们的数据越远,当$\theta$正好落在我们的数据点上时,损失就越小。现在让我们来看看当我们有五个点而不是一个点时,我们的均方误差是如何表现的。我们这次的数据是:$[11,12,15,17,18]$。
```
# HIDDEN
try_thetas(thetas=[12, 13, 14, 15, 16, 17],
y_vals=[11, 12, 15, 17, 18],
xlims=(10.5, 18.5))
```
![](https://img.kancloud.cn/e3/0b/e30b31f56408662024087b3f60e11bf7_695x479.jpg)
在我们尝试的$Theta$值中,$Theta=15$的损失最小。但是,14 到 15 之间的$theta$的值可能比$theta=15$的损失更低。看看你能不能用下面的互动图找到一个更好的值$\theta$。
```
# HIDDEN
mse_interact(theta=(12, 17, 0.2),
y_vals=[11, 12, 15, 17, 18],
xlims=(10.5, 18.5))
```
<button class="js-nbinteract-widget">Loading widgets...</button>
平均平方误差似乎是通过惩罚远离数据中心的$\theta$值来完成的。现在让我们看看损失函数在原始的 Tip 百分比数据集上输出了什么。作为参考,尖端百分比的原始分布如下所示:
```
# HIDDEN
sns.distplot(tips['pcttip'], bins=np.arange(30), rug=True)
plt.xlim(0, 30)
plt.xlabel('Percent Tip Amount')
plt.ylabel('Proportion per Percent');
```
![](https://img.kancloud.cn/4c/11/4c1197b8e25dc4297c9dd000c2904e02_424x290.jpg)
让我们尝试一些值为$\theta$。
```
# HIDDEN
try_thetas(thetas=np.arange(14.5, 17.1, 0.5),
y_vals=tips['pcttip'],
xlims=(0, 30))
```
![](https://img.kancloud.cn/07/2e/072e817ea16e67c83ab44b477388958b_695x479.jpg)
和以前一样,我们创建了一个交互式小部件来测试不同的$theta$值。
```
# HIDDEN
mse_interact(theta=(13, 17, 0.25),
y_vals=tips['pcttip'],
xlims=(0, 30))
```
<button class="js-nbinteract-widget">Loading widgets...</button>
到目前为止,我们已经尝试过的最低价是 16.00 美元,略高于我们最初估计的 15%小费。
### 速记本
我们定义了第一个损失函数,均方误差(mse)。它计算出远离数据中心的$theta$值的高损失。数学上,该损失函数定义为:
$$ \begin{aligned} L(\theta, y_1, y_2, \ldots, y_n) &= \frac{1}{n} \sum_{i = 1}^{n}(y_i - \theta)^2\\ \end{aligned} $$
当我们更改$theta$或$y_1,y_2,ldots,y_n$时,loss 函数将计算不同的损失。当我们尝试不同的$theta$值和添加新的数据点(更改$y_1,y_2,ldots,y_n$)时,我们已经看到了这种情况。
简而言之,我们可以定义向量$\textbf y=[y_1,y_2,ldots,y_n]$。然后,我们可以将 mse 写为:
$$ \begin{aligned} L(\theta, \textbf{y}) &= \frac{1}{n} \sum_{i = 1}^{n}(y_i - \theta)^2\\ \end{aligned} $$
### 最大限度地减少损失
到目前为止,我们只需尝试一系列的价值,然后选择损失最小的价值,就可以找到\theta$的最佳价值。该方法虽然运行良好,但利用损失函数的性质可以找到一种较好的方法。
对于以下示例,我们使用一个包含五个点的数据集:$\textbf y=[11、12、15、16、17]$。
```
# HIDDEN
try_thetas(thetas=[12, 13, 14, 15, 16, 17],
y_vals=[11, 12, 15, 17, 18],
xlims=(10.5, 18.5))
```
![](https://img.kancloud.cn/e3/0b/e30b31f56408662024087b3f60e11bf7_695x479.jpg)
在上面的图中,我们使用了 12 到 17 之间的整数$\theta$值。当我们改变$theta$时,损失似乎开始很高(在 10.92),减少到$theta=15$,然后再次增加。我们可以看到损失随着$theta$的变化而变化,所以让我们制作一个图,将我们尝试的六个$theta$中的每一个的损失与$theta$进行比较。
```
# HIDDEN
thetas = np.array([12, 13, 14, 15, 16, 17])
y_vals = np.array([11, 12, 15, 17, 18])
losses = [mse_loss(theta, y_vals) for theta in thetas]
plt.scatter(thetas, losses)
plt.title(r'Loss vs. $ \theta $ when $\bf{y}$$ = [11, 12, 15, 17, 18] $')
plt.xlabel(r'$ \theta $ Values')
plt.ylabel('Loss');
```
![](https://img.kancloud.cn/ab/4b/ab4b8abdd6875c10a450271508e4e95e_411x303.jpg)
散点图显示了我们以前注意到的向下,然后向上的趋势。我们可以尝试更多的$theta$值,以查看一条完整的曲线,该曲线显示损失如何随着$theta$的变化而变化。
```
# HIDDEN
thetas = np.arange(12, 17.1, 0.05)
y_vals = np.array([11, 12, 15, 17, 18])
losses = [mse_loss(theta, y_vals) for theta in thetas]
plt.plot(thetas, losses)
plt.title(r'Loss vs. $ \theta $ when $\bf{y}$$ = [11, 12, 15, 17, 18] $')
plt.xlabel(r'$ \theta $ Values')
plt.ylabel('Loss');
```
![](https://img.kancloud.cn/90/31/9031ef1b81dd1041599831de06730b48_411x303.jpg)
上面的图显示,事实上,$\theta=15$不是最佳选择;14 到 15 之间的$\theta$损失会更低。我们可以用微积分来精确地找到$\theta$的最小值。在最小损失下,损失函数相对于$\theta$的导数为 0。
首先,我们从损失函数开始:
$$ \begin{aligned} L(\theta, \textbf{y}) &= \frac{1}{n} \sum_{i = 1}^{n}(y_i - \theta)^2\\ \end{aligned} $$
接下来,我们插入点$\textbf y=[11,12,15,17,18]$:
$$ \begin{aligned} L(\theta, \textbf{y}) &= \frac{1}{5} \big((11 - \theta)^2 + (12 - \theta)^2 + (15 - \theta)^2 + (17 - \theta)^2 + (18 - \theta)^2 \big)\\ \end{aligned} $$
为了找到将此函数最小化的$\theta$值,我们计算了与$\theta$相关的导数:
$$ \begin{aligned} \frac{\partial}{\partial \theta} L(\theta, \textbf{y}) &= \frac{1}{5} \big(-2(11 - \theta) - 2(12 - \theta) - 2(15 - \theta) - 2(17 - \theta) -2(18 - \theta) \big)\\ &= \frac{1}{5} \big(10 \cdot \theta - 146 \big)\\ \end{aligned} $$
然后,我们找到了$theta$的值,其中导数为零:
$$ \begin{aligned} \frac{1}{5} \big(10 \cdot \theta - 146 \big) &= 0 \\ 10 \cdot \theta - 146 &= 0 \\ \theta &= 14.6 \end{aligned} $$
我们已经找到了最小化的$\theta$,正如预期的那样,它在 14 到 15 之间。我们表示将损失降至最低的美元。因此,对于数据集$\textbf y=[11、12、15、17、18]$和 mse loss 函数:
$$ \hat{\theta} = 14.6 $$
如果我们恰好计算数据值的平均值,我们会发现一个奇怪的等价性:
$$ \text{mean} (\textbf{y}) = \hat{\theta} = 14.6 $$
### 均方误差最小值[¶](#The-Minimizing-Value-of-the-Mean-Squared-Error)
事实证明,上述等价性不仅仅是巧合;数据值 _ 的平均值 _ 总是产生$\hat \theta$,而$\theta$则使 MSE 损失最小化。
为了证明这一点,我们再次求出损失函数的导数。我们没有插入点,而是保留$y_i$术语的完整性,以便推广到其他数据集。
$$ \begin{aligned} L(\theta, \textbf{y}) &= \frac{1}{n} \sum_{i = 1}^{n}(y_i - \theta)^2\\ \frac{\partial}{\partial \theta} L(\theta, \textbf{y}) &= \frac{1}{n} \sum_{i = 1}^{n} -2(y_i - \theta) \\ &= -\frac{2}{n} \sum_{i = 1}^{n} (y_i - \theta) \\ \end{aligned} $$
由于我们没有用特定的值替换$Y_i$,所以这个公式可以与任何具有任意点数的数据集一起使用。
现在,我们将导数设为零,然后求出$\theta$以找到$\theta$的最小值,如下所示:
$$ \begin{aligned} -\frac{2}{n} \sum_{i = 1}^{n} (y_i - \theta) &= 0 \\ \sum_{i = 1}^{n} (y_i - \theta) &= 0 \\ \sum_{i = 1}^{n} y_i - \sum_{i = 1}^{n} \theta &= 0 \\ \sum_{i = 1}^{n} \theta &= \sum_{i = 1}^{n} y_i \\ n \cdot \theta &= y_1 + \ldots + y_n \\ \theta &= \frac{y_1 + \ldots + y_n}{n} \\ \hat \theta = \theta &= \text{mean} (\textbf{y}) \end{aligned} $$
你看,我们看到有一个单独的$theta$值,不管数据集是什么,它都会给出最小的毫秒。对于均方误差,我们知道$\hat \theta$是数据集值的平均值。
### 返回到原始数据集[¶](#Back-to-the-Original-Dataset)
我们不再像以前那样测试不同的$theta$值。我们可以一次计算平均小费百分比:
```
np.mean(tips['pcttip'])
```
```
16.080258172250463
```
```
# HIDDEN
sns.distplot(tips['pcttip'], bins=np.arange(30), rug=True)
plt.axvline(x=16.08, c='darkblue', linestyle='--', label=r'$ \hat \theta = 16.08$')
plt.legend()
plt.xlim(0, 30)
plt.title('Distribution of tip percent')
plt.xlabel('Percent Tip Amount')
plt.ylabel('Proportion per Percent');
```
![](https://img.kancloud.cn/26/3c/263c0225d68762d26b657749211fda1e_424x301.jpg)
### 摘要[¶](#Summary)
我们引入了一个**常量模型**,这个模型为数据集中的所有条目输出相同的数字。
**损失函数**$L(\theta、\textbf y)$测量给定值$\theta$与数据的匹配程度。在本节中,我们介绍了均方误差损失函数,并证明了对于常数模型,$hat \theta=\text mean(\textbf y)$的值。
我们在本节中采取的步骤适用于许多建模场景:
1. 选择一个模型。
2. 选择损失函数。
3. 通过最小化损失来拟合模型。
在本书中,我们所有的建模技术都扩展到这些步骤中的一个或多个。我们介绍了新的模型(1)、新的损失函数(2)和减少损失的新技术(3)。
- 一、数据科学的生命周期
- 二、数据生成
- 三、处理表格数据
- 四、数据清理
- 五、探索性数据分析
- 六、数据可视化
- Web 技术
- 超文本传输协议
- 处理文本
- python 字符串方法
- 正则表达式
- regex 和 python
- 关系数据库和 SQL
- 关系模型
- SQL
- SQL 连接
- 建模与估计
- 模型
- 损失函数
- 绝对损失和 Huber 损失
- 梯度下降与数值优化
- 使用程序最小化损失
- 梯度下降
- 凸性
- 随机梯度下降法
- 概率与泛化
- 随机变量
- 期望和方差
- 风险
- 线性模型
- 预测小费金额
- 用梯度下降拟合线性模型
- 多元线性回归
- 最小二乘-几何透视
- 线性回归案例研究
- 特征工程
- 沃尔玛数据集
- 预测冰淇淋评级
- 偏方差权衡
- 风险和损失最小化
- 模型偏差和方差
- 交叉验证
- 正规化
- 正则化直觉
- L2 正则化:岭回归
- L1 正则化:LASSO 回归
- 分类
- 概率回归
- Logistic 模型
- Logistic 模型的损失函数
- 使用逻辑回归
- 经验概率分布的近似
- 拟合 Logistic 模型
- 评估 Logistic 模型
- 多类分类
- 统计推断
- 假设检验和置信区间
- 置换检验
- 线性回归的自举(真系数的推断)
- 学生化自举
- P-HACKING
- 向量空间回顾
- 参考表
- Pandas
- Seaborn
- Matplotlib
- Scikit Learn