【吴恩达机器学习-week2】多个变量的线性回归问题


在这个实验中,你将扩展数据结构和之前开发的程序,以 支持多个特征。一些程序已经更新,使得实验看起来很长,但它只是对之前的程序进行了轻微调整,使得复习起来很快。

1.1 目标

  • 扩展我们的回归模型程序,支持多个特征
  • 扩展数据结构,以支持多个特征
  • 重写预测、成本和梯度程序,以支持多个特征
  • 利用NumPy np.dot 来向量化它们的实现,以提高速度和简化操作
import copy, math
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('./deeplearning.mplstyle')
np.set_printoptions(precision=2)  # reduced display precision on numpy arrays
  • 解释一下:np.set_printoptions(precision=2)

    这段代码设置了NumPy打印数组时的选项,其中precision参数设置了打印浮点数时的精度。在这里,precision=2表示浮点数将被打印为小数点后保留两位的形式。

2 问题陈述

您将使用房价预测的示例。训练数据集包含三个示例,具有四个特征(大小、卧室数、楼层数和房龄),如下表所示。请注意,与之前的实验室不同,此处的大小以平方英尺而不是1000平方英尺为单位。这会导致一个问题,您将在下一个实验室中解决!

大小(平方英尺)卧室数量楼层数房龄(年)价格(1000美元)
21045145460
14163240232
8522135178

您将使用这些值构建一个线性回归模型,以便然后可以预测其他房屋的价格。例如,一个面积为1200平方英尺,有3间卧室,1层楼,40年历史的房子。

请运行以下代码单元格以创建您的x_trainy_train变量。

X_train = np.array([[2104, 5, 1, 45], [1416, 3, 2, 40], [852, 2, 1, 35]])
y_train = np.array([460, 232, 178])

2.1 包含我们示例的矩阵 X

与上面的表类似,示例存储在一个 NumPy 矩阵 X_train 中。矩阵的每一行表示一个示例。当您有 m m m 个训练示例(在我们的示例中为三个)和 n n n 个特征(在我们的示例中为四个)时, X \mathbf{X} X 是一个维度为 ( m , n ) (m,n) (m,n) 的矩阵(m 行,n 列)。

X = ( x 0 ( 0 ) x 1 ( 0 ) ⋯ x n − 1 ( 0 ) x 0 ( 1 ) x 1 ( 1 ) ⋯ x n − 1 ( 1 ) ⋯ x 0 ( m − 1 ) x 1 ( m − 1 ) ⋯ x n − 1 ( m − 1 ) ) \mathbf{X} = \begin{pmatrix} x^{(0)}_0 & x^{(0)}_1 & \cdots & x^{(0)}_{n-1} \\ x^{(1)}_0 & x^{(1)}_1 & \cdots & x^{(1)}_{n-1} \\ \cdots \\ x^{(m-1)}_0 & x^{(m-1)}_1 & \cdots & x^{(m-1)}_{n-1} \end{pmatrix} X= x0(0)x0(1)x0(m1)x1(0)x1(1)x1(m1)xn1(0)xn1(1)xn1(m1)

符号说明:

  • x ( i ) \mathbf{x}^{(i)} x(i) 是包含示例 i 的向量。 x ( i ) \mathbf{x}^{(i)} x(i) = ( x 0 ( i ) , x 1 ( i ) , ⋯   , x n − 1 ( i ) ) = (x^{(i)}_0, x^{(i)}_1, \cdots,x^{(i)}_{n-1}) =(x0(i),x1(i),,xn1(i))
  • x j ( i ) x^{(i)}_j xj(i) 是示例 i i i 中的第 j j j 个元素。括号中的上标表示示例编号,而下标表示元素。

显示输入数据。

# data is stored in numpy array/matrix
print(f"X Shape: {X_train.shape}, X Type:{type(X_train)})")
print(X_train)
print(f"y Shape: {y_train.shape}, y Type:{type(y_train)})")
print(y_train)

#print
X Shape: (3, 4), X Type:<class 'numpy.ndarray'>)
[[2104    5    1   45]
 [1416    3    2   40]
 [ 852    2    1   35]]
y Shape: (3,), y Type:<class 'numpy.ndarray'>)
[460 232 178]

2.2 参数向量 w \mathbf{w} w b b b

  • w \mathbf{w} w 是一个包含 n n n 个元素的向量。

    • 每个元素包含与一个特征相关联的参数。

    • 在我们的数据集中, n n n 是 4。

    • 符号上,我们将其绘制为列向量

      w = ( w 0 w 1 ⋯ w n − 1 ) \mathbf{w} = \begin{pmatrix} w_0 \\ w_1 \\ \cdots\\ w_{n-1} \end{pmatrix} w= w0w1wn1

  • b b b 是一个标量参数。

b_init = 785.1811367994083
w_init = np.array([ 0.39133535, 18.75376741, -53.36032453, -26.42131618])
print(f"w_init shape: {w_init.shape}, b_init type: {type(b_init)}")

#print
w_init shape: (4,), b_init type: <class 'float'>

3 使用多个变量进行模型预测

模型使用多个变量进行预测的线性模型如下所示:

f w , b ( x ) = w 0 x 0 + w 1 x 1 + . . . + w n − 1 x n − 1 + b (1) f_{\mathbf{w},b}(\mathbf{x}) = w_0x_0 + w_1x_1 +... + w_{n-1}x_{n-1} + b \tag{1} fw,b(x)=w0x0+w1x1+...+wn1xn1+b(1)

或者使用向量表示:

f w , b ( x ) = w ⋅ x + b (2) f_{\mathbf{w},b}(\mathbf{x}) = \mathbf{w} \cdot \mathbf{x} + b \tag{2} fw,b(x)=wx+b(2)

其中 ⋅ \cdot 表示向量的点乘。

为了演示点乘,我们将使用 (1) 和 (2) 实现预测。

3.1 逐元素单独预测

我们先前的预测是将一个特征值乘以一个参数,然后加上一个偏差参数。对于多个特征的预测,直接扩展我们先前的预测实现是使用循环遍历每个元素,执行乘以其参数的操作,然后在末尾加上偏差参数。

def predict_single_loop(x, w, b): 
    n = x.shape[0] # 得到特征值的个数
    p = 0
    for i in range(n):
        p_i = x[i] * w[i]  
        p = p + p_i         
    p = p + b                
    return p
x_vec = X_train[0,:]
print(f"x_vec shape {x_vec.shape}, x_vec value: {x_vec}")

# make a prediction
f_wb = predict_single_loop(x_vec, w_init, b_init)
print(f"f_wb shape {f_wb.shape}, prediction: {f_wb}")

#print
x_vec shape (4,), x_vec value: [2104    5    1   45]
f_wb shape (), prediction: 459.9999976194083
  • 解释一下:x_vec = X_train[0,:]

    x_vec = X_train[0,:] 这行代码表示从训练数据集 X_train 中提取第一个样本,即第一行,以向量的形式存储在变量 x_vec 中。

x_vec 的形状是 (4,),表示一个包含 4 个元素的 1-D NumPy 向量。而结果 f_wb 是一个标量。

3.2 单一预测,向量

注意到上述的方程 (1) 可以使用向量点乘来实现,就像 (2) 中那样。我们可以利用向量运算来加速预测。

回想一下在 Python/Numpy 实验中,NumPy 的 np.dot() 函数可以用来执行向量点乘。

def predict(x, w, b): 
    p = np.dot(x, w) + b     
    return p    
# get a row from our training data
x_vec = X_train[0,:]
print(f"x_vec shape {x_vec.shape}, x_vec value: {x_vec}")

# make a prediction
f_wb = predict(x_vec,w_init, b_init)
print(f"f_wb shape {f_wb.shape}, prediction: {f_wb}")

#print
x_vec shape (4,), x_vec value: [2104    5    1   45]
f_wb shape (), prediction: 459.9999976194083

结果和形状与之前使用循环的版本相同。在接下来的操作中,将使用 np.dot。预测现在是一个单独的语句。大多数例程将直接实现它,而不是调用单独的预测例程。

4 使用多个变量计算成本

具有多个变量的代价函数的方程为:

J ( w , b ) = 1 2 m ∑ i = 0 m − 1 ( f w , b ( x ( i ) ) − y ( i ) ) 2 (3) J(\mathbf{w},b) = \frac{1}{2m} \sum\limits_{i = 0}^{m-1} (f_{\mathbf{w},b}(\mathbf{x}^{(i)}) - y^{(i)})^2 \tag{3} J(w,b)=2m1i=0m1(fw,b(x(i))y(i))2(3)

其中:

f w , b ( x ( i ) ) = w ⋅ x ( i ) + b (4) f_{\mathbf{w},b}(\mathbf{x}^{(i)}) = \mathbf{w} \cdot \mathbf{x}^{(i)} + b \tag{4} fw,b(x(i))=wx(i)+b(4)

与之前的实验室不同, w \mathbf{w} w x ( i ) \mathbf{x}^{(i)} x(i)现在是向量,而不是标量,支持多个特征。

def compute_cost(X, y, w, b): 
    m = X.shape[0] # 样本的个数
    cost = 0.0
    for i in range(m):                                
        f_wb_i = np.dot(X[i], w) + b           #(n,)(n,) = scalar (see np.dot)
        cost = cost + (f_wb_i - y[i])**2       #scalar
    cost = cost / (2 * m)                      #scalar    
    return cost
# Compute and display cost using our pre-chosen optimal parameters. 
cost = compute_cost(X_train, y_train, w_init, b_init)
print(f'Cost at optimal w : {cost}')

#print
Cost at optimal w : 1.5578904428966628e-12

5 使用多个变量的梯度下降

多个变量的梯度下降算法如下:

重复 直到收敛:    {    w j = w j − α ∂ J ( w , b ) ∂ w j    对于 j = 0..n-1 b    = b − α ∂ J ( w , b ) ∂ b } \begin{align*} \text{重复}&\text{直到收敛:} \; \lbrace \newline\; & w_j = w_j - \alpha \frac{\partial J(\mathbf{w},b)}{\partial w_j} \tag{5} \; & \text{对于 j = 0..n-1}\newline &b\ \ = b - \alpha \frac{\partial J(\mathbf{w},b)}{\partial b} \newline \rbrace \end{align*} 重复}直到收敛:{wj=wjαwjJ(w,b)b  =bαbJ(w,b)对于 j = 0..n-1(5)

其中, n n n 是特征数量,参数 w j w_j wj b b b 同时更新,其中

∂ J ( w , b ) ∂ w j = 1 m ∑ i = 0 m − 1 ( f w , b ( x ( i ) ) − y ( i ) ) x j ( i ) ∂ J ( w , b ) ∂ b = 1 m ∑ i = 0 m − 1 ( f w , b ( x ( i ) ) − y ( i ) ) \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{6} \\ \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{7} \end{align} wjJ(w,b)bJ(w,b)=m1i=0m1(fw,b(x(i))y(i))xj(i)=m1i=0m1(fw,b(x(i))y(i))(6)(7)

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

5.1 使用多个变量计算梯度

下面是计算方程(6)和(7)的实现。有许多方法可以实现这个。在这个版本中,有一个

  • 外循环遍历所有m个示例。
    • 可以直接计算示例的 ∂ J ( w , b ) ∂ b \frac{\partial J(\mathbf{w},b)}{\partial b} bJ(w,b) 并累积
    • 在第二个循环中遍历所有n个特征:
      • 分别为每个 w j w_j wj 计算 ∂ J ( w , b ) ∂ w j \frac{\partial J(\mathbf{w},b)}{\partial w_j} wjJ(w,b)
def compute_gradient(X, y, w, b): 
    m,n = X.shape           #(number of examples, number of features)
    dj_dw = np.zeros((n,))
    dj_db = 0.

    for i in range(m):                             
        err = (np.dot(X[i], w) + b) - y[i]   
        for j in range(n):                         
            dj_dw[j] = dj_dw[j] + err * X[i, j]    
        dj_db = dj_db + err       
                         
    dj_dw = dj_dw / m                                
    dj_db = dj_db / m                                
        
    return dj_db, dj_dw
  • 解释一下:dj_dw = np.zeros((n,))

    这行代码创建了一个大小为(n,)的全零数组,其中 n 是特征的数量。这个数组用于存储每个特征对应的梯度值,初始时全部为零。

#Compute and display gradient 
tmp_dj_db, tmp_dj_dw = compute_gradient(X_train, y_train, w_init, b_init)
print(f'dj_db at initial w,b: {tmp_dj_db}')
print(f'dj_dw at initial w,b: \n {tmp_dj_dw}')

#print
dj_db at initial w,b: -1.6739251501955248e-06
dj_dw at initial w,b: 
[-2.73e-03 -6.27e-06 -2.22e-06 -6.92e-05]

多个变量的梯度下降

下面的例程实现了上面的方程(5)。

def gradient_descent(X, y, w_in, b_in, cost_function, gradient_function, alpha, num_iters): 
    J_history = [] # 用于存储每次迭代后计算得到的代价函数的数值。
    
    w = copy.deepcopy(w_in)  #avoid modifying global w within function
    b = b_in
    
    for i in range(num_iters):

        # Calculate the gradient and update the parameters
        dj_db,dj_dw = gradient_function(X, y, w, b)   ##None

        # Update Parameters using w, b, alpha and gradient
        w = w - alpha * dj_dw               ##None
        b = b - alpha * dj_db               ##None
      
        # Save cost J at each iteration
        if i<100000:      # prevent resource exhaustion 
            J_history.append( cost_function(X, y, w, b))

        # Print cost every at intervals 10 times or as many iterations if < 10
        if i% math.ceil(num_iters / 10) == 0:
            print(f"Iteration {i:4d}: Cost {J_history[-1]:8.2f}   ")
        
    return w, b, J_history #return final w,b and J history for graphing
# initialize parameters
initial_w = np.zeros_like(w_init)
initial_b = 0.

# some gradient descent settings
iterations = 1000 # 迭代次数
alpha = 5.0e-7 # 学习率

# run gradient descent 
w_final, b_final, J_hist = gradient_descent(X_train, y_train, initial_w, initial_b,
                                                    compute_cost, compute_gradient, 
                                                    alpha, iterations)
                                                    
print(f"b,w found by gradient descent: {b_final:0.2f},{w_final} ")

m,_ = X_train.shape

for i in range(m):
    print(f"prediction: {np.dot(X_train[i], w_final) + b_final:0.2f}, target value: {y_train[i]}")
    
# print
Iteration    0: Cost  2529.46   
Iteration  100: Cost   695.99   
Iteration  200: Cost   694.92   
Iteration  300: Cost   693.86   
Iteration  400: Cost   692.81   
Iteration  500: Cost   691.77   
Iteration  600: Cost   690.73   
Iteration  700: Cost   689.71   
Iteration  800: Cost   688.70   
Iteration  900: Cost   687.69   
b,w found by gradient descent: -0.00,[ 0.2   0.   -0.01 -0.07] 
prediction: 426.19, target value: 460
prediction: 286.17, target value: 232
prediction: 171.47, target value: 178
# plot cost versus iteration  
fig, (ax1, ax2) = plt.subplots(1, 2, constrained_layout=True, figsize=(12, 4))
ax1.plot(J_hist)
ax2.plot(100 + np.arange(len(J_hist[100:])), J_hist[100:])
ax1.set_title("Cost vs. iteration");  ax2.set_title("Cost vs. iteration (tail)")
ax1.set_ylabel('Cost')             ;  ax2.set_ylabel('Cost') 
ax1.set_xlabel('iteration step')   ;  ax2.set_xlabel('iteration step') 
plt.show()
  • 解释一下:fig, (ax1, ax2) = plt.subplots(1, 2, constrained_layout=True, figsize=(12, 4))

    这行代码使用Matplotlib创建了一个具有两个子图的图形对象。让我来解释一下参数:

    • fig: 这是整个图形对象的实例。
    • (ax1, ax2): 这是一个包含两个子图对象的元组,分别称为 ax1ax2。您可以在每个子图上绘制数据。
    • plt.subplots(1, 2, constrained_layout=True, figsize=(12, 4)): 这是一个函数调用,用于创建子图。具体地:
      • 1, 2 表示我们希望创建一个包含1行和2列的子图网格。
      • constrained_layout = True 是一个布尔参数,用于启用自动布局调整,以确保子图之间的间距和相对大小是合理的。
      • figsize=(12, 4) 指定了图形的尺寸,宽度为12英寸,高度为4英寸。

    这行代码将返回一个包含图形对象和子图对象的元组 (fig, (ax1, ax2))。您可以使用 ax1ax2 对象分别绘制两个子图中的数据。

  • 解释一下:ax2.plot(100 + np.arange(len(J_hist[100:])), J_hist[100:])

    这行代码使用 ax2 对象绘制损失函数的历史记录,但是仅包含从索引100开始的数据。让我逐步解释:

    • ax2.plot(...): 这是 ax2 对象的 plot 方法,用于绘制图形。
    • 100 + np.arange(len(J_hist[100:])): 这是 x 轴的值。它是从 100 开始的索引值,直到损失函数历史记录的末尾。np.arange(len(J_hist[100:])) 创建一个从 0 开始的索引数组,然后通过添加100来将其偏移。
    • J_hist[100:]: 这是损失函数历史记录的切片,从索引100 开始,一直到末尾。这是 y 轴的值,表示从第100次迭代开始的损失函数值。

在这里插入图片描述

小结

在这个实验中:

  • 重新开发了多变量线性回归的例程。
  • 利用了 NumPy 的 np.dot向量化实现。
  • 13
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FOUR_A

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值