[吴恩达机器学习]5·正则化

吴恩达机器学习系列课程:https://www.bilibili.com/video/BV164411b7dx

过拟合

以多项式回归为例。多项式回归是指在线性回归的基础上,人为的引入高阶项并将其视为新的一维特征,然后进行线性回归,从而达到拟合非线性数据的目的。举例而言,设线性回归的一个数据是 ,我们在多项式回归时可以将其视为 ,这样就人为引入了一个二阶项,我们的回归方程(假设函数)就变成了:,如此就用二次函数去拟合数据了。

我们知道,一个函数可以泰勒展开成幂级数,越展开幂级数越接近原函数,那在回归中是不是也是次数越高越好呢?答案是否定的,因为我们只有有限个数据。如果特征维数 过于接近数据量 ,则它会很好地拟合上我们的数据集,然而对于不在数据集中的数据,这可能并不是一个好的拟合结果。譬如, 时,由插值的原理容易知道,我们可以找到一个唯一的 次多项式完美地经过所有数据,即每个数据集中的数据偏差都是 ,但是这显然不是一个好的拟合,如下图:

同样地,逻辑回归也可能产生过拟合:

解决过拟合的方法有很多,这里学习正则化方法(Regularization)。

正则化

仍然以多项式回归为例,如果我们的假设函数为:,而其中的高阶项 导致了过拟合问题,那么我们自然希望 越小越好。此时,我们只需要对代价函数做出一点修改:,这样当 取最小时, 都接近于 ,我们也就达到了目的。

一般地,我们并不知道究竟应该对哪些参数做出“惩罚”,所以我们设代价函数为: 其中, 是正则化参数, 是正则化项。即我们对除了 以外的参数都做“惩罚”。

如何理解这个代价函数呢?使前一项 尽可能小是为了去拟合数据集中的数据,使正则化项 尽可能小是为了减小各 以避免过拟合,而 就是在二者之间权衡的参数。如果 很大,意味着正则化项占主要地位,有可能导致所有的 都太小了而无法拟合好数据,即欠拟合;如果 很小,意味着拟合数据占主要地位,就有可能过拟合。所以一个合适的正则化参数 非常重要。

线性回归的正则化

线性回归的含有正则化项的代价函数为: 对其求导: 所以梯度下降时,我们的迭代过程为:

若不用梯度下降,而是直接用正规方程,即在数学上解它,那么:

为了记号的方便,我们先假定对 也进行“惩罚”。首先将 写作矩阵形式: 然后令 则: 解得: 现在把 的特殊情况考虑进去,那么最后的结果就是:

逻辑回归的正则化

逻辑回归的含有正则化项的代价函数为: 对其求导: 所以梯度下降时,我们的迭代过程为:


和线性回归一样,我们更希望将上面的式子写作矩阵形式。

注意:以下表达式与 numpy 的矩阵形式相对应,即存在 "broadcast" 的表示方式,在数学上不一定严谨。

代价函数的矩阵形式

回顾逻辑回归的代价函数: 其中, 设: 于是: 所以,设: 于是:

代价函数偏导的矩阵形式

逻辑回归的代价函数的偏导函数向量:

正则化后的矩阵形式

回顾正则化后的逻辑回归代价函数及其偏导: 注意,这里 ,即在 中将 换成 .

有了之前的推导,就容易写出正则化后的矩阵形式了: 这也是我们使用 numpy 实现正则化逻辑回归时的表达式。

实现

首先还是来看一下数据集:

显然,我们需要用一个多项式去进行逻辑回归,这里将数据的两维都扩充为 次,形成有 维特征的数据,然后进行逻辑回归。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import numpy as np
import matplotlib.pyplot as plt

alpha = 0.01
lamb = 1
iteration = 1000000
Z = []

def h(Theta, x):
return 1 / (1 + np.exp(-np.matmul(Theta.T, x)[0][0]))

def h_(Theta, X):
return 1 / (1 + np.exp(-np.matmul(X, Theta)))

def J_reg(Theta, X, y):
tmp = h_(Theta, X)
return (-(np.matmul(y.T, np.log(tmp)) + np.matmul(1 - y.T, np.log(1 - tmp))) / m\
+ lamb / 2 / m * np.matmul(Theta.T, Theta))[0][0]

def partJ_reg(Theta, X, y):
return (np.matmul(X.T, h_(Theta, X) - y) + lamb * np.vstack((np.zeros((1, 1)), Theta[1:, :]))) / m

def GradientDescent(X, Y):
T = np.zeros((n, 1))
for t in range(iteration):
T = T - alpha * partJ_reg(T, X, Y)
Z.append(J_reg(T, X, Y))
return T

def extend(x1, x2):
X = []
for j in range(4):
for k in range(4):
X.append(np.power(x1, j) * np.power(x2, k))
return X

data = np.genfromtxt("ex2data2.txt", delimiter = ',')
(m, n) = data.shape
Y = data[:, -1:]
n = 16
X = np.zeros((m, n))
for i in range(m):
X[i] = extend(data[i][0], data[i][1])

T = GradientDescent(X, Y)
print(T.T)
print(J_reg(T, X, Y))

#============================== draw the picture ==============================#

def calc(x1, x2):
res = np.zeros((x1.shape[0], x1.shape[1]))
for ix in range(x1.shape[0]):
for iy in range(x1.shape[1]):
tmp = np.array(extend(x1[ix][iy], x2[ix][iy]), ndmin = 2)
res[ix][iy] = h(T, tmp.T)
return res

minx1, maxx1 = data[:, 0].min(), data[:, 0].max()+0.1
minx2, maxx2 = data[:, 1].min(), data[:, 1].max()+0.1
delta = 0.01
x1, x2 = np.meshgrid(np.arange(minx1, maxx1, delta), np.arange(minx2, maxx2, delta))

p1 = plt.subplot(121)
plt.contour(x1, x2, calc(x1, x2), [0.5], colors = 'magenta')

X0 = data[data[:, -1] == 0, :]
X1 = data[data[:, -1] == 1, :]
p1.set_title("lambda = " + str(lamb))
p1.set_xlabel("Microchip test 1")
p1.set_ylabel("Microchip test 2")
p1.scatter(X0[:, 0:1], X0[:, 1:2], marker = 'x', c = 'r')
p1.scatter(X1[:, 0:1], X1[:, 1:2], marker = 'o')

p2 = plt.subplot(122)
p2.plot(range(1, iteration+1), Z)
p2.set_title("lambda = " + str(lamb))
p2.set_xlabel("Iteration")
p2.set_ylabel("Cost")

plt.show()
PYTHON

以下是 的回归结果和收敛情况:


接下来我换了一个生成特征的方式:

1
2
3
4
5
6
def extend(x1, x2):
X = []
for j in range(6 + 1):
for k in range(j + 1):
X.append(np.power(x1, k) * np.power(x2, j-k))
return X
PYTHON

结果如下:


[吴恩达机器学习]5·正则化
https://xyfjason.github.io/blog-main/2020/12/24/吴恩达机器学习-5·正则化/
作者
xyfJASON
发布于
2020年12月24日
许可协议