机器学习笔记1-1:过拟合

*注:本博客参考李宏毅老师2020年机器学习课程. 视频链接


过拟合

过拟合现象是指模型在训练集上表现很好,然而在测试集上表现很差。

实例

在实际运用中的数据不可能是完全准确的,可能含有一部分数据是带有误差的,如果这部分数据也送入训练集用于模型训练,那么模型很可能会学习到这部分“脏数据”的特征,而忽略了整体的特征,这在高次模型的训练中尤为明显。


假设有如下训练数据:

  • 绝大部分数据是由y=2*x+5构成的,然而其中包含一些脏数据。
  • 测试集使用完全准确的数据。

这里使用重新编写的回归模型,改模型使用numpy重新实现。

import numpy as np
from matplotlib import pyplot as plt


class Model:
    def __init__(self, level=1):
        # 初始化w为一随机值,注意0次项b也包含在w中,且为w的第1项
        self.w = np.random.rand(1, level+1)*0.1
        self.name = "x^{}".format(level)

    def get_result(self):
        text = "y={:.2f}".format(self.w[0, 0])
        for i in range(1, self.w.shape[1]):
            if i > 1:
                text += "+{:.2f}x^{}".format(self.w[0, i], i)
            else:
                text += "+{:.2f}x".format(self.w[0, i])
        return text

    def show(self, show_loss_figure=False):
        """
        显示结果
        """
        text = self.get_result()
        print(text)
        self.result = text
        print("abs loss: {} ".format(self.log_loss[-1]))
        if show_loss_figure and self.log_loss and len(self.log_loss) > 0:
            plt.title(text)
            plt.plot(self.log_loss)
            plt.show()

    def forward(self, xs):
        """
        单步计算
        """
        xs = self.__get_cal_metrix__(xs)
        # y=w*x
        return self.w.dot(xs)[0]

    def __get_cal_metrix__(self, xs):
        # 复制行保持与w行数相同
        xx = np.tile(xs, (self.w.shape[1], 1))
        # 第一行设为1确保改行计算后为b的值
        xx[0, :] = 1
        # 从第二行开始每行自乘一次得到x的n次方项
        for i in range(1, xx.shape[0]):
            xx[i, :] *= xx[i-1, :]
        return xx

    def __train_one_step__(self, xs, y0s, lr):
        """
        单步训练
        """
        # y=w*x
        y1s = self.w.dot(xs)
        # 计算损失
        loss = y1s-y0s
        # 计算每个训练数值的偏导值
        d_w = loss * xs
        # 行求和得到所有数据的偏导值之和
        d_w = d_w.sum(axis=1)
        # 更新参数
        self.w -= d_w*lr
        return loss

    def train(self, xs, ys, lr=1e-8, epoch=50000):
        """
        开始训练的函数
        """
        self.log_loss = []
        xx = self.__get_cal_metrix__(xs)
        # 开始训练
        for i in range(epoch):
            loss = self.__train_one_step__(xx, ys, lr)
            self.log_loss.append(np.abs(loss).sum())


def func(xs):
    return xs**2+xs+5


train_x = np.array([x for x in range(-5, 10, 2)])
train_y = func(train_x) + np.random.random(train_x.shape)*3
train_y[::6] *= 1.5
test_x = np.array([x for x in range(-6, 10)])
test_y = func(test_x)
plt.scatter(train_x, train_y)
plt.show()

引入新的模型,分别使用1次式到4次式分别进行1万次迭代,各次式的loss的变化如下:

y=14.25+6.53x
abs loss: 172.3861526507747 
y=0.43+0.18x+1.38x^2
abs loss: 67.1417071629449 
y=0.17+0.12x+1.45x^2+-0.01x^3
abs loss: 60.90501297913625 
y=0.10+0.10x+0.29x^2+-0.01x^3+0.01x^4
abs loss: 148.78689560676793 
y=0.01+0.06x+0.08x^2+0.06x^3+0.06x^4+-0.01x^5
abs loss: 82.33042175934335 

在图中画出参考点和预测值

查看测试集上的情况如下,可见,在训练集上取得较好成果的函数,在测试集上不一定会得到较好的成绩。

loss of x^1:280.43754140821176
loss of x^2:99.40738620170984
loss of x^3:99.47073429444674
loss of x^4:160.73662966701812
loss of x^5:224.76763960757953

造成这种情况的原因就是过拟合现象,为了改善这种情况,可以使用正则化的方法,即在损失函数中添加一项正则化项,使得拟合的函数更加平滑。改进后的模型如下:

class Model_with_L2(Model):
    def __train_one_step__(self, xs, y0s, lr):
        """
        单步训练
        """
        # y=w*x
        y1s = self.w.dot(xs)
        # 计算损失
        loss = y1s-y0s
        # 计算每个训练数值的偏导值
        d_w = loss * xs
        # 行求和得到所有数据的偏导值之和
        d_w = d_w.sum(axis=1)
        # L2正则化
        if self.L2 > 0:
            d_w += self.w.sum()*self.L2
            self.w -= d_w*lr
        # 更新参数
        else:
            self.w -= d_w*lr
        return loss

    def set_L2(self, L2):
        self.L2 = L2

正则化参数设置为不同值,通过实验的方法计算最佳的正则化参数,训练过程如下:

models = []
# 使用一个n次函数拟合该数据
lr = 1e-8
l2 = [10, 100, 200, 300, 500, 1000]
for j in range(2, 5):
    for i in range(len(l2)):
        m = Model_with_L2(j)
        m.name = "{}-L2:{:4}".format(m.name, l2[i])
        m.set_L2(l2[i])
        m.train(train_x, train_y, lr=lr, epoch=50000)
        models.append(m)

画出最优的5种模型在测试集上的图像,可见拟合效果比不使用正则化的情况要好得多。

loss of x^3-L2:  10:80.1137||y=y=0.06+0.10x+1.05x^2+0.03x^3 
loss of x^2-L2:1000:85.9671||y=y=-0.33+-0.19x+1.33x^2 
loss of x^3-L2: 100:86.5947||y=y=0.04+-0.00x+1.01x^2+0.04x^3 
loss of x^2-L2: 500:88.3003||y=y=-0.16+-0.04x+1.34x^2 
loss of x^3-L2: 200:88.3710||y=y=-0.01+-0.05x+1.00x^2+0.04x^3 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值