使用CNN(convolutional neural nets)检测脸部关键点教程(五):通过前训练(pre-train)训练专项网络

第九部分 训练专项网络

还记得在刚开始的时候我们丢掉的70%的训练数据吗?如果我们想要得到一个在Kaggle排行榜上有竞争力的成绩,那不是一个好主意。在70%的数据中,还有相当多的特征我们没有看到。

所以改变之前只训练一个模型的方式,我们训练几个专项网络,每一个都可以预测不同的目标集合。我们训练一个模型预测left_eye_center和right_eye_center,另一个模型预测nose_tip……;最终,我们有6个模型,使得我们可以完全利用训练数据,希望能够得到更好的预测效果。

这6个专项网络全都使用同样的网络结构。因为训练时间变得超级久,所以我们可以想一些策略让我们可以不用等到max_epochs结束,甚至让验证错误早点停止改善。这种策略叫做early stopping,我们将会写另外一个on_epoch_finished回调函数来实现这个。下面是实现:

class EarlyStopping(object):
    def __init__(self, patience=100):
        self.patience = patience
        self.best_valid = np.inf
        self.best_valid_epoch = 0
        self.best_weights = None

    def __call__(self, nn, train_history):
        current_valid = train_history[-1]['valid_loss']
        current_epoch = train_history[-1]['epoch']
        if current_valid < self.best_valid:
            self.best_valid = current_valid
            self.best_valid_epoch = current_epoch
            self.best_weights = nn.get_all_params_values()
        elif self.best_valid_epoch + self.patience < current_epoch:
            print("Early stopping.")
            print("Best valid loss was {:.6f} at epoch {}.".format(
                self.best_valid, self.best_valid_epoch))
            nn.load_params_from(self.best_weights)
            raise StopIteration()

可以看到,在call函数里面有两个分支:第一个是现在的验证错误比我们之前看到的要好,第二个是最好的验证错误所在的迭代次数和当前迭代次数的距离已经超过了我们的耐心。在第一个分支里,我们存下网络的权重:

self.best_weights = nn.get_all_params_values()

第二个分支里,我们将网络的权重设置成最优的验证错误时存下的值,然后发出一个StopIteration,告诉NeuralNet我们想要停止训练。

          nn.load_params_from(self.best_weights)
          raise StopIteration()

让我们更新网络定义中的op_epoch_finished句柄,添加early stopping :

net8 = NeuralNet(
    # ...
    on_epoch_finished=[
        AdjustVariable('update_learning_rate', start=0.03, stop=0.0001),
        AdjustVariable('update_momentum', start=0.9, stop=0.999),
        EarlyStopping(patience=200),
        ],
    # ...
    )

到目前为止一切顺利,但是如何定义这些专项网络进行相应的预测呢?让我们做一个列表:

SPECIALIST_SETTINGS = [
    dict(
        columns=(
            'left_eye_center_x', 'left_eye_center_y',
            'right_eye_center_x', 'right_eye_center_y',
            ),
        flip_indices=((0, 2), (1, 3)),
        ),

    dict(
        columns=(
            'nose_tip_x', 'nose_tip_y',
            ),
        flip_indices=(),
        ),

    dict(
        columns=(
            'mouth_left_corner_x', 'mouth_left_corner_y',
            'mouth_right_corner_x', 'mouth_right_corner_y',
            'mouth_center_top_lip_x', 'mouth_center_top_lip_y',
            ),
        flip_indices=((0, 2), (1, 3)),
        ),

    dict(
        columns=(
            'mouth_center_bottom_lip_x',
            'mouth_center_bottom_lip_y',
            ),
        flip_indices=(),
        ),

    dict(
        columns=(
            'left_eye_inner_corner_x', 'left_eye_inner_corner_y',
            'right_eye_inner_corner_x', 'right_eye_inner_corner_y',
            'left_eye_outer_corner_x', 'left_eye_outer_corner_y',
            'right_eye_outer_corner_x', 'right_eye_outer_corner_y',
            ),
        flip_indices=((0, 2), (1, 3), (4, 6), (5, 7)),
        ),

    dict(
        columns=(
            'left_eyebrow_inner_end_x', 'left_eyebrow_inner_end_y',
            'right_eyebrow_inner_end_x', 'right_eyebrow_inner_end_y',
            'left_eyebrow_outer_end_x', 'left_eyebrow_outer_end_y',
            'right_eyebrow_outer_end_x', 'right_eyebrow_outer_end_y',
            ),
        flip_indices=((0, 2), (1, 3), (4, 6), (5, 7)),
        ),
    ]

我们很早前就讨论过在数据扩充中flip_indices的重要性。在数据介绍部分,我们的load_data()函数也接受一个可选参数,来抽取某些列。我们将在用专项网络预测结果的fit_specialists()中使用这些特性:

from collections import OrderedDict
from sklearn.base import clone

def fit_specialists():
    specialists = OrderedDict()

    for setting in SPECIALIST_SETTINGS:
        cols = setting['columns']
        X, y = load2d(cols=cols)

        model = clone(net)
        model.output_num_units = y.shape[1]
        model.batch_iterator_train.flip_indices = setting['flip_indices']
        # set number of epochs relative to number of training examples:
        model.max_epochs = int(1e7 / y.shape[0])
        if 'kwargs' in setting:
            # an option 'kwargs' in the settings list may be used to
            # set any other parameter of the net:
            vars(model).update(setting['kwargs'])

        print("Training model for columns {} for {} epochs".format(
            cols, model.max_epochs))
        model.fit(X, y)
        specialists[cols] = model

    with open('net-specialists.pickle', 'wb') as f:
        # we persist a dictionary with all models:
        pickle.dump(specialists, f, -1)

没有什么值得大惊小怪的事情,只不过是训练了一系列模型,并存进了字典。尽管有early stopping 但是在单块GPU上训练仍然要花上半天时间,而且我也不建议你运行这个。

在多块GPU上跑当然会快,但是还是太奢侈了。下一节介绍一种可以减少训练时间的方法,在这里,我们先看一下这些花费了大量资源的模型的结果。
这里写图片描述

6个模型的学习率,实线代表验证集合上的RMSE(均方根误差),虚线是训练集误差。Mean代表 所有模型乘以权重(模型所拥有的目标数量)的平均验证误差。所有的曲线都在x轴缩放到同样的尺度。

第十部分 有监督的前训练

教程的最后一部分,讨论一种新的方式让专项网络训练的更快。思路是:用net6或者是net7训练好的权重替代随即值来初始化网络权重。如果你还记得early stopping的实现的话,从一个网络复制权重到另一个网络是非常简单的,只要使用load_params_form()方法。下面我们改变fit_specialists方法来实现上述功能。仍然是加了#!的行是新添加的行:

def fit_specialists(fname_pretrain=None):
    if fname_pretrain:  # !
        with open(fname_pretrain, 'rb') as f:  # !
            net_pretrain = pickle.load(f)  # !
    else:  # !
        net_pretrain = None  # !

    specialists = OrderedDict()

    for setting in SPECIALIST_SETTINGS:
        cols = setting['columns']
        X, y = load2d(cols=cols)

        model = clone(net)
        model.output_num_units = y.shape[1]
        model.batch_iterator_train.flip_indices = setting['flip_indices']
        model.max_epochs = int(4e6 / y.shape[0])
        if 'kwargs' in setting:
            # an option 'kwargs' in the settings list may be used to
            # set any other parameter of the net:
            vars(model).update(setting['kwargs'])

        if net_pretrain is not None:  # !
            # if a pretrain model was given, use it to initialize the
            # weights of our new specialist model:
            model.load_params_from(net_pretrain)  # !

        print("Training model for columns {} for {} epochs".format(
            cols, model.max_epochs))
        model.fit(X, y)
        specialists[cols] = model

    with open('net-specialists.pickle', 'wb') as f:
        # this time we're persisting a dictionary with all models:
        pickle.dump(specialists, f, -1)

事实证明复用训练好的网络的权重代替随机初始化有两个实际上的好处:一个是训练收敛的更快,在这里大概有四倍快;第二个优点是网络的泛化能力更强,前训练起到了正则化项的效果。还是和刚刚一样的学习曲线图,展示了采用了前训练的网络:
这里写图片描述

最终,这个解决方案在排行榜上的成绩是2.13 RMSE。

十一部分 结论

现在也许你已经有了一打想法想去尝试,你可以找到教程最终方案的源代码,开始你的尝试。代码中还包括生成提交文件,运行python kfkd.py找出如何在命令行使用这个脚本。

还有一大堆很明显的你可以做的改进:尝试将每一个专项网络进行优化;观察6个网络,可以发现素有的模型都存在不同程度的过拟合。如果模型像绿色或者黄色的曲线那样几乎没有任何过拟合呢,你可以尝试减少dropout的数量;要是过拟合的厉害,就增加dropout的数量。

在SPECIALIST_SETTINGS的定义中,我们能够添加针对某个特定网络的设置。如果说我们想要给第二个网络添加更多的正则化项,我们可以像下面这样改变:

    dict(
        columns=(
            'nose_tip_x', 'nose_tip_y',
            ),
        flip_indices=(),
        kwargs=dict(dropout2_p=0.3, dropout3_p=0.4),  # !
        ),

还有各种各样的可以尝试改进的地方,也许你可以再加一个卷积层或者全连接层?期待你的好消息。

使用CNN(convolutional neural nets)检测脸部关键点教程(一):环境搭建和数据
使用CNN(convolutional neural nets)检测脸部关键点教程(二):浅层网络训练和测试
使用CNN(convolutional neural nets)检测脸部关键点教程(三):卷积神经网络训练和数据扩充
使用CNN(convolutional neural nets)检测脸部关键点教程(四):学习率,学习势,dropout

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: simam是一种简单的、无需参数的注意力模块,用于卷积神经网络。它可以帮助网络更好地捕捉输入数据中的重要信息,提高模型的性能。simam的设计简单,不需要额外的参数,可以方便地集成到现有的卷积神经网络中。 ### 回答2: SimAM(简单、无需参数的注意力模块)是一种卷积神经网络的注意力机制,旨在显著提高图像分类和物体检测任务的性能。这个模块可以自适应地学习到不同层的特征,并对其进行加权,以便更好地捕获相关信息并抑制噪声。 SimAM模块的核心思想是将一组特征向量传递到一个简单的多头自注意力层,以便为不同特征之间的联系评分,并生成那些较为重要的特征向量,这些向量随后用于后续的网络计算中。具体来说,SimAM模块将输入特征张量分为若干个通道,并将每个通道作为输入,送入多头自注意力层。该层是由若干个子层堆叠而成的,其中每个子层都包括一个多头自注意力机制和一些向反馈层。自注意力机制可以捕捉到输入特征张量内部各个通道之间的关系,并生成相应的权重矩阵。向反馈层可以帮助模型更好地适应各种难以预测的数据。 SimAM模块与传统的注意力机制相比,具有以下优点: 1.不需要额外的参数:SimAM模块不需要任何额外的参数,仅仅依靠了平移不变性和自注意力机制即可提取图像特征。 2.易于训练:模块中的每个子层都具有非常简单的结构,可以很容易地进行训练和调整参数。 3.高效:SimAM模块的计算成本低,且可以与传统的卷积神经网络结构相结合,以提高图像分类或物体检测的精度。 总之,SimAM模块是一种非常优秀的注意力机制,具有简单而有效的设计,可用于各种计算机视觉应用中。相信随着更多研究人员的关注,SimAM模块会在未来得到更多的应用和发展。 ### 回答3: Simam是一种简单的、无参数的卷积神经网络注意力模块,它能够提高卷积神经网路的性能。该模块的主要目的是为了实现对卷积层特征的自适应加权,从而优化卷积神经网络训练过程。 在卷积神经网络中,每个卷积层都会生成一系列特征图,这些特征图可以被看作是卷积层对输入图像的不同抽取方式。在经过多层的卷积操作后,这些特征图逐渐变得抽象和复杂,也变得更具有区分度。然而,由于不同的特征图的质量和作用是不同的,因此,需要一种方法来自适应地对它们进行加权,以便提高整个网络的性能。 Simam模块的核心思想是使用特定的权重来实现对特征图进行加权。这些权重的计算不需要任何参数,而是通过应用一些简单的非线性映射来实现的。具体来说,Simam模块会对特征图进行归一化,然后通过一些非线性函数(如ReLU)进行变换,最终得到一个类似于置信度的分数,从而决定特征图的相对重要性。 在使用Simam模块的卷积神经网络中,该模块被添加在卷积层之后,可以作为一种自适应加权机制来对特征图进行加权。这种机制能够更加准确地区分每个特征图的质量和作用,从而提高整个卷积神经网络的性能。 总之,Simam模块是一种简单而有效的自适应加权机制,可以提高卷积神经网络的性能。在实际应用中,它可以被广泛应用于图像分类、物体检测、语义分割等领域,从而提高计算机视觉的精度和效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值