Model Representation

在本次实验中,您学到了以下内容:

  • 线性回归建立一个模型,用于在特征和目标之间建立关系
    • 在下述示例中,特征(feature)是房屋面积,目标(target)是房屋价格
    • 对于简单的线性回归,模型具有两个参数 (权重)w 和 (偏置)b,其值是通过训练数据进行“拟合”的
    • 一旦模型的参数确定后,模型就可以用来对新数据进行预测

特殊符号

以下是您将遇到的一些符号的总结。

通用符号 描述 Python (如适用)
a 标量,非粗体
\mathbf{a} 向量,粗体
Regression 回归
\mathbf{x} 训练示例特征值(在本次实验中为面积,单位为1000平方英尺) x_train
\mathbf{y} 训练示例目标值(在本次实验中为价格,单位为1000美元) y_train
x^{(i)}, y^{(i)} i 个训练示例 x_i, y_i
m 训练示例的数量 m
w 参数:权重 w
b 参数:偏置 b
f_{w,b}(x^{(i)}) 参数为 w, b 时,对 x^{(i)} 进行模型评估的结果 = wx^{(i)} + b f_wb

问题描述

mid

如图中所述,我们将使用房价预测的示例作为动机。
本次实验将使用仅包含两个数据点的简单数据集——一栋1000平方英尺的房屋以30万美元出售,另一栋2000平方英尺的房屋以50万美元出售。这两个数据点将构成我们的数据或训练集。在本次实验中,房屋面积的单位是1000平方英尺,价格的单位是1000美元。

面积 (1000平方英尺) 价格 (1000美元)
1.0 300
2.0 500

您将尝试通过这两个数据点拟合一条线性回归模型(如上图中的蓝色直线),这样您就可以预测其他房屋的价格——例如,1200平方英尺的房屋价格。

请运行以下代码单元来创建您的 x_trainy_train 变量。数据存储在一维 NumPy 数组中。

import numpy as np
import matplotlib.pyplot as plt
plt.style.use('./deeplearning.mplstyle')

# x_train 是输入变量(单位为 1000 平方英尺)
# y_train 是目标变量(单位为 1000 美元)
x_train = np.array([1.0, 2.0])
y_train = np.array([300.0, 500.0])

print(f"x_train = {x_train}")
print(f"y_train = {y_train}")
x_train = [1. 2.]
y_train = [300. 500.]

注意: 本课程将频繁使用 Python 的 f-string 输出格式,具体描述见此处。在生成输出时,大括号中的内容会被计算。

训练示例数量 m

您将使用 m 来表示训练示例的数量。Numpy 数组具有一个 .shape 属性。x_train.shape 返回一个 Python 元组,每个维度对应一个元素。x_train.shape[0] 是数组的长度,即训练示例的数量,如下所示。

# m 是训练示例的数量
print(f"x_train.shape: {x_train.shape}")
m = x_train.shape[0]
print(f"训练示例的数量是: {m}")
x_train.shape: (2,)
训练示例的数量是: 2

也可以使用 Python 的 len() 函数,如下所示。

# m 是训练示例的数量
m = len(x_train)
print(f"训练示例的数量是: {m}")
训练示例的数量是: 2

训练示例 x_i, y_i

您将使用 (x^{(i)}, y^{(i)}) 来表示第 i 个训练示例。由于 Python 的索引从零开始,因此 (x^{(0)}, y^{(0)}) 为 (1.0, 300.0),而 (x^{(1)}, y^{(1)}) 为 (2.0, 500.0)。

要访问 Numpy 数组中的某个值,可以使用所需的偏移量对数组进行索引。例如,访问 x_train 中第零个位置的语法为 x_train[0]。运行下面的代码块以获取第 i 个训练示例。

i = 0 # 将此更改为 1 以查看 (x^1, y^1)

x_i = x_train[i]
y_i = y_train[i]
print(f"(x^({i}), y^({i})) = ({x_i}, {y_i})")
(x^(0), y^(0)) = (1.0, 300.0)

绘制可视化数据图

您可以使用 matplotlib 库中的 scatter() 函数来绘制这两个点,如下方代码单元所示。

  • 函数参数 markerc 将点显示为红色叉号(默认是蓝色点)。

您可以使用 matplotlib 库中的其他函数来设置标题和标签以进行显示。

# Plot the data points
plt.scatter(x_train, y_train, marker='x', c='r')
# Set the title
plt.title("Housing Prices")
# Set the y-axis label
plt.ylabel('Price (in 1000s of dollars)')
# Set the x-axis label
plt.xlabel('Size (1000 sqft)')
plt.show()

png

模型函数

mid

如图中所述,线性回归的模型函数(从 x 映射到 y 的函数)表示为:

f_{w,b}(x^{(i)}) = wx^{(i)} + b \tag{1}

上面的公式是您可以用来表示直线的方法——不同的 $ w $ 和 $ b $ 值会在图上生成不同的直线。

让我们通过下面的代码块更好地理解这一点。让我们从 w = 100b = 100 开始。

注意:您可以返回此单元格来调整模型的 wb 参数

w = 100
b = 100
print(f"w: {w}")
print(f"b: {b}")
w: 100
b: 100

现在,让我们计算f_{w,b}(x^{(i)})在您的两个数据点上的值。您可以对每个数据点显式地写出如下公式:

对于x^{(0)}, f_wb = w * x[0] + b

对于x^{(1)}, f_wb = w * x[1] + b

对于大量数据点,这样写会变得笨拙且重复。因此,您可以通过 for 循环来计算函数的输出,如下方的 compute_model_output 函数所示。

注意:参数描述 (ndarray (m,)) 表示一个形状为 (m,) 的 Numpy n 维数组。(scalar) 描述了一个没有维度的参数,仅表示一个数值大小。
注意np.zeros(n) 将返回一个具有 n 个元素的一维 Numpy 数组

def compute_model_output(x, w, b):
    """
    计算线性模型的预测值
    参数:
      x (ndarray (m,)):数据,包含 m 个示例
      w, b (scalar):模型参数
    返回:
      y (ndarray (m,)):目标值
    """
    m = x.shape[0]
    f_wb = np.zeros(m)
    for i in range(m):
        f_wb[i] = w * x[i] + b
        
    return f_wb

现在让我们调用 compute_model_output 函数并绘制输出结果。

tmp_f_wb = compute_model_output(x_train, w, b,)

# 绘制模型预测的直线
plt.plot(x_train, tmp_f_wb, c='b',label='Our Prediction')

# 绘制数据点
plt.scatter(x_train, y_train, marker='x', c='r',label='Actual Values')

# 设置标题
plt.title("Housing Prices")
# 设置 y 轴标签
plt.ylabel('Price (in 1000s of dollars)')
# 设置 x 轴标签
plt.xlabel('Size (1000 sqft)')
plt.legend()
plt.show()

png

如您所见,设置 w = 100 and b = 100得到适合我们数据的直线。

尝试使用不同的 wb 值。要使直线适合我们的数据,这些值应是多少?

提示:
您可以使用鼠标点击下面绿色“提示”左侧的三角形,以显示一些选择 bw 的提示。

预测

现在我们有了一个模型,可以用它来进行预测。让我们预测一栋1200平方英尺房屋的价格。由于 x 的单位是 1000 平方英尺,所以 x 为 1.2。

w = 200                         
b = 100    
tmp_f_wb = compute_model_output(x_train, w, b,)

# 绘制模型预测的直线
plt.plot(x_train, tmp_f_wb, c='b',label='Our Prediction')

# 绘制数据点
plt.scatter(x_train, y_train, marker='x', c='r',label='Actual Values')

# 设置标题
plt.title("Housing Prices")
# 设置 y 轴标签
plt.ylabel('Price (in 1000s of dollars)')
# 设置 x 轴标签
plt.xlabel('Size (1000 sqft)')
plt.legend()
plt.show()

png

x_i = 1.2
cost_1200sqft = w * x_i + b    

print(f"{cost_1200sqft/10:.0f} 万美元")
34 万美元

Cost Function

在本次实验中,你将:

  • 实现并探索用于单变量线性回归的 cost 函数。
    • “linear regression with one variable”:指的是具有一个输入变量的线性回归。
    • “one X size”:意味着输入数据中只有一个特征(或变量),通常用 X 表示。
    • “univariate linear regression”:这是“单变量线性回归”的术语,表示回归模型中只有一个自变量。

在本次实验中,我们将使用以下内容:

  • NumPy:一个常用的科学计算库

  • Matplotlib:一个常用的数据绘图库

  • 代价方程衡量了你的预测值与训练数据的匹配程度。

  • 通过最小化代价,可以得到 wb 的最优值。

import numpy as np
%matplotlib widget
import matplotlib.pyplot as plt
from lab_utils_uni import plt_intuition, plt_stationary, plt_update_onclick, soup_bowl
plt.style.use('./deeplearning.mplstyle')

问题描述

您希望建立一个模型,通过房屋的面积来预测房价。
我们将使用与之前实验相同的两个数据点 —— 一栋 1000 平方英尺的房子售价为 30 万美元,另一栋 2000 平方英尺的房子售价为 50 万美元。

面积 (千平方英尺) 价格 (千美元)
1 300
2 500
x_train = np.array([1.0, 2.0])           # 面积,单位为千平方英尺
y_train = np.array([300.0, 500.0])       # 价格,单位为千美元

计算代价

  1. 误差计算
    假设我们有一个预测模型,其预测值为 \hat{y}。对于给定的第 i 个样本点,模型的预测值 \hat{y}^{(i)} 和真实值 y^{(i)} 之间的差可以表示为:

y^{(i)} - \hat{y}^{(i)}

  1. 误差平方
    由于误差的正负会相互抵消,直接相加会导致无法正确度量误差的总量。因此,我们对每个误差进行平方,从而将其变为正值:

(y^{(i)} - \hat{y}^{(i)})^2

这称为“平方误差”。

  1. 累加所有样本的平方误差
    我们有 m 个样本,分别计算每个样本的平方误差,然后将它们相加,以便获得所有样本的总平方误差:

\sum_{i=0}^{m-1} (y^{(i)} - \hat{y}^{(i)})^2

  1. 除以 m 防止代价无意义

当我们将所有样本的平方误差累加时,如果样本数量 m 很大,单纯的总平方误差值可能会变得很大,从而导致代价函数的数值没有实际意义。因此,我们将总平方误差除以样本数量 m,得到平均平方误差:

\frac{1}{m} \sum_{i=0}^{m-1} (y^{(i)} - \hat{y}^{(i)})^2

通过这样处理,无论样本数量多少,代价函数的值都能更准确地反映模型的平均误差大小。

  1. 使用预测函数替代 \hat{y}^{(i)}

在实际的模型中,我们用预测函数 f_{w,b}(x^{(i)}) = wx^{(i)} + b 来表示预测值。替换后,平方误差可以表示为:

(y^{(i)} - f_{w,b}(x^{(i)}))^2

  1. 最终代价函数公式

最后,我们将所有样本的平方误差累加,再除以样本数量 m 来得到平均误差。为了计算上的简化以及后续求导的便利性,我们再除以 2,得到最终的代价函数公式:

J(w, b) = \frac{1}{2m} \sum_{i=0}^{m-1} (f_{w,b}(x^{(i)}) - y^{(i)})^2

我们求解的是:

minimizeJ(w,b) = min(J(w, b) = \frac{1}{2m} \sum_{i=0}^{m-1} (f_{w,b}(x^{(i)}) - y^{(i)})^2)

其中
f_{w,b}(x^{(i)}) = wx^{(i)} + b \tag{2}

  • f_{w,b}(x^{(i)}) 是使用参数 w, b 对第 i 个样本的预测值。

  • (f_{w,b}(x^{(i)}) - y^{(i)})^2 是目标值与预测值之间的平方差。

  • 这些差值对所有 m 个样本求和,然后除以 2m 来得到代价 J(w,b)

  • 本来只需要除以 m,而加上 2 只是为了在计算上更加美观,便于后续求导。

注意:在课程讲解中,求和通常是从 1 到 m,而在代码中则是从 0 到 m-1

以下代码通过遍历每个样本来计算代价。在每次循环中:

  • 计算预测值 f_wb
  • 计算目标值与预测值之间的差,并求平方。
  • 将这个值加到总代价中。
def compute_cost(x, y, w, b): 
    """
    计算线性回归的代价函数。
    
    参数:
      x (ndarray (m,)): 数据,共有 m 个样本
      y (ndarray (m,)): 目标值
      w, b (标量): 模型参数  
    
    返回值:
        total_cost (float): 使用 w 和 b 作为参数拟合 x 和 y 数据点时的线性回归代价
    """
    # 训练样本数量
    m = x.shape[0] 
    
    cost_sum = 0 
    for i in range(m): 
        f_wb = w * x[i] + b           # 计算预测值 f_wb
        cost = (f_wb - y[i]) ** 2     # 计算目标值和预测值之间的平方误差
        cost_sum = cost_sum + cost    # 累加平方误差
    
    total_cost = (1 / (2 * m)) * cost_sum  # 计算总代价

    return total_cost

代价函数直观理解

mid

您的目标是找到一个模型 f_{w,b}(x) = wx + b,其中参数 wb 可以准确预测给定输入 x 的房屋价值。代价函数是衡量模型在训练数据上准确性的指标。

上面的代价公式 (1) 显示,如果可以选择 wb 使得预测值 f_{w,b}(x) 与目标数据 y 相匹配,那么 (f_{w,b}(x^{(i)}) - y^{(i)})^2 项将为零,从而使代价最小化。在这个简单的两个点的示例中,您可以做到这一点!

在之前的实验中,您确定 b=100 提供了一个最优解,因此让我们将 b 设置为 100,并专注于调整 w 的值。

plt_intuition(x_train,y_train)

mid1

该图包含一些值得注意的要点:

  • w = 200 时,代价达到最小,这与前一个实验的结果相符。
  • 由于在代价公式中目标值与预测值之间的差被平方,当 w 值过大或过小时,代价会迅速增加。
  • 通过选择使代价最小化的 wb,得到的直线可以完美拟合数据。

代价函数的 3D 可视化

通过 3D 图或等高线图,您可以观察到代价如何随 wb 的变化而变化。

需要注意的是,本课程中的部分绘图可能较为复杂。绘图程序已经提供,虽然通读代码以熟悉这些方法会有所帮助,但这并不是完成课程的必要条件。这些绘图函数位于本地目录中的 lab_utils_uni.py 文件中。

更大的数据集

查看包含更多数据点的场景具有指导意义。在这个数据集中,有些数据点不在同一条直线上。这对代价函数意味着什么?我们能找到使代价为 0 的 wb 吗?

x_train = np.array([1.0, 1.7, 2.0, 2.5, 3.0, 3.2])
y_train = np.array([250, 300, 480,  430,   630, 730,])

在等高线图中,点击一个点来选择 wb,以实现最低的代价。使用等高线来指导您的选择。请注意,图形更新可能需要几秒钟。

plt.close('all') 
fig, ax, dyn_items = plt_stationary(x_train, y_train)
updater = plt_update_onclick(fig, ax, x_train, y_train, dyn_items)

png
上方请注意左侧图中的虚线。它们表示训练集中每个样本对代价的贡献。在这种情况下,选择 w \approx 209b \approx 2.4 可获得较低的代价。请注意,由于我们的训练样本不在同一条直线上,最小代价并不为零。

凸形代价曲面

代价函数对损失求平方,确保了“误差曲面”是凸形的,类似一个汤碗。这种特性保证了误差曲面总会有一个最小值,可以通过沿着各个维度的梯度下降来达到。在之前的图中,由于 wb 维度的比例不同,这种凸形特性不易观察到。在课程中展示的以下图形中,wb 是对称的,更容易观察凸形结构:

soup_bowl()

png