机器学习|吴恩达机器学习课后作业1——单变量线性回归(Linear regression with one variable)

1. 问题和数据 假设你是一家连锁餐车店的老板,但是你又和别的土老板不一样,你又刚好是个懂线性回归,还懂编程的老板,正在考虑在不同的城市开一家新店。该连锁店已经在各个城市开设了餐车,你可以获得这些城市的利润和人口数据。在本部分的练习中,您将透过这些数据,通过单变量实现线性回归,以预测餐车的利润。
数据ex1data1.txt内容为一个97行两列(97,2)的数据;其中第一列表示人口数据,第二列表示利润。具体数据如下:

6.1101,17.592 5.5277,9.1302 8.5186,13.662 7.0032,11.854 5.8598,6.8233 8.3829,11.886 7.4764,4.3483 8.5781,12 6.4862,6.5987 5.0546,3.8166 5.7107,3.2522 14.164,15.505 5.734,3.1551 8.4084,7.2258 5.6407,0.71618 5.3794,3.5129 6.3654,5.3048 5.1301,0.56077 6.4296,3.6518 7.0708,5.3893 6.1891,3.1386 20.27,21.767 5.4901,4.263 6.3261,5.1875 5.5649,3.0825 18.945,22.638 12.828,13.501 10.957,7.0467 13.176,14.692 22.203,24.147 5.2524,-1.22 6.5894,5.9966 9.2482,12.134 5.8918,1.8495 8.2111,6.5426 7.9334,4.5623 8.0959,4.1164 5.6063,3.3928 12.836,10.117 6.3534,5.4974 5.4069,0.55657 6.8825,3.9115 11.708,5.3854 5.7737,2.4406 7.8247,6.7318 7.0931,1.0463 5.0702,5.1337 5.8014,1.844 11.7,8.0043 5.5416,1.0179 7.5402,6.7504 5.3077,1.8396 7.4239,4.2885 7.6031,4.9981 6.3328,1.4233 6.3589,-1.4211 6.2742,2.4756 5.6397,4.6042 9.3102,3.9624 9.4536,5.4141 8.8254,5.1694 5.1793,-0.74279 21.279,17.929 14.908,12.054 18.959,17.054 7.2182,4.8852 8.2951,5.7442 10.236,7.7754 5.4994,1.0173 20.341,20.992 10.136,6.6799 7.3345,4.0259 6.0062,1.2784 7.2259,3.3411 5.0269,-2.6807 6.5479,0.29678 7.5386,3.8845 5.0365,5.7014 10.274,6.7526 5.1077,2.0576 5.7292,0.47953 5.1884,0.20421 6.3557,0.67861 9.7687,7.5435 6.5159,5.3436 8.5172,4.2415 9.1802,6.7981 6.002,0.92695 5.5204,0.152 5.0594,2.8214 5.7077,1.8451 7.6366,4.2959 5.8707,7.2029 5.3054,1.9869 8.2934,0.14454 13.394,9.0551 5.4369,0.61705

2. 数据导入与初步分析 导入包,numpy和pandas是做运算的库,matplotlib是画图的库。
import numpy as np import pandas as pd import matplotlib.pyplot as plt

导入数据集
path = 'ex1data1.txt'# 数据路径,要和.py文件放在同一个文件下 data = https://www.it610.com/article/pd.read_csv(path, header=None, names=['Population', 'Profit'])#names列名,第一列为Population, 第二列为Profit print(data.head())# 预览数据前五行

运行输出结果如下:
PopulationProfit 06.110117.5920 15.52779.1302 28.518613.6620 37.003211.8540 45.85986.8233

可以描述一下数据值。
对于数值数据,描述的结果将包括计数,平均值,标准差,最小值,最大值以及较低的百分位数和50。默认情况下,较低的百分位数为25,较高的百分位数为75,百分位数与中位数相同。
print(data.describe())

描述结果:
PopulationProfit count97.00000097.000000 mean8.1598005.839135 std3.8698845.510262 min5.026900-2.680700 25%5.7077001.986900 50%6.5894004.562300 75%8.5781007.046700 max22.20300024.147000

在开始任何任务之前,通过可视化来理解数据通常是有用的。
对于这个数据集,您可以使用散点图来可视化数据,因为它只有两个属性(利润和人口)。
# 数据可视化,kind选择绘制散点图,取值为line或者scatter;依次选择x列的名字,y列的名字;figsize默认值图像大小 data.plot(kind='scatter', x='Population', y='Profit', figsize=(12, 8)) plt.show()

输出的散点图如下,可以直接看出x和y还是有着较强的线性关系的,这就是画出散点图的好处。
机器学习|吴恩达机器学习课后作业1——单变量线性回归(Linear regression with one variable)
文章图片

?
现在使用梯度下降来实现线性回归,以最小化成本函数。
首先,我们将创建一个以参数θ (theta)为特征函数的代价函数。(注:代价函数,成本函数等叫法其实都是一个意思,是 J ( θ ) J(\theta) J(θ)这个Cost Function。
机器学习|吴恩达机器学习课后作业1——单变量线性回归(Linear regression with one variable)
文章图片

?
因为该问题是单变量,只有一个x,所以本题目中:
机器学习|吴恩达机器学习课后作业1——单变量线性回归(Linear regression with one variable)
文章图片

?
综合上述两个公式,所以我们建模的公式如下(不会就去看我的“吴恩达机器学习打卡day1——P8”):
机器学习|吴恩达机器学习课后作业1——单变量线性回归(Linear regression with one variable)
文章图片

计算代价函数J(θ)
def computeCost(X, y, theta): inner = np.power(((X * theta.T) - y), 2)# 即代价函数公式求和符号右边;.T是转置矩阵的意思 return np.sum(inner) / (2 * len(X))# np.sum()可以将矩阵里的元素全部加起来,(2 * len(X))是成本函数公式求和符号与(1/2m) # return将它后面的值/结果返回给定义的函数,不然打印函数名出来是空值;有了return之后,所以下面打印函数名才能出现值/结果

让我们在训练集中添加一列,以便我们可以使用向量化的解决方案来计算代价和梯度,
即为了使 h θ ( x ) = θ 0 + θ 1 X h_\theta(x) = \theta_0 +\theta_1 X hθ?(x)=θ0?+θ1?X中 θ 0 的 系 数 为 1 \theta_0的系数为1 θ0?的系数为1。
#在数据集的最左侧插入一列全为1的列,以便计算 data.insert(0, 'ones', 1)# 即x0=1 索引index为O, name为ones,value为1.

现在我们来做一些变量初始化。
# .shape会得到该矩阵共有(几行,几列).shape[0]为第一维的长度,shape[1]为第二维的长度,即列。 # pandas中利用.iloc选取数据loc; ','前的部分标明选取的行,','后的部分标明选取的列, 此时三列了 # set X (training data) and y (target variable) cols = data.shape[1]# 表示得到第二维(行)一共有3列,即cols=3 X = data.iloc[ : , 0:cols-1]# [X是所有行, y是从0到cols-1列(即第一、二列)] y = data.iloc[ : , cols-1: cols]# [X是所有行, y是从cols-1到cols列(即第三列)]

打印X表头,观察下X(训练集)是否获取正确。
print(X2.head())

输出X表头:
onesPopulation 016.1101 115.5277 218.5186 317.0032 415.8598

打印y表头,观察下y(训练集)是否获取正确。
print(y2.head()) ```python

输出y表头:
Profit 017.5920 19.1302 213.6620 311.8540 46.8233

代价函数是应该是numpy矩阵,所以我们需要转换x和y成矩阵,然后才能使用它们。 我们还需要初始化theta,即把theta所有元素都设置为0。
# pandas读取的数据是DataFrame形式,优势是能对数据进行很多操作,但要想进行矩阵运算,要将DataFrame形式转换为矩阵,例如x=np.matrix(X.values) X = np.matrix(X.values)# 将X转换为矩阵 y = np.matrix(y.values)# 将y转换为矩阵 theta = np.matrix(np.array([0, 0]))# 因为X是两列,所以theta自然也要有两列

#打印theta检查,应该是个一行两列的零矩阵
print(theta,'theta')

输出theta:
theta: [[0 0]]

看下维度,维度对了矩阵的乘法计算才能正确进行,这一作业的代码错误经常都是矩阵维度不对造成的。
print('X.shape:', X.shape) print('theta.shape', theta.shape) print('y.shape', y.shape)

计算初始的代价函数(theta初始值为0)
computeCost(X, y, theta) print('Cost_init:', computeCost(X, y, theta))#得到初始的成本代价(还未开始迭代)

初始代价函数结果
Cost_init: 32.072733877455676

3. 批量梯度下降(Batch gradient decent) 关键点在于theta0和thata1要同时更新,需要用temp进行临时存储。
temp[0, j]这一行是 θ \theta θ的迭代公式,也是梯度下降函数的核心。也就是:
机器学习|吴恩达机器学习课后作业1——单变量线性回归(Linear regression with one variable)
文章图片

def gradientDescent(X, y, theta, alpha, iters): temp = np.matrix(np.zeros(theta.shape))# 按照theta的shape建立一个(1,2)的零值矩阵temp,为了临时存储theta,方便进行迭代更新。 parameters = int(theta.shape[1]) # theta.shape[1]就是2,parameter就是$\theta_j$的j的意思; cost = np.zeros(iters) # 构建iters个0的数组,用来存放costfor i in range(iters): error = (X * theta.T) - y# 计算出每一组数据的误差,每行都是样本的误差for j in range(parameters): # 线性回归方程对theta求导后的公式为(theta.T*X-y)*xj的平方的sum # 所以这里用X*theta.T,即列向保存,再乘以X中对应的每一列,方便保存求和。 # multiply为数组对应元素相乘,所以这个term就是还未相加的对theta求导后的数据 term = np.multiply(error, X[ : , j])# X[ : , j]的意思是X[所有行,第j列] temp[0, j] = theta[0, j] - ((alpha / len(X)) * np.sum(term))# 前面转不转置和上课学的公式不太一样,这没关系,因为书 # 上的转置是考虑了求和,我们这里的转置是为了变成可以相乘的方块,至于求和最后用了sum直接求theta = temp cost[i] = computeCost(X, y, theta)# 每迭代一次就调用一次成本函数,计算目标方程的值;并存为cost数组里面的第i个值return theta, cost

初始化学习速率和迭代次数
alpha = 0.01# 可以试试0.005,0.001越小迭代得越快 iters = 1000# 10000时,迭代也曲线挺好的

#调用梯度下降函数,计算迭代后的最优值
g, cost = gradientDescent(X, y, theta, alpha, iters) # g为迭代完之后的theta; ( 按return的theta, cost排序) print('g:', g)# 此时的g为满足使成本函数最小的最优值theta

输出最优g为:
g: [[-3.241402141.1272942 ]]

代入最优值theta,计算最小成本
minCost = computeCost(X, y, g)# 代入最优值theta,计算最小成本函数 print('minCost:', minCost)

输出最小成本为:
minCost: 4.515955503078914

现在我们来绘制纯属模型以及数据,以便直观地看出它的拟合
x = np.linspace(data.Population.min(), data.Population.max(), 100)# 从最小数据到最大数据(即所有数据)中随机抽100个数据点, # 用np.linspace可以将这些点用等差数列排列出来,即得到一个个间隔均匀的x点 # 线性回归的最终方程 f = g[0, 0] + (g[0, 1] * x)# 将一个个间隔均匀的x点带入h(\theta)函数,得到一个个f值,也就是我们的预测值fig, ax = plt.subplots(figsize=(12, 8))# fig代表绘图窗口(Figure);ax代表这个绘图窗口上的坐标系(axis),一般会继续对ax进行操作。figsize用来设置图形的大小 ax.plot(x, f, 'r', label='Prediction')# 设置横纵坐标的函数,并设置颜色为红色,在图标上标上'Prediction'标签 ax.scatter(data.Population, data.Profit, label='Training Data')# 画散点图,设置横纵坐标的函数,并设置颜色为红色,在图上标上'Training Data'标签 ax.legend(loc=4)# 给标签选个位置,1表示在第一象限(右上角),2表示在左上角,3表示在左下角,4表示在第四象限 ax.set_xlabel('Population')# 设定x轴名称 ax.set_ylabel('Profit')# 设定y轴名称 ax.set_title('Predicted Profit vs. Population Size')# 设定整个表的名称 plt.show()

输出绘制的拟合曲线为:.
机器学习|吴恩达机器学习课后作业1——单变量线性回归(Linear regression with one variable)
文章图片

由于梯度方程函数在每个训练迭代中输出一个代价的向量,所以我们也可以绘制代价函数的迭代曲线。请注意,代价总是在降低的,这是凸优化问题的一个特点。
代价函数的曲线
#fig代表绘图窗口(Figure);ax代表这个绘图窗口上的坐标系(axis),一般会继续对ax进行操作 fig, ax = plt.subplots(figsize=(12, 8)) ax.plot(np.arange(iters), cost, 'r')# np.arange()自动返回等差数组 ax.set_xlabel('Iterations')# 设定x轴名称 ax.set_ylabel('Cost')# 设定y轴名称 ax.set_title('Error vs. Training Epoch')# 设定整个表的名称 plt.show()

输出绘制的迭代曲线:
机器学习|吴恩达机器学习课后作业1——单变量线性回归(Linear regression with one variable)
文章图片

将迭代次数iters从1000换成10000,迭代更快,迭代曲线更优一点:
机器学习|吴恩达机器学习课后作业1——单变量线性回归(Linear regression with one variable)
文章图片

【机器学习|吴恩达机器学习课后作业1——单变量线性回归(Linear regression with one variable)】参考文献:
[1] https://blog.csdn.net/weixin_43455338/article/details/104794760
[2] https://github.com/PlayPurEo/ML-and-DL/tree/master/basic-model
[3] https://www.bilibili.com/video/BV1cX4y1G7h2?spm_id_from=333.999.0.0
[4] https://www.bilibili.com/video/BV1Xt411s7KY?spm_id_from=333.999.0.0

    推荐阅读