机器学习-正则化

写在前面

本博客是作者在学习机器学习基础时写下的总结,学习的资源为网易云课堂上吴恩达的机器学习课程,参考资料主要为stanford cs229课程的英文讲义,有兴趣的读者可以去网上下载原版的英文讲义来看。此外,由于本人为初学者,对知识点理解有限,因此文中有任何错误非常欢迎大家指出。最后,本文涉及的所有代码的完整版均会上传到github,欢迎大家交流。

1. 欠拟合与过拟合

在线性回归中,我们经常会用多项式来拟合一些非线性的数据,如下图:

当使用一次函数,也就是直线进行拟合时,假设函数为:
h θ ( x ) = θ 0 + θ 1 x h_\theta(x) = \theta_0+\theta_1x hθ(x)=θ0+θ1x
得到的结果如下图所示:

由于数据有趋于平缓的趋势,而拟合直线斜率始终保持不变,因此这种情况称为欠拟合

当使用二次多项式进行拟合时,即假设函数为:
h θ ( x ) = θ 0 + θ 1 x 1 + θ 2 x 2 h_\theta(x) = \theta_0+\theta_1x_1+\theta_2x^2 hθ(x)=θ0+θ1x1+θ2x2
得到的结果如下图所示:

从图中可以看出,利用二次多项式进行拟合的效果比直线进行拟合的效果好得多。

下面用六次多项式进行拟合,假设函数为:
h θ ( x ) = θ 0 + θ 1 x 1 + θ 2 x 2 + θ 3 x 3 + θ 4 x 4 + + θ 5 x 5 + θ 6 x 6 h_\theta(x) = \theta_0+\theta_1x_1+\theta_2x^2+\theta_3x^3+\theta_4x^4++\theta_5x^5+\theta_6x^6 hθ(x)=θ0+θ1x1+θ2x2+θ3x3+θ4x4++θ5x5+θ6x6
得到的结果如下:

可以看到,虽然拟合曲线很好的拟合了所有的数据点,但该模型的泛化能力很差,也就是说该模型对除了训练数据以外的数据不具有很好的性能。我们称这种情况为过拟合

过拟合和欠拟合是机器学习中一个很常见的现象,下面将介绍如何使用正则化来解决过拟合和欠拟合的问题。

2. 正则化

对于高次多项式拟合(以四次多项式为例),有假设函数为:
h θ ( x ) = θ 0 + θ 1 x 1 + θ 2 x 2 + θ 3 x 3 + θ 4 x 4 h_\theta(x) = \theta_0+\theta_1x_1+\theta_2x^2+\theta_3x^3+\theta_4x^4 hθ(x)=θ0+θ1x1+θ2x2+θ3x3+θ4x4
我们的优化目标是:
min ⁡ θ 1 2 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) 2 \min_\theta \frac{1}{2m}\sum_{i=1}^m(h_\theta(x^{(i)})-y^{(i)})^2 θmin2m1i=1m(hθ(x(i))y(i))2
为了防止过拟合,我们对高次项系数 θ 3 、 θ 4 \theta_3、\theta_4 θ3θ4进行惩罚,具体操作是在上述成本函数中加上 θ 3 、 θ 4 \theta_3、\theta_4 θ3θ4项:
min ⁡ θ 1 2 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) 2 + 1000 θ 3 + 1000 θ 4 \min_\theta \frac{1}{2m}\sum_{i=1}^m(h_\theta(x^{(i)})-y^{(i)})^2+1000\theta_3+1000\theta_4 θmin2m1i=1m(hθ(x(i))y(i))2+1000θ3+1000θ4
这里的1000是为了说明惩罚项的作用随意给的,这样使得模型在减少成本函数的时候必须使 θ 3 \theta_3 θ3 θ 4 \theta_4 θ4很小才行,因此 θ 3 \theta_3 θ3 θ 4 \theta_4 θ4的作用会因此大大衰减,从而减小了过拟合的可能性。

下面给出加上正则化之后的成本函数:
J ( θ ) = 1 2 m [ ∑ i = 1 m ( h θ ( x ( i ) − y ( i ) ) 2 + λ ∑ j = 1 n θ j 2 ] J(\theta) = \frac{1}{2m} \left[ \sum_{i=1}^m(h_\theta(x^{(i)}-y^{(i)})^2+\lambda\sum_{j=1}^n\theta_j^2 \right] J(θ)=2m1[i=1m(hθ(x(i)y(i))2+λj=1nθj2]
其中, λ ∑ j = 1 n θ j 2 \displaystyle \lambda\sum_{j=1}^n\theta_j^2 λj=1nθj2为正则化项。 λ \lambda λ的大小决定了正则化的效果,当 λ = 0 \lambda=0 λ=0时,相当于正则项不起作用,因此结果还是会发生过拟合;当 λ \lambda λ大小合适时,我们会得到比较好的拟合效果;而当 λ \lambda λ太大时,会导致拟合效果变差,甚至导致欠拟合,因此选取合适的 λ \lambda λ才能使得正则化能有较好的效果。

此外,通常情况下我们不会对常数项进行正则化,因此上述正则项的求和是从1到n而不是0到n。

加上正则项之后成本函数的偏导数为:
∂ ∂ θ 0 J ( θ ) = 1 m ∑ i = 1 m ( h θ ( x ( i ) − y ( i ) ) x 0 ( i ) ∂ ∂ θ j J ( θ ) = ( 1 m ∑ i = 1 m ( h θ ( x ( i ) − y ( i ) ) x j ( i ) ) + λ m θ j f o r j > 0 \frac{\partial}{\partial\theta_0} J(\theta)= \frac{1}{m}\sum_{i=1}^m(h_\theta(x^{(i)}-y^{(i)})x_0^{(i)} \\\frac{\partial}{\partial\theta_j} J(\theta)= \left( \frac{1}{m}\sum_{i=1}^m(h_\theta(x^{(i)}-y^{(i)})x_j^{(i)}\right)+\frac{\lambda}{m}\theta_j\qquad for\quad j>0 θ0J(θ)=m1i=1m(hθ(x(i)y(i))x0(i)θjJ(θ)=(m1i=1m(hθ(x(i)y(i))xj(i))+mλθjforj>0

下图蓝色线展示了加入正则项之后的拟合效果:

可以看到,正则化减轻了过拟合的程度。

对于正则项 λ m θ j \frac{\lambda}{m}\theta_j mλθj λ \lambda λ的大小决定了正则化的效果,当 λ \lambda λ过小时,正则化不起作用,当 λ \lambda λ过大时,可能会导致欠拟合,如下图:

其中蓝色线为 λ \lambda λ过大时出现的欠拟合情况,进一步增加 λ \lambda λ的值,有

由于常数项 θ \theta θ没有进行正则化,因此当 λ \lambda λ过大时,所有的高次项都不起作用,引起最终会趋于一条平行于x轴的直线。

注:上述线性回归的代码在github,这里不再列出,感兴趣的朋友可以看一看。

3. 应用到逻辑回归中

和线性回归类似,逻辑回归同样存在过拟合的现象。实际上,过拟合现象在机器学习中广泛存在。
考虑下面一个简单的二元分类问题:

有假设函数
h θ ( x ) = g ( θ T x ) h_\theta(x) = g(\theta^Tx) hθ(x)=g(θTx)
其中, g ( z ) = 1 1 + e − z g(z) =\displaystyle\frac{1}{1+e^{-z}} g(z)=1+ez1为Sigmoid函数。

当利用线性函数进行回归时,有
x = ( 1 , x 0 , x 1 ) x = (1, x_0, x_1) x=(1,x0,x1)
当利用高次曲线进行回归时, 有
x = ( 1 , x 0 , x 1 , x 0 2 , x 0 x 1 , x 1 2 . . . ) x = (1, x_0, x_1, x_0^2, x_0x_1, x_1^2...) x=(1,x0,x1,x02,x0x1,x12...)

利用线性函数进行分类的结果如下图:

显然,线性函数达不到我们想要的结果。下面利用二次曲线进行拟合:

当阶次进一步提高时,拟合的曲线对训练集中的数据拟合度越来越高,但随着阶次的提高,将会出现过拟合现象如下:

该图是拟合曲线阶次n=6时的拟合结果,可以看到,这个时候过拟合现象已经非常严重了。对于训练集以外的数据,该模型不具有良好的性能。

下面在逻辑回归的成本函数和偏导数中加入正则项:

成本函数:
J ( θ ) = 1 m ∑ i = 1 m [ − y ( i ) log ⁡ ( h θ ( x ( i ) ) ) − ( 1 − y ( i ) ) log ⁡ ( 1 − h θ ( x ( i ) ) ) ] + λ 2 m ∑ j = 1 n θ j 2 J(\theta) = \frac{1}{m}\sum_{i=1}^m \left[ -y^{(i)}\log(h_\theta(x^{(i)}))-(1-y^{(i)})\log (1-h_\theta(x^{(i)})) \right]+\frac{\lambda}{2m}\sum_{j=1}^n\theta_j^2 J(θ)=m1i=1m[y(i)log(hθ(x(i)))(1y(i))log(1hθ(x(i)))]+2mλj=1nθj2
偏导数:
∂ J ( θ ) ∂ θ 0 = 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x 0 ( i ) \frac{\partial{J(\theta)}}{\partial{\theta_0}} = \frac{1}{m}\sum_{i=1}^m(h_\theta(x^{(i)})-y^{(i)})x_0^{(i)} θ0J(θ)=m1i=1m(hθ(x(i))y(i))x0(i)
∂ J ( θ ) ∂ θ j = [ 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x j ( i ) ] + λ m θ j f o r j > 0 \frac{\partial{J(\theta)}}{\partial{\theta_j}} = \left[\frac{1}{m}\sum_{i=1}^m(h_\theta(x^{(i)})-y^{(i)})x_j^{(i)}\right] +\frac{\lambda}{m}\theta_j\qquad for\quad j > 0 θjJ(θ)=[m1i=1m(hθ(x(i))y(i))xj(i)]+mλθjforj>0

加入正则项后拟合的结果如下:

同样的,当正则化的系数 λ \lambda λ过大时,会导致欠拟合:

从上图中可以看出,由于发生了欠拟合,因此拟合曲线不能很好的将两组数据分离开来。

下面是逻辑回归部分的完整代码,数据集和线性回归的代码在github:

import numpy as np
import matplotlib.pyplot as plt


class logisRegression(object):

    def __init__(self, data, alpha, lam, degree, iter):
        self.data = data
        self.alpha = alpha  # learning rate
        self.lam = lam  # regularizatoin rate
        self.degree = degree    # degree of fitting curve
        self.iter = iter    # iterations

    def mapFeature(self, data):
        self.trainX = np.ones((data.shape[0], 1))
        for i in range(self.degree):  # 0..5 + 1
            for j in range(i+2):  # 0 0..1 ... 0..5
                temp = np.power(data[:, 0], i+1-j)*np.power(data[:, 1], j)
                self.trainX = np.c_[self.trainX, temp]
        self.trainY = data[:, 2].reshape(data.shape[0], 1)
        self.theta = np.ones((self.trainX.shape[1], 1))
        return self.trainX, self.trainY        

    def sigmoid(self, x):
        z = 1/(1+np.exp(-x))
        return z

    def update(self):
        # Partial derivative of the cost function
        self.cost = (1/self.data.shape[0])*(self.sigmoid(self.trainX.dot(self.theta))\
                    -self.trainY).T.dot(self.trainX).T
        # Regularization
        self.cost[1:] = self.cost[1:] + (self.lam/self.data.shape[0])*self.theta[1:] 
        # Update theta
        self.theta = self.theta - self.alpha * self.cost


    def regression(self):
        self.mapFeature(self.data)
        for i in range(self.iter):
            self.update()
        return self.theta


def plotFig(data, X, Y, Z):
    a = data[np.where(data[:, 2] == 1)]
    b = data[np.where(data[:, 2] == 0)]
    plt.subplots(1, 1, figsize=(8, 5))
    plt.plot(a[:, 0], a[:, 1], 'x')  # group A
    plt.plot(b[:, 0], b[:, 1], 'x')  # group B
    plt.contour(X, Y, Z, [0.5], Width=0.5, alpha=0.4)   # decision boundary
    plt.xlabel(r'$x_1$', size=16)
    plt.ylabel(r'$x_2$', size=16)
    plt.tick_params(labelsize=12)
    plt.title("Logistic regression", size=18) 
    plt.show()


def generateTestData():
    a = np.linspace(-1, 1.2, 100)
    b = np.linspace(-1, 1.2, 100)
    X = np.meshgrid(a, b)
    temp = np.ones((X[0].size, 1))
    testData = np.c_[X[0].reshape(X[0].size, 1), X[1].reshape(X[0].size, 1), temp]
    return testData, X


if __name__ == "__main__":

    # data
    trainData = np.loadtxt(open("ex2data2.txt", "rb"), delimiter=",")

    # train
    f = logisRegression(trainData, 0.5, 0.2, 8, 300000)  # data, alpha, lam, degree, iter
    theta = f.regression()

    # test
    [testData, X] = generateTestData()
    [testX, testY] = f.mapFeature(testData)
    testZ = f.sigmoid(testX.dot(theta)).reshape(X[0].shape)

    # plot
    plotFig(trainData, X[0], X[1], testZ)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值