Linear Regression

使用一个变量进行线性回归,以预测一家餐饮加盟店的利润

import numpy as np
import matplotlib.pyplot as plt
from utils import *
import copy
import math

Problem Statement

假设您是一家餐饮加盟店的首席执行官,正在考虑在不同城市开设新店

  • 您希望将业务扩展到能给餐厅带来更高利润的城市;

  • 该连锁店已在多个城市开设了餐厅,您拥有这些城市的利润和人口数据;

  • 您还拥有新餐厅候选城市的数据;

    • 这些城市的人口数量;

利用这些数据来帮助您确定哪些城市有可能为您的企业带来更高的利润;

Dataset

首先,您要为这项任务加载数据集

  • load_data()函数将数据加载到变量x_trainy_train中;

  • x_train是一个城市的人口数量;

  • y_train是该城市餐馆的利润。利润的负值表示亏损;

  • X_trainy_train都是 numpy 数组;

# load the dataset
x_train, y_train = load_data()

View the variables

在开始执行任何任务之前,最好先熟悉一下数据集;

  • 一个好的开始是打印出每个变量,看看它包含什么内容;

  • 下面的代码将打印变量x_train, y_train以及变量的类型;

# print x_train
print("Type of x_train:",type(x_train))
print("First five elements of x_train are:\n", x_train[:5]) 

# print y_train
print("Type of y_train:",type(y_train))
print("First five elements of y_train are:\n", y_train[:5])  

x_train是一个 numpy 数组,包含全部大于零的十进制值;

  • 这些数值代表城市人口乘以 10,000;

  • 例如,6.1101 表示该城市的人口为 61 101 人;

y_train是一个包含十进制值的 numpy 数组,其中有些是负值,有些是正值;

  • 这代表您的餐厅在每个城市的平均月利润,单位为 10,000 美元;

  • 例如,17.592 表示该城市的月平均利润为 175 920 美元;

  • -2.6807代表该市平均每月损失-26 807 美元;

Check the dimensions of your variables - 检查变量的尺寸

熟悉数据的另一个有用方法是查看其维度;

打印x_trainy_train的形状,并查看数据集中有多少训练示例;

print ('The shape of x_train is:', x_train.shape)
print ('The shape of y_train is: ', y_train.shape)
print ('Number of training examples (m):', len(x_train))

城市人口数组有 97 个数据点,月平均利润也有 97 个数据点。这些都是 NumPy 1D 数组

Visualize your data - 数据可视化

将数据可视化通常有助于理解数据;

  • 对于这个数据集,您可以使用散点图来直观显示数据,因为它只有两个属性(利润和人口);

  • 现实生活中遇到的许多其他问题都有两个以上的属性(例如,人口、家庭平均收入、月利润、月销售额);

# Create a scatter plot of the data. To change the markers to red "x",
# we used the 'marker' and 'c' parameters
plt.scatter(x_train, y_train, marker='x', c='r') 

# Set the title
plt.title("Profits vs. Population per city")
# Set the y-axis label
plt.ylabel('Profit in $10,000')
# Set the x-axis label
plt.xlabel('Population of City in 10,000s')
plt.show()

Linear regression - 线性回归

根据数据集拟合线性回归参数 (𝑤,𝑏)(𝑤,𝑏)

  • 线性回归的模型函数,即从x(城市人口)映射到y(餐厅在该城市的月利润)的函数,表示为

fw,b(x)=wx+bf_{w,b}(x) = wx + b
  • 要训练线性回归模型,您需要找到最适合数据集的 (𝑤,𝑏)(𝑤,𝑏) 参数;

    • 要比较一种 (𝑤,𝑏)(𝑤,𝑏) 选择比另一种选择的优劣,可以用成本函数进行评估 𝐽(𝑤,𝑏)𝐽(𝑤,𝑏)

      • J 是(𝑤,𝑏)(𝑤,𝑏) 的函数。也就是说,成本 J(𝑤,𝑏)J(𝑤,𝑏) 的值取决于 (𝑤,𝑏)(𝑤,𝑏) 的值;

    • 最适合数据的 (𝑤,𝑏)(𝑤,𝑏) 是成本最小的 𝐽(𝑤,𝑏)𝐽(𝑤,𝑏)

  • 要找到使可能的最小成本J(𝑤,𝑏)J(𝑤,𝑏) 最小的值 (𝑤,𝑏)(𝑤,𝑏),你可以使用一种称为梯度下降的方法;

    • 每进行一步梯度下降,参数 (𝑤,𝑏)(𝑤,𝑏) 就会更接近最优值,从而实现最低成本 𝐽(𝑤,𝑏)𝐽(𝑤,𝑏)

  • 然后,经过训练的线性回归模型可以利用输入特征 𝑥𝑥(城市人口),输出预测结果 𝑓𝑤,𝑏(𝑥)𝑓_{𝑤,𝑏}(𝑥)(该城市餐馆的预测月利润);

Compute Cost - 计算成本

梯度下降法涉及反复调整参数值的步骤 (𝑤,𝑏)(𝑤,𝑏),以逐渐获得越来越小的成本 𝐽(𝑤,𝑏)𝐽(𝑤,𝑏)

  • 在梯度下降的每一步中,随着 (𝑤,𝑏)(𝑤,𝑏) 的更新,计算成本 𝐽(𝑤,𝑏)𝐽(𝑤,𝑏) ,这将有助于你监控进度;

  • 执行一个函数来计算 𝐽(𝑤,𝑏)𝐽(𝑤,𝑏),以便检查梯度下降的执行进度;

Cost function

对于一个变量,线性回归 𝐽(𝑤,𝑏)𝐽(𝑤,𝑏)J(w,b) 的成本函数定义为

J(w,b)=12mi=0m1(fw,b(x(i))y(i))2J(w,b) = \frac{1}{2m} \sum\limits_{i = 0}^{m-1} (f_{w,b}(x^{(i)}) - y^{(i)})^2
  • 可以将 𝑓𝑤,𝑏(𝑥𝑖)𝑓_{𝑤,𝑏}(𝑥^{𝑖}) 视为模型对餐厅利润的预测,而 𝑦i𝑦^{i} 则是数据中记录的实际利润

  • 𝑚𝑚 是数据集中训练实例的数量

Model prediction

  • 对于只有一个变量的线性回归,模型 𝑓𝑤,𝑏𝑓_{𝑤,𝑏}𝑥𝑖𝑥^{𝑖} 示例的预测表示为

𝑓𝑤,𝑏(𝑥(𝑖))=𝑤𝑥(𝑖)+𝑏𝑓_{𝑤,𝑏}(𝑥^{(𝑖)})=𝑤𝑥^{(𝑖)}+𝑏

这是一条直线的方程,截距为 𝑏𝑏 ,斜率为 𝑤𝑤

  • 下面的compute_cost()函数,计算成本 𝐽(𝑤,𝑏)𝐽(𝑤,𝑏)

Exercise 1

完成成本计算

  • 对训练示例进行迭代,计算每个示例的结果

    • 模型对该示例的预测结果

    • 𝑓𝑤,𝑏(𝑥(𝑖))=𝑤𝑥(𝑖)+𝑏𝑓_{𝑤,𝑏}(𝑥^{(𝑖)})=𝑤𝑥^{(𝑖)}+𝑏

    • 该示例的成本

    • 𝑐𝑜𝑠𝑡(𝑖)=(𝑓𝑤𝑏𝑦(𝑖))2𝑐𝑜𝑠𝑡^{(𝑖)}=(𝑓_{𝑤𝑏}−𝑦^{(𝑖)})^2

  • 返回所有例子的总成本

    • J(w,b)=12mi=0m1cost(i)J(w,b) = \frac{1}{2m} \sum\limits_{i = 0}^{m-1} cost^{(i)}

    • 这里, 𝑚𝑚 是训练实例的数量, 是求和算子

initial_w = 0
initial_b = 0

def compute_cost(x, y, w, b):
    m = x.shape[0]
    total_cost = 0
    cost_sum = 0
    for i in range(m):
        f_wb = w * x[i] + b
        cost = (f_wb - y[i]) ** 2
        cost_sum = cost_sum + cost

    total_cost = (1 / (2 * m)) * cost_sum

    return total_cost

cost = compute_cost(x_train, y_train, initial_w, initial_b)

# print(f'Cost at initial w: {cost:3f}')

Gradient descent - 梯度下降

实现用于线性回归的梯度参数

梯度下降算法是:

repeat until convergence:  {  0000b:=bαJ(w,b)b  0000w:=wαJ(w,b)w  }\begin{align*}& \text{repeat until convergence:} \; \lbrace \newline \; & \phantom {0000} b := b - \alpha \frac{\partial J(w,b)}{\partial b} \newline \; & \phantom {0000} w := w - \alpha \frac{\partial J(w,b)}{\partial w} \; & \newline & \rbrace\end{align*}

其中,参数 𝑤,𝑏𝑤,𝑏 同时更新

J(w,b)b=1mi=0m1(fw,b(x(i))y(i))\frac{\partial J(w,b)}{\partial b} = \frac{1}{m} \sum\limits_{i = 0}^{m-1} (f_{w,b}(x^{(i)}) - y^{(i)})
J(w,b)w=1mi=0m1(fw,b(x(i))y(i))x(i)\frac{\partial J(w,b)}{\partial w} = \frac{1}{m} \sum\limits_{i = 0}^{m-1} (f_{w,b}(x^{(i)}) -y^{(i)})x^{(i)}
  • m 是数据集中训练实例的数量

  • 𝑓𝑤,𝑏(𝑥(𝑖))𝑓_{𝑤,𝑏}(𝑥^{(𝑖)}) 是模型的预测值,而 , 是目标值 𝑦(𝑖)𝑦^{(𝑖)}

将实现一个名为compute_gradient 的函数,用于计算 J(w)w\frac{\partial J(w)}{\partial w}, J(w)b\frac{\partial J(w)}{\partial b}

Exercise 2

  • 对训练示例进行迭代,计算每个示例的结果

  • 模型对该示例的预测结果

fwb(x(i))=wx(i)+bf_{wb}(x^{(i)}) = wx^{(i)} + b
  • 该示例中的参数梯度 𝑤,𝑏𝑤,𝑏

J(w,b)b(i)=(fw,b(x(i))y(i))\frac{\partial J(w,b)}{\partial b}^{(i)} = (f_{w,b}(x^{(i)}) - y^{(i)})
J(w,b)w(i)=(fw,b(x(i))y(i))x(i)\frac{\partial J(w,b)}{\partial w}^{(i)} = (f_{w,b}(x^{(i)}) -y^{(i)})x^{(i)}
  • 返回所有例子的总梯度更新值

J(w,b)b=1mi=0m1J(w,b)b(i)\frac{\partial J(w,b)}{\partial b} = \frac{1}{m} \sum\limits_{i = 0}^{m-1} \frac{\partial J(w,b)}{\partial b}^{(i)}
J(w,b)w=1mi=0m1J(w,b)w(i)\frac{\partial J(w,b)}{\partial w} = \frac{1}{m} \sum\limits_{i = 0}^{m-1} \frac{\partial J(w,b)}{\partial w}^{(i)}
  • 这里, 𝑚𝑚 是训练实例的数量, 是求和算子

test_w = 0.2
test_b = 0.2

def compute_gradient(x, y, w, b):
    m = x.shape[0]
    dj_dw = 0
    dj_db = 0

    for i in range(m):
        f_wb = w * x[i] + b
        dj_dw_i = (f_wb - y[i]) * x[i]
        dj_db_i = f_wb - y[i]
        dj_db += dj_db_i
        dj_dw += dj_dw_i
    
    dj_db = dj_db / m
    dj_dw = dj_dw / m

    return dj_dw,dj_db

tmp_dj_dw, tmp_dj_db = compute_gradient(x_train, y_train, test_w, test_b)
# print(f'Gradient at initial b (zeros): {tmp_dj_db}')
# print(f'Gradient at initial w (zeros): {tmp_dj_dw}')

Learning parameters using batch gradient descent - 使用批量梯度下降法学习参数

现在,使用批量梯度下降法找到线性回归模型的最佳参数。批处理指的是在一次迭代中运行所有示例;

  • 验证梯度下降是否正常工作的一个好方法是查看 𝐽(𝑤,𝑏)𝐽(𝑤,𝑏) 的值,并检查它是否每一步都在减小;

  • 假定你已经正确地实现了梯度和计算了成本,并为学习率 alpha 设定了一个合适的值,那么 𝐽(𝑤,𝑏)𝐽(𝑤,𝑏)应该永远不会增加,并在算法结束时收敛到一个稳定的值;

iterations = 1500
alpha = 0.01

def gradient_descent(x, y, w_in, b_in, cost_function, gradient_function, alpha, num_iters):
    m = len(x)
    J_history = []
    w_history = []
    w = copy.deepcopy(w_in)
    b = b_in

    for i in range(num_iters):
        dj_dw, dj_db = gradient_function(x, y, w, b)

        w = w - alpha * dj_dw
        b = b - alpha * dj_db

        if i<100000:
            cost = cost_function(x, y, w, b)
            J_history.append(cost)

        if i%math.ceil(num_iters/10) == 0:
            w_history.append(w)
            # print(f"Iteration {i:4}: Cost {float(J_history[-1]):8.2f}")

    return w, b, J_history, w_history

w,b,_,_ = gradient_descent(x_train, y_train, initial_w, initial_b, compute_cost, compute_gradient, alpha, iterations)

# print("w,b found by gradient descent:", w, b)
w,b found by gradient descent: 1.166362350335582 -3.63029143940436

将使用梯度下降法得到的最终参数来绘制线性拟合图

回顾一下,我们可以得到单个示例的预测结果 fwb(x(i))=wx(i)+bf_{wb}(x^{(i)}) = wx^{(i)} + b

要计算整个数据集的预测结果,可以循环查看所有训练示例,并计算每个示例的预测结果。

m = x_train.shape[0]
predicted = np.zeros(m)

for i in range(m):
    predicted[i] = w * x_train[i] + b

现在绘制预测值,以查看线性拟合情况

# Plot the linear fit
plt.plot(x_train, predicted, c = "b")

# Create a scatter plot of the data. 
plt.scatter(x_train, y_train, marker='x', c='r') 

# Set the title
plt.title("Profits vs. Population per city")
# Set the y-axis label
plt.ylabel('Profit in $10,000')
# Set the x-axis label
plt.xlabel('Population of City in 10,000s')

Result predict

𝑤,𝑏𝑤,𝑏 的最终值也可以用来预测利润。预测一下 35000 人和 70000 人地区的利润

  • 该模型以万为单位输入城市人口

  • 因此,35,000 人可以转化为np.array([3.5])模型的输入值

  • 同样,70,000 人也可以转化为np.array([7.])模型的输入

predict1 = 3.5 * w + b
print('For population = 35,000, we predict a profit of $%.2f' % (predict1*10000))

predict2 = 7.0 * w + b
print('For population = 70,000, we predict a profit of $%.2f' % (predict2*10000))
For population = 35,000, we predict a profit of $4519.77
For population = 70,000, we predict a profit of $45342.45

Last updated

Was this helpful?