姿态估计1-07:FSA-Net(头部姿态估算)-源码无死角讲解(2)-网络框架整体结构分析

以下链接是个人关于FSA-Net(头部姿态估算) 所有见解,如有错误欢迎大家指出,我会第一时间纠正。有兴趣的朋友可以加微信:a944284742相互讨论技术。若是帮助到了你什么,一定要记得点赞!因为这是对我最大的鼓励。
姿态估计1-00:FSA-Net(头部姿态估算)-目录-史上最新无死角讲解

前言

分析代码肯定要有一个好的思路,不能一头就钻进去。这是我第一次接触kreas深度框架,但是其对于我理解代码似乎并没有什么影响,毕竟万变不离其宗,上篇博客说了,无非就是处理数据,构建模型,加载数据,训练模型,保存模型这几个节奏。那么我就开始分析整体结构吧,这是这篇博客的工作。

思路引导

之前的博客提到,其training_and_testing/FSANET_train.py的核心,在于各个model_type部分,下面是我复制的部分代码:

......
model = FSA_net_Var_Capsule(image_size, num_classes, stage_num, lambda_d, S_set)()
model = FSA_net_noS_Capsule(image_size, num_classes, stage_num, lambda_d, S_set)()
model = FSA_net_NetVLAD(image_size, num_classes, stage_num, lambda_d, S_set)()
......

可以看到,对于每个model_type都构建了一个类,构建类之后,还添加了一个()符号,这里说明什么?说明其构建类之后马上调用了_call_函数。我们选中一个类,如FSA_net_noS_Capsule,一路追踪下去,可以追踪到lib/FSANET_model.py文件中的BaseFSANet类,为什么会追踪到这里,因为他是所有网络结构类的基类,所以要先查看他,类似于砌房子要先打地基一样的道理,但是呢,这个类内部实现的函数还真的挺多的,暂时不去理会,我们先找到其中的def call(self):函数,这是本节的重点。该函数注释如下:

源码注释:

如果看得不是很懂,后面还有带读,不过我觉得聪明的你,看了注释,理解起来应该都是小问题了。注意,下小节我们再深究细节,现在我们只看整体架构即可,就是叫你不要去深究函数的实现过程。

    def __call__(self):
        logging.debug("Creating model...")
        img_inputs = Input(self._input_shape)        


        # Build various models
        # 构建论文中的两个stream网络,输入[b,64,64,3]得到三个[b,8,8,64]
        ssr_G_model = self.ssr_G_model_build(img_inputs)        

        # 根据参数,确定是否构建论文中Scoring模型,输入三个[b,8,8,64],得到一个[b,3*7,64]
        if self.is_noS_model:
            # 三个[b,8,8,64]输入, 链接到一起成为[b,192,64]输出
            ssr_S_model = self.ssr_noS_model_build()
        else:
            # 如果构建Scoring模型,其有两种方式,分别为使用1x1的卷积,或者通过方差计算
            # 三个[b, 8, 8, 64]输入, num_primcaps参数为192,
            # 输出[b,21,64]
            ssr_S_model = self.ssr_S_model_build(num_primcaps=self.num_primcaps,m_dim=self.m_dim)           

        # 构建特征压缩模型,或者说胶囊网络,论文中的Feature aggregation操作
        # 可以看到,这个函数是没有实现的,也就是由子类去实现,为什么呢?
        # 前面的ssr_S_model模型,根据配置不一样,输出结果有两种可能,分别为[b,192,64]与[b,21,64]
        # 继承该类的子类在构建网络的的时候,会根据is_noS_model参数实现合适的该函数
        # 输出为3个[b,16]的特征向量
        ssr_aggregation_model = self.ssr_aggregation_model_build((self.num_primcaps,64))

        # 通过Feature aggregation得到三个特征向量,可以选择选择两种方式进行姿态估算
        # self.F_shape默认为16,刚好耦合上个模型的输出,即输入为3个[b,16]
        # 输出共九个结果,3个[b,3,3]  6个[b,3]
        if self.is_fc_model:
            # 直接使用对每个特征进行全连接层
            ssr_F_Cap_model = self.ssr_FC_model_build(self.F_shape,'ssr_F_Cap_model')
        else:
            # 对每个特征进行合适的分割之后,再进行全链接
            ssr_F_Cap_model = self.ssr_F_model_build(self.F_shape,'ssr_F_Cap_model')        



        # Wire them up,把模型链接起来,进行整体网络的搭建,即前向传播过程
        # img_inputs[b,64,64,3]  -->  三个[b,8,8,64]
        ssr_G_list = ssr_G_model(img_inputs)

        # 三个[b,8,8,64]  -->  ssr_primcaps[b, 3 * 7, 64]
        ssr_primcaps = ssr_S_model(ssr_G_list)

        # 根据子类的实现不一样,各自不同,但是输出结果为3个[b,16]
        ssr_Cap_list = ssr_aggregation_model(ssr_primcaps)

        # 3个[b,16]    -->   3个[b,3,3] + 6个[b,3]
        # ssr_F_Cap_list前三个形状为pred_s1,pred_s2,pred_s3[b,3,3], 为论文中的p
        # ssr_F_Cap_list中间三个为delta_s1,delta_s2,delta_s3形状为[b,3],为论文中的Δ
        # ssr_F_Cap_list后面三个为local_s1,local_s2,local_s3,为论文的的η
        ssr_F_Cap_list = ssr_F_Cap_model(ssr_Cap_list)


        # 根据输入3个[b,3,3] 以及 6个[b,3],进行姿态计算
        # 输出pred_pose[b,3], 默认的self.stage_num为[3,3,3], self.lambda_d=1
        pred_pose = SSRLayer(s1=self.stage_num[0], s2=self.stage_num[1], s3=self.stage_num[2], lambda_d=self.lambda_d, name="pred_pose")(ssr_F_Cap_list)        
        
        return Model(inputs=img_inputs, outputs=pred_pose)

源码带读

ssr_G_model
首先我们看到其上的代码:

        # 构建论文中的两个stream网络,输入[b,64,64,3]得到三个[b,8,8,64]
        ssr_G_model = self.ssr_G_model_build(img_inputs) 

这个模型的构建,是构建论文的如下部分:
在这里插入图片描述
在每个K处,都会获得一个KaTeX parse error: Undefined control sequence: \c at position 17: …\times h\times \̲c̲的特征图。也就是一共或的K个特征图,从论文可以知道,对于该网络结构的实现K=3。

ssr_S_model

        # 根据参数,确定是否构建论文中Scoring模型,输入三个[b,8,8,64],得到一个[b,3*7,64]
        if self.is_noS_model:
            # 三个[b,8,8,64]输入, 链接到一起成为[b,192,64]输出
            ssr_S_model = self.ssr_noS_model_build()
        else:
            # 如果构建Scoring模型,其有两种方式,分别为使用1x1的卷积,或者通过方差计算
            # 三个[b, 8, 8, 64]输入, num_primcaps参数为192,
            # 输出[b,21,64]
            ssr_S_model = self.ssr_S_model_build(num_primcaps=self.num_primcaps,m_dim=self.m_dim)    

可以看到,其根据self.is_noS_model参数,构建不同的ssr_S_model模型,也就是如下部分:
在这里插入图片描述
从论文中,我们可以知道Scoring function的实现有3中方式,第一种即为什么都做,即调用构建函数

ssr_S_model = self.ssr_noS_model_build()

如果使用1x1的的卷积或者方差方法,则调用:

ssr_S_model = self.ssr_S_model_build(num_primcaps=self.num_primcaps,m_dim=self.m_dim) 

也就是该函数中还会有分支存在,再次根据参数是选择1x1的卷积,或者方差的方法去实现Scoring function。
还有一点,就本人猜测,该函数内部已经实现了在这里插入图片描述
红框部分。

ssr_aggregation_model
代码如下:

ssr_aggregation_model = self.ssr_aggregation_model_build((self.num_primcaps,64))

对应论文如下部分:
在这里插入图片描述
要注意的是,该函数这里基类是没有实现,是需要子类去实现,因为输入形状大小和前面的网络有关,而前面网络和由传入的参数决定。

ssr_F_Cap_model

        # 通过Feature aggregation得到三个特征向量,可以选择选择两种方式进行姿态估算
        # self.F_shape默认为16,刚好耦合上个模型的输出,即输入为3个[b,16]
        # 输出共九个结果,3个[b,3,3]  6个[b,3]
        if self.is_fc_model:
            # 直接使用对每个特征进行全连接层
            ssr_F_Cap_model = self.ssr_FC_model_build(self.F_shape,'ssr_F_Cap_model')
        else:
            # 对每个特征进行合适的分割之后,再进行全链接
            ssr_F_Cap_model = self.ssr_F_model_build(self.F_shape,'ssr_F_Cap_model')     

这里主要是为了获得论文中的 { p , η , Δ } k = 1 K {\{\vec{p} ,\vec{η},\Delta \}}_{k=1}^K 参数的集合。

SSRLayer
通过上面的介绍,整体的模型已经构建完成,但是整体模型输出的结果为 { p , η , Δ } k = 1 K {\{\vec{p} ,\vec{η},\Delta \}}_{k=1}^K ,所以最后我们还有使用SSR算法进行处理,得到最后的姿态估计结果,即yaw, pitch, roll。
代码如下:

        # 根据输入3个[b,3,3] 以及 6个[b,3],进行姿态计算
        # 输出pred_pose[b,3], 默认的self.stage_num为[3,3,3], self.lambda_d=1
        pred_pose = SSRLayer(s1=self.stage_num[0], s2=self.stage_num[1], s3=self.stage_num[2], lambda_d=self.lambda_d, name="pred_pose")(ssr_F_Cap_list)        
        
        return Model(inputs=img_inputs, outputs=pred_pose)

结语

快把,是不是一下子就和论文对应起来了,从下小结开始,我们就要去深究代码每个细节的实现了,也就上上面构建模型的细节,我们还会见面的对吧,就在下篇博客!羞涩

发布了219 篇原创文章 · 获赞 687 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/weixin_43013761/article/details/103694464