目录
背景
本文所用的python及相关包与【机器学习】三、逻辑回归(Logistic Regression)中的相同
正则化
在使用高阶多项式做为假设函数时,高阶次项的参数过大时会出现过拟合现象,为此我们需要进高阶次项的参数给予惩罚来避免过拟合。以下结合案例进行分析
案例
类似于上面的例子,假设我们这样一组数据:
这个数据集有118列3行,两个特征,两个标签值
数据的样子长这样
案例分析及代码实现
特征映射
显然上面的数据不能再用一条直线进行划分了,我们认为可能需要一个椭圆来区分y的值。
实际上,我们需要修改我们的假设函数,使用x1和x2产生更多的特征来解决这个问题,我们假设:
x1和x2的最高幂次是6,这样就产生了1/2*n*(n+1)=28个参数
这需要我们进行建立特征映射,先把原始数据换算成这样的矩阵:
# 特征映射,目的是将原始数据映射为新的特征组合,以匹配更复杂的假设函数的计算
# 得到这个特征映射后,就可以使用原先的方法让theta与之相乘进行计算
def feature_mapping(x1: np.ndarray, x2: np.ndarray, max_power: int) -> pd.DataFrame:
feature_data = {} # 特征组合的可能性应该有n*(n+1)/2=28种组合,其中n=7,即特征映射字典的长度=28
for i in range(max_power + 1):
for p in range(i + 1):
# 得到的特征映射为一个字典,keys为'f00','f01','f11','f02','f12','f22','f03','f13','f23','f33'...
feature_data["f{}{}".format(i - p, p)] = np.power(x1, i - p) * np.power(x2, p)
# feature_data = {"f{}{}".format(i - p, p): np.power(x1, i - p) * np.power(x2, p)
# for i in range(max_power + 1)
# for p in range(i + 1)
# }
return pd.DataFrame(feature_data)
特征映射后的数据:
定义theta参数
接下来转换成x和y矩阵,并定义theta:
# 读取特征,返回的X的类型必须是ndarray
def get_x(df: pd.DataFrame) -> np.ndarray:
# 在此不要改变原数据的内容,不要直接在原数据中插入新列
dfc = df.iloc[:, :-1]
# 在首列插入对应于theta_0的x_0列
dfc.insert(0, 'x0', 1)
return np.array(dfc)
# 读取标签
def get_y(df: pd.DataFrame) -> np.ndarray:
# 取最后一列的标签值作为y
return np.array(df.iloc[:, -1])
theta = np.zeros(feature_df.shape[1]) # (28,)
过拟合现象
当我们按照上面的二元多次方程的假设函数直接进行梯度下降计算的话,很容易造成过拟合现象:
所谓过拟合是指训练出的模型model能过分精确地匹配到训练集中的数据,但对于训练集以外的新数据表现过差的现象
下面三个图分别是欠拟合、拟合较好和过拟合的效果图:
代价函数(正则化)
为此我们需要进行正则化处理:在代价函数中减小高阶参数的值theta_n(n>1),增加对这些高阶参数过大的惩罚;所以我们在代价函数后增加一项,代价函数就变成这个样子:(按照惯例,在
中,j>=1,即下标从1开始)
正则化后的代价函数为:
# 正则化的代价函数
def regularized_cost_function(theta: np.ndarray, x: np.ndarray, y: np.ndarray, lamd: int = 1):
# 正则化除第一个参数外的所有参数项
regularized_term = lamd / (2 * len(x)) * np.sum(np.power(theta[1:], 2))
return cost_function(theta, x, y) + regularized_term
梯度下降(正则化)
相应的正则化梯度下降函数:
可以证明,在梯度下降函数中,只需要在原始基础上增加一项即可(实际上就是对新增costFunction的一项对
求偏导数)
# 正则化梯度下降函数
def regularized_gradient_descent_function(theta: np.ndarray, x: np.ndarray, y: np.ndarray, lamd: int = 1):
# 正则化除第一个参数外的所有参数项
regularized_term = lamd / len(x) * theta[1:]
# 记得在第一个元素前添加theta_0保证长度
regularized_term = np.insert(regularized_term, 0, 0) # regularized_term的形状应是(28,)
return gradient_descent_function(theta, x, y) + regularized_term
最小化拟合
跟【机器学习】三、逻辑回归(Logistic Regression)中的最小化拟合函数一样,只不过传入的参数多了一个,这里直接把值设置成为1
# 最小化代价函数,使用scipy库中的梯度下降法,令其自动选择学习率来寻找我们要找的使代价函数最小的参数
# scipy文档:http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html#scipy.optimize.minimize
def minimize(theta: np.array, X: np.array, y: np.array):
import scipy.optimize as opt
res = opt.minimize(fun=regularized_cost_function, x0=theta, args=(X, y, 1), method='Newton-CG',
jac=regularized_gradient_descent_function)
return res # res.x=[-25.16227358, 0.20623923, 0.20147921]不同的计算机可能有误差,误差在10e-2内可接受
主函数
接下来是主函数:
data = pd.read_csv('ex2data2.txt', header=None, names=['x1', 'x2', 'y'])
plot_origin_data(data)
feature_df = feature_mapping(data.x1.values, data.x2.values, 6)
X = get_x(feature_df)
y = get_y(data)
theta = np.zeros(feature_df.shape[1])
# 最小化代价函数,以找到参数值
result = minimize(theta, X, y)
print(result)
最后我们看一下最终的结果:
结果验证
f-score是0.84
图形化展示
红色展示的是决策边界
如果还想了解其它有关机器学习的知识,可以查看作者的《机器学习目录》
(本文所有的数据和代码在附件中都有)