特征缩放和学习率

Feature scaling and Learning Rate

内容:

  • 使用上一实验中开发的多变量处理程序
  • 在具有多特征的数据集上运行梯度下降算法
  • 探索学习率 alpha 对梯度下降的影响
  • 通过使用 z-score 标准化进行特征缩放来提高梯度下降的性能
  • 利用线性回归例程和您在之前的实验中开发的多个功能
  • 探索了学习率的影响alpha收敛时
  • 发现了使用 z 分数归一化进行特征缩放在加速收敛方面的价值

工具

您将使用上一实验中开发的函数,以及 Matplotlib 和 NumPy 库。

import numpy as np
np.set_printoptions(precision=2)
import matplotlib.pyplot as plt
dlblue = '#0096ff'; dlorange = '#FF9300'; dldarkred='#C00000'; dlmagenta='#FF40FF'; dlpurple='#7030A0'; 
plt.style.use('./deeplearning.mplstyle')
from lab_utils_multi import  load_house_data, compute_cost, run_gradient_descent 
from lab_utils_multi import  norm_plot, plt_contour_multi, plt_equal_scale, plot_cost_i_w

符号说明

通用符号 描述 Python(如适用)
a 标量,非加粗
\mathbf{a} 向量,加粗
\mathbf{A} 矩阵,加粗大写字母
回归相关符号
\mathbf{X} 训练样本矩阵 X_train
\mathbf{y} 训练样本目标值 y_train
\mathbf{x}^{(i)}, y^{(i)} i 个训练样本 X[i], y[i]
m 训练样本数量 m
n 每个样本的特征数量 n
\mathbf{w} 参数:权重 w
b 参数:偏置 b
f_{\mathbf{w},b}(\mathbf{x}^{(i)}) 模型在 \mathbf{x}^{(i)} 处的预测结果,由参数 \mathbf{w}, b 决定:f_{\mathbf{w},b}(\mathbf{x}^{(i)}) = \mathbf{w} \cdot \mathbf{x}^{(i)} + b f_wb
\frac{\partial J(\mathbf{w},b)}{\partial w_j} 成本函数对参数 w_j 的梯度/偏导数 dj_dw[j]
\frac{\partial J(\mathbf{w},b)}{\partial b} 成本函数对参数 b 的梯度/偏导数 dj_db

问题描述

Problem Statement

与之前的实验一样,您将使用房价预测作为案例。本次实验的训练数据集包含多个样本,每个样本有 4 个特征(面积、卧室数量、楼层数量和房龄),如下表所示。需要注意的是,本实验中的面积特征以平方英尺(sqft)为单位,而之前的实验使用的是 1000 平方英尺(1000 sqft)。此外,本次实验的数据集规模比之前的实验更大。

我们希望通过这些特征构建一个线性回归模型,以预测其他房屋的价格——例如,预测一栋面积为 1200 平方英尺、3 间卧室、1 层楼、40 年房龄的房子的价格。

数据集:

Dataset

面积(平方英尺) 卧室数量 楼层数量 房龄 价格(以千美元为单位)
952 2 1 65 271.5
1244 3 2 64 232
1947 3 2 17 509.8
# 加载数据集
# load the dataset
X_train, y_train = load_house_data()
X_features = ['size(sqft)','bedrooms','floors','age']

让我们通过绘制每个特征与价格的关系来查看数据集及其特征。

fig,ax=plt.subplots(1, 4, figsize=(12, 3), sharey=True)
for i in range(len(ax)):
    ax[i].scatter(X_train[:,i],y_train)
    ax[i].set_xlabel(X_features[i])
ax[0].set_ylabel("Price (1000's)")
plt.show()

png

绘制每个特征与目标变量 price 的对比图,可以帮助我们分析哪些特征对价格的影响最大。通过观察图表,我们可能得出以下结论:

  • 面积(Size):随着面积的增加,价格显著提高,说明面积对价格影响较大。
  • 卧室数量(Bedrooms)楼层数量(Floors):这两个特征对价格的影响似乎不大,关系较弱。
  • 房龄(Age):新房子的价格普遍高于老房子,说明房龄对价格有一定影响。

通过这些观察,可以更直观地选择对价格影响显著的特征,进一步优化模型。

多变量梯度下降

Gradient Descent With Multiple Variables

以下是上一实验中针对多变量梯度下降推导的公式:

\begin{align*} \text{repeat}&\text{ until convergence:} \; \lbrace \newline\; & w_j := w_j - \alpha \frac{\partial J(\mathbf{w},b)}{\partial w_j} \tag{1} \; & \text{对于 j = 0..n-1}\newline &b\ \ := b - \alpha \frac{\partial J(\mathbf{w},b)}{\partial b} \newline \rbrace \end{align*}

其中,n 是特征数量,参数 w_jb 是同时更新的,公式中的梯度计算如下:

\begin{align} \frac{\partial J(\mathbf{w},b)}{\partial w_j} &= \frac{1}{m} \sum\limits_{i = 0}^{m-1} (f_{\mathbf{w},b}(\mathbf{x}^{(i)}) - y^{(i)})x_{j}^{(i)} \tag{2} \\ \frac{\partial J(\mathbf{w},b)}{\partial b} &= \frac{1}{m} \sum\limits_{i = 0}^{m-1} (f_{\mathbf{w},b}(\mathbf{x}^{(i)}) - y^{(i)}) \tag{3} \end{align}

  • m 是数据集中训练样本的数量
  • f_{\mathbf{w},b}(\mathbf{x}^{(i)}) 是模型的预测值,而 y^{(i)} 是目标值

梯度下降算法通过不断更新参数 w_jb,最终最小化成本函数 J(\mathbf{w}, b),从而找到最佳拟合模型。

学习率

Learning Rate

mid1

课程中讨论了设置学习率 \alpha 时的一些相关问题。学习率控制参数更新的步长大小(参见上文的公式 (1)),并且对所有参数共享相同的值。接下来,让我们在数据集上运行梯度下降,并尝试几种不同的 \alpha 设置。

\alpha = 9.9e-7

#set alpha to 9.9e-7
_, _, hist = run_gradient_descent(X_train, y_train, 10, alpha = 9.9e-7)
Iteration Cost          w0       w1       w2       w3       b       djdw0    djdw1    djdw2    djdw3    djdb  
---------------------|--------|--------|--------|--------|--------|--------|--------|--------|--------|--------|
        0 9.55884e+04  5.5e-01  1.0e-03  5.1e-04  1.2e-02  3.6e-04 -5.5e+05 -1.0e+03 -5.2e+02 -1.2e+04 -3.6e+02
        1 1.28213e+05 -8.8e-02 -1.7e-04 -1.0e-04 -3.4e-03 -4.8e-05  6.4e+05  1.2e+03  6.2e+02  1.6e+04  4.1e+02
        2 1.72159e+05  6.5e-01  1.2e-03  5.9e-04  1.3e-02  4.3e-04 -7.4e+05 -1.4e+03 -7.0e+02 -1.7e+04 -4.9e+02
        3 2.31358e+05 -2.1e-01 -4.0e-04 -2.3e-04 -7.5e-03 -1.2e-04  8.6e+05  1.6e+03  8.3e+02  2.1e+04  5.6e+02
        4 3.11100e+05  7.9e-01  1.4e-03  7.1e-04  1.5e-02  5.3e-04 -1.0e+06 -1.8e+03 -9.5e+02 -2.3e+04 -6.6e+02
        5 4.18517e+05 -3.7e-01 -7.1e-04 -4.0e-04 -1.3e-02 -2.1e-04  1.2e+06  2.1e+03  1.1e+03  2.8e+04  7.5e+02
        6 5.63212e+05  9.7e-01  1.7e-03  8.7e-04  1.8e-02  6.6e-04 -1.3e+06 -2.5e+03 -1.3e+03 -3.1e+04 -8.8e+02
        7 7.58122e+05 -5.8e-01 -1.1e-03 -6.2e-04 -1.9e-02 -3.4e-04  1.6e+06  2.9e+03  1.5e+03  3.8e+04  1.0e+03
        8 1.02068e+06  1.2e+00  2.2e-03  1.1e-03  2.3e-02  8.3e-04 -1.8e+06 -3.3e+03 -1.7e+03 -4.2e+04 -1.2e+03
        9 1.37435e+06 -8.7e-01 -1.7e-03 -9.1e-04 -2.7e-02 -5.2e-04  2.1e+06  3.9e+03  2.0e+03  5.1e+04  1.4e+03
w,b found by gradient descent: w: [-0.87 -0.   -0.   -0.03], b: -0.00

似乎学习率太高了。 结果不会收敛。成本在 增加 而不是减少。让我们绘制结果:

plot_cost_i_w(X_train, y_train, hist)

png

右侧的图展示了其中一个参数 w_0 的值。在每次迭代中,它都超出了最优值,导致成本函数(Cost)增加 而不是接近最小值。需要注意的是,这并不是完全准确的图示,因为每次迭代中实际上有 4 个参数被同时更新,而不仅仅是 w_0。该图仅显示了在其他参数固定于适当值时,w_0 的变化情况。在此图以及后续的图中,您可能会注意到蓝色和橙色线条略有偏差。

\alpha = 9e-7

让我们尝试一个稍小的学习率,看看会发生什么。

#set alpha to 9e-7
_,_,hist = run_gradient_descent(X_train, y_train, 10, alpha = 9e-7)
Iteration Cost          w0       w1       w2       w3       b       djdw0    djdw1    djdw2    djdw3    djdb  
---------------------|--------|--------|--------|--------|--------|--------|--------|--------|--------|--------|
        0 6.64616e+04  5.0e-01  9.1e-04  4.7e-04  1.1e-02  3.3e-04 -5.5e+05 -1.0e+03 -5.2e+02 -1.2e+04 -3.6e+02
        1 6.18990e+04  1.8e-02  2.1e-05  2.0e-06 -7.9e-04  1.9e-05  5.3e+05  9.8e+02  5.2e+02  1.3e+04  3.4e+02
        2 5.76572e+04  4.8e-01  8.6e-04  4.4e-04  9.5e-03  3.2e-04 -5.1e+05 -9.3e+02 -4.8e+02 -1.1e+04 -3.4e+02
        3 5.37137e+04  3.4e-02  3.9e-05  2.8e-06 -1.6e-03  3.8e-05  4.9e+05  9.1e+02  4.8e+02  1.2e+04  3.2e+02
        4 5.00474e+04  4.6e-01  8.2e-04  4.1e-04  8.0e-03  3.2e-04 -4.8e+05 -8.7e+02 -4.5e+02 -1.1e+04 -3.1e+02
        5 4.66388e+04  5.0e-02  5.6e-05  2.5e-06 -2.4e-03  5.6e-05  4.6e+05  8.5e+02  4.5e+02  1.2e+04  2.9e+02
        6 4.34700e+04  4.5e-01  7.8e-04  3.8e-04  6.4e-03  3.2e-04 -4.4e+05 -8.1e+02 -4.2e+02 -9.8e+03 -2.9e+02
        7 4.05239e+04  6.4e-02  7.0e-05  1.2e-06 -3.3e-03  7.3e-05  4.3e+05  7.9e+02  4.2e+02  1.1e+04  2.7e+02
        8 3.77849e+04  4.4e-01  7.5e-04  3.5e-04  4.9e-03  3.2e-04 -4.1e+05 -7.5e+02 -3.9e+02 -9.1e+03 -2.7e+02
        9 3.52385e+04  7.7e-02  8.3e-05 -1.1e-06 -4.2e-03  8.9e-05  4.0e+05  7.4e+02  3.9e+02  1.0e+04  2.5e+02
w,b found by gradient descent: w: [ 7.74e-02  8.27e-05 -1.06e-06 -4.20e-03], b: 0.00

在整个运行过程中,成本不断降低,这表明 Alpha 不会太大。

plot_cost_i_w(X_train, y_train, hist)

png

在左侧图中,可以看到成本函数(Cost)正如预期地逐渐减小。右侧图显示,w_0 仍然围绕最小值振荡,但它在每次迭代中逐渐减小,而不是增加。请注意,上文中 dj_dw[0] 的符号在每次迭代中都会改变,因为 w[0] 每次都跳过了最优值。

这个 \alpha 值会收敛。您可以尝试调整迭代次数,观察它的表现。

#set alpha to 9e-7
_,_,hist = run_gradient_descent(X_train, y_train, 100, alpha = 9e-7)
Iteration Cost          w0       w1       w2       w3       b       djdw0    djdw1    djdw2    djdw3    djdb  
---------------------|--------|--------|--------|--------|--------|--------|--------|--------|--------|--------|
        0 6.64616e+04  5.0e-01  9.1e-04  4.7e-04  1.1e-02  3.3e-04 -5.5e+05 -1.0e+03 -5.2e+02 -1.2e+04 -3.6e+02
       10 3.28712e+04  4.2e-01  7.1e-04  3.2e-04  3.4e-03  3.2e-04 -3.8e+05 -7.0e+02 -3.6e+02 -8.4e+03 -2.5e+02
       20 1.66671e+04  3.7e-01  5.6e-04  2.0e-04 -3.8e-03  3.2e-04 -2.7e+05 -4.8e+02 -2.5e+02 -5.6e+03 -1.8e+02
       30 8.84918e+03  3.3e-01  4.3e-04  8.8e-05 -1.1e-02  3.4e-04 -1.8e+05 -3.3e+02 -1.7e+02 -3.7e+03 -1.2e+02
       40 5.07632e+03  3.1e-01  3.3e-04 -1.1e-05 -1.7e-02  3.6e-04 -1.3e+05 -2.3e+02 -1.1e+02 -2.4e+03 -8.8e+01
       50 3.25458e+03  2.9e-01  2.4e-04 -1.0e-04 -2.3e-02  3.9e-04 -8.9e+04 -1.6e+02 -7.7e+01 -1.5e+03 -6.2e+01
       60 2.37399e+03  2.8e-01  1.6e-04 -1.9e-04 -2.9e-02  4.2e-04 -6.2e+04 -1.1e+02 -5.1e+01 -8.3e+02 -4.5e+01
       70 1.94738e+03  2.7e-01  9.0e-05 -2.7e-04 -3.5e-02  4.6e-04 -4.3e+04 -7.3e+01 -3.3e+01 -3.8e+02 -3.2e+01
       80 1.73978e+03  2.7e-01  2.3e-05 -3.5e-04 -4.1e-02  5.0e-04 -3.0e+04 -4.9e+01 -2.0e+01 -7.8e+01 -2.4e+01
       90 1.63784e+03  2.6e-01 -3.9e-05 -4.3e-04 -4.7e-02  5.4e-04 -2.1e+04 -3.2e+01 -1.2e+01  1.3e+02 -1.8e+01
w,b found by gradient descent: w: [ 2.48e-01 -1.18e-04 -5.10e-04 -5.23e-02], b: 0.00
plot_cost_i_w(X_train, y_train, hist)

png

在左侧图中,可以看到成本函数(Cost)正如预期地逐渐减小,并且在第80迭代后基本上就趋于不在减小了。右侧图显示,w_0 仍然围绕最小值振荡,但它在已经非常接近最优解了

\alpha = 1e-7

让我们尝试一个稍微小一点的值 看看会发生什么。

#set alpha to 1e-7
_,_,hist = run_gradient_descent(X_train, y_train, 10, alpha = 1e-7)
Iteration Cost          w0       w1       w2       w3       b       djdw0    djdw1    djdw2    djdw3    djdb  
---------------------|--------|--------|--------|--------|--------|--------|--------|--------|--------|--------|
        0 4.42313e+04  5.5e-02  1.0e-04  5.2e-05  1.2e-03  3.6e-05 -5.5e+05 -1.0e+03 -5.2e+02 -1.2e+04 -3.6e+02
        1 2.76461e+04  9.8e-02  1.8e-04  9.2e-05  2.2e-03  6.5e-05 -4.3e+05 -7.9e+02 -4.0e+02 -9.5e+03 -2.8e+02
        2 1.75102e+04  1.3e-01  2.4e-04  1.2e-04  2.9e-03  8.7e-05 -3.4e+05 -6.1e+02 -3.1e+02 -7.3e+03 -2.2e+02
        3 1.13157e+04  1.6e-01  2.9e-04  1.5e-04  3.5e-03  1.0e-04 -2.6e+05 -4.8e+02 -2.4e+02 -5.6e+03 -1.8e+02
        4 7.53002e+03  1.8e-01  3.3e-04  1.7e-04  3.9e-03  1.2e-04 -2.1e+05 -3.7e+02 -1.9e+02 -4.2e+03 -1.4e+02
        5 5.21639e+03  2.0e-01  3.5e-04  1.8e-04  4.2e-03  1.3e-04 -1.6e+05 -2.9e+02 -1.5e+02 -3.1e+03 -1.1e+02
        6 3.80242e+03  2.1e-01  3.8e-04  1.9e-04  4.5e-03  1.4e-04 -1.3e+05 -2.2e+02 -1.1e+02 -2.3e+03 -8.6e+01
        7 2.93826e+03  2.2e-01  3.9e-04  2.0e-04  4.6e-03  1.4e-04 -9.8e+04 -1.7e+02 -8.6e+01 -1.7e+03 -6.8e+01
        8 2.41013e+03  2.3e-01  4.1e-04  2.1e-04  4.7e-03  1.5e-04 -7.7e+04 -1.3e+02 -6.5e+01 -1.2e+03 -5.4e+01
        9 2.08734e+03  2.3e-01  4.2e-04  2.1e-04  4.8e-03  1.5e-04 -6.0e+04 -1.0e+02 -4.9e+01 -7.5e+02 -4.3e+01
w,b found by gradient descent: w: [2.31e-01 4.18e-04 2.12e-04 4.81e-03], b: 0.00

成本函数在整个运行过程中都在减小,这表明 \alpha 的值并不过大。

plot_cost_i_w(X_train,y_train,hist)

png

在左侧图中,可以看到成本函数(Cost)正如预期地逐渐减小。右侧图显示,w_0 在减小的过程中没有越过最小值。请注意,上文中 dj_w0 在整个运行过程中始终为负值。

该解也会收敛,但收敛速度不如前一个例子那么快。

特征缩放

Feature Scaling

mid1

课程中描述了将数据集重新缩放到特征具有相似范围的重要性。

如果您对为什么需要这样做的细节感兴趣,请点击下方的“详情”标题。如果不感兴趣,可以直接查看下面关于如何实现特征缩放的部分。

详情

让我们再次回顾 \alpha = 9 \times 10^{-7} 的情况。这接近于在不发散的情况下可以设置的最大 \alpha 值。以下是前几次迭代的短时间运行结果:

mid

上图中,尽管成本函数在减少,但很明显 w_0 的变化速度比其他参数快得多,这是因为其梯度远大于其他参数。

下图显示了 \alpha = 9 \times 10^{-7} 进行长时间运行的结果。这需要几个小时才能完成。

mid

上图中可以看到,成本函数在初始减少之后,下降速度非常缓慢。注意 w_0w_1w_2 的差异,以及 dj_dw0dj_dw1-3 的差异。

  • w_0 很快接近最终值,而 dj_dw0 迅速减小到一个较小的值,表明 w_0 已接近最终值。
  • 其他参数的减小速度明显慢得多。

为什么会这样?是否有可以改进的地方?请看下图:

mid

上图显示了为什么 w 的更新速度不均匀:

  • \alpha 是所有参数更新(wb)共享的学习率。
  • 公共误差项会被乘以对应特征值以更新 w(而非 b)。
  • 特征值的数量级差异显著,导致某些特征的更新速度比其他特征快得多。在此例中,w_0 乘以的是“面积(平方英尺)”,其值通常大于 1000,而 w_1 乘以的是“卧室数量”,其值通常为 2-4。

解决方案是特征缩放(Feature Scaling)。

课程中讨论了三种不同的特征缩放方法:

  • 特征缩放(Feature Scaling):基本思想是将每个特征除以一个用户选择的值,使其范围在 -1 到 1 之间。
  • 均值归一化(Mean Normalization):公式为 x_i := \dfrac{x_i - \mu_i}{\text{max} - \text{min}}
  • Z-score 标准化(Z-score Normalization):我们将在下面深入探讨这一方法。

Z-score 标准化

z-score normalization

经过 Z-score 标准化后,所有特征将具有均值为 0 和标准差为 1。

要实现 Z-score 标准化,请按照以下公式调整输入值:
x^{(i)}_j = \dfrac{x^{(i)}_j - \mu_j}{\sigma_j} \tag{4}
其中,j 表示 X 矩阵中的一个特征或一列,\mu_j 是特征 j 的均值,\sigma_j 是特征 j 的标准差。

\begin{align} \mu_j &= \frac{1}{m} \sum_{i=0}^{m-1} x^{(i)}_j \tag{5}\\ \sigma^2_j &= \frac{1}{m} \sum_{i=0}^{m-1} (x^{(i)}_j - \mu_j)^2 \tag{6} \end{align}

实现注意事项: 在对特征进行标准化时,必须存储用于标准化的值——即计算过程中使用的均值和标准差。在模型训练完参数后,我们通常会预测之前未见过的房屋价格。对于一个新的 x 值(例如客厅面积和卧室数量),我们必须使用之前从训练集计算出的均值和标准差对其进行标准化。

实现

def zscore_normalize_features(X):
    """
    计算 X 的 Z-score 标准化(按列计算)
    
    参数:
      X (ndarray): 形状为 (m, n) 的输入数据,其中 m 为样本数量,n 为特征数量
      
    返回值:
      X_norm (ndarray): 形状为 (m, n) 的标准化后的输入数据
      mu (ndarray):     形状为 (n,) 的每个特征的均值
      sigma (ndarray):  形状为 (n,) 的每个特征的标准差
    """
    # 计算每列(特征)的均值
    mu     = np.mean(X, axis=0)                 # mu 的形状为 (n,)
    # 计算每列(特征)的标准差
    sigma  = np.std(X, axis=0)                  # sigma 的形状为 (n,)
    # 对每个样本,按列逐元素减去对应列的均值,然后除以对应列的标准差
    X_norm = (X - mu) / sigma      

    return (X_norm, mu, sigma)
 
# 检查我们的实现
# from sklearn.preprocessing import scale
# scale(X_orig, axis=0, with_mean=True, with_std=True, copy=True)

让我们来看看 Z-score 标准化所涉及的步骤。下图逐步展示了标准化的各个转换步骤。

mu     = np.mean(X_train,axis=0)   
sigma  = np.std(X_train,axis=0) 
X_mean = (X_train - mu)
X_norm = (X_train - mu)/sigma      

fig,ax=plt.subplots(1, 3, figsize=(12, 3))
ax[0].scatter(X_train[:,0], X_train[:,3])
ax[0].set_xlabel(X_features[0]); ax[0].set_ylabel(X_features[3]);
# 未标准化
ax[0].set_title("unnormalized")
ax[0].axis('equal')

ax[1].scatter(X_mean[:,0], X_mean[:,3])
ax[1].set_xlabel(X_features[0]); ax[0].set_ylabel(X_features[3]);
ax[1].set_title(r"X - $\mu$")
ax[1].axis('equal')

ax[2].scatter(X_norm[:,0], X_norm[:,3])
ax[2].set_xlabel(X_features[0]); ax[0].set_ylabel(X_features[3]);

# Z-score 标准化
ax[2].set_title(r"Z-score normalized")
ax[2].axis('equal')
plt.tight_layout(rect=[0, 0.03, 1, 0.95])

# 特征在标准化前、中、后的分布
fig.suptitle("distribution of features before, during, after normalization")
plt.show()

png

上图显示了训练集中两个参数(“房龄”和“面积(平方英尺)”)之间的关系。这些图使用了相同的比例进行绘制

  • 左图(未标准化):size(sqft) 特征的取值范围或方差远大于 age 特征。
  • 中图(第一步):首先从每个特征中减去均值或平均值,使特征值围绕零分布。虽然在 age 特征中这种变化不明显,但可以清晰地看到 size(sqft) 特征已经接近零。
  • 右图(第二步):接着将每个特征除以方差,使得所有特征都以零为中心,并具有相似的尺度。
# 对原始特征进行标准化
# normalize the original features
X_norm, X_mu, X_sigma = zscore_normalize_features(X_train)
print(f"X_mu = {X_mu}, \nX_sigma = {X_sigma}")
print(f"原始 X 每列的峰值范围(Peak to Peak):{np.ptp(X_train, axis=0)}")   
print(f"标准化后 X 每列的峰值范围(Peak to Peak):{np.ptp(X_norm, axis=0)}")
X_mu = [1.42e+03 2.72e+00 1.38e+00 3.84e+01], 
X_sigma = [411.62   0.65   0.49  25.78]
原始 X 每列的峰值范围(Peak to Peak):[2.41e+03 4.00e+00 1.00e+00 9.50e+01]
标准化后 X 每列的峰值范围(Peak to Peak):[5.85 6.14 2.06 3.69]
fig,ax=plt.subplots(1, 4, figsize=(12, 3))
for i in range(len(ax)):
    norm_plot(ax[i],X_train[:,i],)
    ax[i].set_xlabel(X_features[i])
ax[0].set_ylabel("count");

# 特征在标准化前的分布情况
fig.suptitle("distribution of features before normalization")
plt.show()
fig,ax=plt.subplots(1,4,figsize=(12,3))
for i in range(len(ax)):
    norm_plot(ax[i],X_norm[:,i],)
    ax[i].set_xlabel(X_features[i])
ax[0].set_ylabel("count"); 

# 特征在标准化后的分布情况
fig.suptitle(f"distribution of features after normalization")

plt.show()

png

png

请注意,上述标准化后的数据范围以零为中心,大致在 +/- 1 之间。更重要的是,每个特征的范围都变得相似。

让我们使用标准化后的数据重新运行梯度下降算法。
请注意,显著更大的 \alpha 将会加速下降过程。

w_norm, b_norm, hist = run_gradient_descent(X_norm, y_train, 1000, 1.0e-1, )
Iteration Cost          w0       w1       w2       w3       b       djdw0    djdw1    djdw2    djdw3    djdb  
---------------------|--------|--------|--------|--------|--------|--------|--------|--------|--------|--------|
        0 5.76170e+04  8.9e+00  3.0e+00  3.3e+00 -6.0e+00  3.6e+01 -8.9e+01 -3.0e+01 -3.3e+01  6.0e+01 -3.6e+02
      100 2.21086e+02  1.1e+02 -2.0e+01 -3.1e+01 -3.8e+01  3.6e+02 -9.2e-01  4.5e-01  5.3e-01 -1.7e-01 -9.6e-03
      200 2.19209e+02  1.1e+02 -2.1e+01 -3.3e+01 -3.8e+01  3.6e+02 -3.0e-02  1.5e-02  1.7e-02 -6.0e-03 -2.6e-07
      300 2.19207e+02  1.1e+02 -2.1e+01 -3.3e+01 -3.8e+01  3.6e+02 -1.0e-03  5.1e-04  5.7e-04 -2.0e-04 -6.9e-12
      400 2.19207e+02  1.1e+02 -2.1e+01 -3.3e+01 -3.8e+01  3.6e+02 -3.4e-05  1.7e-05  1.9e-05 -6.6e-06 -2.7e-13
      500 2.19207e+02  1.1e+02 -2.1e+01 -3.3e+01 -3.8e+01  3.6e+02 -1.1e-06  5.6e-07  6.2e-07 -2.2e-07 -2.7e-13
      600 2.19207e+02  1.1e+02 -2.1e+01 -3.3e+01 -3.8e+01  3.6e+02 -3.7e-08  1.9e-08  2.1e-08 -7.3e-09 -2.6e-13
      700 2.19207e+02  1.1e+02 -2.1e+01 -3.3e+01 -3.8e+01  3.6e+02 -1.2e-09  6.2e-10  6.9e-10 -2.4e-10 -2.6e-13
      800 2.19207e+02  1.1e+02 -2.1e+01 -3.3e+01 -3.8e+01  3.6e+02 -4.1e-11  2.1e-11  2.3e-11 -8.1e-12 -2.6e-13
      900 2.19207e+02  1.1e+02 -2.1e+01 -3.3e+01 -3.8e+01  3.6e+02 -1.4e-12  6.9e-13  7.7e-13 -2.7e-13 -2.6e-13
w,b found by gradient descent: w: [110.56 -21.27 -32.71 -37.97], b: 363.16

经过缩放的特征能够 快得多 地获得非常精确的结果!注意,在较短的运行结束时,每个参数的梯度值都非常小。对于标准化后的特征,学习率 0.1 是回归的一个不错的起点。

接下来,让我们绘制预测值与目标值的对比图。需要注意的是,预测是基于标准化后的特征进行的,而绘图则使用原始特征值。

# 使用标准化特征预测目标值
# predict target using normalized features
m = X_norm.shape[0]
yp = np.zeros(m)
for i in range(m):
    yp[i] = np.dot(X_norm[i], w_norm) + b_norm
    # 绘制预测值和目标值相对于原始特征的对比图
    # plot predictions and targets versus original features    
fig,ax=plt.subplots(1,4,figsize=(12, 3),sharey=True)
for i in range(len(ax)):
    ax[i].scatter(X_train[:,i],y_train, label = 'target')
    ax[i].set_xlabel(X_features[i])
    ax[i].scatter(X_train[:,i],yp,color=dlorange, label = 'predict')
ax[0].set_ylabel("Price"); ax[0].legend();
# 使用 Z-score 标准化模型的预测值与目标值对比
fig.suptitle("target versus prediction using z-score normalized model")
plt.show()

png

结果看起来不错。需要注意以下几点:

  • 当有多个特征时,我们无法再用单一的图展示预测结果与特征之间的关系。
  • 在生成图表时,使用的是标准化后的特征。因此,使用从标准化训练集学到的参数进行预测时,输入数据也必须进行相同的标准化处理。

预测
生成模型的目的是用它来预测数据集中没有出现的房屋价格。例如,预测一栋面积为 1200 平方英尺、3 间卧室、1 层楼、40 年房龄的房子的价格。请记住,必须使用从训练数据标准化时得出的均值和标准差对输入数据进行标准化处理。

# 首先,对示例数据进行标准化
# First, normalize out example.
x_house = np.array([1200, 3, 1, 40])
x_house_norm = (x_house - X_mu) / X_sigma
print(x_house_norm)
x_house_predict = np.dot(x_house_norm, w_norm) + b_norm
# 预测一栋面积为1200平方英尺、3间卧室、1层楼、40年房龄的房子的价格为
print(f"预测一栋面积为1200平方英尺、3间卧室、1层楼、40年房龄的房子的价格为:${x_house_predict*1000:0.0f}")
[-0.53  0.43 -0.79  0.06]
预测一栋面积为1200平方英尺、3间卧室、1层楼、40年房龄的房子的价格为:$318709

成本等高线图
mid1

另一种观察特征缩放的方法是从成本等高线的角度进行分析。当特征的尺度不匹配时,成本相对于参数的等高线图会表现出不对称性。

在下图中,参数的尺度经过匹配。左侧图是标准化特征之前,w[0](平方英尺)与 w[1](卧室数量)的成本等高线图。由于特征尺度差异显著,图中等高线非常不对称,以至于无法看到完整的轮廓曲线。相反,当特征经过标准化后,成本等高线变得更加对称。其结果是,梯度下降过程中对每个参数的更新可以实现均衡的进展。

plt_equal_scale(X_train, X_norm, y_train)

png