特征缩放
课程与数据集
本文对应 Andrew Ng(吴恩达)《Machine Learning》梯度下降法实践 1——特征缩放部分。代码使用的
ex1data2.txt(房屋面积、卧室数、价格)为课程官方编程练习数据,与线性回归同属第二周内容。数据与参考代码可在黄海广教授整理的中文笔记仓库下载:fengdu78/Coursera-ML-AndrewNg-Notes(见
code/ex1/目录)。课程主页:Coursera Machine Learning。
一、为什么进行特征缩放
一个由两个变量评估的数据:
如果仅仅只是按照我们之前的代码,直接从
这个在吴恩达机器学习里有一个很好的例子(不要犹豫,肯定是和房价有关的东西):
| 场景 | 结果 | ||||||
|---|---|---|---|---|---|---|---|
| 已知数据 | 2000 | 5 | 500k | — | — | — | — |
| 拟合 A | 2000 | 5 | — | 50 | 0.1 | 50 | |
| 拟合 B | 2000 | 5 | — | 0.1 | 50 | 50 |
我们自然而然地想到:如果钱的单位是元,我们就把它变成千元;如果人数的单位是个,就把它变成千个、万个,将所有的数据通过这种简单的单位调配使其达到差不多的值。
但是别忘了我们通常输入的都是零向量,也就是说如果可以把所有数据通过简单的线性或加减变换使其都在 0 的某一个领域内,那就再好不过了。
二、特征缩放的数学原理
2.1 方法一(最大最小值归一化)
假设
2.2 方法二(Z-score 标准化)
特征缩放之前,舍弃明显不对劲的数据尤为重要,这个在中学生物实验就已经提到过了。
三、将 数组变换回来
我们无非就是运行了以下的式子:
这里
联立可以得出:
四、代码实现
4.1 导入库与读取数据
首先导入要用的库和数据:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
path = 'ex1data2.txt'
data = pd.read_csv(path, header=None, names=['Size', 'Bedrooms', 'Price'])
data.head() # 查看数据有多少列,方便定义 theta_begin4.2 特征缩放
对数据进行特征缩放:
# 处理数据
means = np.array(data.mean().values).reshape(1, 3) # 把平均值保存下来并且化为 array 类型
std = np.array(data.std().values).reshape(1, 3) # 保存 mean std
temp = data.copy() # 保存源数据
data = (data - data.mean(axis=0)) / data.std(axis=0) # data.mean(axis=0) 表示每列替换为平均值
print(data.head())
data.insert(0, 'ones', 1)
cols = data.shape[1]
X = data.iloc[:, 0:cols - 1] # X 是所有行去掉最后一列
Y = data.iloc[:, cols - 1:cols]
X = np.array(X.values)
Y = np.array(Y.values)
theta_begin = np.array([0, 0, 0]).reshape(1, 3) # 调整为 (1,3) 形状以匹配原矩阵形状4.3 代价函数与梯度下降
代价函数和梯度下降函数还是老样子,因为我们之前写的函数对于任意列数都有用:
iters = 1000
cost = np.zeros(iters)
def cost_function(X, Y, theta):
inner = np.power((X @ theta.T) - Y, 2)
return np.sum(inner) / (2 * len(X))
def gradient_descent(X, y, theta, alpha, iters):
temp = np.zeros(theta.shape)
parameters = theta.ravel().shape[0]
global cost
for i in range(iters):
error = (X @ theta.T) - y
for j in range(parameters):
term = np.multiply(error, X[:, j].reshape(-1, 1))
temp[0, j] = theta[0, j] - ((alpha / len(X)) * np.sum(term))
theta = temp.copy()
cost[i] = cost_function(X, Y, theta)
return theta4.4 训练与可视化
我们查看特征缩放得出来的
g = gradient_descent(X, Y, theta_begin, 0.01, iters)
print(g)
print(cost_function(X, Y, g))
# 迭代 cost 可视化
fig, ax = plt.subplots(figsize=(12, 8))
ax.plot(np.arange(iters), cost, 'r')
ax.set_xlabel('iters')
ax.set_ylabel('cost')
ax.set_title('change of cost')
plt.show()4.5 参数反变换
我们接下来将参数转换回去,即从
def theta_back(theta, means, std):
thetaback = theta.copy()
thetaback[0, 0] = (theta[0, 0] - (np.sum((means[0, :-1] * theta[0, 1:]) / std[0, :-1]))) * std[0, -1] + means[0, -1] # 目标变量也被标准化
thetaback[0, 1:] = (theta[0, 1:] / std[0, :-1]) * std[0, -1]
return thetaback我们看看最终结果:
gf = theta_back(g, means, std)
print(gf)示例输出:
[[88307.21151185 138.22534685 -7709.05876589]]前置知识:《线性回归》笔记(ex1data1.txt)。后续:《逻辑回归》《正则化》笔记(ex2data1.txt / ex2data2.txt)。
