机器学习——线性回归编程训练

机器学习——线性回归编程训练

参考资料:

1.Python机器学习算法:线性回归

2.黄海广老师:吴恩达机器学习笔记github

3.梯度下降法

本文是吴恩达机器学习课程中的第一个编程训练。关于线性回归的详细介绍可以参考吴恩达机器学习课程,参考资料1也介绍的较为详细

1.线性回归

线性回归其本质上就是对数据进行拟合,从大量的数据中,获得一个方程来近似描述这些数据,并用该方程对新的输入进行预测

举个例子就是:知晓了很多房子的面积与价格,我们可以拟合出面积与价格之间的一个函数关系,并用这个函数关系预测一个新房子的价格。

2.单变量(一元)线性回归

我们所要做的就是获得这么一个函数关系:
h θ ( x ) = θ 0 x 0 + θ 1 x h_θ(x) = θ_0x_0 + θ_1x hθ(x)=θ0x0+θ1x
这里 x 0 = 1 x_0=1 x0=1​,而 h θ ( x ) h_θ(x) hθ(x)​​就是我们要根据面积来预测的价格,其中 x x x​ 就是我们所说的面积

因此,我们已知 x 0 x_0 x0 x x x 可从数据集中获得,待求 θ 0 、 θ 1 θ_0、θ_1 θ0θ1​​​

2.1如何评价一个拟合结果的好坏?

预测价格和实际价格越接近,则拟合结果越好,即:

∣ h θ ( x ) − y ∣ |h_θ(x)-y| hθ(x)y​​ 的值越小越好。

其中 y y y​ 是实际价格,我们可以从数据集中获得。

因此我们定义一个函数如下,我们称这个函数为代价函数或损失函数:
J ( θ ) = 1 / ( 2 m ) ∗ ∑ [ h θ ( x ) − y ] 2 J(θ)=1/(2m)*\sum{[h_θ(x)-y]^2} J(θ)=1/(2m)[hθ(x)y]2我们用平方来去掉绝对值的影响。

同上,如果 J ( θ ) J(θ) J(θ)​取得到最小值,那么拟合结果应该就是最好的。通常情况下,为了方便计算,我们使用矩阵来表示这个方程。

2.2如何求取 J ( θ ) J(θ) J(θ)的最小值

通常采用梯度下降法来求取最小值。此外还有正规方程,但这里不做介绍。

梯度下降法的思想就是:从山顶向山脚走,每走一步就就下降一点

梯度下降法会对 J ( θ ) J(θ) J(θ)随机一个初始值A,然后根据步长计算一个值B,如果B<A,则我们就获得了一个新的低点,之后更新 θ 0 、 θ 1 θ_0、θ_1 θ0θ1。如此迭代,直到找到最低点。

关于梯度下降法的介绍可以参考梯度下降法

权重的更新规则:
θ = θ − α ∗ d ( J ( θ ) ) / d θ θ = θ -α*d(J(θ))/dθ θ=θαd(J(θ))/dθ其中 d ( J ( θ ) ) / d θ d(J(θ))/dθ d(J(θ))/dθ​​​表示求偏导, α α α表示学习速率

2.3学习速率的选取

通常要进行多次尝试才能够选取到合适的学习速率。

合适的学习速率可以使得代价函数 J ( θ ) J(θ) J(θ)获得较快的下降速度,使其快速收敛。

学习速率通常选取:

0.001,0.003,0.01,0.03,0.1,0.3,1等,通常采用3倍进行选取,不断尝试,选择最合理的学习速率。

在下文的多元线性回归编程训练中可以看到具体的实例。

2.4多元线性回归

多元线性回归和一元线性回归是类似,仅仅表现在其函数关系不同:

多元线性回归的函数关系:
h θ ( x ) = θ 0 x 0 + θ 1 x 1 + θ 2 x 2 + θ 3 x 3 + . . . h_θ(x) = θ_0x_0 + θ_1x_1+θ_2x_2+θ_3x_3+... hθ(x)=θ0x0+θ1x1+θ2x2+θ3x3+...

3.一元线性回归编程训练:

数据集:来自黄海广老师的GitHub仓库,见参考资料2,具体链接

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np


def get_x(df):  # 获取特征x
    '''
    h_θ(x) = θ_0*x_0 + θ_1*x_1 + θ_2*x_2 + ...
    这里默认x_0为1,因此需要在原始的数据中增加一列1
    并将该列和原始数据集中的特征合并,获得特征数组
    :param df: 数据集
    :return: 该操作返回的是特征数组
    '''
    ones = pd.DataFrame({'ones': np.ones(len(df))})  # 以字典的形式创建一个DataFrame,即x_0=1
    data = pd.concat([ones, df], axis=1)  # 合并数据,根据列合并
    return data.iloc[:, :-1].values  # 返回的是一组数组


def get_y(df):  # 获取标签y
    return np.array(df.iloc[:, -1])  # df.iloc[:, -1]是指df的最后一列


def normalize_feature(df):  # 特征缩放,在本例中并没有应用
    return df.apply(lambda column: (column - column.mean()) / column.std())


def lr_cost(theta, x, y):
    '''
    定义代价(损失、成本)函数:
    J(θ) = 1/(2m) [h_θ(x) - y ]^2
    :param theta: 权重(系数)θ
    :param x: 特征x
    :param y: 标签y
    :return: 代价函数的值
    '''
    m = x.shape[0]  # m为样本数
    inner = x @ theta - y  # h_θ(x) - y   其中 h_θ(x) = x @ theta,x @ theta等价于x.dot(theta)
    square_sum = inner.T @ inner  # [h_θ(x) - y ]^2
    cost = square_sum / (2 * m)  # J(θ) = 1/(2m) [h_θ(x) - y ]^2
    return cost


def gradient(theta, x, y):
    '''
    代价函数求偏导
    :param theta: 权重(系数)θ
    :param x: 特征x
    :param y: 标签y
    :return: 代价函数的偏导
    '''
    m = x.shape[0]
    inner = x.T @ (x @ theta - y)  # 涉及到矩阵求偏导,这里不讨论具体的数学问题
    return inner / m


def batch_gradient_decent(theta, x, y, epoch, alpha=0.01):
    '''
    批量梯度下降函数,求取使代价函数最小的权重θ
    :param theta: 初始权重θ
    :param x: 特征x
    :param y: 标签y
    :param epoch: 最大迭代次数
    :param alpha: 学习速率,这里默认为0.01
    :return: 最终权重θ,每次迭代的代价函数
    '''
    cost_data = [lr_cost(theta, x, y)]  # 存放每次迭代的代价函数值的列表
    for i in range(epoch):  # 更新权重θ
        theta = theta - alpha * gradient(theta, x, y)  # θ = θ -α df/dθ
        cost_data.append(lr_cost(theta, x, y))
    return theta, cost_data


sns.set(context="notebook", style="whitegrid", palette="dark")  # 设置图形界面的背景色等
df = pd.read_csv("C:/Users/Administrator/Desktop/data1.txt", names=["population", "profit"])  # 打开数据文件,给每列命名
x = get_x(df)  # 特征x
y = get_y(df)  # 标签y
theta = np.zeros(shape=x.shape[1])  # 权重(系数)θ,这里并没有对权重进行随机初始化
epoch = 500   # 迭代500次
final_theta, final_cost_data = batch_gradient_decent(theta, x, y, epoch)   # 最终权重θ,每次迭代的代价函数
data_cost = pd.DataFrame(final_cost_data)  # 将final_cost_data转为DataFrame类型

#  每次迭代的代价函数值可视化
ax = sns.lineplot(x=np.arange(epoch + 1), y=final_cost_data, data=data_cost)  # 将每次迭代后的代价函数值绘制成折线图
ax.set_xlabel('epoch') # 设置横坐标标签
ax.set_ylabel('cost') # 设置纵坐标标签
plt.show()
# 一元特征,h_θ(x) = θ_0*x_0 + θ_1*x_1 = mx+b
b = final_theta[0]
m = final_theta[1]
plt.scatter(df.population, df.profit, label="Training data")  # 画出散点图
plt.plot(df.population, df.population * m + b, label="Prediction") # 画出拟合直线
plt.legend(loc=2)
plt.show()

运行结果:
请添加图片描述
请添加图片描述

4.多元线性回归编程训练:

数据集:来自黄海广老师的GitHub仓库,见参考资料2,具体链接

对于多元函数仅仅是增加了一个特征缩放的归一化问题。

# 仅修改文件读取时的代码即可,修改如下:
row_df = pd.read_csv("C:/Users/Administrator/Desktop/data2.txt", names=['square', 'bedrooms', 'price'])  # 打开数据文件,给每列命名
df = normalize_feature(row_df)

此外,由于多元变量是没有办法通过平面图像绘制的,因此在本例中仅可绘制代价函数的折线图,无法绘制出散点图和拟合(超)平面

运行结果:
请添加图片描述

由于在批量梯度下降过程中默认学习速率为0.01,可适当更改学习速率来减少收敛所需要的迭代次数,如更改学习速率为0.1,如下图:

final_theta, final_cost_data = batch_gradient_decent(theta, x, y, epoch, alpha=0.1)  # 最终权重θ,每次迭代的代价函数

请添加图片描述

4.1学习速率的选取

上文已经说了,学习速率的通常选取的值,不妨做成列表,进行遍历,然后根据迭代次数与代价函数的关系图选取最合适的学习速率。

#  将代价函数可视化部分的代码修改如下:
#  每次迭代的代价函数值可视化
fig, ax = plt.subplots(figsize=(16, 9))  # 设置窗口大小
alphas = [0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1]   # 学习速率α待取值
for alpha in alphas:   # 遍历学习速率α
    _, final_cost_data = batch_gradient_decent(theta, x, y, epoch, alpha=alpha)  # _表示此处有值,但是下文并不使用,用来占位
    ax.plot(np.arange(epoch + 1), final_cost_data, label=alpha)  # 将每次迭代后的代价函数值绘制成折线图
ax.set_xlabel('epoch')  # 设置横坐标标签
ax.set_ylabel('cost')  # 设置纵坐标标签
ax.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)  # 设置标签位置
ax.set_title('learning rate', fontsize=18)  # 标题,字号
plt.show()

运行结果:
请添加图片描述
综上,我们可以选取的学习速率为1,之后可以尝试更高的学习速率,看是否存在更合适的。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值