美文网首页
【论文复现】无关键点的头部姿态估计《Fine-Grained H

【论文复现】无关键点的头部姿态估计《Fine-Grained H

作者: AIPlayer | 来源:发表于2019-08-17 18:16 被阅读0次

    未经同意,不得转载

    本文以复现论文《Fine-Grained Head Pose Estimation Without Keypoints》为例说明如何使用Tensorflow & Keras自定义 multi-loss 函数。

    论文地址:
    https://arxiv.org/abs/1710.00925
    复现代码:
    https://github.com/Oreobird/tf-keras-deep-head-pose

    一、原理简述

    论文提出了一种无需人脸关键点的人脸头部姿态估计算法,通过训练一个multi-loss的卷积神经网络,该网络结合了分类和回归两种目标函数来预测3个人脸头部姿态角度(yaw,pitch 和 roll),网络结构比较简单,以ResNet50作为主干网络提取特征,然后分别对3个角度进行分类和回归,属于多输出模型,结构如下图所示:

    二、计算细节

    (1)角度的分类定义

    角度属于连续值,怎么转换为分类问题呢?作者使用了分箱的思想,将连续的角度值在[-99, 99]的范围内以 3 为间隔划分为66个区间,也就是66个类,如下图的例子,类别标签从0开始,-94度落在了第 1 类的范围内,所以分为第1类。

    (2)回归损失的计算

    分类得出的softmax是分类的概率结果,作者先对类别求了个期望值,即用softmax的值乘以各对应的类别标签再相加,再乘以3 - 99来恢复成连续的角度值,最后才和实际的角度计算MSE损失。这个过程从作者的代码可以看出:

    idx_tensor = [idx for idx in xrange(66)]
    yaw_predicted = torch.sum(yaw_predicted * idx_tensor, 1)
    

    (3) loss最终式

    将MSE-loss前面乘以一个权重系数α,再与分类loss加权得到最终的multi-loss。

    三、Tensorflow & Keras 复现

    复现比较简单,关键就是multi-loss函数的计算过程,理清楚作者的思路后基于Keras可以很容易写出整个过程。

    (1)自定义mult-loss函数

    def __loss_angle(self, y_true, y_pred, alpha=0.5):       
        bin_true = y_true[:,0] #离散值
        cont_true = y_true[:,1] #连续值
    
        # cross entropy loss
        cls_loss = tf.losses.softmax_cross_entropy(onehot_labels=tf.keras.utils.to_categorical(bin_true, 66), logits=y_pred)
    
        # MSE loss
        pred_cont = tf.reduce_sum(tf.nn.softmax(y_pred) * self.idx_tensor, 1) * 3 - 99
        mse_loss = tf.losses.mean_squared_error(labels=cont_true, predictions=pred_cont)
    
        # Total loss
        total_loss = cls_loss + alpha * mse_loss
        return total_loss
    

    其中的y_true传进来的是[bin_y, cont_y],即离散的角度值和连续的角度值,具体可以看datasets.py的data_generator()函数。

    (2)模型构建

    有3个角度需要计算loss,所以是多输出模型:

    def __create_model(self):
        inputs = tf.keras.layers.Input(shape=(self.input_size, self.input_size, 3))
            
        feature = tf.keras.layers.Conv2D(filters=64, kernel_size=(11, 11), strides=4, padding='same', activation=tf.nn.relu)(inputs)
        feature = tf.keras.layers.MaxPool2D(pool_size=(3, 3), strides=2)(feature)
        feature = tf.keras.layers.Conv2D(filters=192, kernel_size=(5, 5), padding='same', activation=tf.nn.relu)(feature)
        feature = tf.keras.layers.MaxPool2D(pool_size=(3, 3), strides=2)(feature)
        feature = tf.keras.layers.Conv2D(filters=384, kernel_size=(3, 3), padding='same', activation=tf.nn.relu)(feature)
        feature = tf.keras.layers.Conv2D(filters=256, kernel_size=(3, 3), padding='same', activation=tf.nn.relu)(feature)
        feature = tf.keras.layers.Conv2D(filters=256, kernel_size=(3, 3), padding='same', activation=tf.nn.relu)(feature)
        feature = tf.keras.layers.MaxPool2D(pool_size=(3, 3), strides=2)(feature)
        feature = tf.keras.layers.Flatten()(feature)
        feature = tf.keras.layers.Dropout(0.5)(feature)
        feature = tf.keras.layers.Dense(units=4096, activation=tf.nn.relu)(feature)
            
        fc_yaw = tf.keras.layers.Dense(name='yaw', units=self.class_num)(feature)
        fc_pitch = tf.keras.layers.Dense(name='pitch', units=self.class_num)(feature)
        fc_roll = tf.keras.layers.Dense(name='roll', units=self.class_num)(feature)
        
        model = tf.keras.Model(inputs=inputs, outputs=[fc_yaw, fc_pitch, fc_roll])
            
        losses = {'yaw':self.__loss_angle,
                  'pitch':self.__loss_angle,
                  'roll':self.__loss_angle}
            
        model.compile(optimizer=tf.train.AdamOptimizer(), loss=losses)
           
        return model
    

    四、总结

    (1)论文的思路巧妙在于把回归的问题转化为分类+回归的问题,利用分类的结果来引导回归。

    (2)Keras实现这种multi-loss的任务,除了自定义损失函数的方式外,还可以自定义层,比如使用简单的Lambda层来实现,此为后话。

    相关文章

      网友评论

          本文标题:【论文复现】无关键点的头部姿态估计《Fine-Grained H

          本文链接:https://www.haomeiwen.com/subject/lnuksctx.html