从 0 开始机器学习 - 一文入门多维特征梯度下降法!
今天登龙跟大家分享下我对多维特征的读取、缩放和多变量梯度下降算法的理解,文章不长,有理论也有实际的代码,下面开始,Go!
一、如何表示多维特征?
1.1 特征缩放
实际项目中在读取多维特征之前需要先对数据进行缩放,为什么呢?
因为在有了多维特征向量和多变量梯度下降法后,为了帮助算法更快地收敛,还需要对选用的特征进行尺度缩放,其实就是缩小特征值,将多个特征的值都缩小到同样大小的区间,通常是缩小到 [-1, 1] 周围。
以下图为例:
image在没有进行特征缩放之前,两参数梯度下降的等高线图呈竖的椭圆形,这是因为横轴和纵轴参数范围不同,进而导致算法在寻找最小值时会迭代很多次。
而当进行缩放使得横纵轴范围大致相同后,等高线图基本呈圆形,算法在迭代的时候往一个方向很快就能找到最小值,大大减少迭代次数。
缩放的最终结果不一定非要准确到 [-1, 1],比如 [-3, 3],[-2, 1] 这些范围不是太大都是可以的,一个又常用有简单的特征缩放计算方法是:
其中 是平均值, 是(max - min),比如用这个公式将所有的房屋面积和卧室数量进行缩放:
- ,其中 1000 是面积平均值,2000 是最大面积减最小面积。
- ,其中 2 是卧室数量平均值,5 个最大卧室数量减去最小卧室数量。
理论学会后,再来学习下实际的特征缩放代码:
# 特征缩放
def normalize_feature(df):
# 对原始数据每一列应用一个 lambda 函数,mean() 求每列平均值,std() 求标准差
return df.apply(lambda column: (column - column.mean()) / column.std())
我们用这个函数来实际缩放一下含有 2 个特征的原始房价数据:
# 读取原始数据
raw_data = pd.read_csv('ex1data2.txt', names = ['square', 'bedrooms', 'price'])
# 显示前 5 行
raw_data.head()
image
# 对原始数据进行特征缩放
data = normalize_feature(raw_data)
# 显示缩放后的前 5 行数据
data.head()
image
可以看到缩放后的数据范围基本都在 区间左右,说明我们的特征缩放成功了 _!下面来学习如何读取多维特征!
1.2 读取多维特征
还记得上篇文章我们介绍的第一个机器学习算法吗?
即通过房屋面积来预测价格,这个问题中只使用一个输入特征房屋面积,可现实生活中要解决的问题通常都含有多个特征,因为用多个特征训练出的模型准确度更高。
那么如何机器学习算法如何处理多个特征的输入呢?
我们还用预测房价的例子,不过这次要增加另外 3 个特征,卧室数量,房屋楼层,房屋年龄:
image这样一来,我们就有了 4 个输入特征了,特征多了,表示的方法也要升升级了:
-
:输入特征的数量,即特征矩阵列数,也即特征向量的维度
-
:训练集中第 个实例向量,就是特征矩阵的第 行,比如列向量
-
:训练集中第 个实例的第 个特征,比如
特征数量增加了,之前的假设函数肯定也需要修改,要把增加的特征变量和参数加上:
虽然这样表示没问题,但是却不方便利用向量来计算,因为参数 有 n + 1 个,但 只有 n 个,那怎么办呢?
很简单,我们额外增加一个 ,则上式变为:
这样一来就可以写成向量相乘的形式:
你可能要问了为何要写成向量的形式?主要因为 2 点:
- 使用向量方便程序编写,一句计算特征向量的代码就可以同时计算多个输入参数,因为一个特征向量中包含所有输入参数
- 使用向量方便算法执行,梯度下降算法要求参数同时更新,如果不使用向量,那更新起来非常麻烦。
通过增加一个维度 ,最终训练集的特征矩阵的大小为:,其中 m 为行数,n + 1 为列数。
那来看下读取多维数据并添加一列全 1 向量的函数代码:
# 读取原始数据,返回 m * (n + 1) 维特征矩阵
def get_X(df):
# 创建 m 行 1 列的数据帧
ones = pd.DataFrame({'ones': np.ones(len(df))})
# 合并全 1 向量作为元素数据第一列,axis = 0 按行合并,anix = 1 按列合并
data = pd.concat([ones, df], axis=1)
# 返回特征矩阵
return data.iloc[:, :-1].values
为了简单点,这里假设原始房价数据只有 2 个输入特征,即房屋面积和卧室数量:
[图片上传失败...(image-4d05e3-1585821392478)]
我们用上面的函数来读取下数据特征到向量 X
:
# 读取原始数据,增加第一列全 1 向量
X = get_X(data)
# 输出数据、维度和类型
print(X.shape, type(X))
print(X)
输出结果如下:
# 47 行,3 列 = 47 * (2 + 1)
(47, 3) <class 'numpy.ndarray'>
可以看到特征矩阵的第一维列向量全为 1,后两列不变(数据换成科学计数法表示),这与我们上面介绍的对多维特征的操作方法结果相同!
[图片上传失败...(image-2f453e-1585821392478)]
读取多维特征之后,我们就可以将特征矩阵 X
的每一行作为一个特征向量(就是特征组成的向量 =_=),并用它们来训练机器学习算法啦!
以上就是我对多维特征作为机器学习算法输入的一些理解,非常感谢吴恩达老师的公开课 _。
上面的代码都在文末我的 Github 仓库,直接下载就能运行,记得给我个 star 哦!
二、多变量梯度下降法
多维特征读取后,就可以学习多变量梯度下降法了,其实与上一篇博客的单变量梯度下降原理是一样的,只不过增加了特征变量,相应地参数也就增加了。
比如线性回归的多变量假设函数、代价函数、梯度下降法分别如下:
- 假设函数:
- 代价函数:
- 多变量梯度下降法
因为参数增加到 n 个,所以梯度下降的偏导数也要分别对每个参数求一次,然后同时更新 n 个参数:
image比如当 时更新前 3 个参数:
image我觉得挺好理解的,只需要按照单变量梯度下降的逻辑拓展下变量和参数的数量即可,前提一定要完全理解单变量的梯度下降。
那继续来看下多变量梯度下降的算法代码,与单变量梯度下降一毛一样,先计算偏导数:
# 计算偏导数
def gradient(theta, X, y):
m = X.shape[0]
inner = X.T @ (X @ theta - y)
return inner / m
再迭代下降:
# 批量梯度下降
# epoch: 下降迭代次数
# alpha: 初始学习率
def batch_gradient_decent(theta, X, y, epoch, alpha = 0.01):
# 计算初始成本:theta 都为 0
cost_data = [lr_cost(theta, X, y)]
# 创建新的 theta 变量,不与原来的混淆
_theta = theta.copy()
for _ in range(epoch):
# 新的 theta = 旧的 theta - 学习率 * 偏导数
_theta = _theta - alpha * gradient(_theta, X, y)
# 累加成本数据,用于可视化
cost_data.append(lr_cost(_theta, X, y))
return _theta, cost_data
来调用下这个梯度下降函数,初始学习率 alpha
设置为 0.01,迭代 epoch = 500
次:
final_theta, cost_data = batch_gradient_decent(theta, X, y, epoch, alpha = alpha)
这是最终的成本和迭代次数的曲线,可以看到成本 cost 最终基本趋于不变,说明梯度下降算法收敛啦!
image话说我之前忘记讲解单变量梯度下降的代码了,下次一定补上!文章内的代码仓库:
https://github.com/DLonng/AI-Notes/blob/master/MachineLearning/code/ex1-linear-regression/multi_feature.ipynb
OK,今天就跟大家分享这些,喜欢的小伙伴记得持续关注我哦!