Loading...
首页专栏正文

【深度学习】深入浅出CRF as RNN(以RNN形式做CRF后处理)

 
1人已赏
Fizz 发布于 2021-06-09 17:29:12 浏览 2536 点赞 93 收藏 5

【深度学习】深入浅出CRF as RNN(以RNN形式做CRF后处理)

文章目录
1 概述
2 目标
3 思路
4 简述
5 论文原文
    5.1 Introduction
    5.2 相关工作
    5.3 关键步骤
6 仓库代码

1 概述

条件随机场(CRF或CRFs)与隐马尔科夫模型有着千丝万缕的联系。为了理解CRF,这里先简单说一下马尔科夫链(MC, Markov Chain)和隐马尔科夫模型。

1.1 马尔科夫链

马尔科夫链是指具有马尔可夫性质且存在于离散指数集合状态空间内的随机过程。那么什么是马尔科夫性质呢?

从定义上来说,当一个随机过程在给定现在状态及过去所有状态的情况下,其未来状态的条件概率分布仅依赖于当前状态;换句话说,在给定现在状态时,其过去状态(即该过程的历史路径)是条件独立的。

这个表述比较抽象,我们举个马尔科夫链的例子理解一下:

比如有一只蚂蚁在下图所示的网格内爬行(网格区域无限大),由于墙壁的存在,它只能向前、后、左、右四个防线之一前进,每次前进一步。假设蚂蚁从位置S开始,那么在给定前n步的选择后,当前蚂蚁的所在位置就是知道的(假设在红色点处);那么下一步依然是四个方向之一,即下一步的状态仅依赖与当前状态,且选择随机。同时,之前走过的每一步之间是条件独立的,即上一步走的方向不会影响这一步的方向。

由于存在的选择只有四个,即选择离散,所以我们称这个过程为马尔科夫链。当选择连续时,称为马尔科夫过程(Markov Process)。

在这里插入图片描述 1.2 隐式马尔科夫模型

隐式马尔科夫模型(HMM,Hidden Markov Model)是关于时序的概率模型,描述由一个隐藏的马尔科夫链随机生成的不可观测的状态随机序列,再由各个状态生成一个观测而产生观测序列的过程。隐藏的部分称为状态序列;生成的观测组成的随机序列称为观测序列。

同样,我们举个例子来理解一下隐式马尔科夫模型:

比如我们现在需要进行天气预测,已知天气的状态有两种:天气好(晴天)和天气不好(雨雪雾、阴天等),分别用1和2表示。

很明显,要预测明天天气的话,今天及以前的天气是已知的。因此,我们可以通过历史天气计算出下图中各个状态之间的传递概率。比如,如果今天是晴天,那么明天是晴天的概率为0.6,则状态1到1为0.6(图中的1-α)、状态1到2的概率则为0.4;同理,假设今天天气不好那么明天天气也不好的概率为0.9,则状态2到2概率为0.9(图中的1-β),那么状态2到1的概率为0.1。 在这里插入图片描述 什么是CRF

CRF是一种判别式概率模型,是随机场的一种,结合了最大熵模型和隐式马尔科夫模型的特点;CRF是一种无向图模型,图中的顶点代表随机变量,顶点间的连线代表随机变量间的相依关系。其条件概率分布模型可以表述为P(Y|X),即给定一组随机变量的条件下,随机变量Y的马尔科夫随机场(MRF,Markov Random Field)。

2 目标

本文的目标是 Pixel-level labelling 。

一张N个像素的图片,像素标号

{1,2,⋯,N}{1,2,⋯,N}

标签集合 L={l1,l2,⋯,lL}L={l1,l2,⋯,lL}

我们的目标是,给每个像素分配一个其所属的标签。

3 思路

在2012年之前,CRF是这个领域最先进的技术。 2012年之后,深度学习在图像识别中取得了很大成功。

想办法把二者的优势结合起来。

4 简述

本文所述方法有三个要点:

  1. 用训练的方式求解CRF。
  2. 把CRF挂在一个FCN后边,一起训练。
  3. 求解CRF时,用独立分布近似真实分布,其算法可以包装成一个RNN。

在这里插入图片描述

5 论文原文

5.1 Introduction

我们的目标,是实现像素级别的语义分割,即给每个像素标注一个标签。对于这个任务,Features的设计,对于标准的准确程度有至关重要的作用。引用论文中的一句话:Designing a strong feature representation is key for the problem.

而深度学习所解决的重大问题之一,就是自动选取高质量的Features。因而,利用CNN替代手工选取特征,来增进语义分割的效果,很自然地成为了本文思路的起点。

实现目标,Feature很重要,选取好的Features也很重要。 Designing a strong feature representation is key for the problem.

这个路线是如此的明朗,预期的收益是如此的诱人,所以早已有研究者先行一步了。FCN和DeepLab是其中优秀的代表。

FCN开辟了卷积网络用于像素级语义分割的新思路,为CNN与CRF结合提供了必要的技术基础。但其输出的分割结果比较边界粗糙。

DeepLab使用CRF来改进FCN的结果。但是CRF只作为一个后处理工具,不参与训练,所以不能发挥出CRF的全部威力。

本文在上述结果的基础上,设计了一个End-to-End的网络,将FCN和CRF融合进了一个网络中,同时训练。从而解决了上述短板。

5.2 相关工作

本文的几个关键点,都涉及一些前面的工作。

使用CNN获得Classification Map,主要来自于FCN论文以及更早的 D.Eigen, C.Puhrsch, and R.Fergus. Depth map prediction from a single image using a multi-scale deep network. In NIPS, 2014. 用训练的方式求解CRF。相关文章很多,本文作者主要参考了Philipp Krähenbühl的两篇文章,相关文章可从论文引文列表获得,这里不再赘述。

5.3 关键步骤

网络结构

网络结构是前边一个FCN,后边挂上一个CRF-RNN,一起训练。第三大部分中的图展示了这个结构。

难题

对于每次迭代来说,FCN出来的是分类的Map,之后要求解CRF。CRF求解由于计算量太大,只能用估计的方式。所以,关键问题是:第一,如何估计?第二,如何在一个迭代里能够自动地求出CRF的结果?

作者利用Krähenbühl文章的算法,求解CRF。他巧妙地将求解步骤,设计成了一个RNN,从而在一个迭代中,能够求出CRF的解。

具体方法

将Krähenbühl文章的算法,拆解成几个步骤,每个步骤都作为一个CNN的Layer。

在这里插入图片描述 将上述堆叠的Layers作为一个大模块,挂载到一个RNN网络结构中。RNN的网络结构如图所示 在这里插入图片描述 求解CRF的这个RNN网络,又作为整体网络的一部分,挂在FCN网络之后。

6 仓库代码

在这里插入图片描述

class CrfRnnLayer(Layer):
    """ Implements the CRF-RNN layer described in:
    Conditional Random Fields as Recurrent Neural Networks,
    S. Zheng, S. Jayasumana, B. Romera-Paredes, V. Vineet, Z. Su, D. Du, C. Huang and P. Torr,
    ICCV 2015
    """

    def __init__(self, image_dims, num_classes,
                 theta_alpha, theta_beta, theta_gamma,
                 num_iterations, **kwargs):
        self.image_dims = image_dims
        self.num_classes = num_classes
        self.theta_alpha = theta_alpha
        self.theta_beta = theta_beta
        self.theta_gamma = theta_gamma
        self.num_iterations = num_iterations
        self.spatial_ker_weights = None
        self.bilateral_ker_weights = None
        self.compatibility_matrix = None
        super(CrfRnnLayer, self).__init__(**kwargs)

    def build(self, input_shape):
        # Weights of the spatial kernel
        self.spatial_ker_weights = self.add_weight(name='spatial_ker_weights',
                                                   shape=(self.num_classes, self.num_classes),
                                                   initializer=_diagonal_initializer,
                                                   trainable=True)

        # Weights of the bilateral kernel
        self.bilateral_ker_weights = self.add_weight(name='bilateral_ker_weights',
                                                     shape=(self.num_classes, self.num_classes),
                                                     initializer=_diagonal_initializer,
                                                     trainable=True)

        # Compatibility matrix
        self.compatibility_matrix = self.add_weight(name='compatibility_matrix',
                                                    shape=(self.num_classes, self.num_classes),
                                                    initializer=_potts_model_initializer,
                                                    trainable=True)

        super(CrfRnnLayer, self).build(input_shape)

    def call(self, inputs):
        unaries = tf.transpose(inputs[0][0, :, :, :], perm=(2, 0, 1))
        rgb = tf.transpose(inputs[1][0, :, :, :], perm=(2, 0, 1))

        c, h, w = self.num_classes, self.image_dims[0], self.image_dims[1]
        all_ones = np.ones((c, h, w), dtype=np.float32)

        # Prepare filter normalization coefficients
        spatial_norm_vals = custom_module.high_dim_filter(all_ones, rgb, bilateral=False,
                                                          theta_gamma=self.theta_gamma)
        bilateral_norm_vals = custom_module.high_dim_filter(all_ones, rgb, bilateral=True,
                                                            theta_alpha=self.theta_alpha,
                                                            theta_beta=self.theta_beta)
        q_values = unaries

        for i in range(self.num_iterations):
            softmax_out = tf.nn.softmax(q_values, 0)

            # Spatial filtering
            spatial_out = custom_module.high_dim_filter(softmax_out, rgb, bilateral=False,
                                                        theta_gamma=self.theta_gamma)
            spatial_out = spatial_out / spatial_norm_vals

            # Bilateral filtering
            bilateral_out = custom_module.high_dim_filter(softmax_out, rgb, bilateral=True,
                                                          theta_alpha=self.theta_alpha,
                                                          theta_beta=self.theta_beta)
            bilateral_out = bilateral_out / bilateral_norm_vals

            # Weighting filter outputs
            message_passing = (tf.matmul(self.spatial_ker_weights,
                                         tf.reshape(spatial_out, (c, -1))) +
                               tf.matmul(self.bilateral_ker_weights,
                                         tf.reshape(bilateral_out, (c, -1))))

            # Compatibility transform
            pairwise = tf.matmul(self.compatibility_matrix, message_passing)

            # Adding unary potentials
            pairwise = tf.reshape(pairwise, (c, h, w))
            q_values = unaries - pairwise

        return tf.transpose(tf.reshape(q_values, (1, c, h, w)), perm=(0, 2, 3, 1))

    def compute_output_shape(self, input_shape):
        return input_shape

安装指南 第 1 步:克隆存储库

$ git clone https://github.com/sadeepj/crfasrnn_keras.git

克隆的根目录将在crfasrnn_keras下文中被称为。

第二步:安装依赖 注意:如果您使用的是 Python virtualenv,请确保在运行本指南中的每个命令之前将其激活。

使用此存储库中的requirements.txt文件(或者requirements_gpu.txt,如果您有 GPU 设备)通过以下方式安装所有依赖项pip

$ cd crfasrnn_keras
$ pip install -r requirements.txt  # If you have a GPU device, use requirements_gpu.txt instead

正如你可以从内容请注意requirements.txt,我们只有依靠tensorflow,keras和h5py。此外,Pillow运行演示需要。安装依赖项后,运行以下命令以确保它们已正确安装:

$ python
import tensorflow
mport keras

第 3 步:构建 CRF-RNN 自定义操作 C++ 代码

$ cd crfasrnn_keras/src/cpp
$ make

第 4 步:下载预训练的模型权重 第 5 步:运行

$ cd crfasrnn_keras
$ python run_demo.py
前端架构师,精通前端技术栈,混迹云原生开源社区。

*本文仅代表作者观点,不代表易百纳技术社区立场。系作者授权易百纳技术社区发表,未经许可不得转载。

精彩评论

内容存在敏感词
打赏
打赏作者
Fizz
您的支持将鼓励我继续创作!
金额:
¥1 ¥5 ¥10 ¥50 ¥100
支付方式:
微信支付
支付宝支付
微信支付
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

易百纳技术社区
确定要删除此文章、专栏、评论吗?
确定
取消
易百纳技术社区