Regression Modelled
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 |
问题描述
如图中所述,我们将使用房价预测的示例作为动机。
本次实验将使用仅包含两个数据点的简单数据集——一栋1000平方英尺的房屋以30万美元出售,另一栋2000平方英尺的房屋以50万美元出售。这两个数据点将构成我们的数据或训练集。在本次实验中,房屋面积的单位是1000平方英尺,价格的单位是1000美元。
面积 (1000平方英尺) | 价格 (1000美元) |
---|---|
1.0 | 300 |
2.0 | 500 |
您将尝试通过这两个数据点拟合一条线性回归模型(如上图中的蓝色直线),这样您就可以预测其他房屋的价格——例如,1200平方英尺的房屋价格。
请运行以下代码单元来创建您的 x_train
和 y_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()
函数来绘制这两个点,如下方代码单元所示。
- 函数参数
marker
和c
将点显示为红色叉号(默认是蓝色点)。
您可以使用 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()
模型函数
如图中所述,线性回归的模型函数(从 x
映射到 y
的函数)表示为:
f_{w,b}(x^{(i)}) = wx^{(i)} + b \tag{1}
上面的公式是您可以用来表示直线的方法——不同的 $ w $ 和 $ b $ 值会在图上生成不同的直线。
让我们通过下面的代码块更好地理解这一点。让我们从 w = 100 和 b = 100 开始。
注意:您可以返回此单元格来调整模型的 w 和 b 参数
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()
如您所见,设置 w = 100 and b = 100 并未得到适合我们数据的直线。
尝试使用不同的 w 和 b 值。要使直线适合我们的数据,这些值应是多少?
提示:
您可以使用鼠标点击下面绿色“提示”左侧的三角形,以显示一些选择 b 和 w 的提示。
预测
现在我们有了一个模型,可以用它来进行预测。让我们预测一栋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()
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:一个常用的数据绘图库
-
代价方程衡量了你的预测值与训练数据的匹配程度。
-
通过最小化代价,可以得到 w 和 b 的最优值。
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]) # 价格,单位为千美元
计算代价
- 误差计算
假设我们有一个预测模型,其预测值为 \hat{y}。对于给定的第 i 个样本点,模型的预测值 \hat{y}^{(i)} 和真实值 y^{(i)} 之间的差可以表示为:
y^{(i)} - \hat{y}^{(i)}
- 误差平方
由于误差的正负会相互抵消,直接相加会导致无法正确度量误差的总量。因此,我们对每个误差进行平方,从而将其变为正值:
(y^{(i)} - \hat{y}^{(i)})^2
这称为“平方误差”。
- 累加所有样本的平方误差
我们有 m 个样本,分别计算每个样本的平方误差,然后将它们相加,以便获得所有样本的总平方误差:
\sum_{i=0}^{m-1} (y^{(i)} - \hat{y}^{(i)})^2
- 除以 m 防止代价无意义
当我们将所有样本的平方误差累加时,如果样本数量 m 很大,单纯的总平方误差值可能会变得很大,从而导致代价函数的数值没有实际意义。因此,我们将总平方误差除以样本数量 m,得到平均平方误差:
\frac{1}{m} \sum_{i=0}^{m-1} (y^{(i)} - \hat{y}^{(i)})^2
通过这样处理,无论样本数量多少,代价函数的值都能更准确地反映模型的平均误差大小。
- 使用预测函数替代 \hat{y}^{(i)}
在实际的模型中,我们用预测函数 f_{w,b}(x^{(i)}) = wx^{(i)} + b 来表示预测值。替换后,平方误差可以表示为:
(y^{(i)} - f_{w,b}(x^{(i)}))^2
- 最终代价函数公式
最后,我们将所有样本的平方误差累加,再除以样本数量 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
代价函数直观理解
您的目标是找到一个模型 f_{w,b}(x) = wx + b,其中参数 w 和 b 可以准确预测给定输入 x 的房屋价值。代价函数是衡量模型在训练数据上准确性的指标。
上面的代价公式 (1) 显示,如果可以选择 w 和 b 使得预测值 f_{w,b}(x) 与目标数据 y 相匹配,那么 (f_{w,b}(x^{(i)}) - y^{(i)})^2 项将为零,从而使代价最小化。在这个简单的两个点的示例中,您可以做到这一点!
在之前的实验中,您确定 b=100 提供了一个最优解,因此让我们将 b 设置为 100,并专注于调整 w 的值。
plt_intuition(x_train,y_train)
该图包含一些值得注意的要点:
- 当 w = 200 时,代价达到最小,这与前一个实验的结果相符。
- 由于在代价公式中目标值与预测值之间的差被平方,当 w 值过大或过小时,代价会迅速增加。
- 通过选择使代价最小化的 w 和 b,得到的直线可以完美拟合数据。
代价函数的 3D 可视化
通过 3D 图或等高线图,您可以观察到代价如何随 w
和 b
的变化而变化。
需要注意的是,本课程中的部分绘图可能较为复杂。绘图程序已经提供,虽然通读代码以熟悉这些方法会有所帮助,但这并不是完成课程的必要条件。这些绘图函数位于本地目录中的 lab_utils_uni.py
文件中。
更大的数据集
查看包含更多数据点的场景具有指导意义。在这个数据集中,有些数据点不在同一条直线上。这对代价函数意味着什么?我们能找到使代价为 0 的 w 和 b 吗?
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,])
在等高线图中,点击一个点来选择 w
和 b
,以实现最低的代价。使用等高线来指导您的选择。请注意,图形更新可能需要几秒钟。
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)
上方请注意左侧图中的虚线。它们表示训练集中每个样本对代价的贡献。在这种情况下,选择 w \approx 209 和 b \approx 2.4 可获得较低的代价。请注意,由于我们的训练样本不在同一条直线上,最小代价并不为零。
凸形代价曲面
代价函数对损失求平方,确保了“误差曲面”是凸形的,类似一个汤碗。这种特性保证了误差曲面总会有一个最小值,可以通过沿着各个维度的梯度下降来达到。在之前的图中,由于 w 和 b 维度的比例不同,这种凸形特性不易观察到。在课程中展示的以下图形中,w 和 b 是对称的,更容易观察凸形结构:
soup_bowl()