tokenpocket官网app下载最新版本安卓版|crf
全网最详细的CRF算法讲解 - 知乎
全网最详细的CRF算法讲解 - 知乎切换模式写文章登录/注册全网最详细的CRF算法讲解水孕冰岁月呵! (笔者在写的过程中,难免会出现纰漏,而且可能有些地方笔者理解的也有问题,所以如果读者觉得文章哪里有错误,可以在下面评论,笔者看到后会及时改正。) 这篇文章主要是关于CRF算法的理论推导,如果大家觉得这篇文章还可以的话,笔者之后会尝试再开两篇文章将CRF的代码和理论推导结合起来,带领大家重新认识CRF算法,并且,会将CRF算法和HMM算法进行比较,从概率图模型的角度介绍CRF算法。 CRF算法在面试过程中经常会被问到。面试官尤其喜欢问:“CRF、HMM和MEMM的区别”,“维特比算法”,“CRF 中的特征模板特征函数”等诸如此类的问题。同时,由于CRF算法的学习具有一定门槛,且网上大量的所谓详解CRF算法的教程要么千篇一律,要么错误百出,导致我们很难彻底理解CRF算法(笔者在学习CRF的过程中就因为看了网上的很多错误教程,一度导致学习的很困难)。因此,笔者在对CRF算法学习后,想要对CRF算法进行一个彻底的梳理,帮助大家彻底理解CRF算法。 一、CRF算法是干什么的? 如果你有某位明星一天的生活照片,且你想为每一张照片标上相应的活动标签(如:吃饭, 睡觉, 唱歌等)。你该怎么做?一种方式是忽略照片间的顺序关系,为照片构建分类器。但,忽视顺序关系的话,你会丢失很多信息。例如,如果你看到一张“张嘴”的照片,你会思考它是要唱歌还是吃饭?如果你知道前一张照片是该明星吃饭或做饭的照片,那么这张照片就很有可能是关于“吃饭”了;如果前一张照片是关于该明星唱歌或跳舞的照片,那么这一张就很有可能是他在“唱歌”。因此,为了增加标注的准确率,我们应该加入相邻的照片的labels,这就是CRF(Conditional Random Field)要做的事。(本章大部分内容引自Introduction to Conditional Random Fields (echen.me))Pos Tagging 让我们讲的细些,用Pos Tagging来举个例子。在POS Tagging中,目标是用tags标注句子。例如,我们需要将“Bob drank coffee at Starbucks”标注为“Bob(NOUN) drank(VERB) coffee(NOUN) at(PERPOSITION) starbucks(NOUN)”。如果我们采用CRF来标注句子,就像分类器一样,我们需要首先要确定一个特征集 f_i 。CRF中的feature functions 在一个CRF模型中,feature functions的输入参数有以下4种,包括: • 句子 s , • 句子中单词的位置 i , • 当前位置单词的标签 l_i , • 前一个单词的标签 l_{i-1} ,并且输出一个实数值(虽然这个数大部分情况下都是0或1)。Features to Weights 接下来,我们要为每一个feature function f_k 赋予一个权重 \lambda_k (我之后会讲怎样从数据中学习这些权重)。给一个句子 s , s可以对应许许多多的标签序列 l 。因此,我们可以通过将所有words的weighted features相加,对每一种标签序列l打分,如式(1.1)所示。score(l|s)=\sum_{k=1}^{m}\sum_{i=1}^{n}\lambda_kf_k(s, i, l_i, l_{i-1}),\quad\quad (1.1) 其中,句子 s 长度为 n ,feature functions的数量为 m 。 最后,我们将这些scores转换为概率 P(l|s) ,就会得到下式(1.2),这也是CRF的核心表达式(因为score值有可能是负值,所以就加上了 exp )。P(l|s)=\frac{exp[score(l|s)]}{\sum_{l'}exp[score(l'|s)]}=\frac{exp[\sum_{k=1}^{m}\sum_{i=1}^{n}\lambda_kf_k(s, i, l_i, l_{i-1})]}{\sum_{l'}exp[\sum_{k=1}^{m}\sum_{i=1}^{n}\lambda_kf_k(s, i, l'_i, l'_{i-1})]}.\quad\quad(1.2) Example Feature Functions 铺垫了这么多,那么这些feature function长的什么样呢?Pos Tagging的feature functions示例如下: • f_1(s, i, l_i, l_{i-1})=1 if l_i=ADVERB and the i th word ends in "-ly"; 0 other wise. 如果这个feature对应的权重 \lambda_1 很大且是正值,这个feature就说明我们倾向于将以”-ly”结尾的单词标注为ADVERB的标签序列。 • f_2(s, i, l_i, l_{i-1})=1 if l_{(i-1)}=ADJECTIVE and l_i=NOUN ; 0 otherwise。同理,这个feature的权重 \lambda_2 很大且是正值意味着我们倾向于选择adjectives后面跟着nouns的标签序列。 • f_3(s, i, l_i, l_{i-1})=1 if l_{i-1}=PREPOSITION and l_i=PREPOSITION. 这个feature的对应的权重 \lambda_3 是负值时,意味着介词后面不可能跟着介词,所以含有这个特征的标签序列的分值会降低。 • f_4(s, i, l_i, l_{i-1})=1 if i=1 , l_i=VERB and the sentence ends in a question mark ("?"); 0 other wise. 同理,如果这个feature对应的权重 \lambda_4 很大且是正值,就说明我们倾向于为问句中的第一个词赋予VERB的标签序列。 总结一下:为了构建一个CRF模型,我们只需要定义一些feature functions(这些features依赖于entire sentence, a current position, and nearby labels),赋予weights,并加起来,如果有需要的话,可以转换为概率。二、CRF的目标函数 经过第一部分的介绍,我们最终得到了CRF的核心表达式(1.2),我们将(1.2)进行简单的变形。同时,为了方便起见,我们将 \sum_if_k(y_{i-1},y_i,x,i) 记为 f_k(x,y) ,则就可得到(2.1)式, P(y|x)=\frac{1}{Z(x)}exp[\sum_{k}w_k(f_k(x,y))],\quad\quad(2.1) 其中,Z(x)=\sum_yexp[\sum_{k}w_k(f_k(x,y))],\quad\quad(2.2) 与(1.2)式相比,我们将(1.2)式中的句子 s 替换为更抽象的观测序列 x ,将标签序列 l 替换为隐状态序列 y ,如下图1所示。我们把(1.2)式中的特征权重 \lambda_k 替换为符号 w_k 。 Z(x) 可以看作所有可能的隐状态序列的score值之和。我们一般也把(2.1)式看作CRF模型的定义式。图1 观测序列x和隐状态序列y之间的关系 由于我们的最终目标是找到能使得score值最大的隐状态序列,因此我们需要计算每个隐状态序列 y 对应的 P(y|x) (和利用score值没区别哈)。如果我们已经自定义好了一组feature functions,根据定义式(2.1),我们唯一不知道的就是每个feature functions f_k 的权重 w_k 。因此,我们接下来要做的就是确定 w_k 。要确定 w_k ,我们就要确定CRF的目标函数(也就是损失函数),通过优化目标函数,达到确定 w_k 的目的。 注意,这里我们再次重申我们的优化目标为找到能使得score值最大的隐状态序列,并且最理想的情况就是真实的隐状态序列 y^* 的score值最大。因此,我们将优化目标函数设计为式(2.3),\underset{w\in{R^+}}{max}\prod_{x, y}{P_w(y|x)^{\widetilde{P}(x, y)}},\quad\quad(2.3) 我们这里将 P(y|x) 表示为 P_w(y|x) ,是为了体现出这个目标函数是为了计算 w 的。同时, \widetilde{P}(x, y) 是 x 和 y 的真实联合分布。那么大家看到这里自然会迷惑为什么优化函数要这样设计?为什么要加上一个之前从没见过的 \widetilde{P}(x, y) ?假设P_w(y|x)这个分布如图2所示,图2且, \widetilde{P}(x,y) 的分布如图3所示,图3因此,式(2.3)就可以被转化为\underset{w\in{R^+}}{max}\prod_{x, y}{P_w(y|x)^{\widetilde{P}(x, y)}}=\underset{w\in{R^+}}{max}\prod_x{P_w(y^*|x)^{\widetilde{P}(x)}},\quad\quad(2.4) 因此,通过最大化(2.3)式就可以使得观测序列 x 对应的真实隐状态序列 y^* 的score值在优化的过程中不断增大。当然,我们在图3中画出的是 x 只有一个真实隐状态序列 y^* 的特殊情况(一般情况下 x 有可能以不同概率对应不同的真实隐状态序列 (y^{*1},y^{*2}, ...) ,只是为了方便大家理解),式(2.4)就是这种特殊情况的简化式。 那么肯定有同学会问,既然 \widetilde{P}(x, y) 的作用是挑选出真实隐状态序列 y^* ,使得最大化 P_w(y^*|x) ,那为什么不用真实条件分布 \widetilde{P}(y|x) ,即式(2.5)?\underset{w\in{R^+}}{max}\prod_{x, y}{P_w(y|x)^{\widetilde{P}(y|x)}},\quad\quad(2.5) 虽然 \widetilde{P}(y|x) 的分布和 \widetilde{P}(x,y) 非常类似,即(当然,这里也是和图3一样的特殊情况,方便大家理解)\widetilde{P}(y|x)=\left\{ \begin{aligned} 1, \quad if \quad y=y^*\\ 0, \quad if \quad y\neq y^* \end{aligned} \right.\quad\quad (2.6) 但\widetilde{P}(y|x)由于没有考虑到 \widetilde{P}(x) 分布的影响,导致式(2.5)会被转化为式(2.7),\underset{w\in{R^+}}{max}\prod_{x, y}{P_w(y|x)^{\widetilde{P}(x, y)}}=\underset{w\in{R^+}}{max}\prod_x{P_w(y^*|x)},\quad\quad(2.7) 我们将式(2.4)和式(2.7)进行对比,就可看到\widetilde{P}(x, y)和\widetilde{P}(y|x)的区别。其实,在一般情况下,我们利用大量数据训练CRF模型时,所有的观测序列肯定都是不同的,且每一个观测序列都肯定对应一个真实隐状态序列,则所有的观测序列出现的概率都相同,即 \widetilde{P}(x)=\frac{1}{N} , N 为总的数据量。所以对我们来说,(2.4)和(2.6)没什么区别。但是,式(2.4)通过引入 \widetilde{P}(x) ,使得一旦训练数据中存在大量的重复观测序列,比如若\widetilde{P}(x^5)\gg{\widetilde{P}(x^i), i\neq 5} ,则说明观测序列 x^5 在实际中出现的概率更高,那么CRF模型会更倾向于优化 {P_w(y|x^5)^{\widetilde{P}(y|x^5)}} 这一项,这也更符合实际。所以说明,训练数据的分布要尽可能接近真实数据的分布,CRF模型才会训练的越好。我们也可以从另外一个角度去理解(2.3)式。其实,(2.3)式就是对CRF模型求最大似然,如果是求似然的话,我们一般的做法就是求log,则(2.3)式可被转化为式(2.8),\underset{w\in{R^+}}{max}f(w)=\sum_{x, y}{{\widetilde{P}(x, y)}\log{P_w(y|x)}},\quad\quad(2.8) 观察(2.8)式就可发现, \sum_{x, y}{{\widetilde{P}(x, y)}\log{P_w(y|x)}} 是 \log{P_w(y|x)} 关于真实分布 {\widetilde{P}(x, y)} 的期望。因此,最大化 f(w) 也便是让预测分布 P_w(y|x) 在满足 \widetilde{P}(x) 的条件下尽可能拟合真实分布 \widetilde{P}(y|x) 。 我们将 max 转化为 min ,因此,最终的损失函数为\underset{w\in{R^+}}{min}f(w)=-\sum_{x, y}{{\widetilde{P}(x, y)}\log{P_w(y|x)}},\quad\quad(2.9) 三、CRF的训练方法--拟牛顿法 第二章,我们已经提出了CRF模型的损失函数(2.9)式,并对(2.3)式进行了充分的分析,解释了为什么要这样设置目标函数。本章,我们将继续讨论如何利用这个损失函数(2.9)式对CRF模型的weights进行学习。CRF模型的训练算法一般采用的是拟牛顿法。其实像随机梯度下降法等等其它优化算法也都可以,至于CRF为什么常采用拟牛顿法?我也不太清楚,我猜测是因为拟牛顿法是利用到了二阶导数,收敛速度快的原因?1、分析损失函数梯度由于拟牛顿法的计算过程中需要用到目标函数的一阶梯度函数,如(3.1)式所示,g(w)=[\frac{\partial{f(w)}}{\partial{w_1}}, \frac{\partial{f(w)}}{\partial{w_2}}, ..., \frac{\partial{f(w)}}{\partial{w_n}}], \quad\quad(3.1) 因此,对 f(w) 求梯度,结果如下(限于篇幅,我省略了中间过程的推导),\frac{\partial{f(w)}}{\partial{w_k}}=\sum_{x,y}\widetilde{P}(x)[f_k(x,y)\cdot P_w(y|x)]-\sum_{x,y}\widetilde{P}(x,y){f_k(x,y)},\quad\quad (3.2) 并且,我们将 F(x,y) 记为所有feature functions组成的向量 F(x,y)=[f_k(x,y)] ,则g(w)=\sum_{x,y}\widetilde{P}(x)[F(x,y)\cdot P_w(y|x)]-\sum_{x,y}\widetilde{P}(x,y)F(x,y),\quad\quad(3.3) 其中,(3.3)式的第一项是 F(x,y) 关于 P_w(x,y) (P_w(x,y)=\widetilde{P}(x)P_w(y|x))的期望,第二项是F(x,y) 关于 \widetilde{P}(x,y) 的期望。根据拟牛顿法,只要找到 g(w)=0 的点,对应的 w 就是我们想要的权重。(PS:我看网上说 f(w) 是凸函数,所以只要有驻点,就一定是最优解,不知道是不是这样?)。 针对(3.3),我们简单分析就可知,当 g(w)=0 ,且如果 f(w) 真是凸函数的情况下只会有一个驻点,则,\widetilde{P}(x)\cdot P_w(y|x)=\widetilde{P}(x)\cdot\widetilde{P}(y|x),\quad\quad(3.4) 我们可以发现,当 f(w) 的值达到最小时,此时 P_w(y|x)=P(y|x) ,即此时 P_w(y|x) 完全拟合 P(y|x) 。(这里插一句话,如果我们采用(2.9)式的 \widetilde{P}(x,y) 替换为 \widetilde{P}(y|x) 进行接下来的求梯度运算,即不考虑 \widetilde{P}(x) 的影响,最终也会得到 P_w(y|x)=\widetilde{P}(y|x) 的式子,所以殊途同归。但为什么我们还是要用\widetilde{P}(x,y)呢?我的猜想是,理论上\widetilde{P}(x,y) 和 \widetilde{P}(y|x) 对应的两个式子的最优解是相同的,但在实际训练的过程中我们不可能达到最优解,而在达不到最优解时,使用\widetilde{P}(x,y) 往往训练出来的效果会更好,因为考虑到 \widetilde{P}(x) 的影响,更接近真实情况。)2、拟牛顿法 既然 f(w) 和 g(w) 都有了,那么如何训练?本质就是利用如下这个公式(3.5):x_{n+1}=x_n-\frac{f(x_n)}{f'(x_n)},\quad\quad(3.5) 这是牛顿法(拟牛顿法同理)在一维上搜索零点的核心公式,表达的含义就是,给定一个初始点 x_n ,通过上述公式进行迭代更新,逐渐收敛到零点的过程,如图4所示。但我们这里的问题是求 f(w) 的极小值点,即 g(w)=0 的点,则我们应该用(3.6)式,w^{n+1}=w^n-\frac{f'(w^n)}{f''(w^n)}=w^n-\frac{g(w^n)}{g'(w^n)},\quad\quad(3.6) 图4 一维牛顿法求实根(注意:这里的 w^n 和 w^{n+1} 指迭代到第 n 步和第 n+1 步的 w 向量,和(3.5)中的 x_n 和 x_{n+1} 表示的含义相仿,只不过不是一维而已,笔者为了和我们之前用的 w_k 和 w_{k+1} 区分开,不要搞混了。) 但这时,大家可能就要问了,既然牛顿法这么厉害,我们为什么要用拟牛顿法,关键就是这个 w 。在(3.5)式中,这个 x 只是一个数字,即(3.5)只是牛顿法在一维搜索中的应用,但我们这里的 w 可是一个相当高维的向量,且 w 的维数就是feature functions的数量,那么牛顿法在高维搜索中的公式呢?如(3.7)式所示,w^{n+1}=w^n-g(w^n)\cdot H^{-1}(w^n),\quad\quad(3.7) 其中, H(w)=[\frac{\partial^2f}{\partial w_i\cdot\partial w_j}]_{n\times n} 就是 f(w) 的二阶“导数”,被称为Hessian矩阵(注意,这里的 n 只是表示矩阵的维数,和前面的那些 x_n 啥的没关系)。且,这里的 w_i 和 w_j 和之前的 w_k 和 w_{k+1} 表示的意思一样。 讲到这里,一些明眼人一定已经发现问题了,每一轮的 H(w^n) 很好求,但 H^{-1}(w^n) 就不好求了,因为计算复杂度太高。因此有人提出了近似计算 H^{-1}(w^n) 矩阵的快速算法,这也就是拟牛顿思想的由来。通过对 H^{-1}(w^n) 进行近似,虽然降低了一些准确率,但极大提高了牛顿法的计算速度。 因此,就有很多不同的近似算法被提了出来,最著名的包括以下三种:DFP算法、BFGS算法、L-BFGS算法(CRF模型的训练一般采用L-BFGS算法),这三种算法的详细介绍可以参考梯度下降法、牛顿法和拟牛顿法 - Eureka的文章 - 知乎。3、L-BFGS算法步骤我们这里也把L-BFGS算法的具体迭代步骤介绍一下,供大家理解,(注意,这里的 B 矩阵就是对 H^{-1} 矩阵的近似)选定初始权重向量 w^0 ,取 G_0=I ,置 k=0 (表示第 k 次迭代)。计算 g^k=g(w^{k}) ( g^k 是每一次迭代时,我们输入L-BFGS的值)。若 g^k=0 ,则计算结束,转7;否则转3。进行一维搜索,求 \lambda^k ,使得(这里的 \lambda^k 是避免牛顿法存在的不收敛情况,而加入的步长因子) f(w^{k}-\lambda^k\cdot\frac{g^k}{B^k})=\underset{\lambda\geq0}\min f(w^{k}-\lambda\cdot\frac{g^k}{B^k})。 进行 w 更新: w^{k+1}=w^k-\lambda\cdot\frac{g^k}{B^k} 。计算 g^{k+1}=g(w^{k+1}) (再一次调用我们的代码计算梯度)。若 g^{k+1}=0 ,则计算结束,转7;否则,更新 B^{k+1} , B^{k+1}=B^k+\frac{(g^{k+1}-g^k)\cdot(g^{k+1}-g^k)^T}{(g^{k+1}-g^k)\cdot(w^{k+1}-w^k)^T}-\frac{B^k\cdot(w^{k+1}-w^k)\cdot(w^{k+1}-w^k)^T\cdot B_k}{(w_{k+1}-w^k)^T\cdot B_k\cdot(w^{k+1}-w^k)}。 置 k=k+1 ,转3。输出此时的 w 。 总结:我们在调用L-BFGS包时,需要输入提前初始化好的权重向量 w^0 ,然后在每一轮迭代时我们需要不断计算好 g(w) 和 f(w) 。四、前向递推和后向递推 本来,我们在介绍完CRF模型的训练方法后就应该介绍CRF模型的预测方法(CRF怎么用)了,但其实在第三章最后我们还留下了两个问题没有解决,即我们在总结中说道,我们在调用L-BFGS算法时,需要计算 f(w) 和 g(w) 。但是这两个值怎么算呢?这便是第四章要解决的问题。我们要思考的第一个问题就是如何计算 f(w) ?回顾第2章,根据(2.9)式, f(w) 可被表示为f(w)=-\sum_{x, y}{{\widetilde{P}(x, y)}\log{P_w(y|x)}},\quad\quad(4.1) 我们将(4.1)式和(2.1)式结合,就可得到(4.2)式(这里也省略了推导的过程,有兴趣的同学可以自己试试),f(w)=\sum_{x}\widetilde{P}(x)[\log{Z(x)}-\sum_{y}\widetilde{P}(y|x)\sum_{k}w_k(f_k(x,y))],\quad\quad(4.2) 可以发现,我们只需要计算 \widetilde{P}(x) 、 Z(x) 和 \sum_{y}\widetilde{P}(y|x)\sum_{k}w_k(f_k(x,y)) 这三部分即可。其中,\widetilde{P}(x) 很容易计算,只需要对训练集中每条观测序列的概率进行统计即可,一般都是 \frac{1}{N} ( N 是总训练数据量),且 \sum_{y}\widetilde{P}(y|x)\sum_{k}w_k(f_k(x,y)) 可以对观测序列 x 在训练数据中出现过的隐状态序列 y 的分布进行,从而计算得到。而且在我们的项目中,一般情况下一个观测序列只会对应一个真实的隐状态序列 y^* ,因此在这种特殊情况下, \sum_{y}\widetilde{P}(y|x)\sum_{k}w_k(f_k(x,y))=\widetilde{P}(y^*|x)\sum_{k}w_k(f_k(x,y^*))=\sum_{k}w_k(f_k(x,y^*)) ,所以\sum_{y}\widetilde{P}(y|x)\sum_{k}w_k(f_k(x,y))这一项也很好计算。那么唯一有难度的就是这个 Z(x) 怎么计算?如何计算Z(x)? 回顾第二章(2.2)式,可以看到, Z(x) 的计算涉及到对一个观测序列 x 的所有有可能输出的隐状态序列 y 的求和。那么问题就来了?我怎么知道 x 有多少个可能的隐状态序列?以Pos Tagging为例,假如 x 长度为 n ,且一共有 m 个词性符号,那么 x 对应的隐状态序列理论上就有 m^n 种可能,如果我们要逐一求和,这个搜索空间未免太大了。因此,有人提出了利用前向递推和后向递推来计算 Z(x) 的方法(一个字,绝!)。Z(x)=\sum_yexp[\sum_{k}w_k(f_k(x,y))],\quad\quad(2.2) 在计算之前,我们需要引入特殊的起点和终点状态标记 y_0=start 、 y_{n+1}=stop ,这时 P_w(y|x) 可以用矩阵形式表示。对观测序列 x 的每一个位置 i=1,2,...,n+1 ,定义一个 m+2 阶矩阵( m 是隐状态符号的个数(和我们刚提到的词性符号个数一样),由于多了start和stop,所以要加2;且因为 x 是给定的,所以 i-1 位置和 i 位置各有 m+2 种可能),可记为M_i(x)=[M_i(y^a_{i-1}, y^b_i|x)],\quad\quad(4.3) 其中, M_i(y^a_{i-1}, y^b_i|x) 表示矩阵 M_i(x) 在位置 (a,b) 上的元素,且M_i(y^a_{i-1}, y^b_i|x)=exp(\sum_kw_k(f_k(y^a_{i-1}, y^b_i, x, i))),\quad\quad(4.4) 其中, y^a 和 y^b 分别表示 m+2 个隐状态符号中的第 a 和 b 个符号,且 f_k(y^a_{i-1}, y^b_i, x, i) 表示当隐状态序列 y 的第 i-1 和 i 位置上的隐状态分别是 y^a 和 y^b 时,特征函数 f_k 的值。(PS:大家别忘了我们之前将 \sum_if_k(y_{i-1},y_i,x,i) 记为了 f_k(x,y) ,所以大家搞明白f_k(y^a_{i-1}, y^b_i, x, i)和f_k(x,y)之间的关系,不要混淆!我再重申一遍,这里的 y 是隐状态序列,而y^a 和 y^b 分别表示 m+2 个隐状态符号中的第 a 和 b 个符号,且 y_{i-1} 和 y_i 分别表示y的第 i-1 和 i 位置上的隐状态。) 因此,(2.1)式中的条件概率 P_w(y|x) 可以被重新表示为(我这里是直接这样写的,至于为什么可以写成(4.5)式,需要大家自己思考,比如,可以自己尝试 M_i(x) 的每一项M_i(y^a_{i-1}, y^b_i|x) 都构造出来,然后不断相乘去理解)P_w(y|x)=\frac{1}{Z(x)}\prod_{i=1}^{n+1}M_i(y_{i-1}, y_i|x), \quad\quad(4.5) 写了这么多,大家可能会好奇,这个式子 和我们相求的 Z(x) 有什么关系呢?我直接上结论:Z(x)=\alpha_0^T(x)\cdot M_1(x)\cdot M_2(x)\cdot\cdot\cdot M_n(x)\cdot\vec{1},\quad\quad(4.6) 其中, \alpha_0 是一个 m+2 维向量,且这里的 \vec{1} 是一个元素均为 1 的 m+2 维列向量。\alpha_0(x)=[1,0,0,...,0]^T,\quad\quad(4.7) 至于(4.6)这个结论是怎么得到的?其实是因为这里有一个前向向量的递推公式:\alpha^T_i(x)=\alpha^T_{i-1}(x)\cdot M_i(x),\quad\quad(4.8) 而 \alpha_i(x) 就表示位置 i 的隐状态是 y_i ,且从位置 0 到位置 i 的前 i+1 长度的隐状态序列的非规范化概率。 y_i 的取值有 m+2 个,所以 \alpha_i(x) 是一个 m+2 维列向量。(PS:从 0 到 i 的序列长度为 i+1 应该没有问题吧?) 当然,这里很多人肯定不懂“什么是非规范化概率”(我在第五章开头会讲这个概念,大家可以去第五章看看。这里我简单说一下)。本质上,非规范化概率和概率的作用一样,只不过概率的取值范围是 [0,1] ,而这里的非规范化概率的取值范围是 [0, +\infty] 。同时,这里的 Z(x)=\alpha_{n+1}(x) ,即 Z(x) 表示位置 n+1 的隐状态是 stop ,且从位置 0 到位置 n+1 的前 n+1 长度的隐状态序列的非规范化概率。其实, M_i(x) 在位置 (a,b) 的元素 M_i(y^a_{i-1}, y^b_i|x) 表示的就是位置 i-1和 i 处分别是 y_{i-1}^a 和 y_i^b 的非规范化概率。而且,我们本文在一开头时说的 exp[\sum_{k}w_k(f_k(x,y))] 也是非规范化概率。(我这里多说一句,其实 Z(x) 被称为非规范化概率之和更好一些,但非规范化概率之和依旧是非规范化概率,所以也没啥毛病。) 我说了这么多,可能还是有很多同学不懂,所以我的建议还是尝试把 M_i(x) 的每一项M_i(y^a_{i-1}, y^b_i|x) 都构造出来,然后不断相乘去理解 \alpha_0^T(x) 、 \alpha_1^T(x) 、 \alpha_2^T(x) 等等,当然,还要思考一下为什么(4.6)式要最后乘以 \vec{1} 。如何计算g(w)?通过前面的讲解,我们已经知道了 f(w) 值怎么计算,那么 f(w) 的梯度 g(w) 怎么计算呢?回顾第三章,我们已经知道 g(w) 的表达式为(3.3)式。我们将(3.3)式进行简单变换,就可得到(4.9)式,g(w)=\sum_{x,y}\widetilde{P}(x)[F(x,y)\cdot P_w(y|x)]-\sum_{x,y}\widetilde{P}(x,y)F(x,y),\quad\quad(3.3) g(w)=\sum_{x}\widetilde{P}(x)[\sum_yF(x,y)\cdot P_w(y|x)-\sum_{y}\widetilde{P}(y|x)F(x,y)],\quad\quad(4.9) 其中, \sum_{y}\widetilde{P}(y|x)F(x,y) 这一项的计算非常简单,同时,在我们的训练集中,一般情况下,每个观测序列 x 只有一个真实的隐状态序列 y^* ,则 \sum_{y}\widetilde{P}(y|x)F(x,y)=F(x,y^*) 。同时,在我们的训练集中,一般情况下,每个观测序列 x的概率为 \widetilde{P}(x)=\frac{1}{N} , N 是训练数据量。并且, F(x,y^*)=[\sum_if_k(y^*_{i-1}, y^*_i, x, i)] 的计算也非常简单。因此,我们唯一需要考虑的是 \sum_yF(x,y)\cdot P_w(y|x) 怎么计算?这里 P_w(y|x) 的计算面临着和 Z(x) 计算同样的问题,涉及到对一个观测序列 x 的所有有可能输出的隐状态序列 y 的遍历,复杂度太高。因此,和 Z(x) 类似,这里的解决办法是后向递推。 在这里,我们引入后向递推公式,Z(x)=\vec{1}^T\cdot M_1(x)\cdot M_2(x)\cdot\cdot\cdot M_{n+1}(x)\cdot \beta_{n+1}(x),\quad\quad(4.10) 其中, \beta_{n+1} 是一个 m+2 维向量,且这里的 \vec{1} 也是一个元素均为 1 的 m+2 维列向量。\beta_{n+1}(x)=[0, 0, 0, 0,..., 1]^T,\quad\quad(4.11) 且后向向量的递推公式为:\beta_i(x)=M_{i+1}(x)\cdot \beta_{i+1}(x),\quad\quad(4.12) 与 \alpha_i(x) 同理, \beta_i(x) 表示位置 i 的隐状态是 y_i ,且从位置 i 到位置 n+1 的后 n+2-i 长度的隐状态序列的非规范化概率, y_i 的取值有 m+2 个,所以 \beta_i(x) 就是 m+2 维列向量。 因此,\sum_yF(x,y)\cdot P_w(y|x)=[\sum_{i=1}^{n+1}\sum_{y_{i-1}, y_i}f_k(y_{i-1}, y_i, x, i)\cdot\frac{\alpha_{i-1}^TM_i(y_{i-1},y_i|x)\beta_i(y_i|x)}{Z(x)}],\quad\quad(4.13) 这个式子(4.13)不再细讲,大家知道怎么计算就行,感兴趣的同学可以自己思考一下。 最终,通过前向递推和后向递推, f(w) 和 g(w) 就都计算出来了,然后在每次迭代时,L-BFGS算法会自动调用。五、CRF的预测方法--Viterbi算法 但目前为止,经过前几章的讲述,我们已经得到了一个完整的CRF模型。但是,我们怎么使用这个CRF模型?这便是第五章要讲内容,即CRF的预测方法--Viterbi算法。 在介绍Viterbi算法之前,我们先应该思考一个问题,如果是我们自己的话,怎么使用这个CRF模型去预测可能的隐状态序列 y 呢?假如CRF模型的输入是一条长度为 n 的观测序列 x ,而我们一共有 m 个隐状态符号。那么和我们之前说过的一样,该观测序列 x 可能对应着 m^n 条可能的隐状态序列。如果我们逐一遍历每一条隐状态序列并计算其 score(y|x) ,那么仅CRF的预测模块的时间复杂度就达到了 O(m^n) ,这么高的时间复杂度是我们所不能接受的。 因此,暴力搜索这一思路是行不通的,那还有别的降低时间复杂度的方法吗?那我们首先想到的就是贪婪算法。假如我们保证前 i 长度的子观测序列 x^{i} 对应的隐状态序列 y^{i} 的非归一化概率最大,那么加上下一个 x_{i+1} 后,将 y^{i} 与每一个隐状态符号尝试结合,得到前 i+1 长度的子观测序列 x^{i+1}对应的非归一化概率最大的隐状态序列 y^{i+1} 。并且,这样不断迭代下去,直到得到前 n 长度的子观测序列 x对应的非归一化概率“最大”的隐状态序列 y^n 并输出。请问这样做的问题在哪里?很明显,这是一个局部最优解,这个y^n 和全局最优解相差很远(原因的话,大家可以自己思考一下)。 那么有没有既可以降低时间复杂度,又可以达到全局最优解的办法呢?一般情况下,要达到这样两全其美的结果的话,我们肯定要从数据本身入手。如果数据本身具有某些独特的特性,我们将其利用起来的话,说不定就可以降低时间复杂度。既然我们要搜索的数据是所有可能的隐状态序列,那么我们就分析一下,如图5所示。图5Viterbi核心思想讲解 在介绍图5之前,我们先提一下非规范化概率这个概念。在讲解贪婪算法时,我们一直提到了非规范化概率,这到底是什么呢?回顾第二章,P(y|x)=\frac{1}{Z(x)}exp[\sum_{k}w_k(f_k(x,y))]=\frac{1}{Z(x)}exp[\sum_i\sum_kw_kf_k(y_{i-1},y_i,x,i)],\quad\quad(2.1) 那么非规范化概率就是指 exp[\sum_i\sum_kw_kf_k(y_{i-1},y_i,x,i)] ,如果隐状态序列 y 的非规范化概率越高,则 y 成为观测序列 x 的隐状态序列的概率也就越高。因此,非归一化概率有一个很好的性质,就是连乘。如果我们将前 s 长度的子观测序列 x^{s} 对应的隐状态序列 y^{s} 的非归一化概率记为 \delta_s ,\delta_s = \prod_{i=1}^{i=s} exp[\sum_kw_kf_k(y_{i-1},y_i,x,i)],\quad\quad(4.1) 则前 s+1 长度的子观测序列 x^{s+1} 对应的隐状态序列 y^{s+1} 的非归一化概率为,\delta_{s+1}=\delta_s\cdot exp[\sum_{k}w_kf_k(y_{s}, y_{s+1}, x, s+1)],\quad\quad(4.2) 因此,我们的目标是输出 \delta_n 最大的 y 作为 x 的预测的隐状态序列。 话锋一转,我们继续分析图5。假设隐状态符号集为 \{a,b,c,d\} , y_{i-1} 和 y_i 分别表示隐状态序列 y 的 i-1 和 i 位置。那么针对这个隐状态序列,我们在搜索时有什么特点呢?以 a_i 为例,如果同时有从 a_{i-1} 和 b_{i-1} 过来的两条序列希望与 a_i 结合,那么 a_i 应该结合哪一条呢?难道是和每一条都结合?换句话说, a_i 如何判断和哪一条序列结合,使得其所在的序列未来最终更有可能成为非规范化概率 \delta_n 最大的那条序列,从而被模型确定为输出的隐状态序列? 由于隐状态序列自身的特点,导致这两条序列和 a_i 结合后,下一步,和之后的位置(如 {i+1, i+2, ..., n} )结合的可能情况完全一致。因此, a_i 只需选择当前 a_{i-1} 和 b_{i-1} 更优秀的那一条序列进行结合即可,将另外一条序列舍弃。因此,Viterbi算法就是利用了这个特点,实现了在极大减小搜索空间的情况下,依旧找到了全局最优解。图6 Viterbi算法示例 图6是我们针对Viterbi算法举的一个例子,隐状态序列 y 长度为4,隐状态符号集为 \{a,b,c,d\} 。在第一步之后,针对子序列 y^2 ,我们得到了4个可能的隐状态序列( (a_1, a_2) 、 (c_1, b_2) 、 (c_1, c_2) 、 (d_1, d_2) ),并且 \delta_2 最大的隐状态序列肯定在这4个之内。在第二步之后,针对子序列 y^3 ,我们得到了4个可能的隐状态序列( (a_1, a_2, c_3) 、 (c_1, b_2, b_3) 、 (c_1, b_2, d_3)、 (c_1, c_2, a_3) ), \delta_3 最大的隐状态序列肯定在这4个之内,且(d_1, d_2) 由于在第二步竞争失败消失了。第三步同理,最终, \delta_4 最大的隐状态序列肯定在( (c_1, c_2, a_3, a_4) 、 (c_1, c_2, a_3, b_4) 、 (c_1, c_2, a_3, d_4)、 (c_1, b_2, d_3, c_4) )。 因此,Viterbi算法利用这个特点,将计算复杂度从 O(m^n) 降低为 O(m\cdot n) ,其中, n 是隐状态序列长度, m 是隐状态符号数。 这便是CRF算法的全部理论推导,大家有什么问题的话, 可以在评论区留言。编辑于 2022-03-22 21:00自然语言处理自动语音识别算法赞同 13518 条评论分享喜欢收藏申请
CRF条件随机场的原理、例子、公式推导和应用 - 知乎
CRF条件随机场的原理、例子、公式推导和应用 - 知乎首发于统计学习方法-笔记切换模式写文章登录/注册CRF条件随机场的原理、例子、公式推导和应用刘启林国防科学技术大学 软件工程硕士条件随机场(Conditional Random Field,CRF)是自然语言处理的基础模型,广泛应用于中文分词、命名实体识别、词性标注等标注场景。条件随机场CRF与深度学习结合,产生了BiLSTM-CRF、BiLSTM-CNN-CRF等模型,在中文分词、命名实体识别、词性标注也取得不错的效果。条件随机场CRF与Attention机制结合,又发展成了Transformer-CRF、BERT-BiLSTM-CRF等模型,使中文分词、命名实体识别、词性标注效果又有显著提高。本文先引出条件随机场CRF的场、随机场、团、最大团等相关基础概念;接着介绍CRF的原理,重点阐述了线性链条件随机场的原理;然后介绍CRF在中文分词、命名实体识别、词性标注的具体应用;最后对CRF进行总结,指出图模型之间的演化关系,及CRF模型的发展简史。本文结构如下:一、CRF基础1、无向图2、马尔可夫随机场3、最大团4、无向图的因子分解二、CRF原理1、条件随机场2、线性链条件随机场3、线性链条件随机场公式4、条件随机场例子三、CRF应用1、中文分词2、命名实体识别3、词性标注四、CRF总结1、CRF的概括总结2、图模型之间的关系3、CRF的发展简史直接上PPT。CRF条件随机场的原理、例子、公式推导和应用一、CRF基础CRF基础的目录1、无向图什么是图?什么是无向图?什么是图?什么是无向图?2、马尔可夫随机场什么是场?什么是随机过程?什么是场?什么是随机过程?什么是随机场?什么是马尔可夫随机场?什么是随机场?什么是马尔可夫随机场?3、最大团什么是团?什么是最大团?什么是团?什么是最大团?4、无向图的因子分解Hammersley-Clifford 定理如下:无向图的因子分解举个无向图的因子分解的例子。无向图的因子分解的例子二、CRF原理CRF原理的目录1、条件随机场2001年,John Lafferty, Andrew McCallum 和 Fernando Pereira,在论文《 Conditional Random fields :Probabilistic Models for Segmenting and Labeling Sequence Data》提出条件随机场。 提出条件随机场。条件随机场条件随机场定义如下:条件随机场定义2、线性链条件随机场线性链条件随机场线性链条件随机场的定义如下:线性链条件随机场的定义线性链条件随机场CRF的图结构线性链条件随机场CRF的图结构3、线性链条件随机场公式线性链条件随机场公式特征函数定义如下:特征函数为了简单起见,将转移特征和状态特征及其权值用统一符号表示。条件随机场简化公式如下:条件随机场简化公式4、条件随机场例子例2:已知中文文本"刘启林”,采用IOB标注方法,求实体标注序列为{ B,I,I }的概率。求实体标注序列为{ B,I,I }的概率例1的几何描述如下:例1的几何描述例1的标注序列为{B、I、I}的联合概率分布如下:例1的标注序列为{B、I、I}的联合概率分布三、CRF应用CRF应用的目录1、中文分词基于CRF由字构词方法的基本思想,基本原理如下:基于CRF中文分词的基本思想,基本原理CRF中文分词的图结构如下:CRF中文分词的图结构2、命名实体识别基于CRF的命名实体识别过程如下:基于CRF的命名实体识别CRF命名实体识别的图结构如下:CRF命名实体识别的图结构3、词性标注基于CRF词性标注方法的基本思想,基本原理如下:基于CRF词性标注的基本思想,基本原理CRF中文词性标注的图结构如下:CRF中文词性标注的图结构四、CRF总结CRF总结的目录1、CRF的概括总结CRF的概括总结2、图模型之间的关系朴素贝叶斯、HMM、逻辑回归、CRF等图模型关系如下:朴素贝叶斯、HMM、逻辑回归、CRF等图模型关系朴素贝叶斯、HMM、逻辑回归、CRF对比如下表所示:朴素贝叶斯、HMM、逻辑回归、CRF对比更多HMM可参考:更多LR逻辑回归模型可参考:3、CRF的发展简史CRF的发展简史机器学习阶段:CRF深度学习阶段:BiLSTM-CRF、BiLSTM-CNN-CRFAttention阶段:Transformer-CRF、BERT-BiLSTM-CRF条件随机场CRF一直是标注问题的基础模型。由于能力和水平的限制,我的可能是错的。参考文献:1、王元等, 数学大词典(第二版), 科学出版社[M], 2017.092、John Lafferty, Andrew McCallum, and Fernando Pereira, "Conditional Random Fields: Probabilistic Models for Segmenting and Labeling Sequence Data", June 2001.3、李航, 统计学习方法(第2版), 清华大学出版社[M], 2019.054、宗成庆, 统计自然语言处理(第2版), 清华大学出版社[M], 2013.085、Charles Sutton and Andrew McCallum, An Introduction to Conditional Random Fields, 2011编辑于 2020-11-17 01:11条件随机场CRFs自然语言处理赞同 57657 条评论分享喜欢收藏申请转载文章被以下专栏收录统计学习方法-笔记python遇见NLP主要关注机器学习、自然语言处理
分享,让知识共享!数据科学之路--机器学习数据挖掘、自然语言处理、推荐系统、人
码率控制(二):CRF详解 - 知乎
码率控制(二):CRF详解 - 知乎首发于码率控制切换模式写文章登录/注册码率控制(二):CRF详解寒衣视频编码从业者在上一篇文章码率控制(一):理解码率控制模式(x264,x264,vpx)已经介绍过CRF(Constant Rate Factor)了,本篇文章进一步介绍CRF的原理。CRF是x264和x265默认的码率控制模式,在libvpx中也可以使用CRF。CRF取值在0到51间。取值越小质量越好压缩率越低,取值越大压缩率越高质量越低。CRF在进行码率控制时不会试图达到某个特定码率,而是要保持稳定的质量。码流的大小将由源视频的复杂度决定。对于x264,CRF建议取值在18到28间,默认为23。ffmpeg -i input.mp4 -c:v libx264 -crf 23 output.mp4
对于x265默认为28。ffmpeg -i input.mp4 -c:v libx265 -crf 28 output.mp4
libvpx没有默认值,它的CRF取值范围为0到63。对于1080p视频建议取值31。ffmpeg -i input.mp4 -c:v libvpx-vp9 -crf 31 -b:v 0 output.mkv
如果你无法确定需要的CRF值,你可以从默认值开始慢慢尝试。如果质量低于预期则减小CRF,如果文件太大则增大CRF。每当CRF增大/减小6,文件尺寸将减半/加倍。CRF应该用于离线场景以达到最优效果。码率如何变化?为了让你直观了解不同分辨率视频的码率与CRF的关系,下面给出了复杂度不同的4段1分钟视频的平均码率(MBit/s)与CRF的关系。编码器为x264。可以看出不同分辨率的视频的码率和CRF都满足对数关系。CRF vs CQPCQP模式在编码时会保持每帧的QP不变,例如设定QP=18,则整个序列每帧的QP都为18(依据帧类型不同会有QP offset,但是影响不大)。而CRF会通过动态调整每帧的QP保持质量恒定,例如编码是设定CRF=18,对于运动多的帧QP可能增加到20,运动少的帧QP可能会减小到16。下图是QP和CRF等于17和23时,每帧比特数的变化。可以看到CRF的比特数总是少于QP,意味着保持质量的同时CRF能节省比特。为什么运动如此重要?相比运动物体,人眼对于静止物体能感知到更多细节。因此,编码器可以对运动物体采用更大的压缩(去除更多细节),对静止物体采用更小的压缩(保留更多细节)。人眼视觉系统会被运动分散注意力,且运动物体在屏幕上停留时间少,所以能觉察到的失真少。而静止物体在屏幕停留时间长有足够时间观察,且无法分散注意力,所以能觉察到的失真多。用什么评价指标?如果你用PSNR等简单的评价指标比较视频质量,你可能会发现CRF的质量低于CQP。但是人眼主观观察可以发现CRF的质量不低于甚至高于CQP的质量。这是因为PSNR等指标不考虑感知质量,仅考虑每帧的统计结果。可以使用VQM或VMAF进行主观评价。翻译自:https://slhck.info/video/2017/02/24/crf-guide.html感兴趣的请关注微信公众号Video Coding发布于 2020-09-17 15:52MP4FFmpeg1080p赞同 253 条评论分享喜欢收藏申请转载文章被以下专栏收录码率控制码率控制经
用深度学习做命名实体识别(七)-CRF介绍 - 知乎
用深度学习做命名实体识别(七)-CRF介绍 - 知乎首发于一起学爬虫切换模式写文章登录/注册用深度学习做命名实体识别(七)-CRF介绍程序员一一涤生心安茅屋稳,性定菜根香。还记得之前介绍过的命名实体识别系列文章吗,可以从句子中提取出人名、地址、公司等实体字段,当时只是简单提到了BERT+CRF模型,BERT已经在上一篇文章中介绍过了,本文将对CRF做一个基本的介绍。本文尽可能不涉及复杂晦涩的数学公式,目的只是快速了解CRF的基本概念以及其在命名实体识别等自然语言处理领域的作用。什么是CRF?CRF,全称 Conditional Random Fields,中文名:条件随机场。是给定一组输入序列的条件下,另一组输出序列的条件概率分布模型。什么时候可以用CRF?当输出序列的每一个位置的状态,需要考虑到相邻位置的状态的时候。举两个例子:1、假设有一堆小明日常生活的照片,可能的状态有吃饭、洗澡、刷牙等,大部分情况,我们是能够识别出小明的状态的,但是如果你看到一张小明露出牙齿的照片,在没有相邻的小明的状态为条件的情况下,是很难判断他是在吃饭还是刷牙的。这时,就可以用crf。2、假设有一句话,这里假设是英文,我们要判断每个词的词性,那么对于一些词来说,如果不知道相邻词的词性的情况下,是很难准确判断每个词的词性的。这时,也可以用crf。什么是随机场?我们先来说什么是随机场。The collection of random variables is called a stochastic process.A stochastic process that is indexed by a spatial variable is called a random field.随机变量的集合称为随机过程。由一个空间变量索引的随机过程,称为随机场。也就是说,一组随机变量按照某种概率分布随机赋值到某个空间的一组位置上时,这些赋予了随机变量的位置就是一个随机场。比如上面的例子中,小明的一系列照片分别是什么状态组成了一组位置,我们从一组随机变量{吃饭、洗澡、刷牙}中取值,随机变量遵循某种概率分布,随机赋给一组照片的某一张的输出位置,并完成这组照片的所有输出位置的状态赋值后,这些状态和所在的位置全体称为随机场。为什么叫条件随机场?回答这个问题需要先来看看什么是马尔可夫随机场(也叫马尔可夫网络),如果一个位置的赋值只和与它相邻的位置的值有关,与和它不相邻的位置的值无关,那么这个随机场就是一个马尔可夫随机场。这个假设用在小明和词性标注的例子中的话就是我们是通过前一张照片或者后一张照片的状态来判断当前照片的状态是刷牙还是吃饭,我们是根据前一个词的词性或者后一个词的词性来判断当前词的词性是什么。然而,无论是照片还是词性的判断,如果仅仅通过相邻的照片或词性来判断当前照片或词的状态还是过于草率了,如果还能加入对照片中的内容或词的含义的参考,最后的判断当然会更准确啦。而条件随机场(CRF),就是给定了一组观测状态(照片可能的状态/可能出现的词)下的马尔可夫随机场。也就是说CRF考虑到了观测状态这个先验条件,这也是条件随机场中的条件一词的含义。CRF的数学描述 设X与Y是随机变量,P(Y|X)是给定X时Y的条件概率分布,若随机变量Y构成的是一个马尔科夫随机场,则称条件概率分布P(Y|X)是条件随机场。在实际的应用中,比如上面的两个例子,我们一般都要求X和Y有相同的结构,如下:X=(X1,X2,...Xn),Y=(Y1,Y2,...Yn)比如词性标注,我们要求输出的词性序列和输入的句子中的每个词是一一对应的。X和Y有相同的结构的CRF就构成了线性链条件随机场(Linear chain Conditional Random Fields,简称 linear-CRF)。上面的例子中没有提到命名实体识别,但其实命名实体识别的原理和上面的例子是一样的,也是用到了linear-CRF,后面会提到。CRF如何提取特征?CRF中有两类特征函数,分别是状态特征和转移特征,状态特征用当前节点(某个输出位置可能的状态中的某个状态称为一个节点)的状态分数表示,转移特征用上一个节点到当前节点的转移分数表示。其损失函数定义如下:CRF损失函数的计算,需要用到真实路径分数(包括状态分数和转移分数),其他所有可能的路径的分数(包括状态分数和转移分数)。这里的路径用词性来举例就是一句话对应的词性序列,真实路径表示真实的词性序列,其他可能的路径表示其他的词性序列。这里的分数就是指softmax之前的概率,或称为未规范化的概率。softmax的作用就是将一组数值转换成一组0-1之间的数值,这些数值的和为1,这样就可以表示概率了。对于词性标注来说,给定一句话和其对应的词性序列,那么其似然性的计算公式(CRF的参数化公式)如下:l表示某个词上定义的状态特征的个数,k表示转移特征的个数,i表示词在句子中的位置。tk和sl分别是转移特征函数和状态特征函数。λk和μl分别是转移特征函数和状态特征函数的权重系数,通过最大似然估计可以得到。上面提到的状态分数和转移分数都是非规范化的对数概率,所以概率计算都是加法,这里加上一个exp是为了将对数概率转为正常概率。实际计算时还会除以一个规范化因子Z(x),其实就是一个softmax过程。在只有CRF的情况下,上面说的2类特征函数都是人工设定好的。通俗的说就是人工设定了观测序列的特征。人为设定状态特征模板,比如设定“某个词是名词”等。人为设定转移特征模板,比如设定“某个词是名词时,上一个词是形容词”等。给定一句话的时候,就根据上面设定的特征模板来计算这句话的特征分数,计算的时候,如果这句话符合特征模板中的特征规则,则那个特征规则的值就为1,否则就为0。实体识别的表现取决于2种特征模板设定的好坏。所以如果我们能使用深度神经网络的方式,特征就可以由模型自己学习得到,这就是使用BERT+CRF的原因。命名实体识别中的BERT和CRF是怎么配合的?由BERT学习序列的状态特征,从而得到一个状态分数,该分数直接输入到CRF层,省去了人工设置状态特征模板。这里的状态特征是说序列某个位置可能对应的状态(命名实体识别中是指实体标注),状态分数是每个可能的状态的softmax前的概率(又称非规范化概率,或者直接称作分数),实体标注通常用BIO标注,B表示词的开始,I表示词的延续,O表示非实体词,比如下面的句子和其对应的实体标注(假设我们要识别的是人名和地点):小 明 爱 北 京 的 天 安 门 。 B-Person I-Person O B-Location I-Location O B-Location I-Location I-Location O也就是说BERT层学到了句子中每个字符最可能对应的实体标注是什么,这个过程是考虑到了每个字符左边和右边的上下文信息的,但是输出的最大分数对应的实体标注依然可能有误,不会100%正确的,出现B后面直接跟着B,后者标注以I开头了,都是有可能的,而降低这些明显不符规则的问题的情况的发生概率,就可以进一步提高BERT模型预测的准确性。此时就有人想到用CRF来解决这个问题。CRF算法中涉及到2种特征函数,一个是状态特征函数,计算状态分数,一个是转移特征函数,计算转移分数。前者只针对当前位置的字符可以被转换成哪些实体标注,后者关注的是当前位置和其相邻位置的字符可以有哪些实体标注的组合。BERT层已经将状态分数输出到CRF层了,所以CRF层还需要学习一个转移分数矩阵,该矩阵表示了所有标注状态之间的组合,比如我们这里有B-Person I-Person B-Location I-Location O 共5种状态,有时候还会在句子的开始和结束各加一个START 和 END标注,表示一个句子的开始和结束,那么此时就是7种状态了,那么2个状态(包括自己和自己)之间的组合就有7*7=49种,上面说的转移分数矩阵中的元素就是这49种组合的分数(或称作非规范化概率),表示了各个组合的可能性。这个矩阵一开始是随机初始化的,通过训练后慢慢会知道哪些组合更符合规则,哪些更不符合规则。从而为模型的预测带来类似如下的约束:句子的开头应该是“B-”或“O”,而不是“I-”。“B-label1 I-label2 I-label3…”,在该模式中,类别1,2,3应该是同一种实体类别。比如,“B-Person I-Person” 是正确的,而“B-Person I-Organization”则是错误的。“O I-label”是错误的,命名实体的开头应该是“B-”而不是“I-”。矩阵示意如下:为什么不能通过人工来判断标注规则并编写好修正逻辑呢?因为人工虽然能判断出预测的标注前后关系是否符合规则,但是无法知道如何对不符合规则的预测进行调整,比如我们知道句子的开头应该是“B-”或“O”,而不是“I-”,但是究竟是B-还是O呢?而且对于标注状态非常多的场景下,人工编写的工作量和逻辑是非常大且复杂的。CRF损失函数的计算,需要用到真实路径分数(包括状态分数和转移分数),其他所有可能的路径的分数(包括状态分数和转移分数)。其中真实路径的状态分数是根据训练得到的BERT模型的输出计算出来的,转移分数是从CRF层提供的转移分数矩阵得到的。其他路径的状态分数和转移分数其实也是这样计算的。其中的真实路径的计算原理,其实是使用了维特比算法,关于维特比算法,会在下一篇文章中介绍。总结命名实体识别中,BERT负责学习输入句子中每个字和符号到对应的实体标签的规律,而CRF负责学习相邻实体标签之间的转移规则。ok,本篇就这么多内容啦~,感谢阅读O(∩_∩)O。本博客内容来自公众号“程序员一一涤生”,欢迎扫码关注 o(∩_∩)o发布于 2019-11-30 14:54深度学习(Deep Learning)命名实体识别自然语言处理赞同 171 条评论分享喜欢收藏申请转载文章被以下专栏收录一起学爬虫Python3网络爬
CRF模型详解-CSDN博客
>CRF模型详解-CSDN博客
CRF模型详解
最新推荐文章于 2024-03-06 16:41:29 发布
phoenix-bai
最新推荐文章于 2024-03-06 16:41:29 发布
阅读量3w
收藏
189
点赞数
32
分类专栏:
NLP
文章标签:
机器学习
算法
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_40103562/article/details/109780449
版权
NLP
专栏收录该内容
2 篇文章
4 订阅
订阅专栏
条件随机场(CRF)是自然语言处理中的基础模型, 广泛用于分词, 实体识别和词性标注等场景. 随着深度学习的普及, BILSTM+CRF, BERT+CRF, TRANSFORMER+CRF等模型, 逐步亮相, 并在这些标注场景, 效果有显著的提升.
下面是我学习CRF的学心总结, 看了多篇知乎, paper, 和CRF++的实现代码后, 终于有了深刻的理解.
基础概念
首先, 一起看一下随机过程, 随机场, 马尔可夫随机场的定义, 在最后请出条件随机场.
随机过程: 设
T
T
T是一无限实数集, 把依赖于参数
t
∈
T
t\in T
t∈T的一族(无限多个)随机变量称为随机过程, 记为
X
(
t
)
,
t
∈
T
{X(t), t \in T}
X(t),t∈T
随机场: 从平面(随机过程)到向量空间(随机场) 若
T
T
T是
n
n
n维空间的某个子集, 即
t
t
t是一个
n
n
n维向量, 此时随机过程又称为随机场. 常见随机场有: 马尔可夫随机场(MRF), 吉布斯随机场(GRF), 条件随机场(CRF)和高斯随机场.
马尔可夫随机场: 具有马尔可夫性的随机场. 马尔可夫性:
P
(
Y
v
∣
Y
w
,
w
≠
v
)
=
P
(
Y
v
∣
Y
w
,
w
∼
v
)
P(Y_v|Y_w, w\neq v) = P(Y_v|Y_w, w \sim v)
P(Yv∣Yw,w=v)=P(Yv∣Yw,w∼v)
w
∼
v
w \sim v
w∼v表示在图
G
=
(
V
,
E
)
G=(V, E)
G=(V,E)中与顶点
v
v
v有边连接的所有顶点
w
w
w
w
≠
v
w \neq v
w=v表示顶点
v
v
v以外的所有顶点
Y
v
Y_v
Yv与
Y
w
Y_w
Yw为顶点
v
v
v与
w
w
w对应的随机变量
那么, 条件随机场是如何定义的呢?
条件随机场:
设
X
X
X与
Y
Y
Y是随机变量,
P
(
Y
∣
X
)
P(Y|X)
P(Y∣X)是在给定
X
X
X的条件下
Y
Y
Y的条件概率分布. 若随机变量
Y
Y
Y构成一个由无向图
G
=
(
V
,
E
)
G=(V, E)
G=(V,E)表示的马尔可夫随机场, 即
P
(
Y
v
∣
X
,
Y
w
,
w
≠
v
)
=
P
(
Y
v
∣
X
,
Y
w
,
w
∼
v
)
P(Y_v|X, Y_w, w \neq v) = P(Y_v|X, Y_w, w \sim v)
P(Yv∣X,Yw,w=v)=P(Yv∣X,Yw,w∼v) 对任意顶点
v
v
v成立, 则称条件概率分布
P
(
Y
∣
X
)
P(Y|X)
P(Y∣X)为条件随机场.
w
∼
v
w \sim v
w∼v表示在图
G
=
(
V
,
E
)
G=(V, E)
G=(V,E)中与顶点
v
v
v有边连接的所有顶点
w
w
w
w
≠
v
w \neq v
w=v表示顶点
v
v
v以外的所有顶点
Y
v
Y_v
Yv与
Y
w
Y_w
Yw为顶点
v
v
v与
w
w
w对应的随机变量
只基于Y序列做预测, 太单调了, 所以额外给出一个观测序列X, 帮助你更好的做决策. 这就是从马尔可夫随机场变成条件随机场的过程. 条件随机场中, "条件"指的是给定观测序列X的情况, 求状态序列Y的概率, 即输出的是条件概率分布. 而"随机场"指的是状态序列Y构成的随机场.
线性链条件随机场:
常见的标注场景, 如分词, 实体识别和词性标注等, 都是典型的线性链条件随机场. 那么什么是线性链条件随机场呢?
X
X
X和
Y
Y
Y有相同结构(线性表示的随机变量序列)的条件随机场就构成了线性链条件随机场. (这里指的线性, 指语言天然具有的先后顺序,成线性特性.)
定义: 设
X
=
(
X
1
,
X
2
,
.
.
.
,
X
n
)
X=(X_1, X_2, ..., X_n)
X=(X1,X2,...,Xn),
Y
=
(
Y
1
,
Y
2
,
.
.
.
,
Y
n
)
Y=(Y_1, Y_2, ..., Y_n)
Y=(Y1,Y2,...,Yn)均为线性表示的随机变量序列, 若在给定随机变量序列
X
X
X的条件下, 随机变量序列
Y
Y
Y的条件概率分布
P
(
Y
∣
X
)
P(Y|X)
P(Y∣X)构成条件随机场, 即满足马尔可夫性
P
(
Y
i
∣
X
,
Y
1
,
.
.
.
,
Y
i
−
1
,
Y
i
+
1
,
.
.
.
,
Y
n
)
=
P
(
Y
i
∣
X
,
Y
i
−
1
,
Y
i
+
1
)
P(Y_i|X, Y_1, ..., Y_{i-1}, Y_{i+1}, ..., Y_n) = P(Y_i|X, Y_{i-1}, Y_{i+1})
P(Yi∣X,Y1,...,Yi−1,Yi+1,...,Yn)=P(Yi∣X,Yi−1,Yi+1) 则称
P
(
Y
∣
X
)
P(Y|X)
P(Y∣X)为线性链条件随机场. 其中
i
=
(
1
,
2
,
.
.
n
)
i=(1,2,..n)
i=(1,2,..n), 在
i
=
1
i=1
i=1和
i
=
n
i=n
i=n时只考虑单边.
两种主要的线性链条件随机场的图结构如下: 由于线性链条件随机场应用非常广泛, 所以习惯把"线性链条件随机场"简称为条件随机场(CRF).
CRF公式
根据Hammersley Clifford定理, 一个无向图模型的概率, 可以表示为定义在图上所有最大团上的势函数的乘积. 那么, CRF的条件概率可以在因子分解下表示为: 线性链CRF的因子分解
如上图, 线性链CRF的因子分解, 根据函数类型, 可细化为:
P
(
y
∣
x
)
=
1
Z
(
x
)
exp
(
∑
i
,
k
λ
k
t
k
(
y
i
−
1
,
y
i
,
x
,
i
)
+
∑
i
,
l
μ
l
s
l
(
y
i
,
x
,
i
)
)
P(y|x) = \frac{1}{Z(x)} \exp (\sum_{i,k}\lambda_k t_k(y_{i-1}, y_i, x, i) + \sum_{i, l}\mu_l s_l (y_i, x, i))
P(y∣x)=Z(x)1exp(∑i,kλktk(yi−1,yi,x,i)+∑i,lμlsl(yi,x,i)) 其中,
Z
(
x
)
=
∑
y
exp
(
∑
i
,
k
λ
k
t
k
(
y
i
−
1
,
y
i
,
x
,
i
)
+
∑
i
,
l
μ
l
s
l
(
y
i
,
x
,
i
)
)
Z(x) = \sum_y \exp (\sum_{i,k}\lambda_k t_k(y_{i-1}, y_i, x, i) + \sum_{i,l}\mu_l s_l(y_i, x, i))
Z(x)=∑yexp(∑i,kλktk(yi−1,yi,x,i)+∑i,lμlsl(yi,x,i))
t
k
t_k
tk为转移特征函数, 在CRF++中转移特征由Bigram Template + 输入字(词)序列生成
s
l
s_l
sl为状态特征函数, 在CRF++中状态特征由Uigram Template + 输入字(词)序列生成
CRF中, 通常有两类特征函数, 分别为转移特征和状态特征. 状态特征表示输入序列与当前状态之间的关系. 转移特征表示前一个输出状态与当前输出状态之间的关系. CRF++中, 特征模板需要人工设定, 有一定的局限性. 若结合深度神经网络, 如BERT+CRF, 则特征就可由模型自动学习得到, 具体讲, BERT学习状态特征, CRF学习转移特征, 并用viterbi获取最优路径.
理论讲了很多, 接下来讲讲CRF的实现代码, 以牛逼的CRF++为例.
CRF++实现详解
https://taku910.github.io/crfpp/ 是开源的CRF++的工具源代码.
CRF++中, 核心功能分为模型训练和预测两部分.
模型训练
整个训练过程分为三步:
准备训练数据集通过定义特征模板, 自动生成特征函数集通过LBFGS算法, 学习特征函数的权重
训练数据集
训练数据集, 按照文档要求准备即可, 例子如下:
山 B_PROV 东 I_PROV 省 I_PROV 菏 B_CITY 泽 I_CITY 市 I_CITY 牡 B_DIST 丹 I_DIST 区 I_DIST 重 B_ROAD 庆 I_ROAD 路 I_ROAD
特征函数定义
特征模板格式:%x[row,col]。x可取U或B,对应两种类型。方括号里的编号用于标定特征来源,row表示相对当前位置的行,0即是当前行;col对应训练文件中的列。这里只使用第1列(编号0),即文字。若值为1, 则使用标签值.
定义特征模板:
# Unigram U00:%x[-2,0] U01:%x[-1,0] U02:%x[0,0] U03:%x[1,0] U04:%x[2,0] U05:%x[-2,0]/%x[-1,0]/%x[0,0] U06:%x[-1,0]/%x[0,0]/%x[1,0] U07:%x[0,0]/%x[1,0]/%x[2,0] U08:%x[-1,0]/%x[0,0] U09:%x[0,0]/%x[1,0] # Bigram B
特征函数, 前面提到过, 分为两种, 转移特征和状态特征. 特征模板中的Unigram or Bigram, 是针对输出序列
Y
Y
Y, 即标注label而言的. 如转移特征
t
k
(
y
i
−
1
,
y
i
,
x
,
i
)
t_k(y_{i-1}, y_i, x, i)
tk(yi−1,yi,x,i), 涉及到
y
i
−
1
y_{i-1}
yi−1和
y
i
y_i
yi, 所以叫Bigram特征. 状态特征
s
l
(
y
i
,
x
,
i
)
s_l(y_i, x, i)
sl(yi,x,i)只涉及到
y
i
y_i
yi, 所以叫Unigram特征.
以训练集中的第一行为例, 基于上面的特征模板, 生成10个特征 (此时还未与label进行结合):
U00:%x[-2, 0] =>“U00:_B-2” (这里的_B-2指的是beginning of sentence) U01:%x[-1, 0] => “U01:_B-1” (_B-1, _B-2均为指代) U02:%x[0, 0] => “U02:山” U05:%x[-2,0]/%x[-1,0]/%x[0,0] => “U05:_B-2/_B-1/山” U06:%x[-1,0]/%x[0,0]/%x[1,0] => “U06:_B-1/山/东” U07:%x[0,0]/%x[1,0]/%x[2,0] => “U07:山/东/省” U08:%x[-1,0]/%x[0,0] => “U08:_B-1/山” U09:%x[0,0]/%x[1,0] => “U09:山/东”
基于输入序列生成的单边特征, 再与标签集合(Y集合)结合, 生成最终的特征函数. 若特征类型为Unigram, 每行模板生成一组状态特征函数,数量是L*N 个,L是标签状态数。N是此行模板在训练集上展开后的去重后的样本数, 如:
func0 = if (output = B_PROV and feature=“U02:山”) return 1 else return 0 func1 = if (output = I_PROV and feature=“U02:山”) return 1 else return 0 func1 = if (output = B_CITY and feature=“U02:山”) return 1 else return 0 …
若特征类型为Bigram, 每行模板生成一组转移特征函数, 数量是L*L*N 个。经过训练后,这些函数的权值反映了上一个节点的标签对当前节点的影响。例如对应 B00:%x[0, 0]:
func0 = if (prev_output = B_PROV and output = B_PROV and feature=“U02:山”) return 1 else return 0 func1 = if (prev_output = B_PROV and output = I_PROV and feature=“U02:山”) return 1 else return 0 func2 = if (prev_output = B_PROV and output = B_CITY and feature=“U02:山”) return 1 else return 0 … funcN = if (prev_output = I_DIST and output = B_PROV and feature=“U02:山”) return 1 else return 0 …
现实中, 若Bigram模板的定义中, 涉及输入序列, 容易导致特征数巨大, 所以默认只使用简单的B, 即简化的转移特征函数
t
k
(
y
i
−
1
,
y
i
)
t_k(y_{i-1}, y_i)
tk(yi−1,yi), 与输入序列无关.
模型预测
HMM, MEMM vs. CRF对比
优惠劵
phoenix-bai
关注
关注
32
点赞
踩
189
收藏
觉得还不错?
一键收藏
知道了
1
评论
CRF模型详解
条件随机场(CRF)是自然语言处理中的基础模型, 广泛用于分词, 实体识别和词性标注等场景. 随着深度学习的普及, BILSTM+CRF, BERT+CRF, TRANSFORMER+CRF等模型, 逐步亮相, 并在这些标注场景, 效果有显著的提升.下面是我学习CRF的学心总结, 看了多篇知乎, paper, 和CRF++的实现代码后, 终于有了深刻的理解.基础概念首先, 一起看一下随机过程, 随机场, 马尔可夫随机场的定义, 在最后请出条件随机场.随机过程:设 TTT是一无限实数集
复制链接
扫一扫
专栏目录
CRF原理详解及实战
06-19
原理结合公式推导,让大家更容易理解条件随机场的本质原理,并使用pytorch ner序列标注模型实战CRF
CRF-NER模型
12-11
NER技术实现命名实体识别,可以识别出文本中的人名地名年份组织机构名
1 条评论
您还未登录,请先
登录
后发表或查看评论
最通俗易懂的BiLSTM-CRF模型中的CRF层讲解
datayx的文章
09-16
2432
向AI转型的程序员都关注了这个号????????????人工智能大数据与深度学习 公众号:datayx本文翻译自GitHub博客上的原创文章,结尾有原文链接。文章没有晦涩的数学公式,而是...
理解条件随机场(转)
dianwei0041的博客
07-24
2152
理解条件随机场最好的办法就是用一个现实的例子来说明它。但是目前中文的条件随机场文章鲜有这样干的,可能写文章的人都是大牛,不屑于举例子吧。于是乎,我翻译了这篇文章。希望对其他伙伴有所帮助。原文在这里[http://blog.echen.me/2012/01/03/introduction-to-conditional-random-fields/]
想直接看英文的朋...
条件随机场(CRF)的详细解释
热门推荐
deephub
03-08
1万+
条件随机场(CRF)结合了最大熵模型和隐马尔可夫模型的特点,是一种无向图模型,其中相邻的上下文信息或状态会影响当前预测,常用于标注或分析序列资料,如自然语言文字或是生物序列
[DL]CRF模型解读
sinat_41506268的博客
09-08
223
CRF(conditional random field,条件随机场)是给定一组输入随机变量条件下另一组输出随机变量的条件概率分布模型,其特点是假设输出随机变量构成马尔科夫随机场。
想要理解CRF模型,需要先对概率图相关概念有所了解,并与HMM(Hidden Markov Model,隐马尔科夫模型)一起比较学习。(本文只解读思想、原理,不涉及求解过程)
CRF总结
weixin_33918357的博客
04-06
434
条件随机场理解
随机场理解
先从随机变量说起。
对于一个时间集合T内,每一个时间点t点,X(t)的数值都是随机的,那么X(t)称为随机过程。x(t)是依赖于时间的一组随机变量。它的分布函数叫做x(t)的一维概率分布函数。
如果有一个变量,依赖于两个时间t1,t2,就称为二维随机变量,有二维概率分布函数。-。。。。。到n维概率分布函数
两个随机过程之间的数字特征有:方差,期望,均值,协方差函数,互...
CRF模型
m0_57332527的博客
07-10
1859
用简单的俗话来理解CRF以便于更好的应用
nlp基础—12.LSTM-CRF模型介绍
柳杰的博客
07-08
4781
文章目录引言一、模型介绍1. LSTM模型2. BI-LSTM(双向LSTM)模型3.CRF模型4. LSTM+CRF模型5. BiLSTM+CRF模型
引言
本文讨论的是序列标注问题,所使用的模型是将两种现有的模型(LSTM+CRF)进行拼接,一种模型是双向LSTM模型,一种是CRF模型。下面介绍如何结合LSTM和CRF用于sequence tagging,并且对这些结合的效果进行测量。
一、模型介绍
本篇文章涉及以下几种模型:LSTM,BI-LSTM,CRF,LSTM+CRF,BI-LSTM+C
CRF详解(NLP向)
Xu_Wave
04-27
5779
CRF详解(NLP向)
CRF模型——条件随机场模型
MyHeartWillGoOn
12-08
2518
文章目录CRF含义作用隐含假设使用过程维特比算法与HMM的差异
CRF
含义
CRF(Conditional Random Fields), 中文称作条件随机场, 同HMM一样, 它一般也以文本序列数据为输入, 以该序列对应的隐含序列为输出.
作用
同HMM一样, 在NLP领域, CRF用来解决文本序列标注问题. 如分词, 词性标注, 命名实体识别.
隐含假设
隐含序列中每个单元的可能性只与上一个单元有关
使用过程
首先, CRF模型表示为: lambda = CRF(w1, w2, …, w
LSTM+CRF模型项目完整代码
12-20
LSTM+CRF模型项目完整代码
keras 解决加载lstm+crf模型出错的问题
09-16
主要介绍了keras 解决加载lstm+crf模型出错的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
基于BERT-BiLSTM-CRF模型的中文实体识别
05-06
为解决该问题, 本文提出了一种基于BERT-BiLSTM-CRF模型的研究方法. 首先通过BERT模型预处理生成基于上下文信息的词向量, 其次将训练出来的词向量输入BiLSTM-CRF模型做进一步训练处理. 实验结果表明, 该模型在MSRA...
基于机器学习的垃圾分类
xia_hua_yan的博客
03-06
779
垃圾分类有减少环境污染、节省土地资源、再生资源的利用、提高民众价值观念等的好处,在倡导绿色生活,注重环境保护的今天,正确的垃圾分类和处理对我们的生态环境显得尤为重要。在国外很多国家,经过了几十年的不断改进发展,已经有了一套很严格规范的垃圾分类政策,民众也形成了很强的垃圾分类意识,并且在人工智能广泛应用的今天,也有很多公司将人工智能与自动垃圾分类相结合。前几年波兰创业公司Bin-e公司开发了一种全新的人工智能垃圾桶,用户只需要在垃圾桶前扫描一下垃圾,舱门便会打开。
gan, pixel2pixel, cyclegan, srgan图像超分辨率
最新发布
tony365的博客
03-06
553
上图的左上部分如下就是1个 gan, gan生成目标B, 但是没有label条件约束,因此pixel2pixel中的L1损失就没法使用了,那么如何保持生成的图像目标图像的一致性呢?那么生成器呢,除了原来的损失,再加上一个L1损失。就是通过添加限制条件,来控制GAN生成数据的特征(类别),比如之前我们的随机噪声可以生成数字0-9但是我们并不能控制生成的是0还是1,还是2.噪声z 输入生成器,希望判别器得到 1, 即希望生成器生成的图 输入判别器时 是 1,即希望生成器生成的图,和real更接近。
【机器学习300问】27、高偏差与高方差是什么?他们对评估机器学习模型起何作用?
qq_39780701的博客
03-06
985
高偏差和高方差的定义,高偏差、高方差和欠拟合、过拟合什么关系?用人类表现作为模型评估的参考标准,如何知道当前的模型存在高偏差问题还是高方差问题?降低偏差和方差的策略
【机器学习】三要素——数据、模型、算法
2302_76305195的博客
02-28
609
机器学习,就是算法通过在数据上进行运算产生模型。
GFP-GAN环境搭建&推理测试
zzq1989_的专栏
03-06
475
近期,文生图,wav2lip很火,文生图,见识的太多,不多说了。wav2lip其通过语音驱动唇部动作并对视频质量进行修复,里面一般涉及到三个步骤,文本到语音转化,语音驱动唇部动作,图像质量修复。最后一步骤涉及到图像质量修复,考虑到之前做过基于GFP-GAN相关的工作,在此,总结汇总下。在 wav2lip 中扮演视频质量判别器的任务,负责对嘴唇修复后的图像帧进行质量修复,提供更高质量的视频效果。OK,让我们开始吧。# 安装realesrgan包,用于增强没有人脸情况下的背景,项目刚好需要,就安装了。
BiLSTM-CRF模型原理详解
04-29
BiLSTM-CRF模型是一种用于序列标注任务的深度学习模型,它结合了双向长短时记忆网络(BiLSTM)和条件随机场(CRF)两个模型的优点,能够有效地解决序列标注问题。
BiLSTM模型是一种循环神经网络,它能够对序列中的每个元素进行处理,并利用上下文信息来预测当前元素的标签。而CRF模型则是一种统计学习方法,它将标注问题看作是一个序列标注的联合概率分布问题,通过最大化联合概率分布来得到最优的标注序列。
BiLSTM-CRF模型将BiLSTM和CRF结合起来,首先使用BiLSTM网络对序列中的每个元素进行特征提取,然后将这些特征作为CRF模型的输入,通过CRF模型来计算标注序列的联合概率分布,并得到最优的标注序列。
具体来说,BiLSTM-CRF模型的输入是一个序列,每个元素都包含了一组特征向量。这些特征向量可以包括单词、词性、上下文信息等。BiLSTM网络通过对序列中每个元素的特征向量进行处理,得到一个隐层向量表示。由于BiLSTM是双向的,因此对于每个元素,它的隐层向量表示将包括它本身以及它前后的上下文信息。
接下来,CRF模型将这些隐层向量作为输入,计算标注序列的联合概率分布。具体来说,CRF模型会考虑当前元素的标签以及前一个元素的标签,通过定义一个转移矩阵来计算它们之间的转移概率。同时,CRF模型还会考虑每个元素的标签的概率分布,通过定义一个发射矩阵来计算每个元素的标签的概率分布。最终,CRF模型会选择一个最优的标注序列,使得它的联合概率分布最大化。
BiLSTM-CRF模型在序列标注任务中取得了很好的效果,特别是在自然语言处理领域中的实体识别、命名实体识别、词性标注等任务中,都得到了较好的表现。
“相关推荐”对你有帮助么?
非常没帮助
没帮助
一般
有帮助
非常有帮助
提交
phoenix-bai
CSDN认证博客专家
CSDN认证企业博客
码龄7年
暂无认证
14
原创
25万+
周排名
109万+
总排名
3万+
访问
等级
291
积分
12
粉丝
35
获赞
2
评论
195
收藏
私信
关注
热门文章
CRF模型详解
30487
深度学习模型CPT的环境配置经验
1997
numpy编译打包: 集成openblas/atlas加速库
1494
ffmpeg 利用AVIOContext自定义IO 输出结果写buffer
1094
隐马尔可夫模型详解 (英文版)
689
分类专栏
NLP
2篇
工程wiki
3篇
笔记
C++
1篇
语音识别
4篇
数学公式
PRML
3篇
最新评论
CRF模型详解
zhang,:
干的漂亮
最新文章
kaldi解码中, 排查静音对应到“啊“的异常问题
深度学习模型CPT的环境配置经验
ffmpeg 利用AVIOContext自定义IO 输出结果写buffer
2022年2篇
2021年4篇
2020年3篇
2019年6篇
目录
目录
分类专栏
NLP
2篇
工程wiki
3篇
笔记
C++
1篇
语音识别
4篇
数学公式
PRML
3篇
目录
评论 1
被折叠的 条评论
为什么被折叠?
到【灌水乐园】发言
查看更多评论
添加红包
祝福语
请填写红包祝福语或标题
红包数量
个
红包个数最小为10个
红包总金额
元
红包金额最低5元
余额支付
当前余额3.43元
前往充值 >
需支付:10.00元
取消
确定
下一步
知道了
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝
规则
hope_wisdom 发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额
0
抵扣说明:
1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。
余额充值
CRF++: Yet Another CRF toolkit
CRF++: Yet Another CRF toolkit
CRF++: Yet Another CRF toolkit
Introduction
CRF++ is a simple, customizable, and open source
implementation of Conditional Random Fields (CRFs)
for segmenting/labeling sequential data. CRF++ is designed for generic purpose and will be applied to a variety of NLP tasks, such as
Named Entity Recognition, Information Extraction and Text Chunking.
Table of contents
Features
News
Download
Source
Binary package for MS-Windows
Installation
Usage
Training and Test file formats
Preparing feature templates
Training (encoding)
Testing (decoding)
Case studies
Useful Tips
To do
Links
Features
Can redefine feature sets
Written in C++ with STL
Fast training based on LBFGS, a quasi-newton algorithm
for large scale numerical optimization problem
Less memory usage both in training and testing
encoding/decoding in practical time
Can perform n-best outputs
Can perform single-best MIRA training
Can output marginal probabilities for all candidates
Available as an open source software
News
2013-02-13: CRF++ 0.58 Released
Added createModelFromArray() method to load model file from fixed buffer.
Added getTemplate() method to get template string.
2012-03-25
Fixed build issue around libtool.
Fixed C++11 compatible issue.
2012-02-24
Added CRFPP:Tagger::set_model() method.
Fixed minor bugs
2012-02-15: CRF++ 0.55
Added new CRFPP:Model class so that multiple threads can share single CRF++ model.
Added Tagger::set_penalty and Tagger::penalty() method for dual decompositon decoding
Fixed crash bug on Windows
Fixed minor bugs
2010-05-16: CRF++ 0.54 Released
fixed the bug in L1 regularization. Reported by Fujii Yasuhisa
2009-05-06: CRF++ 0.5 Released
fixed build failure on libtool
2009-04-19: CRF++ 0.52
Code clean up
replaced obsolete sstream with stringstream
2007-07-12: CRF++ 0.51
Fixed a compilation error on gcc 4.3
2007-12-09: CRF++ 0.50
Bug fix in --convert mode (Could not generate model from text file)
2007-08-18: CRF++ 0.49
Added setter/getter for nbest, cost_factor and vlevel to API
2007-07-07: CRF++ 0.48 Released
Support L1-CRF. use -a CRF-L1 option to enable L1 regularization.
2007-03-07: CRF++ 0.47 Released
Fixed a bug in MIRA training
2007-02-12: CRF++ 0.46 Released
Changed the licence from LGPL to LGPL/BSD dual
license
Perl/Ruby/Python/Java binding supports (see
perl/ruby/python/java directory respectively)
Code refactoring
2006-11-26: CRF++ 0.45
Support 1-best MIRA training (use -a MIRA option)
2006-08-18: CRF++ 0.44
Fixed a bug in feature extraction
Allowed redundant spaces in training/test files
Determined real column size by looking at template
Added sample code of API (sdk/example.cpp)
Described usage of each API function (crfpp.h)
2006-08-07: CRF++ 0.43
implemented several API functions to get lattice
information
added -c option to control cost-factor
2006-03-31: CRF++ 0.42
Fixed a bug in feature extraction
2006-03-30: CRF++ 0.41
Support parallel training
2006-03-21: CRF++ 0.40
Fixed a fatal memory leak bug
make CRF++ API
2005-10-29: CRF++ 0.3
added -t option that enables you to have not only binary
model but also text model
added -C option for converting a text model to a binary model
2005-07-04: CRF++ 0.2
Released
Fixed several bugs
2005-05-28: CRF++ 0.1
Released
Initial Release
Download
CRF++ is free software; you can redistribute it
and/or modify it under the terms of the GNU Lesser General
Public License or new BSD License
Please let me know if you use
CRF++ for research purpose or find any research
publications where CRF++ is applied.
Source
CRF++-0.58.tar.gz: HTTP
Binary package for MS-Windows
HTTP
Installation
Requirements
C++ compiler (gcc 3.0 or higher)
How to make
% ./configure
% make
% su
# make install
You can change default install path by using --prefix
option of configure script.
Try --help option for finding out other options.
Usage
Training and Test file formats
Both the training file and the test file need to be in a
particular format for CRF++ to work properly.
Generally speaking, training and test file must consist of
multiple tokens. In addition, a token
consists of multiple (but fixed-numbers) columns. The
definition of tokens depends on tasks, however, in
most of typical cases, they simply correspond to
words. Each token must be represented in one line,
with the columns separated by white space (spaces or
tabular characters). A sequence of token becomes a
sentence. To identify the boundary between
sentences, an empty line is put.
You can give as many columns as you like, however the
number of columns must be fixed through all tokens.
Furthermore, there are some kinds of "semantics" among the
columns. For example, 1st column is 'word', second column
is 'POS tag' third column is 'sub-category of POS' and so
on.
The last column represents a true answer tag which is going
to be trained by CRF.
Here's an example of such a file: (data for CoNLL shared
task)
He PRP B-NP
reckons VBZ B-VP
the DT B-NP
current JJ I-NP
account NN I-NP
deficit NN I-NP
will MD B-VP
narrow VB I-VP
to TO B-PP
only RB B-NP
# # I-NP
1.8 CD I-NP
billion CD I-NP
in IN B-PP
September NNP B-NP
. . O
He PRP B-NP
reckons VBZ B-VP
..
There are 3 columns for each token.
The word itself (e.g. reckons);
part-of-speech associated with the word (e.g. VBZ);
Chunk(answer) tag represented in IOB2 format;
The following data is invalid, since the number of
columns of second and third are 2. (They have no POS
column.) The number of columns should be fixed.
He PRP B-NP
reckons B-VP
the B-NP
current JJ I-NP
account NN I-NP
..
Preparing feature templates
As CRF++ is designed as a general purpose tool, you have to
specify the feature templates in advance. This file describes
which features are used in training and testing.
Template basic and macro
Each line in the template file denotes one template.
In each template, special macro %x[row,col] will be
used to specify a token in the input data. row specfies the
relative position from the current focusing token
and col specifies the absolute position of the column.
Here you can find some examples for the replacements
Input: Data
He PRP B-NP
reckons VBZ B-VP
the DT B-NP << CURRENT TOKEN
current JJ I-NP
account NN I-NP
template
expanded feature
%x[0,0]
the
%x[0,1]
DT
%x[-1,0]
reckons
%x[-2,1]
PRP
%x[0,0]/%x[0,1]
the/DT
ABC%x[0,1]123
ABCDT123
Template type
Note also that there are two types of templates.
The types are specified with the first character of templates.
Unigram template: first character, 'U'
This is a template to describe unigram features.
When you give a template "U01:%x[0,1]", CRF++ automatically
generates a set of feature functions (func1 ... funcN) like:
func1 = if (output = B-NP and feature="U01:DT") return 1 else return 0
func2 = if (output = I-NP and feature="U01:DT") return 1 else return 0
func3 = if (output = O and feature="U01:DT") return 1 else return 0
....
funcXX = if (output = B-NP and feature="U01:NN") return 1 else return 0
funcXY = if (output = O and feature="U01:NN") return 1 else return 0
...
The number of feature functions generated by a template amounts to
(L * N), where L is the number of output classes and N is the
number of unique string expanded from the given template.
Bigram template: first character, 'B'
This is a template to describe bigram features.
With this template, a combination of the current output token and previous output token
(bigram) is automatically generated. Note that this type of template generates a total of
(L * L * N) distinct features, where L is the
number of output classes and N is the number
of unique features generated by the templates.
When the number of classes is large, this type of templates would produce
a tons of distinct features that would cause inefficiency both
in training/testing.
What is the diffrence between unigram and bigram features?
The words unigram/bigram are confusing, since a macro for unigram-features
does allow you to write word-level bigram like %x[-1,0]%x[0,0]. Here,
unigram and bigram features mean uni/bigrams of output tags.
unigram: |output tag| x |all possible strings expanded with a macro|
bigram: |output tag| x |output tag| x |all possible strings expanded with a macro|
Identifiers for distinguishing relative positions
You also need to put an identifier in templates when relative positions of
tokens must be distinguished.
In the following case, the macro "%x[-2,1]" and "%x[1,1]" will be replaced
into "DT". But they indicates different "DT".
The DT B-NP
pen NN I-NP
is VB B-VP << CURRENT TOKEN
a DT B-NP
To distinguish both two, put an unique identifier (U01: or U02:) in the
template:
U01:%x[-2,1]
U02:%x[1,1]
In this case both two templates are regarded as different ones, as
they are expanded into different features, "U01:DT" and "U02:DT".
You can use any identifier whatever you like, but
it is useful to use numerical numbers to manage them, because they simply
correspond to feature IDs.
If you want to use "bag-of-words" feature, in other words,
not to care the relative position of features, You don't need to
put such identifiers.
Example
Here is the template example for CoNLL 2000 shared task and Base-NP chunking
task. Only one bigram template ('B') is used. This means that
only combinations of previous output token and current token are
used as bigram features. The lines starting from # or empty lines are
discarded as comments
# Unigram
U00:%x[-2,0]
U01:%x[-1,0]
U02:%x[0,0]
U03:%x[1,0]
U04:%x[2,0]
U05:%x[-1,0]/%x[0,0]
U06:%x[0,0]/%x[1,0]
U10:%x[-2,1]
U11:%x[-1,1]
U12:%x[0,1]
U13:%x[1,1]
U14:%x[2,1]
U15:%x[-2,1]/%x[-1,1]
U16:%x[-1,1]/%x[0,1]
U17:%x[0,1]/%x[1,1]
U18:%x[1,1]/%x[2,1]
U20:%x[-2,1]/%x[-1,1]/%x[0,1]
U21:%x[-1,1]/%x[0,1]/%x[1,1]
U22:%x[0,1]/%x[1,1]/%x[2,1]
# Bigram
B
Training (encoding)
Use crf_learn command:
% crf_learn template_file train_file model_file
where template_file and train_file
are the files you need to prepare in advance.
crf_learn generates the trained model file in
model_file.
crf_learn outputs the following information.
CRF++: Yet Another CRF Tool Kit
Copyright(C) 2005 Taku Kudo, All rights reserved.
reading training data: 100.. 200.. 300.. 400.. 500.. 600.. 700.. 800..
Done! 1.94 s
Number of sentences: 823
Number of features: 1075862
Number of thread(s): 1
Freq: 1
eta: 0.00010
C: 1.00000
shrinking size: 20
Algorithm: CRF
iter=0 terr=0.99103 serr=1.00000 obj=54318.36623 diff=1.00000
iter=1 terr=0.35260 serr=0.98177 obj=44996.53537 diff=0.17161
iter=2 terr=0.35260 serr=0.98177 obj=21032.70195 diff=0.53257
iter=3 terr=0.23879 serr=0.94532 obj=13642.32067 diff=0.35138
iter=4 terr=0.15324 serr=0.88700 obj=8985.70071 diff=0.34134
iter=5 terr=0.11605 serr=0.80680 obj=7118.89846 diff=0.20775
iter=6 terr=0.09305 serr=0.72175 obj=5531.31015 diff=0.22301
iter=7 terr=0.08132 serr=0.68408 obj=4618.24644 diff=0.16507
iter=8 terr=0.06228 serr=0.59174 obj=3742.93171 diff=0.18953
iter: number of iterations processed
terr: error rate with respect to tags. (# of error tags/# of all tag)
serr: error rate with respect to sentences. (# of error sentences/#
of all sentences)
obj: current object value. When this value converges to a
fixed point, CRF++ stops the iteration.
diff: relative difference from the previous object value.
There are 4 major parameters to control the training condition
-a CRF-L2 or CRF-L1:
Changing the regularization algorithm. Default setting is L2.
Generally speaking, L2 performs slightly better than L1, while
the number of non-zero features in L1 is drastically smaller than
that in L2.
-c float:
With this option, you can change the hyper-parameter for the CRFs.
With larger C value, CRF tends to overfit to the give training corpus.
This parameter trades the balance between overfitting and
underfitting. The results will significantly be influenced by
this parameter. You can find an optimal value by using
held-out data or more general model selection method such as
cross validation.
-f NUM:
This parameter sets the cut-off threshold for the features.
CRF++ uses the features that occurs no less than NUM times
in the given training data. The default value is 1.
When you apply CRF++ to large data, the number of unique features
would amount to several millions. This option is useful in such cases.
-p NUM:
If the PC has multiple CPUs, you can make the training faster
by using multi-threading. NUM is the number of threads.
Here is the example where these two parameters are used.
% crf_learn -f 3 -c 1.5 template_file train_file model_file
Since version 0.45, CRF++ supports single-best MIRA training.
MIRA training is used when -a MIRA option is set.
% crf_learn -a MIRA template train.data model
CRF++: Yet Another CRF Tool Kit
Copyright(C) 2005 Taku Kudo, All rights reserved.
reading training data: 100.. 200.. 300.. 400.. 500.. 600.. 700.. 800..
Done! 1.92 s
Number of sentences: 823
Number of features: 1075862
Number of thread(s): 1
Freq: 1
eta: 0.00010
C: 1.00000
shrinking size: 20
Algorithm: MIRA
iter=0 terr=0.11381 serr=0.74605 act=823 uact=0 obj=24.13498 kkt=28.00000
iter=1 terr=0.04710 serr=0.49818 act=823 uact=0 obj=35.42289 kkt=7.60929
iter=2 terr=0.02352 serr=0.30741 act=823 uact=0 obj=41.86775 kkt=5.74464
iter=3 terr=0.01836 serr=0.25881 act=823 uact=0 obj=47.29565 kkt=6.64895
iter=4 terr=0.01106 serr=0.17011 act=823 uact=0 obj=50.68792 kkt=3.81902
iter=5 terr=0.00610 serr=0.10085 act=823 uact=0 obj=52.58096 kkt=3.98915
iter=0 terr=0.11381 serr=0.74605 act=823 uact=0 obj=24.13498 kkt=28.00000
...
iter, terr, serror: same as CRF training
act: number of active examples in working set
uact: number of examples whose dual parameters reach soft margin
upper-bound C. 0 uact suggests that given training data was
linear separable
obj: current object value, ||w||^2
kkt: max kkt violation value. When it gets 0.0, MIRA training finishes
There are some parameters to control the MIRA training condition
-c float:
Changes soft margin parameter, which is an analogue to the soft margin
parameter C in Support Vector Machines.
The definition is basically the same as -c option in CRF training.
With larger C value, MIRA tends to overfit to the give training
corpus.
-f NUM:
Same as CRF
-H NUM:
Changes shrinking size. When a training sentence is not used
in updating parameter vector NUM times, we can consider that the
instance doesn't contribute training any more. MIRA tries to
remove such instances. The process is called
"shrinking". When setting smaller NUM, shrinking occurs in early
stage, which drastically reduces
training time. However, too small NUM is not recommended.
When training finishes, MIRA tries to go through all training
examples again to know whether or not all KKT conditions are really
satisfied. Too small NUM would increase the chances of recheck.
Testing (decoding)
Use crf_test command:
% crf_test -m model_file test_files ...
where model_file is the file crf_learncreates.
In the testing, you don't need to specify the template file,
because the model file has the same information for the template.
test_file is the test data you want to assign sequential tags.
This file has to be written in the same format as training file.
Here is an output of crf_test:
% crf_test -m model test.data
Rockwell NNP B B
International NNP I I
Corp. NNP I I
's POS B B
Tulsa NNP I I
unit NN I I
..
The last column is given (estimated) tag.
If the 3rd column is true answer tag , you can evaluate the accuracy
by simply seeing the difference between the 3rd and 4th columns.
verbose level
The -v option sets verbose level. default
value is 0. By increasing the level, you can have an
extra information from CRF++
level 1
You can also have marginal probabilities for each tag
(a kind of confidece measure for each output tag)
and a conditional probably for the output (confidence measure for
the entire output).
% crf_test -v1 -m model test.data| head
# 0.478113
Rockwell NNP B B/0.992465
International NNP I I/0.979089
Corp. NNP I I/0.954883
's POS B B/0.986396
Tulsa NNP I I/0.991966
...
The first line "# 0.478113" shows the conditional probably for the output.
Also, each output tag has a probability represented like "B/0.992465".
level 2
You can also have marginal probabilities for all other candidates.
% crf_test -v2 -m model test.data
# 0.478113
Rockwell NNP B B/0.992465 B/0.992465 I/0.00144946 O/0.00608594
International NNP I I/0.979089 B/0.0105273 I/0.979089 O/0.0103833
Corp. NNP I I/0.954883 B/0.00477976 I/0.954883 O/0.040337
's POS B B/0.986396 B/0.986396 I/0.00655976 O/0.00704426
Tulsa NNP I I/0.991966 B/0.00787494 I/0.991966 O/0.00015949
unit NN I I/0.996169 B/0.00283111 I/0.996169 O/0.000999975
..
N-best outputs
With the -n option, you can obtain N-best results
sorted by the conditional probability of CRF.
With n-best output mode, CRF++ first gives one additional line like "# N prob", where N means that
rank of the output starting from 0 and prob denotes the conditional
probability for the output.
Note that CRF++ sometimes
discards enumerating N-best results if it cannot find candidates any
more. This is the case when you give CRF++ a short
sentence.
CRF++ uses a combination of forward Viterbi and backward A* search. This combination
yields the exact list of n-best results.
Here is the example of the N-best results.
% crf_test -n 20 -m model test.data
# 0 0.478113
Rockwell NNP B B
International NNP I I
Corp. NNP I I
's POS B B
...
# 1 0.194335
Rockwell NNP B B
International NNP I I
Tips
CRF++ uses the exactly same data format as YamCha uses.
You may use both two toolkits for an input data and compare the
performance between CRF and SVM
The output of CRF++ is also compatible to CoNLL 2000 shared task.
This allows us to use the perl script
conlleval.pl to
evaluate system outputs. This script is very useful and
give us a list of F-measures for all chunk types
Case studies
In the example directories, you can find three case studies, baseNP
chunking, Text Chunking, and Japanese named entity recognition, to use CRF++.
In each directory, please try the following commands
% crf_learn template train model
% crf_test -m model test
To Do
Support semi-Markov
CRF
Support
piece-wise CRF
Provide useful C++/C API (Currently no APIs are available)
References
J. Lafferty, A. McCallum, and F. Pereira.
Conditional random fields: Probabilistic models for segmenting and
labeling sequence data, In Proc. of ICML, pp.282-289, 2001
F. Sha and F. Pereira. Shallow
parsing with conditional random fields, In Proc. of HLT/NAACL 2003
NP chunking
CoNLL
2000 shared task: Chunking
$Id: index.html,v 1.23 2003/01/06 13:11:21 taku-ku Exp
$;
taku@chasen.org
NLP第15课:基于 CRF 的中文命名实体识别模型实现 - 知乎
NLP第15课:基于 CRF 的中文命名实体识别模型实现 - 知乎切换模式写文章登录/注册NLP第15课:基于 CRF 的中文命名实体识别模型实现刘剑成都吾同蜀下网络科技股份有限公司 技术总监命名实体识别在越来越多的场景下被应用,如自动问答、知识图谱等。非结构化的文本内容有很多丰富的信息,但找到相关的知识始终是一个具有挑战性的任务,命名实体识别也不例外。前面我们用隐马尔可夫模型(HMM)自己尝试训练过一个分词器,其实 HMM 也可以用来训练命名实体识别器,但在本文,我们讲另外一个算法——条件随机场(CRF),来训练一个命名实体识别器。浅析条件随机场(CRF)条件随机场(Conditional Random Fields,简称 CRF)是给定一组输入序列条件下另一组输出序列的条件概率分布模型,在自然语言处理中得到了广泛应用。首先,我们来看看什么是随机场。“随机场”的名字取的很玄乎,其实理解起来不难。随机场是由若干个位置组成的整体,当按照某种分布给每一个位置随机赋予一个值之后,其全体就叫做随机场。还是举词性标注的例子。假如我们有一个十个词形成的句子需要做词性标注。这十个词每个词的词性可以在我们已知的词性集合(名词,动词……)中去选择。当我们为每个词选择完词性后,这就形成了一个随机场。了解了随机场,我们再来看看马尔科夫随机场。马尔科夫随机场是随机场的特例,它假设随机场中某一个位置的赋值仅仅与和它相邻的位置的赋值有关,和与其不相邻的位置的赋值无关。继续举十个词的句子词性标注的例子。如果我们假设所有词的词性只和它相邻的词的词性有关时,这个随机场就特化成一个马尔科夫随机场。比如第三个词的词性除了与自己本身的位置有关外,还只与第二个词和第四个词的词性有关。理解了马尔科夫随机场,再理解 CRF 就容易了。CRF 是马尔科夫随机场的特例,它假设马尔科夫随机场中只有 X 和 Y 两种变量,X 一般是给定的,而 Y 一般是在给定 X 的条件下我们的输出。这样马尔科夫随机场就特化成了条件随机场。在我们十个词的句子词性标注的例子中,X 是词,Y 是词性。因此,如果我们假设它是一个马尔科夫随机场,那么它也就是一个 CRF。对于 CRF,我们给出准确的数学语言描述:设 X 与 Y 是随机变量,P(Y|X) 是给定 X 时 Y 的条件概率分布,若随机变量 Y 构成的是一个马尔科夫随机场,则称条件概率分布 P(Y|X) 是条件随机场。基于 CRF 的中文命名实体识别模型实现在常规的命名实体识别中,通用场景下最常提取的是时间、人物、地点及组织机构名,因此本模型也将提取以上四种实体。1.开发环境。本次开发所选用的环境为:Sklearn_crfsuite Python 3.6 Jupyter Notebook 2.数据预处理。本模型使用人民日报1998年标注数据,进行预处理。语料库词性标记中,对应的实体词依次为 t、nr、ns、nt。对语料需要做以下处理:将语料全角字符统一转为半角; 合并语料库分开标注的姓和名,例如:温/nr 家宝/nr; 合并语料库中括号中的大粒度词,例如:[国家/n 环保局/n]nt; 合并语料库分开标注的时间,例如:(/w 一九九七年/t 十二月/t 三十一日/t )/w。 首先引入需要用到的库:import re
import sklearn_crfsuite
from sklearn_crfsuite import metrics
from sklearn.externals import joblib数据预处理,定义 CorpusProcess 类,我们还是先给出类实现框架:class CorpusProcess(object):
def __init__(self):
"""初始化"""
pass
def read_corpus_from_file(self, file_path):
"""读取语料"""
pass
def write_corpus_to_file(self, data, file_path):
"""写语料"""
pass
def q_to_b(self,q_str):
"""全角转半角"""
pass
def b_to_q(self,b_str):
"""半角转全角"""
pass
def pre_process(self):
"""语料预处理 """
pass
def process_k(self, words):
"""处理大粒度分词,合并语料库中括号中的大粒度分词,类似:[国家/n 环保局/n]nt """
pass
def process_nr(self, words):
""" 处理姓名,合并语料库分开标注的姓和名,类似:温/nr 家宝/nr"""
pass
def process_t(self, words):
"""处理时间,合并语料库分开标注的时间词,类似: (/w 一九九七年/t 十二月/t 三十一日/t )/w """
pass
def pos_to_tag(self, p):
"""由词性提取标签"""
pass
def tag_perform(self, tag, index):
"""标签使用BIO模式"""
pass
def pos_perform(self, pos):
"""去除词性携带的标签先验知识"""
pass
def initialize(self):
"""初始化 """
pass
def init_sequence(self, words_list):
"""初始化字序列、词性序列、标记序列 """
pass
def extract_feature(self, word_grams):
"""特征选取"""
pass
def segment_by_window(self, words_list=None, window=3):
"""窗口切分"""
pass
def generator(self):
"""训练数据"""
pass由于整个代码实现过程较长,我这里给出重点步骤,最后会在 Github 上连同语料代码一同给出,下面是关键过程实现。对语料中的句子、词性,实体分类标记进行区分。标签采用“BIO”体系,即实体的第一个字为 B_,其余字为 I_,非实体字统一标记为 O。大部分情况下,标签体系越复杂,准确度也越高,但这里采用简单的 BIO 体系也能达到相当不错的效果。这里模型采用 tri-gram 形式,所以在字符列中,要在句子前后加上占位符。def init_sequence(self, words_list):
"""初始化字序列、词性序列、标记序列 """
words_seq = [[word.split(u'/')[0] for word in words] for words in words_list]
pos_seq = [[word.split(u'/')[1] for word in words] for words in words_list]
tag_seq = [[self.pos_to_tag(p) for p in pos] for pos in pos_seq]
self.pos_seq = [[[pos_seq[index][i] for _ in range(len(words_seq[index][i]))]
for i in range(len(pos_seq[index]))] for index in range(len(pos_seq))]
self.tag_seq = [[[self.tag_perform(tag_seq[index][i], w) for w in range(len(words_seq[index][i]))]
for i in range(len(tag_seq[index]))] for index in range(len(tag_seq))]
self.pos_seq = [[u'un']+[self.pos_perform(p) for pos in pos_seq for p in pos]+[u'un'] for pos_seq in self.pos_seq]
self.tag_seq = [[t for tag in tag_seq for t in tag] for tag_seq in self.tag_seq]
self.word_seq = [[u'
def __init__(self):
"""初始化参数"""
pass
def initialize_model(self):
"""初始化"""
pass
def train(self):
"""训练"""
pass
def predict(self, sentence):
"""预测"""
pass
def load_model(self):
"""加载模型 """
pass
def save_model(self):
"""保存模型"""
pass在 CRF_NER 类中,分别完成了语料预处理和模型训练、保存、预测功能,具体实现如下。第一步,init 函数实现了模型参数定义和 CorpusProcess 的实例化和语料预处理:def __init__(self):
"""初始化参数"""
self.algorithm = "lbfgs"
self.c1 ="0.1"
self.c2 = "0.1"
self.max_iterations = 100 #迭代次数
self.model_path = dir + "model.pkl"
self.corpus = CorpusProcess() #Corpus 实例
self.corpus.pre_process() #语料预处理
self.corpus.initialize() #初始化语料
self.model = None第二步,给出模型定义,了解 sklearn_crfsuite.CRF 详情可查该文档。def initialize_model(self):
"""初始化"""
algorithm = self.algorithm
c1 = float(self.c1)
c2 = float(self.c2)
max_iterations = int(self.max_iterations)
self.model = sklearn_crfsuite.CRF(algorithm=algorithm, c1=c1, c2=c2,
max_iterations=max_iterations, all_possible_transitions=True)第三步,模型训练和保存,分为训练集和测试集:def train(self):
"""训练"""
self.initialize_model()
x, y = self.corpus.generator()
x_train, y_train = x[500:], y[500:]
x_test, y_test = x[:500], y[:500]
self.model.fit(x_train, y_train)
labels = list(self.model.classes_)
labels.remove('O')
y_predict = self.model.predict(x_test)
metrics.flat_f1_score(y_test, y_predict, average='weighted', labels=labels)
sorted_labels = sorted(labels, key=lambda name: (name[1:], name[0]))
print(metrics.flat_classification_report(y_test, y_predict, labels=sorted_labels, digits=3))
self.save_model()第四至第六步中 predict、load_model、save_model 方法的实现,大家可以在文末给出的地址中查看源码,这里就不堆代码了。最后,我们来看看模型训练和预测的过程和结果:ner = CRF_NER()
model = ner.train()经过模型训练,得到的准确率和召回率如下:进行模型预测,其结果还不错,如下: 总结本文浅析了条件随机场,并使用 sklearn_crfsuite.CRF 模型,对人民日报1998年标注数据进行了模型训练和预测,以帮助大家加强对条件随机场的理解。发布于 2020-03-12 10:05自然语言处理NLP:自我改变的惊人秘密(书籍)命名实体识别赞同 83 条评论分享喜欢收藏申请
条件随机场(CRF)模型详解_crf模型-CSDN博客
>条件随机场(CRF)模型详解_crf模型-CSDN博客
条件随机场(CRF)模型详解
最新推荐文章于 2024-01-13 23:11:48 发布
Ivan.J
最新推荐文章于 2024-01-13 23:11:48 发布
阅读量1.7w
收藏
71
点赞数
18
分类专栏:
个人学习
文章标签:
CRF
机器学习
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_36774795/article/details/98493621
版权
个人学习
专栏收录该内容
8 篇文章
0 订阅
订阅专栏
近期做一个小整理,把之前学过的一些模型重新拿出来学习一下。而CRF(Conditional Random Fields,条件随机场)模型是自己了解到的第一批模型之一,所以拿过来复习一下
假设你有许多小明同学一天内不同时段的照片,从小明提裤子起床到脱裤子睡觉各个时间段都有(小明是照片控!)。现在的任务是对这些照片进行分类。比如有的照片是吃饭,那就给它打上吃饭的标签;有的照片是跑步时拍的,那就打上跑步的标签;有的照片是开会时拍的,那就打上开会的标签。问题来了,你准备怎么干?
一个简单直观的办法就是,不管这些照片之间的时间顺序,想办法训练出一个多元分类器。就是用一些打好标签的照片作为训练数据,训练出一个模型,直接根据照片的特征来分类。例如,如果照片是早上6:00拍的,且画面是黑暗的,那就给它打上睡觉的标签;如果照片上有车,那就给它打上开车的标签。
这样可行吗?
乍一看可以!但实际上,由于我们忽略了这些照片之间的时间顺序这一重要信息,我们的分类器会有缺陷的。举个例子,假如有一张小明闭着嘴的照片,怎么分类?显然难以直接判断,需要参考闭嘴之前的照片,如果之前的照片显示小明在吃饭,那这个闭嘴的照片很可能是小明在咀嚼食物准备下咽,可以给它打上吃饭的标签;如果之前的照片显示小明在唱歌,那这个闭嘴的照片很可能是小明唱歌瞬间的抓拍,可以给它打上唱歌的标签。
所以,为了让我们的分类器能够有更好的表现,在为一张照片分类时,我们必须将与它相邻的照片的标签信息考虑进来。这——就是条件随机场(CRF)大显身手的地方!
从例子说起——词性标注问题
啥是词性标注问题?
非常简单的,就是给一个句子中的每个单词注明词性。比如这句话:“Bob drank coffee at Starbucks”,注明每个单词的词性后是这样的:“Bob (名词) drank(动词) coffee(名词) at(介词) Starbucks(名词)”。
下面,就用条件随机场来解决这个问题。
以上面的话为例,有5个单词,我们将:(名词,动词,名词,介词,名词)作为一个标注序列,称为l,可选的标注序列有很多种,比如l还可以是这样:(名词,动词,动词,介词,名词),我们要在这么多的可选标注序列中,挑选出一个最靠谱的作为我们对这句话的标注。
怎么判断一个标注序列靠谱不靠谱呢?
就我们上面展示的两个标注序列来说,第二个显然不如第一个靠谱,因为它把第二、第三个单词都标注成了动词,动词后面接动词,这在一个句子中通常是说不通的。
假如我们给每一个标注序列打分,打分越高代表这个标注序列越靠谱,我们至少可以说,凡是标注中出现了动词后面还是动词的标注序列,要给它负分!!
上面所说的动词后面还是动词就是一个特征函数,我们可以定义一个特征函数集合,用这个特征函数集合来为一个标注序列打分,并据此选出最靠谱的标注序列。也就是说,每一个特征函数都可以用来为一个标注序列评分,把集合中所有特征函数对同一个标注序列的评分综合起来,就是这个标注序列最终的评分值。
定义CRF中的特征函数
现在,我们正式地定义一下什么是CRF中的特征函数,所谓特征函数,就是这样的函数,它接受四个参数:
句子s(就是我们要标注词性的句子)
i,用来表示句子s中第i个单词
l_i,表示要评分的标注序列给第i个单词标注的词性
l_i-1,表示要评分的标注序列给第i-1个单词标注的词性
它的输出值是0或者1,0表示要评分的标注序列不符合这个特征,1表示要评分的标注序列符合这个特征。
Note:这里,我们的特征函数仅仅依靠当前单词的标签和它前面的单词的标签对标注序列进行评判,这样建立的CRF也叫作线性链CRF,这是CRF中的一种简单情况。为简单起见,本文中我们仅考虑线性链CRF。
从特征函数到概率
定义好一组特征函数后,我们要给每个特征函数f_j赋予一个权重λ_j。现在,只要有一个句子s,有一个标注序列l,我们就可以利用前面定义的特征函数集来对l评分。
上式中有两个求和,外面的求和用来求每一个特征函数f_j评分值的和,里面的求和用来求句子中每个位置的单词的的特征值的和。
对这个分数进行指数化和标准化,我们就可以得到标注序列l的概率值p(l|s),如下所示:
几个特征函数的例子:
前面我们已经举过特征函数的例子,下面我们再看几个具体的例子,帮助增强大家的感性认识。
当l_i是“副词”并且第i个单词以“ly”结尾时,我们就让f1 = 1,其他情况f1为0。不难想到,f1特征函数的权重λ1应当是正的。而且λ1越大,表示我们越倾向于采用那些把以“ly”结尾的单词标注为“副词”的标注序列
如果i=1,l_i=动词,并且句子s是以“?”结尾时,f2=1,其他情况f2=0。同样,λ2应当是正的,并且λ2越大,表示我们越倾向于采用那些把问句的第一个单词标注为“动词”的标注序列。
当l_i-1是介词,l_i是名词时,f3 = 1,其他情况f3=0。λ3也应当是正的,并且λ3越大,说明我们越认为介词后面应当跟一个名词。
如果l_i和l_i-1都是介词,那么f4等于1,其他情况f4=0。这里,我们应当可以想到λ4是负的,并且λ4的绝对值越大,表示我们越不认可介词后面还是介词的标注序列。
好了,一个条件随机场就这样建立起来了,让我们总结一下:
为了建一个条件随机场,我们首先要定义一个特征函数集,每个特征函数都以整个句子s,当前位置i,位置i和i-1的标签为输入。然后为每一个特征函数赋予一个权重,然后针对每一个标注序列l,对所有的特征函数加权求和,必要的话,可以把求和的值转化为一个概率值。
CRF与逻辑回归的比较
观察公式:
是不是有点逻辑回归的味道?
事实上,条件随机场是逻辑回归的序列化版本。逻辑回归是用于分类的对数线性模型,条件随机场是用于序列化标注的对数线性模型。
CRF与HMM的比较
对于词性标注问题,HMM模型也可以解决。HMM的思路是用生成办法,就是说,在已知要标注的句子s的情况下,去判断生成标注序列l的概率,如下所示:
这里:
p(l_i|l_i-1)是转移概率,比如,l_i-1是介词,l_i是名词,此时的p表示介词后面的词是名词的概率。
p(w_i|l_i)表示发射概率(emission probability),比如l_i是名词,w_i是单词“ball”,此时的p表示在是名词的状态下,是单词“ball”的概率。
如果有不太想看枯燥的公式的同学,可以看如何用简单易懂的例子解释隐马尔可夫模型?,该文将HMM的原理讲的非常清楚,包括老师上课时候也给我们推荐了一波。
那么,HMM和CRF怎么比较呢?
答案是:CRF比HMM要强大的多,它可以解决所有HMM能够解决的问题,并且还可以解决许多HMM解决不了的问题。事实上,我们可以对上面的HMM模型取对数,就变成下面这样:
我们把这个式子与CRF的式子进行比较:
不难发现,如果我们把第一个HMM式子中的log形式的概率看做是第二个CRF式子中的特征函数的权重的话,我们会发现,CRF和HMM具有相同的形式。
换句话说,我们可以构造一个CRF,使它与HMM的对数形式相同。怎么构造呢?
对于HMM中的每一个转移概率p(l_i=y|l_i-1=x),我们可以定义这样的一个特征函数:
该特征函数仅当l_i = y,l_i-1=x时才等于1。这个特征函数的权重如下:
同样的,对于HMM中的每一个发射概率,我们也都可以定义相应的特征函数,并让该特征函数的权重等于HMM中的log形式的发射概率。
用这些形式的特征函数和相应的权重计算出来的p(l|s)和对数形式的HMM模型几乎是一样的!
用一句话来说明HMM和CRF的关系就是这样:
每一个HMM模型都等价于某个CRF每一个HMM模型都等价于某个CRF每一个HMM模型都等价于某个CRF
但是,CRF要比HMM更加强大,原因主要有两点:
CRF可以定义数量更多,种类更丰富的特征函数。HMM模型具有天然具有局部性,就是说,在HMM模型中,当前的单词只依赖于当前的标签,当前的标签只依赖于前一个标签。这样的局部性限制了HMM只能定义相应类型的特征函数,我们在上面也看到了。但是CRF却可以着眼于整个句子s定义更具有全局性的特征函数,如这个特征函数:
如果i=1,l_i=动词,并且句子s是以“?”结尾时,f2=1,其他情况f2=0。
CRF可以使用任意的权重将对数HMM模型看做CRF时,特征函数的权重由于是log形式的概率,所以都是小于等于0的,而且概率还要满足相应的限制,如
但在CRF中,每个特征函数的权重可以是任意值,没有这些限制。
转自NLP系列学习:CRF条件随机场(2)
优惠劵
Ivan.J
关注
关注
18
点赞
踩
71
收藏
觉得还不错?
一键收藏
知道了
0
评论
条件随机场(CRF)模型详解
近期做一个小整理,把之前学过的一些模型重新拿出来学习一下。而CRF(Conditional Random Fields,条件随机场)模型是自己了解到的第一批模型之一,所以拿过来复习一下假设你有许多小明同学一天内不同时段的照片,从小明提裤子起床到脱裤子睡觉各个时间段都有(小明是照片控!)。现在的任务是对这些照片进行分类。比如有的照片是吃饭,那就给它打上吃饭的标签;有的照片是跑步时拍的,那就...
复制链接
扫一扫
专栏目录
条件随机场(CRF)
04-17
这个文档是对条件随机场(Condition Random Field)的简单介绍,介绍清晰简单、浅显,易于理解
条件随机场ppt(CRF)
04-17
条件随机场,condition random Field ,公式讲解,词性标注,序列标注
参与评论
您还未登录,请先
登录
后发表或查看评论
【机器学习】条件随机场
最新发布
cxyhjl的博客
01-13
967
一、马尔可夫随机场1.1 概率图模型什么是有向图模型和无向图模型?https://www.jianshu.com/p/dabbc78471d7团、极大团、最大团 - 简书 (jianshu.com)1.2马尔可夫随机场二、条件随机场概述2.1条件随机场简介条件随机场(Conditional Random Field,简称 CRF)是一种用于序列标注(sequence labeling)的概率...
CRF模型详解
热门推荐
weixin_40103562的博客
03-04
3万+
条件随机场(CRF)是自然语言处理中的基础模型, 广泛用于分词, 实体识别和词性标注等场景. 随着深度学习的普及, BILSTM+CRF, BERT+CRF, TRANSFORMER+CRF等模型, 逐步亮相, 并在这些标注场景, 效果有显著的提升.
下面是我学习CRF的学心总结, 看了多篇知乎, paper, 和CRF++的实现代码后, 终于有了深刻的理解.
基础概念
首先, 一起看一下随机过程, 随机场, 马尔可夫随机场的定义, 在最后请出条件随机场.
随机过程:
设 TTT是一无限实数集
CRF模型——打通crf模型的任督二脉(一)
zlb872551601的博客
07-24
1364
CRF模型 是nlp领域的经典模型,也是公认的不好学习的模型(相比其他机器学习模型)
,我记得作为小蓝书《统计机器学习》的最后一章,当年看得那叫一个晦涩难懂呢2333333,反正看了一两遍是看不太懂,
网上博客中 照抄小蓝书《统计机器学习》的最后一章的尤为多,也不能说不对,只是对我这种 小白,还是希望能有掰开算法细节和公式细节,甚至源代码细节来看的文章。
网上关于CRF模型的各种文章,我觉得问题在...
最通俗易懂的BiLSTM-CRF模型中的CRF层介绍
cz的博客
03-01
1265
背景知识
你唯一需要了解的是什么叫命名实体识别。如果你不了解神经网络,CRF以及其他相关知识也没有关系,我会用通俗易懂的语言来解释清楚。
1 简介
在命名实体识别领域,基于神经网络的实现方法是非常流行和常用的。举个例子,该文讲述的用词嵌入和字嵌入的BiLSTM-CRF模型就是其中一种。我将以该模型为例解释CRF层的工作原理。
开始之前
我们规定在数据集中有两类实体,人名和组织机构名称。所以,其实在我们的数据集中总共有5类标签:
B-Person (人名的开始部分)
I- Person (人名的中间部分)
B
CRF_matlab_条件随机场_
10-02
matlab实现CRF基础模型,CRF chain
条件随机场模型
weixin_42045968的博客
05-31
947
线性链条件随机场用于标注问题,在条件概率模型P(Y|X)中,Y是输出变量表示标记序列,X是输入变量表示需要标注的观测序列。设有联合分布P(Y),由无向图G=(V, E)表示,在图G中,节点表示随机变量,边表示随机变量之间的依赖关系。均为线性链表示的随机变量序列,若在给定随机变量序列X的条件下,随机变量序列Y的条件概率分布P(Y|X)构成条件随机场,即满足马尔可夫性。条件随机场,设X与Y是随机变量,P(Y|X)是在给定X的条件下Y的条件概率分布。对任意结点v成立,则称条件概率分布P(Y|X)为条件随机场。
理解条件随机场(转)
dianwei0041的博客
07-24
2152
理解条件随机场最好的办法就是用一个现实的例子来说明它。但是目前中文的条件随机场文章鲜有这样干的,可能写文章的人都是大牛,不屑于举例子吧。于是乎,我翻译了这篇文章。希望对其他伙伴有所帮助。原文在这里[http://blog.echen.me/2012/01/03/introduction-to-conditional-random-fields/]
想直接看英文的朋...
最通俗易懂的BiLSTM-CRF模型中的CRF层讲解
datayx的文章
09-16
2432
向AI转型的程序员都关注了这个号????????????人工智能大数据与深度学习 公众号:datayx本文翻译自GitHub博客上的原创文章,结尾有原文链接。文章没有晦涩的数学公式,而是...
CRF总结
weixin_33918357的博客
04-06
434
条件随机场理解
随机场理解
先从随机变量说起。
对于一个时间集合T内,每一个时间点t点,X(t)的数值都是随机的,那么X(t)称为随机过程。x(t)是依赖于时间的一组随机变量。它的分布函数叫做x(t)的一维概率分布函数。
如果有一个变量,依赖于两个时间t1,t2,就称为二维随机变量,有二维概率分布函数。-。。。。。到n维概率分布函数
两个随机过程之间的数字特征有:方差,期望,均值,协方差函数,互...
条件随机场 CRF (Matble)源码
07-16
% minFunc
fprintf('Compiling minFunc files...\n');
mex minFunc/lbfgsC.c
% KPM
fprintf('Compiling KPM files...\n');
mex -IKPM KPM/repmatC.c
% crfChain
fprintf('Compiling crfChain files...\n');
mex crfChain/mex/crfChain_makePotentialsC.c
mex crfChain/mex/crfChain_inferC.c
mex crfChain/mex/crfChain_lossC2.c
条件随机场CRF1
08-04
条件随机场CRF1
CRF算法
woshiliulei0的专栏
05-28
5078
一、概念
1.定义
条件随机场:是给定一组输入随机变量条件下,另一组输出随机变量的条件概率分布模型。
2.特点
假设输出随机变量构成马尔科夫随机场。
参考:
1.https://spaces.ac.cn/archives/5542/comment-page-1
2.https://www.zhihu.com/question/35866596/answer/236886066
...
条件随机场CRF简介
xmdxcsj的专栏
09-28
1万+
Crf模型
1. 定义
一阶(只考虑y前面的一个)线性条件随机场:
相比于最大熵模型的输入x和输出y,crf模型的输入输出都是序列化以后的矢量,是对最大熵模型的序列扩展。
相比于最大熵模型的另外一个不同是,crf多出了一个维度j(j表示序列x的位置),即任意一个输出yi,都跟所有的输入x有关。
经过变换,crf概率模型可以转化为
CRF模型
m0_57332527的博客
07-10
1859
用简单的俗话来理解CRF以便于更好的应用
nlp基础—12.LSTM-CRF模型介绍
柳杰的博客
07-08
4781
文章目录引言一、模型介绍1. LSTM模型2. BI-LSTM(双向LSTM)模型3.CRF模型4. LSTM+CRF模型5. BiLSTM+CRF模型
引言
本文讨论的是序列标注问题,所使用的模型是将两种现有的模型(LSTM+CRF)进行拼接,一种模型是双向LSTM模型,一种是CRF模型。下面介绍如何结合LSTM和CRF用于sequence tagging,并且对这些结合的效果进行测量。
一、模型介绍
本篇文章涉及以下几种模型:LSTM,BI-LSTM,CRF,LSTM+CRF,BI-LSTM+C
条件随机场(conditional random field,CRF)模型初探
qq_34519492的博客
05-13
1145
转载原链接:https://www.cnblogs.com/LittleHann/p/8655354.html
阅读目录(Content)
1. 条件随机场,一种特殊的概率图模型结构
0x1:概率无向图模型
1. 概率无向图模型定义
2. 概率无向图模型的因子分解
2. 条件随机场的发展脉络
0x1:条件随机场的学术发展脉络
0x2:3种不同算法在标注问题中的计算要素
0x3:CRF算法对HMM和HEMM的主要改进点
1. CRF避免了HMM中的严格的独立.
CRF模型——条件随机场模型
MyHeartWillGoOn
12-08
2518
文章目录CRF含义作用隐含假设使用过程维特比算法与HMM的差异
CRF
含义
CRF(Conditional Random Fields), 中文称作条件随机场, 同HMM一样, 它一般也以文本序列数据为输入, 以该序列对应的隐含序列为输出.
作用
同HMM一样, 在NLP领域, CRF用来解决文本序列标注问题. 如分词, 词性标注, 命名实体识别.
隐含假设
隐含序列中每个单元的可能性只与上一个单元有关
使用过程
首先, CRF模型表示为: lambda = CRF(w1, w2, …, w
crf条件随机场python
08-28
对于使用 Python 实现条件随机场(CRF)模型,你可以使用第三方库 `sklearn-crfsuite`。以下是一个简单的示例代码,演示了如何使用 `sklearn-crfsuite` 进行 CRF 模型训练和预测:
首先,确保你已经安装了 `sklearn-crfsuite` 库。你可以使用以下命令来安装:
```python
pip install sklearn-crfsuite
```
然后,你可以使用以下代码来训练和预测 CRF 模型:
```python
import numpy as np
import sklearn_crfsuite
from sklearn_crfsuite import metrics
# 准备训练数据和标签
X_train = np.array([['feature1', 'feature2', ...], ...])
y_train = np.array(['label1', 'label2', ...])
# 创建 CRF 模型
crf = sklearn_crfsuite.CRF()
# 训练 CRF 模型
crf.fit(X_train, y_train)
# 准备测试数据
X_test = np.array([['feature1', 'feature2', ...], ...])
# 预测标签
y_pred = crf.predict(X_test)
# 评估模型性能
print(metrics.flat_classification_report(y_test, y_pred))
```
在上面的代码中,你需要将训练数据和标签 `X_train` 和 `y_train` 替换为你自己的数据。同样,测试数据 `X_test` 和对应的真实标签 `y_test` 也需要进行相应的替换。
这只是一个简单的例子,你可以根据自己的数据和需求进行进一步的调整和扩展。你还可以通过调整 CRF 模型的参数来优化模型性能。
希望这能帮到你!如果还有其他问题,请随时提问。
“相关推荐”对你有帮助么?
非常没帮助
没帮助
一般
有帮助
非常有帮助
提交
Ivan.J
CSDN认证博客专家
CSDN认证企业博客
码龄7年
暂无认证
11
原创
19万+
周排名
56万+
总排名
3万+
访问
等级
358
积分
10
粉丝
28
获赞
4
评论
132
收藏
私信
关注
热门文章
条件随机场(CRF)模型详解
17434
Python生成项目依赖requirements及其安装
2820
深度学习之词向量Word Embedding总结
2795
Python爬虫之四:使用BeautifulSoup爬取微博热搜
2409
Python爬虫之一:获取简单的网页源代码
2309
分类专栏
心得感悟
1篇
Python操作
3篇
个人学习
8篇
Python爬虫
4篇
最新评论
Python爬虫之三:使用requests模块动态爬取微博评论
ssulakii:
这个怎么爬更多的数据啊
Python爬虫之三:使用requests模块动态爬取微博评论
Ivan.J:
你应该在电脑上没登录https://m.weibo.cn吧
Python爬虫之三:使用requests模块动态爬取微博评论
weixin_44544274:
说keyerror data
Python爬虫之三:使用requests模块动态爬取微博评论
weixin_44544274:
我的会报错诶
您愿意向朋友推荐“博客详情页”吗?
强烈不推荐
不推荐
一般般
推荐
强烈推荐
提交
最新文章
Error: Can‘t locate revision identified by ‘xxx‘
Python爬虫之四:使用BeautifulSoup爬取微博热搜
Python爬虫之三:使用requests模块动态爬取微博评论
2022年1篇
2020年1篇
2019年8篇
2018年1篇
目录
目录
分类专栏
心得感悟
1篇
Python操作
3篇
个人学习
8篇
Python爬虫
4篇
目录
评论
被折叠的 条评论
为什么被折叠?
到【灌水乐园】发言
查看更多评论
添加红包
祝福语
请填写红包祝福语或标题
红包数量
个
红包个数最小为10个
红包总金额
元
红包金额最低5元
余额支付
当前余额3.43元
前往充值 >
需支付:10.00元
取消
确定
下一步
知道了
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝
规则
hope_wisdom 发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额
0
抵扣说明:
1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。
余额充值
什么是病例报告表? | Novotech CRO
什么是病例报告表? | Novotech CRO
Sorry, you need to enable JavaScript to visit this website.
跳转到主要内容
新闻&资源
解决方案
专业服务
招贤纳士
关于我们
地点
联系
EnglishKoreanChinese, SimplifiedChinese, Traditional
新闻
文章
行业报告
白皮书
常见问题
案例研究
网络研讨会
活动
医学和注册咨询
患者招募和研究中心选择
澳大利亚早期临床试验
临床运营
临床中心现场管理
生物统计学和数据管理
虚拟临床试验
真实世界数据
Novotech实验室解决方案
肿瘤学CRO服务
药物计量学服务
药物开发咨询
转基因解决方案
肝病合同研究组织CRO服务
传染病和疫苗CRO服务
孤儿病和罕见病CRO服务
临床和监管策略
人文文化
工作环境
生活环境
职业发展
FLEX
全球职业机会
Europe Career Opportunities
中国职业机会
关于 Novotech
我们的文化
董事会
领导层
获奖情况
澳大利亚
欧洲
大中华区
印度
马来西亚
新西兰
菲律宾
新加坡
韩国
泰国
美国
Search Novotech
新闻&资源
解决方案
专业服务
招贤纳士
关于我们
地点
联系
EnglishKoreanChinese, SimplifiedChinese, Traditional
新闻
文章
行业报告
白皮书
常见问题
案例研究
网络研讨会
活动
医学和注册咨询
患者招募和研究中心选择
澳大利亚早期临床试验
临床运营
临床中心现场管理
生物统计学和数据管理
虚拟临床试验
真实世界数据
Novotech实验室解决方案
肿瘤学CRO服务
药物计量学服务
药物开发咨询
转基因解决方案
肝病合同研究组织CRO服务
传染病和疫苗CRO服务
孤儿病和罕见病CRO服务
临床和监管策略
人文文化
工作环境
生活环境
职业发展
FLEX
全球职业机会
Europe Career Opportunities
中国职业机会
关于 Novotech
我们的文化
董事会
领导层
获奖情况
澳大利亚
欧洲
大中华区
印度
马来西亚
新西兰
菲律宾
新加坡
韩国
泰国
美国
Search Novotech
EnglishKoreanChinese, SimplifiedChinese, Traditional
新闻&资源
解决方案
专业服务
招贤纳士
关于我们
地点
联系
返回
最新消息
Latest Articles
最新动态
最新网络研讨会
活动预告
最新案例研究
最新白皮书
最新常见问题
新冠防疫通知
新的 Novotech 报告发现全球有约 1,000 项人类免疫缺陷病毒 (HIV) 临床试验
Novotech 高管团队出席 J.P. Morgan 医疗保健大会:为 25 个地区的生物技术公司设计的国际CRO
2023 年早期生物技术投资下降 40%:Novotech 白皮书
查看所有新闻
杜兴氏肌营养不良(进行性肌营养不良) - 全球临床试验概况
类风湿关节炎:全球临床试验概况
念珠菌病—全球临床试验概况
查看所有报告
中国开展临床试验:释放先进疗法的潜力
Endpoints at BIO 2023——与中国建立新的生物科技合作
Endpoints at ASCO23: 如何推动肿瘤新兴突破性疗法加速发展:从起跑线出发
查看所有网络研讨会
澳大利亚精准肿瘤研讨会(APOS)
SOT年会(Tox Expo)
BIOCHINA (EBC)
查看所有活动
专家咨询联合多区域临床试验(MRCT)战略携手共同挽救肿瘤项目
突破性进展:从单一国家I期试验的成功到跨国多中心I/II期临床试验
实现受试者快速入组:一项由专注于加快疫苗研发来促进全球健康的跨国公司所开展的 COVID-19 疫苗研究
查看所有案例研究
杜兴氏肌营养不良(进行性肌营养不良)的全球患病率和临床试验情况
标题:探索类风湿关节炎的全球临床研究视角
洞察念珠菌病:全球临床研究概况
查看所有常见问题
免疫检查点抑制剂全球试验格局(2023年)
探索生物技术领域:洞察疫情及疫情后时代(2023年)的临床试验、融资趋势、挑战与转型
中国CAR-T研究状况
查看所有白皮书
欧洲当前临床试验利弊评估
专访合同研究组织Novotech首席医学官Judith Ng-Cashing - 2023年及自此的药物开发
助力全球创新: Novotech对加速中国生物技术发展的见解
View All Article
BACK
医学和注册咨询
患者招募和研究中心选择
在澳洲开展早期临床试验
临床运营
临床试验现场管理组织(SMO)
生物统计学和数据管理
虚拟临床试验
真实世界数据
Novotech实验室解决方案
BACK
肿瘤学CRO服务
药物计量学服务
药物开发咨询
转基因解决方案
肝病合同研究组织CRO服务
传染病和疫苗CRO服务
孤儿病和罕见病CRO服务
临床和监管策略
返回
人文文化
工作环境
生活环境
职业发展
FLEX
全球职业机会
Europe Career Opportunities
中国职业机会
2024/1/10
新闻
新的 Novotech 报告发现全球有约 1,000 项人类免疫缺陷病毒 (HIV) 临床试验 波士顿, 2023 年 12 月 28 日--
阅读更多内容
2024/1/9
新闻
Novotech 高管团队出席 J.P. Morgan 医疗保健大会:为 25 个地区的生物技术公司设计的国际CRO 旧金山, Jan.
阅读更多内容
2024/1/9
新闻
2023 年早期生物技术投资下降 40%:Novotech 白皮书 波士顿, Dec.
阅读更多内容
查看所有新闻
2024/2/13
行业报告
杜兴氏肌营养不良(进行性肌营养不良) - 全球临床试验概况 杜兴氏肌营养不良(进行性肌营养不良)是一种罕见的遗传性疾病,其特征是进行性肌肉无力和骨骼退化,影响全球大约五千分之一的男性。杜兴氏肌营养不良(进行性肌营养不良)的患病率在某些地区较高,其中欧洲(特别是瑞典和挪威),以及美国、加拿大和中国的患病率也较高。这强调了与此情况相关的相当重的健康负担,需共同努力进行研究和治疗。
下载
2024/1/29
行业报告
类风湿关节炎:全球临床试验概况 类风湿关节炎(RA)是一种慢性自身免疫性疾病,会引起关节疼痛和肿胀。如果不加治疗,50%的病例会在十年内丧失工作能力,预期寿命可能减少约12年。该疾病影响全球1%的人口,通常在60至70岁之间发病,其对女性的影响显著,影响程度至少为男性的两倍。
下载
查看所有报告
2023/9/19
网络研讨会
中国开展临床试验:释放先进疗法的潜力 专题讨论涉及以下主要领域 :
观看
2023/6/21
网络研讨会
Endpoints at BIO 2023——与中国建立新的生物科技合作 苏慰国
执行董事、首席执行官兼首席科学官
和黄医药
利民
观看
查看所有网络研讨会
2023/12/23
案例研究
专家咨询联合多区域临床试验(MRCT)战略携手共同挽救肿瘤项目 背景
一家总部位于中国、专注于创新新型免疫疗法的生物技术申办方,曾在一项在美国地区开展的针对晚期/转移性实体肿瘤患者的I期研究中遭遇了重大挫折。
阅读更多内容
查看所有案例研究
2024/2/22
常见问题
杜兴氏肌营养不良(进行性肌营养不良)的全球患病率和临床试验情况 1.杜兴氏肌营养不良(进行性肌营养不良)的主要原因是什么?它对男性和女性有何影响?
阅读更多内容
查看所有常见问题
2023/11/24
白皮书
免疫检查点抑制剂全球试验格局(2023年) 探索免疫检查点抑制剂临床试验的新动态。了解2023年Novotech综合白皮书中的主要发现和区域分析。
下载
查看所有白皮书
新冠防疫最新重要通知
阅读更多内容
查找相关内容:
肝脏疾病 肿瘤学 血液学 基因治疗 免疫学 公司新闻 行业报告 网络研讨会
2024/3/8-9
活动
澳大利亚精准肿瘤研讨会(APOS)澳大利亚悉尼
阅读更多内容
2024/3/11-13
活动
SOT年会(Tox Expo) 犹他州盐湖城
阅读更多内容
查看所有活动
医学和注册咨询
Novotech的医疗和监管咨询团队提供全方位的临床前、监管事务支持、医疗和药物警戒咨询服务。
了解更多
患者招募和研究中心选择
Novotech能够凭借多年经验、当地信息和现实大数据为客户找到并推荐合适的研究中心。
了解更多
在澳洲开展早期临床试验
澳大利亚监管流程简单快捷,且拥有利润丰厚的研发退税机制,因此是早期试验的优选目的地
了解更多
临床运营
Novotech 拥有资深的专业团队,深入了解临床开发各个阶段所需的行业和治疗领域的专业知识,为您提供全面高效的临床研究服务。
了解更多
临床试验现场管理组织(SMO)
作为Novotech集团旗下的运营实体之一,Acrostar(康达)的SMO部门已在各类临床试验中展现出卓越的执行力。自2013年成立以来,Acrostar已成为亚太地区临床研究领域的一个关键参与者。在中国(包括中国台湾)和韩国等临床科研蓬勃发展的地区,Acrostar SMO已建立起扎实的业务基础。
了解更多
生物统计学和数据管理
提供准确、优质且及时的临床试验生物统计学服务,包括统计规划、分析和报告。
了解更多
虚拟临床试验
与传统试验相比,虚拟临床试验如何保留患者并获得成本效益
了解更多
真实世界数据
采用真实世界数据(RWD)加快患者招募和药物研发
了解更多
Novotech实验室解决方案
我们提供生物分析服务,帮助客户完成分子开发的各个阶段。
了解更多
肿瘤学CRO服务
亚太地区的肿瘤学现状
生物技术公司面临着受试者招募和保留困难、时间周期较长和成本相对高昂等诸多挑战。
因此,我们发现,生物技术公司愈发希望将亚太地区纳入肿瘤药物研发的进程之中。
了解更多
药物计量学服务
Novotech的团队技艺精湛且经验丰富,能够在临床研究各阶段(第0、I、II和III期)和研究设计中提供帮助,其中包括人体首次试验(FIH)、单次递增剂量(SAD)、多次递增剂量(MAD)、药物相互作用(DDI)、生物利用度/生物等效性(BA/BE)、食物效应以及特殊人群研究。
了解更多
药物开发咨询
Novotech药物开发咨询是一支可以提供国际药物开发和战略监管解决方案的专业团队,提供全面的“从实验室至上市”计划的开发服务。
了解更多
转基因解决方案
Novotech在澳大利亚成立了首家全国性的私营商业化机构生物安全委员会(IBC),并成功获得澳大利亚基因技术监管办公室(OGTR)的认可。
拥有IBC的权限是在澳大利亚获得并保有转基因(GMO)试验用药物研究认证的先决条件。
了解更多
肝病合同研究组织CRO服务
亚太地区肝病概况
为了以更低成本招募广大初治患者人群并加快临床试验速度,许多生物技术公司考虑在韩国、中国等亚太地区进行临床试验。亚太地区有肝脏病学领域活跃的关键意见领袖和研究者,他们有着丰富的与生物技术公司合作经验。
了解更多
传染病和疫苗CRO服务
传染病服务和疫苗CRO服务
由于亚太地区肝炎(乙型肝炎、丙型肝炎)、艾滋病毒和流感等疾病的患病率很高,许多生物技术公司将目光投向亚太地区,进行传染病和疫苗试验。
了解更多
孤儿病和罕见病CRO服务
亚太地区罕见病和孤儿病现状
世界上的罕见病可能多达8,000余种,约累及全球总人口的6%至8%。据估计,欧盟和北美地区约有3000万人罹患罕见病,而亚洲罕见病患者则超过4500万人,其中,仅中国就存在1000万例患者。
了解更多
临床和监管策略
许多生物技术公司选择澳大利亚进行早期临床试验,希望充分借助其直接简便的监管流程和利润丰厚的研发退税(最高达43.5%)机制。随后,生物技术公司通常会将目光投向亚洲地区(如韩国、泰国和中国),挖掘庞大的初治患者人群,进行后期临床研究。
了解更多
人文文化
我们的工作场所文化反映了我们员工的热情,我们将支持您在职业和生活的各个阶段发展并取得成就。
工作环境
Novotech是亚太地区优秀的区域性全方位服务合同研究组织(CRO),通过内部BioDesk团队,提供各临床试验分期和各治疗领域临床开发服务以及全球产品开发和监管事务咨询服务。
生活环境
我们用价值观日复一日推动自身的工作文化。
在Novotech,每个人都有乐在工作的独特理由,不过,只要加入这个大家庭,相信大家最先注意到的会是我们开放、包容而且深具弹性的工作文化。
职业发展
我们致力于为我们的员工提供持续的职业发展培训、具有竞争力的奖金结构、支持性的工作环境、多元化的角色,以及在组织的所有领域中提供职业发展和晋升的机会。我们对职业发展和晋升的承诺之所以能够实现,是因为我们的员工、经理和领导者共同努力,作为一个团队成长。
FLEX
FLEX 是一个以客户为中心的临床驻场服务平台,可以为药物研发公司提供人才、程序、技术和信息基础设施以及全球访问服务,从而让药物研发的整个生命周期焕发活力。
全球职业机会
我们致力于聘用雄心勃勃、有道德的专业人士,他们真诚地为成为充满活力的生命科学行业的一员而感到兴奋,并乐于接受挑战。
中国职业机会
我们致力于聘用雄心勃勃、有道德的专业人士,他们真诚地为成为充满活力的生命科学行业的一员而感到兴奋,并乐于接受挑战。
搜索Novotech
2021/6/3
首页 / 问题
什么是病例报告表?
病例报告表(CRF)是临床试验中所用的电子或纸质文档,用于记录方案和每例受试者的必要信息。病例报告表可高效和完整地进行数据收集、处理、分析和报告。病例报告表是研究者收集临床试验受试者信息的主要工具,因此每例受试者均需填写一份。
申办方-研究者将CRF填写指南放置在一起,并确保中心人员经过培训才能收集、录入和审查数据。
所有用户都分配到唯一的用户名和密码,用于访问CRF和试验数据库。主要研究者为每个用户分配权限,包括“录入数据”或“只读数据”。数据访问权限必须经过受试者的同意。
CRF应在每例受试初次访视后立即填写,以确保在方便与受试者接触时可以跟进信息。
收集所有数据时应考虑到用户和监管机构规定的要求。问题应尽可能简洁明了,以防止重复。
与方案的关系
临床试验方案确定应收集的数据类型——方案中应指定CRF中列示的信息。CRF中不应包含任何不纳入分析的数据。
CRF的要素
病例报告表由三个主要部分组成:标题、安全相关模块和疗效相关模块。
标题
标题必须包含关键识别信息,包括研究编号、研究中心编号和受试者ID编号。
安全模块
包含方案中的关键安全分析要求,其中包括人口统计学信息、不良事件、病史、体格检查、死亡、脱落和资格确认。
疗效模块
方案概述了疗效模块中必要的要素。该模块的要素包括临床试验的关键疗效终点、测定疗效的额外检查、病变测量方式以及要求的诊断。
CRF编制过程
CRF的编制随开发过程的开始而开始。设计的职责因临床研究组织、临床研究助理、数据管理经理、研究护士和数据库开发而异。
针对CRF而收集的数据应仅限于方案要求的数据,不多余也不缺失。每份CRF都需要进行跨学科审查:
每家公司都有自己的审查流程。
应包括参与临床试验实施、分析和报告的相关人员。
设计妥当和设计不当的CRF
有效的CRF,其内容可以在整个临床试验中重复使用,从而节省时间和金钱。
设计不当的CRF,缺失所要求的数据或包含不必要的信息,意味着需要在临床试验中对其进行编辑和修改。
CRF填写
填妥的CRF需要包含下列内容:
已填写的部分,包括带有标识项目的标题。
相关修改。
不良事件记录和任何严重不良事件的具体书面记录。
数据需要从相关的源文档(病历)中收集,并由研究人员录入CRF。只有指定的临床试验人员才能在CRF中记录或编辑数据。
填写的重要提示
应遵循CRF填写指南,以确保每份CRF准确并及时填妥。
确保标题中包含适当的方案、研究者和受试者识别信息。
确保数据录入正确的数据字段(以框或线标示答案应录入的位置)。
使用适当的计量单位。
前后一致。
仔细检查拼写和语法。
注意转录错误。
使用备注栏补充答案(尽可能简洁)。
保持格式、字体样式和字体大小一致。
视觉提示(框)指示要记录的数据的位置和格式。
使用复选框。
各列间用粗线分隔。
尽可能使用预编码的答案集(男性/女性、是/否和轻度/中度/重度等)。
电子病例报告表(eCRF)
确保数据完整。
提供数据变更审计跟踪。
确保数据保存(自动备份)。
从第一页到所有其他页面均显示基本识别信息(方案ID、研究中心代码和受试者ID),确保不出现重复。
容易在2个或2个以上页面之间链接数据。
由于软件故障或eCRF未成功备份,始终存在丢失有价值数据的风险。
保密和安全
为了保护受试者机密和临床试验数据,eCRF必须存储在访问受限的安全服务器上,而纸质CRF必须保存在安全位置。必须定期备份和存档以供日后查阅。
冻结数据库
数据最终确定后,有必要锁定数据库,以保护所收集数据的完整性并防止任何未经授权的编辑。
其他相关内容
2021/8/13
常见问题
数据监查委员会
阅读更多内容
2021/6/10
常见问题
什么是临床试验主文档?(TMF)
阅读更多内容
2021/4/30
常见问题
临床试验中的数据管理
阅读更多内容
2022/11/11
常见问题
临床数据管理的类型
阅读更多内容
2022/11/16
常见问题
临床数据管理:过去、现在和未来
阅读更多内容
2022/7/21
新闻
Novotech与 Medidata 扩大合作伙伴关系,继续推进临床研究
阅读更多内容
咨询专家团队
我们将及时回复有关服务、流程或技术问题的咨询。 请输入您的详细信息,Novotech 的相关人员将很快与您取得联系。
名
姓
公司名称
公司邮箱
Job Title
CountryAfghanistanAlbaniaAlgeriaAmerican SamoaAndorraAngolaAnguillaAntarcticaAntigua & BarbudaArgentinaArmeniaArubaAscension IslandAustraliaAustriaAzerbaijanBahamasBahrainBangladeshBarbadosBelarusBelgiumBelizeBeninBermudaBhutanBoliviaBosnia & HerzegovinaBotswanaBouvet IslandBrazilBritish Indian Ocean TerritoryBritish Virgin IslandsBruneiBulgariaBurkina FasoBurundiCambodiaCameroonCanadaCanary IslandsCape VerdeCaribbean NetherlandsCayman IslandsCentral African RepublicCeuta & MelillaChadChileChinaChristmas IslandClipperton IslandCocos (Keeling) IslandsColombiaComorosCongo - BrazzavilleCongo - KinshasaCook IslandsCosta RicaCroatiaCubaCuraçaoCyprusCzechiaCôte d’IvoireDenmarkDiego GarciaDjiboutiDominicaDominican RepublicEcuadorEgyptEl SalvadorEquatorial GuineaEritreaEstoniaEswatiniEthiopiaFalkland IslandsFaroe IslandsFijiFinlandFranceFrench GuianaFrench PolynesiaFrench Southern TerritoriesGabonGambiaGeorgiaGermanyGhanaGibraltarGreeceGreenlandGrenadaGuadeloupeGuamGuatemalaGuernseyGuineaGuinea-BissauGuyanaHaitiHeard & McDonald IslandsHondurasHong Kong SAR ChinaHungaryIcelandIndiaIndonesiaIranIraqIrelandIsle of ManIsraelItalyJamaicaJapanJerseyJordanKazakhstanKenyaKiribatiKosovoKuwaitKyrgyzstanLaosLatviaLebanonLesothoLiberiaLibyaLiechtensteinLithuaniaLuxembourgMacao SAR ChinaMadagascarMalawiMalaysiaMaldivesMaliMaltaMarshall IslandsMartiniqueMauritaniaMauritiusMayotteMexicoMicronesiaMoldovaMonacoMongoliaMontenegroMontserratMoroccoMozambiqueMyanmar (Burma)NamibiaNauruNepalNetherlandsNetherlands AntillesNew CaledoniaNew ZealandNicaraguaNigerNigeriaNiueNorfolk IslandNorthern Mariana IslandsNorth KoreaNorth MacedoniaNorwayOmanOutlying OceaniaPakistanPalauPalestinian TerritoriesPanamaPapua New GuineaParaguayPeruPhilippinesPitcairn IslandsPolandPortugalPuerto RicoQatarRomaniaRussiaRwandaRéunionSamoaSan MarinoSaudi ArabiaSenegalSerbiaSeychellesSierra LeoneSingaporeSint MaartenSlovakiaSloveniaSolomon IslandsSomaliaSouth AfricaSouth GeorgiaSouth KoreaSouth SudanSpainSri LankaSt. BarthélemySt. HelenaSt. Kitts & NevisSt. LuciaSt. MartinSt. Pierre & MiquelonSt. Vincent & GrenadinesSudanSurinameSvalbard & Jan MayenSwedenSwitzerlandSyriaSão Tomé & PríncipeTaiwanTajikistanTanzaniaThailandTimor-LesteTogoTokelauTongaTrinidad & TobagoTristan da CunhaTunisiaTurkeyTurkmenistanTurks & Caicos IslandsTuvaluU.S. Outlying IslandsU.S. Virgin IslandsUgandaUkraineUnited Arab EmiratesUnited KingdomUnited StatesUruguayUzbekistanVanuatuVatican CityVenezuelaVietnamWallis & FutunaWestern SaharaYemenZambiaZimbabweÅland Islands
StateAlabamaAlaskaArizonaArkansasCaliforniaColoradoConnecticutDelawareDistrict of ColumbiaFloridaGeorgiaHawaiiIdahoIllinoisIndianaIowaKansasKentuckyLouisianaMaineMarylandMassachusettsMichiganMinnesotaMississippiMissouriMontanaNebraskaNevadaNew HampshireNew JerseyNew MexicoNew YorkNorth CarolinaNorth DakotaOhioOklahomaOregonPennsylvaniaRhode IslandSouth CarolinaSouth DakotaTennesseeTexasUtahVermontVirginiaWashingtonWest VirginiaWisconsinWyoming
电话号码
我们可以如何帮助您?
我同意服务条款中的规定
By ticking this box, you consent to us storing and processing your personal data to provide you this content and future content that may be of interest. To withdraw consent or unsubscribe at any time contact communications@novotech-cro.com.
验证码
新闻&资源
解决方案
专业服务
招贤纳士
关于我们
联系
© Novotech 2024 - 版权所有
隐私
举报者政策
免责声明
NHH Slavery Statement
NHH Recruitment Scams - Website Alert
Change of Banking Details Scam
zh-hans
Cookie Consent by Privacy Policies website
条件随机场(CRF)极简原理与超详细代码解析_crf原理-CSDN博客
>条件随机场(CRF)极简原理与超详细代码解析_crf原理-CSDN博客
条件随机场(CRF)极简原理与超详细代码解析
常鸿宇
已于 2022-11-30 20:45:52 修改
阅读量8.4k
收藏
244
点赞数
65
分类专栏:
自然语言处理
文章标签:
深度学习
自然语言处理
于 2022-08-28 21:05:06 首次发布
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_44826203/article/details/126505670
版权
自然语言处理
专栏收录该内容
47 篇文章
59 订阅
订阅专栏
条件随机场(CRF)极简原理与超详细代码解析
1. 原理1.1 从名称说起1.2 优化的目标1.3 如何计算
2. 代码2.1 基本结构2.2 模型初始化2.3 BERT提取的特征如何输入给CRF2.4 计算得分2.4.1 CRF的输入与计算逻辑2.4.2 计算真实得分2.4.2.1 计算发射得分2.4.2.2 计算转移得分2.4.2.3 小结
2.4.3 计算全部路径得分
2.5 模型的训练2.6 viterbi解码
结束
CRF是一个非常经典的图模型,网上关于CRF的详细介绍也很多,但本文不会针对原理做很多介绍和推导,我也不是很擅长这方面,而是从NLP应用的角度,以经典的LSTM-CRF或BERT-CRF等
序列标注模型为切入点,介绍CRF模型是怎样用pytorch实现的,以及在实现的过程中,CRF模型到底在做什么。
如果你符合以下情况之一,那么我认为此文适合你阅读:
刚接触NLP任务,对序列标注模型有大概的理解,但不是很熟练;已经做NLP很久了,但是每次建模都是调包导模型,并不清楚模型是怎样运作的;概率论学的不是很好,不想看公式;看过很多关于CRF的介绍文章,当时懂了,回头又忘记CRF是怎么回事儿。
本文将以pytorch版本CRF的一个实现为例,尽可能详细地说明CRF是怎样实现的,对代码的解释几乎精细到每一行,相信你耐心读完本文,会从实践的角度对CRF的理解更加深刻。
1. 原理
尽管在使用CRF时,好像很简单,只需要实例化一个crf对象,然后把它拼接在特征抽取模型之后就可以了,但是说起CRF的原理,推导起来还比较复杂,多数介绍理论的文章会从图模型说起,讲到马尔可夫模型,最后再讲到CRF。但是本文是一篇偏向实践的介绍,在这里介绍原理,也只是为了更好地理解代码,所以我会说的非常简略。并且,我个人理解可能不是特别透彻,如果有讲的不准确的地方,还请路过的大佬们帮忙指正。
1.1 从名称说起
CRF全称Conditional Random Field,条件随机场,本质上是一个无向图模型,其中的“随机”,可以理解为图中的每个节点,都是一个随机变量。对于我们常提起的条件随机场模型,其实指的是狭义的线性条件随机场,我们有两组随机变量X和Y,其中Y是我们所关心的目标变量,X是可以直接获取到的观测变量。
同时也是马尔可夫随机场的一个特例,具备马尔可夫性。说起马尔可夫性,直观的理解就是,图上的每个节点的分布,只与它相邻的节点的分布有关。对于线性、有向的条件随机场来讲,也就是每一个目标变量
y
i
y_{i}
yi,只会受到它的上一个变量
y
i
−
1
y_{i-1}
yi−1的影响。
但真实世界中,这种假设,有点难以满足。刚才的假设,是只看目标变量Y的情况下,但实际情况下,Y会受到外界因素X的影响,例如,我们关心一个孩子10岁能长多高,除了需要知道他9岁的时候长了多高之外,我们还希望知道他吃的好不好,营养能不能跟得上。这也就是条件随机场中的“条件”,即CRF是对P(Y|X)进行建模的。
1.2 优化的目标
在序列标注任务中,CRF是一个链式的模型。假设我们有一个长度为seq_len的序列,我们希望把序列中的每一个token,预测它的类型,所以我们的目标可以看做是获取一个(seq_len, num_labels)的矩阵,进而可以把问题看做是一个找正确路径的问题。
假设有一个人,要从序列的开始位置走到序列的结束位置,对于每一个位置,都可以取num_labels个格子可以走,但是其中每次只有一个格子是正确的格子。那么最终可以组合出来的路径总数,一共有seq_len ** num_labels种,这其中,有一条路径,是正确的路径(也就是每个位置上的label全都预测正确),我们希望这个人,尽量能找到正确的那条路径(注意,我们关心的是尽量让每个位置的label都找正确,而不仅仅是最后的结束位置的label正确)。 假设深色的格子是每一步中正确的label,则途中红色的路径就是正确的路径。
假如每一条路径都有一个得分
p
p
p,第
k
k
k条路径的得分为
p
=
e
s
k
p=e^{s_{k}}
p=esk,我们的目标就是正确的那一条路径的得分
p
r
e
a
l
p_{real}
preal,越大越好,为了消除分数的量纲,我们的目标确定为:
P
r
o
b
=
e
p
r
e
a
l
e
p
1
+
e
p
2
+
.
.
.
+
e
s
e
q
l
e
n
∗
n
u
m
l
a
b
e
l
s
Prob = \frac{e^{p_{real}}}{e^{p_{1}}+e^{p_{2}}+...+e^{seqlen*numlabels}}
Prob=ep1+ep2+...+eseqlen∗numlabelsepreal
在上图的例子中,总共有
3
3
=
27
3^{3}=27
33=27种路径,分母就一共有27项。
我们希望真实路径的得分越大越好,所以上式取负对数,就可以作为损失函数:
L
o
s
s
=
−
log
P
r
o
b
=
log
(
e
p
1
+
e
p
2
+
.
.
.
+
e
s
e
q
l
e
n
∗
n
u
m
l
a
b
e
l
s
)
−
P
r
e
a
l
Loss = -\log_{}{Prob}=\log{}{(e^{p_{1}}+e^{p_{2}}+...+e^{seqlen*numlabels})}-P_{real}
Loss=−logProb=log(ep1+ep2+...+eseqlen∗numlabels)−Preal
对于每一条路径的得分,定义发射得分和转移得分,
发射得分
e
m
t
,
i
em_{t, i}
emt,i:第i个token的label的index是t的得分;转移得分
t
r
t
1
,
t
2
tr_{t_{1}, t_{2}}
trt1,t2:上一个token,label index是
t
1
t_{1}
t1,当前token的label index是
t
1
t_{1}
t1的得分。
1.3 如何计算
从损失函数的构成可以看到,有两部分需要计算,一是真实路径的得分,二是达到该点的所有路径的总分。 其中真实路径的得分,我们可以直接根据真实的label,分别计算出发射得分和转移得分,然后将这两项相加即可。 而所有路径的得分之和
log
(
e
p
1
+
e
p
2
+
.
.
.
)
\log{}{(e^{p_{1}}+e^{p_{2}}+...)}
log(ep1+ep2+...),如果计算每一条路径的得分,然后加起来的话,复杂度太高了,实际情况下,会采取动态规划的技巧。
假设用
P
i
,
t
P_{i, t}
Pi,t表示
t
t
t时刻,label index为
i
i
i的logsumexp形式的所有路径的得分之和,那么我们想要求个多有路径得分的总和,就是
i
i
i取所有情况时的结果之和:
log
(
e
p
1
+
e
p
2
+
.
.
.
)
=
P
1
,
s
e
q
l
e
n
+
P
2
,
s
e
q
l
e
n
+
.
.
.
+
P
n
u
m
l
a
b
e
l
s
,
s
e
q
l
e
n
\log{}{(e^{p_{1}}+e^{p_{2}}+...)}=P_{1,seqlen}+P_{2,seqlen}+...+P_{numlabels, seqlen}
log(ep1+ep2+...)=P1,seqlen+P2,seqlen+...+Pnumlabels,seqlen
我们在seq_len的维度上,也就是对时刻t,做动态规划:对于某个时刻
t
t
t,可以由上一个时刻
t
−
1
t-1
t−1转移得到这个时刻的总分。 每一步的全部得分,都可以从它上一步的所有可能的得分转移得到,由此来实现状态转移。
具体而言,反应在公式上就是: 假如这个时刻的label index是1,它上一个时刻的label index 也是1,那么有:
P
1
,
t
−
1
→
P
1
,
t
=
P
1
,
t
−
1
+
e
m
t
,
1
+
t
r
1
,
1
P_{1, t-1}\to P_{1, t} = P_{1, t-1}+em_{t, 1}+tr_{1,1}
P1,t−1→P1,t=P1,t−1+emt,1+tr1,1 同理,当它上一个时刻的label index是2时:
P
2
,
t
−
1
→
P
1
,
t
=
P
2
,
t
−
1
+
e
m
t
,
1
+
t
r
2
,
1
P_{2, t-1}\to P_{1, t} = P_{2, t-1}+em_{t, 1}+tr_{2,1}
P2,t−1→P1,t=P2,t−1+emt,1+tr2,1 于是,上一个节点的label是对于num_labels(简写为
m
m
m)中的每一种情况,我们令:
Q
1
=
P
1
,
t
−
1
+
e
m
t
,
1
+
t
r
1
,
1
Q_{1} = P_{1, t-1}+em_{t, 1}+tr_{1,1}
Q1=P1,t−1+emt,1+tr1,1 可以从1写到
m
m
m(label的总数):
Q
m
=
P
m
,
t
−
1
+
e
m
t
,
1
+
t
r
m
,
1
Q_{m} = P_{m, t-1}+em_{t, 1}+tr_{m,1}
Qm=Pm,t−1+emt,1+trm,1 (从上式我们可以看到,num_labels这一维度,只影响到了上一个时刻的得分,以及转移得分,对发射得分没有影响。)
于是由
P
i
,
t
P_{i, t}
Pi,t的定义可以得到,
P
1
,
t
=
log
(
e
Q
1
+
e
Q
2
+
.
.
.
+
e
Q
m
)
(*)
P_{1, t}=\log{}{(e^{Q_{1}}+e^{Q_{2}}+...+e^{Q_{m}})}\tag{*}
P1,t=log(eQ1+eQ2+...+eQm)(*) 这是我们考虑
i
=
1
i=1
i=1的情况时,那么对于时刻
t
t
t,一共有
m
m
m个label,同样的式子我们就可以写
m
m
m个。到这里,最终的解法也就呼之欲出了。
我们维护一个previous矩阵,它的每一行考虑了上一个状态的每一种情况时的得分,然后重复了
m
m
m行:
(
P
1
,
t
−
1
,
P
2
,
t
−
1
,
.
.
.
,
P
m
,
t
−
1
.
.
.
.
.
.
.
.
.
.
.
.
P
1
,
t
−
1
,
P
2
,
t
−
1
,
.
.
.
,
P
m
,
t
−
1
)
\begin{pmatrix} P_{1, t-1}, & P_{2, t-1}, &..., &P_{m, t-1} \\ ... & ...& ...& ...\\ P_{1, t-1}, & P_{2, t-1}, &..., &P_{m, t-1} \end{pmatrix}
⎝⎛P1,t−1,...P1,t−1,P2,t−1,...P2,t−1,...,......,Pm,t−1...Pm,t−1⎠⎞
以及发射矩阵:
(
e
m
t
,
1
,
e
m
t
,
1
,
.
.
.
,
e
m
t
,
1
e
m
t
,
2
,
e
m
t
,
2
,
.
.
.
,
e
m
t
,
2
.
.
.
.
.
.
.
.
.
.
.
.
e
m
t
,
m
,
e
m
t
,
m
,
.
.
.
,
e
m
t
,
m
)
\begin{pmatrix} em_{t, 1}, & em_{t, 1}, &..., &em_{t, 1} \\ em_{t, 2}, & em_{t, 2}, &..., &em_{t, 2} \\ ... & ...& ...& ...\\ em_{t, m}, & em_{t, m}, &..., &em_{t, m} \end{pmatrix}
⎝⎜⎜⎛emt,1,emt,2,...emt,m,emt,1,emt,2,...emt,m,...,...,......,emt,1emt,2...emt,m⎠⎟⎟⎞
以及转移矩阵:
(
t
r
1
,
1
,
t
r
2
,
1
,
.
.
.
,
t
r
m
,
1
t
r
1
,
2
,
t
r
2
,
2
,
.
.
.
,
t
r
m
,
2
.
.
.
.
.
.
.
.
.
.
.
.
t
r
1
,
m
,
t
r
2
,
m
,
.
.
.
,
t
r
m
,
m
)
\begin{pmatrix} tr_{1, 1}, & tr_{2, 1}, &..., &tr_{m, 1} \\ tr_{1, 2}, & tr_{2, 2}, &..., &tr_{m, 2} \\ ... & ...& ...& ...\\ tr_{1, m}, & tr_{2, m}, &..., &tr_{m, m} \end{pmatrix}
⎝⎜⎜⎛tr1,1,tr1,2,...tr1,m,tr2,1,tr2,2,...tr2,m,...,...,......,trm,1trm,2...trm,m⎠⎟⎟⎞
现在把这三个矩阵的对应位置的元素相加,就得到了我们刚刚定义的
Q
Q
Q的矩阵,
(
Q
1
,
1
,
Q
1
,
2
,
.
.
.
,
Q
1
,
m
.
.
.
.
.
.
.
.
.
.
.
.
Q
m
,
1
,
Q
m
,
2
,
.
.
.
,
Q
m
,
m
)
\begin{pmatrix} Q_{1, 1}, & Q_{1, 2}, &..., &Q_{1, m} \\ ... & ...& ...& ...\\ Q_{m, 1}, & Q_{m, 2}, &..., &Q_{m, m} \end{pmatrix}
⎝⎛Q1,1,...Qm,1,Q1,2,...Qm,2,...,......,Q1,m...Qm,m⎠⎞
由(*)式我们可以得到,
Q
Q
Q矩阵的每一行,取logsumexp就是一个路径得分之和
P
P
P,第一行的logsumexp就是
P
1
,
t
P_{1, t}
P1,t,以此类推。这样
m
m
m行都取logsumexp,然后转置,恰好就得到了
[
P
1
,
t
,
P
2
,
t
,
.
.
.
,
P
m
,
t
]
[P_{1, t}, P_{2, t},...,P_{m, t}]
[P1,t,P2,t,...,Pm,t]
我们对这个东西求和,也就是我们要求的就是t时刻所有路径的得分之和(优化目标的分母)。
并且,把这个行复制
m
m
m份,拼起来的新的矩阵,刚好就是下一个时刻
t
+
1
t+1
t+1对应的prev矩阵:
(
P
1
,
t
,
P
2
,
t
,
.
.
.
,
P
m
,
t
.
.
.
.
.
.
.
.
.
.
.
.
P
1
,
t
,
P
2
,
t
,
.
.
.
,
P
m
,
t
)
\begin{pmatrix} P_{1, t}, & P_{2, t}, &..., &P_{m, t} \\ ... & ...& ...& ...\\ P_{1, t}, & P_{2, t}, &..., &P_{m, t} \end{pmatrix}
⎝⎛P1,t,...P1,t,P2,t,...P2,t,...,......,Pm,t...Pm,t⎠⎞
至此,某时刻的所有路径得分之和的动态规划解法,就介绍完了,如果有同学觉得这样讲的不是很详细,可以参考这篇推送。
2. 代码
crf的pytorch实现有很多很多版本,在之前的博客中,我们简单介绍了torchcrf中的代码,但是这篇博客不再沿用之前的那一版代码进行介绍,而是采用另一个版本(多看不同版本的代码有助于加深理解)。
这个版本的代码其实是一种mask的CRF,也就是说,对所有非法的路径进行了mask,例如,从B-PER到I-LOC的转移。下面的代码中会涉及到mask,所以提前说明一下。
这一版本的CRF实现,是从经典的信息抽取模型OneIE的开源代码中粘出来的,我不确定这个代码是来自其他开源项目,还是该项目原创,总之我们现在有这样一套代码,接下来让我们一步步弄清楚在这段代码里边发生了什么。
2.1 基本结构
首先我们从整体的角度看一下代码结构,可以分为三个部分:
初始化 计算得分 viterbi解码
下面是代码基本结构:
class CRF(nn.Module):
def __init__(self, label_vocab, bioes=False):
super(CRF, self).__init__()
pass
def initialize(self):
"""初始化转移矩阵"""
pass
@staticmethod
def pad_logits(logits):
"""辅助padding方法"""
pass
# 以下5个方法用来在训练过程中计算得分
def calc_binary_score(self, labels, lens):
"""计算转移得分"""
pass
def calc_unary_score(self, logits, labels, lens)
"""计算发射得分"""
pass
def calc_gold_score(self, logits, labels, lens):
"""获取正确得分"""
pass
def calc_norm_score(self, logits, lens):
"""计算所有路径得分"""
pass
def loglik(self, logits, labels, lens):
"""计算损失"""
pass
# viterbi解码
def viterbi_decode(self, logits, lens):
pass
作为一个记录了节点状态的过程,CRF其实并不算复杂,我们需要注意的特征维度其实只有两个,标签数量m,以及状态节点的总数n,然后在实际使用中,通常还有一个batch size:
batch_size: 训练的批次,下文的介绍中为了简单起见我们通常假设它为1;num_labels: 也就是标签数量m,序列每个位置上的label可能的取值,例如只有一个实体的BIO标注的情况下,m=3(B, I, O),如果是BIOES标注,则m=5;seq_len: 序列的长度n。
在正式开始之前,还是要唠叨一句,看代码的过程中一定要有维度的意识,这对我们理解代码是如何操作的非常重要,在下文中,我把所有的维度信息全都高亮了,以引起大家的注意。接下来我们将逐步拆解每一部分代码。
2.2 模型初始化
我们知道,在一个线性的CRF中,每一个节点的状态由它上一个节点的状态以及输入x计算获得:
对于BERT-CRF模型,输入x是由BERT编码得到的特征,相对CRF模型来讲可以看做是外部输入,所以在构建CRF模型本身时,我们并不需要在类中维护这样一个变量,只需要将它以数据流的形式参与计算即可。而节点与节点之间的状态转移,则是CRF模型内部的,需要在训练的过程中对其进行维护,它的内容是每一个label到另一个label(包括其自身)的得分,所以我们需要维护一个尺寸为[num_labels, num_labels]的一个矩阵。
模型的初始化部分包括了__init__和initialize。
需要注意的是,CRF的整个路径的长度,与待标注的序列的长度其实是不一样的,多了一个开始标记位和一个结束标记位,这就造成了转移矩阵的尺寸需要加2,所以准确地说,我们的状态转移矩阵的尺寸应该是[num_labels+2, num_labels+2],并且拿到bert编码输出之后,传入CRF之前,需要一个额外的padding操作,我们把padding的方法pad_logits也放在初始化这部分中讲。
具体的解释详见下面代码中的注解。
class CRF(nn.Module):
def __init__(self, label_vocab, bioes=False):
"""
:param label_vocab: Dict: 每个label对应的idx,例如{"O": 0, "B-PER": 1, ...}
:param bioes: bool: 是bioes形式的标注还是bio形式的标注,默认bio
整个初始化过程其实就是创建了一个状态转移矩阵transition
"""
super(CRF, self).__init__()
self.label_vocab = label_vocab
self.label_size = len(label_vocab) + 2 # 增加了
self.bioes = bioes
self.start = self.label_size - 2 # 倒数第2个label是
self.end = self.label_size - 1 # 倒数第1个label是
transition = torch.randn(self.label_size, self.label_size) # 初始化一个(num_labels+2, num_labels+2)的矩阵
self.transition = nn.Parameter(transition) # 将状态转移矩阵转化为可训练参数
self.initialize()
def initialize(self):
"""
对转移矩阵进行进一步操作,将所有必然不可达的状态都设置为一个默认值-100
注意第一个axis是to_label, 第二个axis才是from_label
"""
self.transition.data[:, self.end] = -100.0 #
self.transition.data[self.start, :] = -100.0 # 没有任何一个label可以转移到
# 对num_labels两层遍历,排除所有不合理的情况
for label, label_idx in self.label_vocab.items(): # ("O": 0), ("B-PER": 1), ...
if label.startswith('I-') or label.startswith('E-'): #
self.transition.data[label_idx, self.start] = -100.0
if label.startswith('B-') or label.startswith('I-'): #
self.transition.data[self.end, label_idx] = -100.0
for label_from, label_from_idx in self.label_vocab.items():
if label_from == 'O':
label_from_prefix, label_from_type = 'O', 'O'
else:
label_from_prefix, label_from_type = label_from.split('-', 1)
for label_to, label_to_idx in self.label_vocab.items():
if label_to == 'O':
label_to_prefix, label_to_type = 'O', 'O'
else:
label_to_prefix, label_to_type = label_to.split('-', 1)
if self.bioes:
# 1. 如果是BIOES形式,则
# 1) [O, E, S]中的任意一个状态,都可以转移到[O, B, S]中任意一个状态,不论前后两个状态的label是否相同
# - 例如,可以从E-PER转移到B-LOC
# 2) 当label相同时,允许B->I, B->E, I->I, I->E
is_allowed = any(
[
label_from_prefix in ['O', 'E', 'S']
and label_to_prefix in ['O', 'B', 'S'],
label_from_prefix in ['B', 'I']
and label_to_prefix in ['I', 'E']
and label_from_type == label_to_type
]
)
else:
# 2. 如果是BIO形式,则
# 1) 任何一个状态都可能转移到B和O
# 2) I状态只能由相同label的B或者I得到
is_allowed = any(
[
label_to_prefix in ['B', 'O'],
label_from_prefix in ['B', 'I']
and label_to_prefix == 'I'
and label_from_type == label_to_type
]
)
if not is_allowed:
self.transition.data[label_to_idx, label_from_idx] = -100.0
下面是padding的方法,其实就是给logits在第axis=2上添加了一个sos和一个eos,并且把序列的所有位置的sos和eos的概率全都设置为-100:
@staticmethod
def pad_logits(logits):
"""Pad the linear layer output with
:param logits: Linear layer output (no non-linear function).
"""
batch_size, seq_len, _ = logits.size() # (batch, seq_len, num_labels)
pads = logits.new_full((batch_size, seq_len, 2), -100.0,
requires_grad=False) # 返回一个形状为(batch, seq_len, 2)的tensor,所有位置填充为-100
logits = torch.cat([logits, pads], dim=2) # 拼接得到(batch, seq_len, num_labels+2)
return logits
经过上面的变化,我们就可以实现BERT输出的logits与CRF中转移矩阵的尺寸对应了。
2.3 BERT提取的特征如何输入给CRF
在展开计算得分的介绍之前,我们需要先搞明白,CRF与我们的特征提取模型(BERT编码器)是如何进行交互的。 假如我们有一个用transformers创建的BERT模型:
from transformers import BertModel, BertTokenizer
bert = BertModel.from_pretrained('bert-base-uncased')
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
然后我们对输入进行tokenize之后,得到input_ids和attention_mask,然后把它们传给模型。
text = 'your text here.'
inputs = tokenizer(text)
bert_out = bert(inputs['input_ids'], attention_mask=inputs['attention_mask'])[0]
对这一块不熟悉的同学可以先去学一下transformers模块。 我们这里拿到的bert_out,是一个尺寸为(batch, seq_len, hidden)的tensor,
到这里还不够,细心的同学可能已经发现,在前文的维度介绍中,并没有bert的hidden_size这个尺寸,CRF关心的是每个token被分为每个label的概率,并不关心bert所定义的向量空间,所以需要用一个linear层把它干掉。
import torch.nn as nn
label_ffn = nn.Linear(bert_hidden_size, num_labels, bias=True)
label_scores = label_ffn(bert_out)
经过这样的一个线性变换,我们得到的label_scores,也就是输入给CRF的外部特征x序列,尺寸就变成了(batch, seq_len, num_labels)。
然而,我们刚刚也说了label_scores的尺寸中是num_labels,而CRF中需要的是num_labels+2,所以我们利用刚才的padding方法,进行转换:
crf = CRF(vocab) # 这里实例化一个CRF类,目的是利用它的padding方法
label_scores = crf.pad_logits(label_scores)
于是现在label_scores的尺寸变成了CRF所需要的(batch, seq_len, num_labels+2)。
万事俱备,只差CRF。
2.4 计算得分
2.4.1 CRF的输入与计算逻辑
终于到了CRF的核心部分。接着2.3节中的内容,我们看一下crf计算损失的总控函数是怎样的。在这一版的代码中,CRF.loglik就是这个总控方法,其调用时传入的参数如下:
label_loglik = crf.loglik(label_scores,
label_idxs,
token_nums)
label_scores: 上文所述的每个位置上每个label得分,(batch, seq_len, num_labels+2); label_idxs: 每个位置上正确的label index,(batch, seq_len); token_nums: batch中每个序列的token数量,(batch).
为了更好地帮助读者理解,我们举一个例子: 假如我们有一个序列(简单起见不考虑[CLS], [SEP]和subword分词):
['Tom', 'went', 'to', 'New', 'York', '.']
我们的label是BIO的形式标注的两类实体,PER和LOC,那么所有label为:
['O', 'B-PER', 'I-PER', 'B-LOC', 'I-LOC']
以及label对应的vocab:
{'O': 0, 'B-PER': 1, 'I-PER': 2, 'B-LOC': 3, 'I-LOC': 4}
那么,我们需要输入CRF的label_scores就是一个(1, 6, 5)的tensor, 对应的label_idxs是一个(1, 6)的tensor,它应该是:
tensor([[1, 0, 0, 3, 4, 0]])
# PER, O, O, LOC, LOC, O
以及token_nums是一个(batch)的tensor,它应该是:
tensor([6])
至于它们三个分别是干什么用的,到了具体的计算中自然就清楚了。
说回我们的总控方法:
def loglik(self, logits, labels, lens):
norm_score = self.calc_norm_score(logits, lens) # 全部路径的得分总和
gold_score = self.calc_gold_score(logits, labels, lens) # 由正确的label计算出来的得分
return gold_score - norm_score
这一部分就很清楚地写出了CRF的训练目标,
L
o
s
s
=
−
log
P
r
o
b
=
log
(
e
p
1
+
e
p
2
+
.
.
.
+
e
s
e
q
l
e
n
∗
n
u
m
l
a
b
e
l
s
)
−
P
r
e
a
l
Loss = -\log_{}{Prob}=\log{}{(e^{p_{1}}+e^{p_{2}}+...+e^{seqlen*numlabels})}-P_{real}
Loss=−logProb=log(ep1+ep2+...+eseqlen∗numlabels)−Preal
但是这个loglik中跟损失函数实际上是反着的,所以在优化这个损失的时候,记得给它取负号。
2.4.2 计算真实得分
在loglik中我们看到,CRF的训练目标,是gold_score与norm_score之间的差值,我们希望这两部分尽量接近,也就是真实路径的得分所占全部路径得分之和的比值尽可能大。
那么gold_score,也就是真实得分,是怎么来的呢,它由发射得分和转移得分两部分构成:
def calc_gold_score(self, logits, labels, lens):
"""计算真实得分"""
unary_score = self.calc_unary_score(logits, labels, lens).sum(1).squeeze(-1)
binary_score = self.calc_binary_score(labels, lens).sum(1).squeeze(-1)
return unary_score + binary_score
接下来的2.4.2.1和2.4.2.2两节中,将详细介绍发射得分和转移得分是怎么来的。
2.4.2.1 计算发射得分
发射得分,是由外部输入x得到的,有些人也习惯称其为Unigram得分,因为只考虑了x这一项(与此同时,转移得分也称作Bigram,因为同时考虑了yi和yi-1)。
发射得分的代码是这样写的:
def calc_unary_score(self, logits, labels, lens):
"""
计算发射得分
logits: (batch, seq_len, num_labels+2)
labels: (batch, seq_len)
lens: (batch)
"""
labels_exp = labels.unsqueeze(-1)
scores = torch.gather(logits, 2, labels_exp).squeeze(-1)
mask = sequence_mask(lens).float()
scores = scores * mask
return scores
可以看到它把三项输入全都用上了。 这个方法的核心就是一个torch.gather(),具体而言,就是对根据一个idx tensor,对目标tensor取值,然后再拼接再一起。
其中第一个参数logits,是目标tensor,也就是说,gather返回的值,是从这个tensor中取出来的;第二个参数2,意思是在哪一个维度上进行操作,我们知道logits有0, 1, 2三个维度,这里的2意思就是在维度2,也就是num_labels+2的那个维度上进行操作;第三个参数label_exp,是告诉gather方法,在取值的时候怎么取。
由于torch.gather要求idx tensor和目标tensor的维度是一样的,而labels相比logits少了一个维度,所以我们需要先对它unsqueeze出一个维度。
简便起见,我们忽略batch维度,假设输入的logits是这样的:
# logits: (seq_len, num_labels+2)
tensor([[-3.5, 7.1, 6.9, -5.4, 2.0, -8.7, -1.9],
[15.7, -4.2, 6.6, -5.3, -11.9, -2.2, 2.3],
...])
假设输入的label,在忽略batch,并且unsqueeze之后,是这样的:
# labels: (seq_len, 1)
tensor([[1],
[0],
...,])
那么gather之后的结果就是这样的(同样是忽略batch):
# scores: (seq_len, 1)
tensor([[7.1],
[15.7],
...])
也就是logits中的第0行,根据label的第0行,取了第1个元素,logits的第1行,根据label的第1行取了第0个元素,……,然后再把取完之后的结果拼起来,具有形状(batch, seq_len, 1),最后在squeeze掉最后一个维度,就是我们需要的scores了,形状是(batch, seq_len)。
然后要进行mask,这个sequence_mask是一个辅助函数,定义如下:
def sequence_mask(lens, max_len=None):
"""Generate a sequence mask tensor from sequence lengths, used by CRF."""
batch_size = lens.size(0)
if max_len is None:
max_len = lens.max().item()
ranges = torch.arange(0, max_len, device=lens.device).long()
ranges = ranges.unsqueeze(0).expand(batch_size, max_len)
lens_exp = lens.unsqueeze(1).expand_as(ranges)
mask = ranges < lens_exp
return mask
这个方法很简单,就是根据一个batch生成一个mask tensor,举个例子,如果你的batch_size=4,序列的长度分别为3,5,2,6,那么生成的mask矩阵就是一个形状为(batch, max_seq_len)的tensor:
tensor([[True, True, True, False, False, False],
[True, True, True, True, True, Flase],
[True, True, False, False, False, False],
[True, True, True, True, True, True]])
将mask作用在scores上,就实现了对scores的padding部分(注意这里说的padding是序列长度方向上的padding,不是上文padding函数的num_labels方向上的padding,因为到这里num_labels这个维度已经被gather掉了)的mask。
以上就是ugram特征(发射得分)的计算过程,最终我们得到的scores是形状是(batch, seq_len),还是比较容易理解的。
趁热打铁,我们来直观地理解一下这个发射得分是什么东西。回顾一下这个计算过程,首先我们利用BERT编码器,获取到了batch中每个instance的token的特征((batch, seq_len, hidden)),然后我们利用一个Linear层,将每个token上的特征,转化成了在label空间上的logits((batch, seq_len, num_labels+2)),这一步的结果,可以理解为,每个token属于某一类label的“概率”。
我们知道,CRF是一个判别模型,所以直观地理解,我们希望这个模型“更准确”,那么就是希望对于某一个token,能够以更大的“概率”,将其判断为正确的label,所以自然地,在我们知道label的情况下,把所有正确label位置上的得分都取出来,就是整个序列的发射得分,我们希望它尽可能大。
2.4.2.2 计算转移得分
如2.2节中所述,转移得分的计算与发射得分不同,是需要用到CRF类的transition矩阵的(刚刚的calc_unary_score放到CRF类里,在pycharm中会有黄色的波浪线,因为它是静态方法)。
需要注意的是,既然是状态转移“矩阵”,那就是涉及到两个方向的,所以num_labels维度和seq_len维度在这一部分中都需要注意。
为了方便说明转移得分的计算过程,我们还是以之前的例子,两类实体BIO格式标注,共有5个label,那么,加上sos和eos两个额外的label,一共就是7个label,其中每个label对应的idx如下:
‘O’: 0‘B-PER’: 1‘I-PER’: 2‘B-LOC’: 3‘I-LOC’: 4‘sos’: 5‘eos’: 6
为了与前文保持统一,仍然称真实的label数量为num_labels(5),添加了开始和结束位的数量为num_labels+2(7)。
这一部分可能有点绕,但是别慌,跟着下面的思路走,理解应该不成问题。先把注解版的代码贴在下面,然后我们一段一段的讲这部分代码。
def calc_binary_score(self, labels, lens):
"""
计算转移得分
:param labels: (batch, seq_len)
:param lens: (batch)
:return:
"""
batch_size, seq_len = labels.size()
# 1. 扩展label:其实就是对labels在seq_len的维度上扩展了一个开头和末尾
# A tensor of size batch_size * (seq_len + 2)
labels_ext = labels.new_empty((batch_size, seq_len + 2)) # 生成一个(batch_size, seq_len + 2)没有初始化的tensor
labels_ext[:, 0] = self.start # batch中每个instance的第1个位置的值变成start(label_size -2)
labels_ext[:, 1:-1] = labels
mask = sequence_mask(lens + 1, max_len=(seq_len + 2)).long() # 开头start位置为True,后边true部分每一位向后移动一位
pad_stop = labels.new_full((1,), self.end, requires_grad=False) # (batch), 以eos生成一个tensor([6,...,6])
pad_stop = pad_stop.unsqueeze(-1).expand(batch_size, seq_len + 2) # (batch) -> (batch, seq_len+2)
labels_ext = (1 - mask) * pad_stop + mask * labels_ext # 被mask的部分变成6,剩下的部分是正确的label
labels = labels_ext
# 2. 扩展transition:复制了batch份,另batch中的每个instance都有一个transition矩阵
trn = self.transition # 注意,self.transition的行是from_label, 列是to_label
trn_exp = trn.unsqueeze(0).expand(batch_size, self.label_size,
self.label_size)
# 接下来两部分是重点,计算了从一个label转移到另一个label的得分
# 3. to_label的得分计算
lbl_r = labels[:, 1:] # 在原始的seq_len上去掉了第一个token
lbl_rexp = lbl_r.unsqueeze(-1).expand(*lbl_r.size(), self.label_size) # (batch, seq_len+1) -> (batch, seq_len+1, num_labels)
# score of jumping to a tag
# 取trn_exp的lbl_rexp中对应的一行(也就是取真实label对应的转移),然后拼起来
# (batch, num_labels+2, num_labels+2) -> (batch, seq_len-1, num_labels+2)
trn_row = torch.gather(trn_exp, 1, lbl_rexp) # 这个就是每一个token上,由某个label转移到当前label的得分
# 4. from_label的得分计算
lbl_lexp = labels[:, :-1].unsqueeze(-1) # (batch, seq_len+1, 1) 每个位置是从哪个label转移来的
trn_scr = torch.gather(trn_row, 2, lbl_lexp) # (batch, seq_len+1, 1) from_label到to_label的真实得分
trn_scr = trn_scr.squeeze(-1) # (batch, seq_len+1, 1) -> (batch, seq_len-1)
# 5. mask掉seq_len维度上的start,注意不是mask掉num_labels上的start
mask = sequence_mask(lens + 1).float()
trn_scr = trn_scr * mask
score = trn_scr
return score
我们把这部分代码拆解成五个部分,
§ 第一部分
第一部分是将labels做了一个扩展,看似很简单,但是第一眼看过去可能有点疑惑,这么做的目的是什么,
其实是为这个函数的核心思想——错位——做准备。
# 1. 扩展label:其实就是对labels在seq_len的维度上扩展了一个开头和末尾
# A tensor of size batch_size * (seq_len + 2)
labels_ext = labels.new_empty((batch_size, seq_len + 2)) # 生成一个(batch_size, seq_len + 2)没有初始化的tensor
labels_ext[:, 0] = self.start # batch中每个instance的第1个位置的值变成start(label_size -2)
labels_ext[:, 1:-1] = labels
mask = sequence_mask(lens + 1, max_len=(seq_len + 2)).long() # 开头start位置为True,后边true部分每一位向后移动一位
pad_stop = labels.new_full((1,), self.end, requires_grad=False) # (batch), 以eos生成一个tensor([6,...,6])
pad_stop = pad_stop.unsqueeze(-1).expand(batch_size, seq_len + 2) # (batch) -> (batch, seq_len+2)
labels_ext = (1 - mask) * pad_stop + mask * labels_ext # 被mask的部分变成eos的idx,剩下的部分是正确的label
labels = labels_ext
这几行其实就是在seq_len的维度上加了2,从形状为(batch, seq_len)的labels,生成了一个扩展的形状为(batch, seq_len+2)的label_ext。 细心的同学可能发现了,之前我们+2,增加的sos和eos位置,那不是在num_labels的维度上加的吗,意思是标记状态转移的开始和结束,现在怎么又加在seq_len维度上了,这不就乱了吗?其实这两者正好是对应的,也就是我们在开始之前有一个虚拟的开始位置,它的label是sos,结束之后有一个结束位置,label是eos。
说回错位的思想:
由CRF的(条件)马尔可夫性可知,某一个时刻的状态,除了受外界条件(输入x,也就是发射得分的部分)影响之外,只受它的前一时刻的状态影响,所以理论上我们需要两个label序列,一个from序列,和一个to序列。但是对于label来讲,两个序列其实是共享的,他们之间只差了一个时间步(也就是在seq_len维度上),所以我们没有必要做两条label序列出来,而是给它做长一点就可以了:
生成一个label_ext,使得取[:, 1:]时,获取的是to序列的label,取[:, :-1]时,获取的是from序列的label。 具体是怎么操作的呢,还是假设batch_size是1,假如我们的label原本是这个样子:
[[1, 0, 0, 3, 4, 0]]
# [[B-PER, O, O, B-LOC, I-LOC, O]]
经过前三行之后,就变成了:
[[5, 1, 0, 0, 3, 4, 0, ?]]
# [[sos, B-PER, O, O, B-LOC, I-LOC, O, empty]]
然后生成了一个mask,mask跟之前的mask相比,其实就是对batch中的每一个instance,在第一个1之前又加了一个1。于是我们有mask:
[[1., 1., 1., 1., 1., 1., 1., 0.]]
再然后,以eos(idx=6)做了一个用来pad_stop,形状为(batch, seq_len+2),其中的内容全都是eos对应的idx,也就是6.,然后把这个结合mask作用在label_ext上,也就是所有被mask的位置的label,变成eos的idx,其余位置保留。
折腾了一大圈下来,我们的label_ext也就变成了:
[[5, 1, 0, 0, 3, 4, 0, 6]]
假如我们的batch_size不是1,还有另外一句话,“Tom is cool.”,那生成的label_ext,可能是这个样子:
[[5, 1, 0, 0, 3, 4, 0, 6],
[5, 1, 0, 0, 0, 6, 6, 6]]
§ 第二部分
第二部分是扩展转移矩阵transition
# 2. 扩展transition:复制了batch份,另batch中的每个instance都有一个transition矩阵
trn = self.transition # 注意,self.transition的行是from_label, 列是to_label
trn_exp = trn.unsqueeze(0).expand(batch_size, self.label_size, # 这里的self.label_size就是一直强调的num_labels+2
self.label_size)
这部分很容易理解,就是在batch维度上复制,相当于给了batch中的每一个instance都有一个转移矩阵,扩展之后的trn_exp的形状为(batch, num_labels+2, num_labels+2)。
需要注意的是,self.transition的两个num_labels+2的维度,是to在前,from在后,如果这里没有注意到,后边的代码可能就优点困惑了。详见2.2节中的初始化部分。
§ 第三部分
第三部分计算是to label,得到的是:
“假如我们知道当前位置的label,从上一个位置的每一个label转移过来的得分”
可能比较拗口,下面根据代码解释一下:
# 3. to_label的得分计算
lbl_r = labels[:, 1:] # 在原始的seq_len上去掉了第一个token
lbl_rexp = lbl_r.unsqueeze(-1).expand(*lbl_r.size(), self.label_size) # (batch, seq_len+1) -> (batch, seq_len+1, num_labels)
# score of jumping to a tag
# 取trn_exp的lbl_rexp中对应的一行(也就是取真实label对应的转移),然后拼起来
# (batch, num_labels+2, num_labels+2) -> (batch, seq_len+1, num_labels+2)
trn_row = torch.gather(trn_exp, 1, lbl_rexp) # 这个就是每一个token上,由某个label转移到当前label的得分
代码第一行:如第一部分中所述,我们对labels取[:, 1:]时,获取的是to序列的label 代码第二行:扩展出一个维度,(batch, seq_len+2-1) 扩展为 (batch, seq_len+2-1, num_labels) 代码第三行:以to label(每个位置上的真实label)为标准,在转移矩阵的维度1(num_labels+2的维度上),取相应的行拼在一起,得到一个形状为(batch, seq_len+1, num_labels+2)的tensor trn_row。
具体的,在不考虑batch的情况下,假如我们在第二部分中扩展trn_ext是这个样子(实际不会是这样样子,这样写为了方便理解gather):
tensor([[0, 0, 0, 0, 0, 0, -100],
[1, 1, 1, 1, 1, 1, -100],
[2, 2, 2, 2, 2, 2, -100],
...,])
gather之后的trn_row就是分别取了to label对应的每一行,然后再拼起来,也就是:
tensor([[5, 5, 5, 5, 5, 5, -100],
[1, 1, 1, 1, 1, 1, -100],
[0, 0, 0, 0, 0, 0, -100],
...])
再强调一遍,trn_row中的每一行,不会是像 [1, 1, 1, 1, 1, 1, -100],它的实际意义是,每一个token(列的方向上),由上一个token的每一种可能的label(行的方向上),转移到当前label的得分。也就是说,这一步的计算,固定了to label,而没有固定from label。
每一行中,指的是上一个token的label,而不是当前这个token的label。所以我们看到,任意一行,最后一个元素一定是-100,因为不可能出现“上一个token是eos”的状况。
§ 第四部分
第四部分计算的是from label
得到的是:当上一个token的label是
l
a
b
e
l
i
label_{i}
labeli时,转移到当前token的label是
l
a
b
e
l
j
label_{j}
labelj的得分。
理解了第三部分之后,第四部分就相对容易理解了。刚才说了,第三部分中只确定了当前token的label,而上一个token的label是任意的,第四部分就是把上一个token的label也确定下来,这样就得到了在当前的转移矩阵下,真实情况下的从labeli转移到labelj的转移得分。
# 4. from_label的得分计算
lbl_lexp = labels[:, :-1].unsqueeze(-1) # (batch, seq_len+1, 1) 每个位置是从哪个label转移来的
trn_scr = torch.gather(trn_row, 2, lbl_lexp) # (batch, seq_len+1, 1) from_label到to_label的真实得分
trn_scr = trn_scr.squeeze(-1) # (batch, seq_len+1, 1) -> (batch, seq_len-1)
第一行代码:取label的左移部分,模拟的是上一个token的label; 第二行代码:简单解释一下,就是在我们刚刚得到的trn_row中的每一行,根据真实的label,取对应的列,然后重新组合,得到真实的分值; 第三行代码:删除多余的num_labels+2维度。
至此,我们就成功取到了转移得分。
§ 第五部分 最后把多余的虚拟的start位置的得分给mask掉。这部分就不展开介绍了。
2.4.2.3 小结
全部讲完之后,还是定性的来理解一下转移得分,其实就是每个token上,我们不考虑这个token的词是谁,有什么特征,我们只关心它的上一个token的label是什么,以此来判断当前这个词的label,说白了,就像是“找规律”一样,类似于一种先验知识,我们通过学习一个transition矩阵,知道了某个label它的下一个label更有可能是谁,BERT-CRF模型,也正是因为在BERT编码器学到的特征之外,额外考虑了这部分“先验”的知识,才使得效果能够有所提升。
在发射分和转移分的计算上,最终都是落在了某个token的得分上,但是我们想要的,是整个序列整体的好坏,而不是某个位置的得失。对于这个问题,其实很简单,直接取平均就好。
回顾2.4.2中gold_score的计算,也正是在seq_len维度上取了平均。
拓展一下思维,除了取平均,我们当然也可以采用其他的思想来综合地衡量整个序列,例如,如果我们不怎么关心’O’label,更关心实体的话,可以根据label的类型不同,进行加权。
2.4.3 计算全部路径得分
这一部分一定要结合第1.3节来理解。
代码中的alpha,其实就是前面介绍的动态规划过程中为何的previous矩阵。
完整的代码如下:
def calc_norm_score(self, logits, lens):
"""
:param logits: (batch, seq_len, num_labels+2))
:param lens: (batch)
:return:
"""
batch_size, _, _ = logits.size()
alpha = logits.new_full((batch_size, self.label_size), -100.0) # 生成一个(batch, num_labels+2)的-100
alpha[:, self.start] = 0 # 每一步的状态分
lens_ = lens.clone()
logits_t = logits.transpose(1, 0) # (seq_len, batch, num_labels+2)
for logit in logits_t:
# 对每一个step进行遍历
logit_exp = logit.unsqueeze(-1).expand(batch_size, # 新增了一个维度,并复制了num_labels+2份
self.label_size,
self.label_size)
alpha_exp = alpha.unsqueeze(1).expand(batch_size, # 同理, (batch, num_labels+2, num_labels+2)
self.label_size,
self.label_size)
trans_exp = self.transition.unsqueeze(0).expand_as(alpha_exp) # (batch, num_labels+2, num_labels+2)
# 状态转移,每一步的得分是上一步的得分+状态分+转移分
mat = logit_exp + alpha_exp + trans_exp # (batch, num_labels+2, num_labels+2)
# 为下一步的转移生成prev矩阵
alpha_nxt = log_sum_exp(mat, 2).squeeze(-1) # (batch, num_labels+2)
mask = (lens_ > 0).float().unsqueeze(-1).expand_as(alpha)
alpha = mask * alpha_nxt + (1 - mask) * alpha
lens_ = lens_ - 1
alpha = alpha + self.transition[self.end].unsqueeze(0).expand_as(alpha) # 所有token遍历完之后加结束位
norm = log_sum_exp(alpha, 1).squeeze(-1)
return norm
然后我们一点一点来消化这部分代码: 首先前几行:
batch_size, _, _ = logits.size()
alpha = logits.new_full((batch_size, self.label_size), -100.0) # 生成一个(batch, num_labels+2)的-100
alpha[:, self.start] = 0
lens_ = lens.clone()
初始化了一个形状为(batch, num_labels+2)的alpha,如果不考虑batch的话,它其实就是1.3节里介绍的previous矩阵中的一行。
然后对logits进行了转置:
logits_t = logits.transpose(1, 0)
交换了batch和seq_len这两个维度,因为我们的状态转移发生在一个time step到下一个time step之间,所以接下来要对seq_len这个维度进行循环,所以把它换到最前面。
然后开始遍历,计算每一个step的所有可能的路径的路径分之和:
for logit in logits_t:
# 对每一个step进行遍历
logit_exp = logit.unsqueeze(-1).expand(batch_size, # 新增了一个维度,并复制了num_labels+2份
self.label_size,
self.label_size)
alpha_exp = alpha.unsqueeze(1).expand(batch_size, # 同理, (batch, num_labels+2, num_labels+2)
self.label_size,
self.label_size)
trans_exp = self.transition.unsqueeze(0).expand_as(alpha_exp) # (batch, num_labels+2, num_labels+2)
# 状态转移,每一步的得分是上一步的得分+状态分+转移分
mat = logit_exp + alpha_exp + trans_exp # (batch, num_labels+2, num_labels+2)
# 为下一步的转移生成prev矩阵
alpha_nxt = log_sum_exp(mat, 2).squeeze(-1) # (batch, num_labels+2)
mask = (lens_ > 0).float().unsqueeze(-1).expand_as(alpha)
alpha = mask * alpha_nxt + (1 - mask) * alpha
lens_ = lens_ - 1
可以看到,logit被复制了num_labels+2份(也就是
m
m
m份),logits是什么呢,前边的代码介绍中说了,是特征提取模型的特征映射到num_labels这个空间下的“概率”,也就是发射得分。
然后alpha也被同样的复制,就得到了previous矩阵。
再然后,得到了转移矩阵。
三个矩阵具有相同的形状(batch, num_labels+2, num_labels+2),对应位置元素三项相加,就是在执行状态转移了,得到的这个mat,就是1.3节对应的
Q
Q
Q了,紧接着对它求logsumexp,就得到了当前step(也就是截止当前token长度)的所有路径的得分之和。
算完了当前的step,再把这个alpha_nxt更新到alpha,作为下一个step的previous矩阵。
在遍历的最后,我们还需要把seq_len维度上的padding的部分给mask掉(因为代码在实际操作的时候是批处理的,所以短的句子末尾的padding需要mask),这样一来,一次完整的遍历就结束了。
当整个for循环完成之后,就到达了序列的末尾,最后要注意一下边界条件,要把结束位再算一下,就大功告成啦。
至于logsumexp是怎么计算的,这一版代码的实现方法如下:
def log_sum_exp(tensor, dim=0, keepdim: bool = False):
"""LogSumExp operation used by CRF."""
m, _ = tensor.max(dim, keepdim=keepdim)
if keepdim:
stable_vec = tensor - m
else:
stable_vec = tensor - m.unsqueeze(dim)
return m + (stable_vec.exp().sum(dim, keepdim=keepdim)).log()
回顾优化目标:
P
r
o
b
=
e
p
r
e
a
l
e
p
1
+
e
p
2
+
.
.
.
+
e
s
e
q
l
e
n
∗
n
u
m
l
a
b
e
l
s
Prob = \frac{e^{p_{real}}}{e^{p_{1}}+e^{p_{2}}+...+e^{seqlen*numlabels}}
Prob=ep1+ep2+...+eseqlen∗numlabelsepreal 2.4.2节中,我们介绍了目标变量的分子部分的计算,2.4.3节中,介绍了分母的计算,这样一来,就可以回到2.4.1节中,完成损失函数的计算了。
2.5 模型的训练
之前讲过了本文的重点是帮助大家理解CRF的运作,所以具体怎么使用,会比较简略的介绍。
在计算损失函数之后,接下来要关心的是,如何把CRF模型放进BERT-CRF这个框架中去,并实现对它的训练呢?
在2.3节中,已经介绍了,BERT的编码结果是怎样输入给CRF的,
假设已经实例化好了bert模型和crf模型:
import torch.nn as nn
from transformers import BertModel, BertTokenizer
bert = BertModel.from_pretrained('bert-base-uncased')
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
crf = CRF(vocab)
假设bert部分的损失采用的是交叉熵损失:
bert_criteria = nn.CrossEntropyLoss()
然后计算了bert的序列标注得分:
text = 'your text here.'
inputs = tokenizer(text)
bert_out = bert(inputs['input_ids'], attention_mask=inputs['attention_mask'])[0]
label_ffn = nn.Linear(bert_hidden_size, num_labels, bias=True)
label_scores = label_ffn(bert_out)
label_scores_softmax = label_scores.softmax(dim=2)
进而计算bert的损失:
label_scores_softmax = label_scores_softmax .view(-1, entity_type_num) # 这里的entity_type_num是实体类型数
bert_loss = bert_criterira(label_scores_softmax, real_labels) # real_labels是真实标签铺平
然后按照2.3节的步骤,把bert_out给到crf,计算crf的损失,并加到bert损失上(注意符号,求crf的损失要取负号):
label_scores = crf.pad_logits(label_scores)
crf_loglik = crf.loglik(label_scores,
label_idxs,
token_nums)
total_loss = bert_loss - crf_loss.mean()
最后,把这部分内容写到整个BERT-CRF模型训练时的forward方法里就可以了。
2.6 viterbi解码
解码发生在模型的预测阶段。这一部分会讲的相对简略一点,因为涉及到的思想和代码在之前其实都已经出现过了。
当我们训练好了一个CRF模型,这个模型的转移矩阵transition已经确定下来了,接下来我们需要根据外部的输入
X
X
X,也就是特征提取模型计算出来的logits,来结合CRF模型的状态转移,解码出最优的路径,作为序列标注任务的最终输出结果。
暴力解法当然是最容易想到的,不管序列有多长,全部的路径是有限的,那就把每一条路径的得分全都计算出来,然后取得分最高的。但是暴力法的复杂度,随着序列长度的增加,是呈指数增长的,因为在每一个token上,都会有
m
m
m种label可以选择,这样的复杂度,在真实场景中显然是无法接受的。
因此,viterbi解码被引入了,它的本质其实还是动态规划。 我们用
S
c
o
r
e
[
s
t
a
r
t
]
[
e
n
d
]
Score_{[start][end]}
Score[start][end]来表示从
s
t
a
r
t
start
start到
e
n
d
end
end位置的所有可能的得分,那么显然,求解的目标就是从开始位置
s
o
s
sos
sos到结束位置
e
o
s
eos
eos的所有可能的路径中,得分最大的一条路径:
P
a
t
h
(
m
a
x
(
S
c
o
r
e
[
s
o
s
]
[
e
o
s
]
)
)
Path(max(Score_{[sos][eos]}))
Path(max(Score[sos][eos])) 那么,就可以向前递推,当我们想要求eos位置的最大分数,其实就是求红色圈中的三者得分转移到eos位置中的得分最大者: 那么在step3时,每一项,同样是求前面一步中,三项转移到step3的得分的最大者。
下面是代码,如果不太理解的话可以结合前面求所有路径分的动态规划过程,回顾一下torch中这几个操作时在做什么。
def viterbi_decode(self, logits, lens):
"""Borrowed from pytorch tutorial
Arguments:
logits: [batch_size, seq_len, n_labels] FloatTensor
lens: [batch_size] LongTensor
"""
batch_size, _, n_labels = logits.size()
vit = logits.new_full((batch_size, self.label_size), -100.0) # (batch, num_labels)形状的全-100
vit[:, self.start] = 0 # vit是动态规划中的状态转移,记录所有路径得分
c_lens = lens.clone()
logits_t = logits.transpose(1, 0) # (seq_len, batch, num_labels)
pointers = [] # 记录每一个step的label中对应的上一步的最大分
for logit in logits_t:
# 仍然是在seq_len的维度上进行遍历
vit_exp = vit.unsqueeze(1).expand(batch_size, n_labels, n_labels) # (batch, num_labels, num_labels)
trn_exp = self.transition.unsqueeze(0).expand_as(vit_exp) # 相同形状的转移分
vit_trn_sum = vit_exp + trn_exp
vt_max, vt_argmax = vit_trn_sum.max(2) # 在from的维度上求最大
vt_max = vt_max.squeeze(-1) # 删除求最值时作废的维度
vit_nxt = vt_max + logit # 为下一个step做准备
pointers.append(vt_argmax.squeeze(-1).unsqueeze(0)) # 当前step的所有label各自对应的上一step的最大分
mask = (c_lens > 0).float().unsqueeze(-1).expand_as(vit_nxt) # 每走一步,剩下的部分的有效mask就会少一个
vit = mask * vit_nxt + (1 - mask) * vit
mask = (c_lens == 1).float().unsqueeze(-1).expand_as(vit_nxt)
vit += mask * self.transition[self.end].unsqueeze( # mask掉padding部分
0).expand_as(vit_nxt)
c_lens = c_lens - 1 # 对mask生效
pointers = torch.cat(pointers)
scores, idx = vit.max(1) # 在to_label上求最大以找到得分最高的路径
paths = [idx.unsqueeze(1)] # 删除求最值时作废的维度
for argmax in reversed(pointers):
idx_exp = idx.unsqueeze(-1)
idx = torch.gather(argmax, 1, idx_exp)
idx = idx.squeeze(-1)
paths.insert(0, idx.unsqueeze(1))
paths = torch.cat(paths[1:], 1)
scores = scores.squeeze(-1)
return scores, paths
结束
本文从代码实现的角度,详细的介绍了CRF的运作原理,希望以此来帮助大家加深对CRF的理解和印象。创作不易,如果本文对你有所帮助的话,麻烦留下一个免费的赞。我们下期再见。
优惠劵
常鸿宇
关注
关注
65
点赞
踩
244
收藏
觉得还不错?
一键收藏
知道了
4
评论
条件随机场(CRF)极简原理与超详细代码解析
本文将以pytorch版本CRF的一个实现为例,尽可能详细地说明CRF是怎样实现的,对代码的解释几乎精细到每一行,相信你耐心读完本文,会从实践的角度对CRF的理解更加深刻。
复制链接
扫一扫
专栏目录
CRF++ 0.58 windows版
03-06
CRF在NLP技术领域中主要用于文本标注,并有多种应用场景,例如:
分词(标注字的词位信息,由字构词)
词性标注(标注分词的词性,例如:名词,动词,助词)
命名实体识别(识别人名,地名,机构名,商品名等具有一定内在规律的实体名词)
条件随机场(CRF)的Java源代码实现(最新版本)
01-15
有详细的算法描述,适合条件随机场的编程爱好者使用,很好的参考作用
4 条评论
您还未登录,请先
登录
后发表或查看评论
CRF 条件随机场
满腹的小不甘
11-13
4980
目录
1. 基本概念
1.1 各种随机场
1.2 CRF模型的训练原理
1.3 条件随机场的参数化形式
1.4条件随机场对应的简化概率表达
2. 例子
定义CRF中的特征函数
从特征函数到概率
CRF与逻辑回归的比较
CRF与HMM的比较
HMM和CRF区别
3. Tensorflow实现
tf.contrib.crf
(1)tf.contrib.......
lstm-crf_LSTM_CRF_
10-02
使用了LSTM和CRF的模型,作用是进行英语命名实体识别,需要自己找数据训练集。
CRF_matlab_条件随机场_
10-02
matlab实现CRF基础模型,CRF chain
CRF原理
终极香蕉大菠萝的博客
04-29
1672
CRF(条件随机场)与Viterbi(维特比)算法原理详解
Data_driver 2019-04-17 10:36:52 4013 收藏 11
展开
摘自:https://mp.weixin.qq.com/s/GXbFxlExDtjtQe-OPwfokA
https://www.cnblogs.com/zhibei/p/9391014.html
CRF(Conditional...
CRF原理和代码实现
weixin_42961017的博客
05-14
1289
CRF原理和代码实现说明
机器学习——条件随机场(CRF)原理
hei653779919的博客
04-09
3640
机器学习——条件随机场(CRF)原理
1. 条件随机场(CRF)基本原理
1.1 马尔科夫随机场的引入
在一个无向图中,任意两个具有边连接的节点x,y,如果从x节点走的y节点是具有一定概率数值的,则这种图被称为无向概率图。马尔科夫随机场也是一种无向的概率图。
1.1.1 团与极大团
在无向图中,任意两个节点之间具有边连接的各个节点集合构成了一个团。在各个团中,如果再加入一个节点,就不能再构成团的节...
条件随机场原理(CRF)
weixin_52862386的博客
06-03
324
条件随机场(Conditional Random Fiedl)是指给定一组输入的随机变量条件下另一组输出随机变量的条件概率分布模型,其特点是假设输出随机变量构成马尔可夫随机场。条件随机场打破了隐马尔可夫模型的俩个假设(观测独立性假设和齐次马尔可夫性假设),使输入向量和输出向量之间的关系更加明显,从而使其在文本处理等问题上的表现更加优越。概率无向图模型又称马尔可夫随机场,它是一个可以由无向图表示的联合概率分布。设无向图G=(V,E)G=(V,E)G=(V,E)表示概率分布P(Y)P(Y)P(Y),其中结v∈V
CRF详解(NLP向)
Xu_Wave
04-27
5779
CRF详解(NLP向)
机器学习C++源码解析-条件随机场CRF_CWS算法-源码+数据
08-07
机器学习C++源码解析-条件随机场CRF_CWS算法-源码+数据
手写CRF 语言模板
12-11
手写的java crf模板,支持unigram与bigram两种模板输入,其中train集需要两列(在材料中也有),test集需要一列文字
lstm-crf-pytorch:PyTorch中的LSTM-CRF
05-24
PyTorch中的LSTM-CRF
用于序列标记的双向LSTM-CRF的最小PyTorch(1.7.1)实现。
支持的功能:
CUDA的小批量培训
嵌入层中的查找,CNN,RNN和/或自我关注
分层递归编码(HRE)
条件随机场(CRF)的PyTorch实现
CRF损失的矢量化计算
矢量化维特比解码
用法
培训数据的格式应如下:
token/tag token/tag token/tag ...
token/tag token/tag token/tag ...
...
有关更多详细信息,请参见每个子目录中的README.md。
准备数据:
python3 prepare.py training_data
训练:
python3 train.py model char_to_idx word_to_idx tag_to_idx training_data.csv (v
条件随机场 CRF (Matble)源码
07-16
% minFunc fprintf('Compiling minFunc files...\n'); mex minFunc/lbfgsC.c % KPM fprintf('Compiling KPM files...\n'); mex -IKPM KPM/repmatC.c % crfChain fprintf('Compiling crfChain files...\n');...
条件随机场(CRF)
04-17
这个文档是对条件随机场(Condition Random Field)的简单介绍,介绍清晰简单、浅显,易于理解
大模型训练——PEFT与LORA介绍
热门推荐
weixin_44826203的博客
03-29
3万+
本文针对大模型的低资源训练策略LORA的原理和代码实现进行了介绍。
Meta最新模型LLaMA细节与代码详解
weixin_44826203的博客
03-06
3万+
本文对Facebook Meta AI最新提出的大语言模型LLaMA进行简单的介绍,以及对其开源出的代码和细节进行了详细的说明。
超详细!“看图说话”(Image Caption)项目实战
weixin_44826203的博客
07-27
1万+
超详细!基于pytorch的“看图说话”(Image Caption)项目实战0.简介1.运行环境1.1 我的环境1.2 建立环境2.理论介绍3.运行项目3.1 项目结构3.2 数据准备3.2 开始训练3.3 报错及解决4.效果演示
0.简介
本文将介绍一个“看图说话”的项目实战,用的是git上一个大神的代码,首先放出来地址:
https://github.com/sgrvinod/a-PyTorch-Tutorial-to-Image-Captioning
作者对项目的原理进行了比较详细的介绍,为了方便大
(跨模态)AI作画——使用stable-diffusion生成图片
weixin_44826203的博客
09-08
1万+
自从DallE问世以来,AI绘画越来越收到关注,从最初只能画出某些特征,到越来越逼近真实图片,并且可以利用prompt来指导生成图片的风格。前不久,stable-diffusion的v1-4版本终于开源,本文主要面向不熟悉huggingface的同学,介绍一下stable-diffusion如何使用(非常简单)。
python crf
最新发布
12-24
Python CRF是一种基于Python编程语言的条件随机场模型。条件随机场是一种概率图模型,常用于序列标注、命名实体识别、自然语言处理等任务。Python CRF库可以帮助用户快速地构建和训练条件随机场模型,并且提供了丰富的功能和工具,用于特征抽取、模型训练、预测和评估。使用Python CRF库可以有效地解决序列标注问题,帮助用户实现文本分类、分词、词性标注等自然语言处理任务。Python CRF库的优点在于其简单易用的接口和丰富的功能,适合于从初学者到专业工程师的用户群体。同时,Python CRF库还提供了大量的文档和示例代码,可以帮助用户快速入门和学习如何使用条件随机场模型进行序列标注任务。总之,Python CRF是一个强大的工具,可以帮助用户轻松地构建和训练条件随机场模型,并且可以应用于各种自然语言处理任务中。
“相关推荐”对你有帮助么?
非常没帮助
没帮助
一般
有帮助
非常有帮助
提交
常鸿宇
CSDN认证博客专家
CSDN认证企业博客
码龄5年
暂无认证
54
原创
2万+
周排名
2万+
总排名
33万+
访问
等级
2423
积分
3809
粉丝
1057
获赞
626
评论
2735
收藏
私信
关注
热门文章
大模型训练——PEFT与LORA介绍
38618
Meta最新模型LLaMA细节与代码详解
31490
超详细!使用Mask R-CNN训练自己的数据过程记录
21613
RuntimeError: “LayerNormKernelImpl“ not implemented for ‘Half‘
13396
超详细!“看图说话”(Image Caption)项目实战
13180
分类专栏
实验记录
6篇
计算机视觉
6篇
生成模型
13篇
自然语言处理
47篇
杂谈
4篇
笔记
2篇
最新评论
大模型训练——PEFT与LORA介绍
惊鸿若梦一书生:
作者对技术细节的把握令人印象深刻。
大模型训练——PEFT与LORA介绍
惊鸿若梦一书生:
文章中的实例非常贴切,易于将理论应用于实践。
NLP项目实践——中文序列标注Flat Lattice代码解读、运行与使用
时有细碟立窗头:
老哥,你用博主的MSRA数据集爆显存了么,我照着博主的方法,3090显存直接爆了
NLP实践——Bert转onnx格式简介与踩坑记录
qq_40374634:
如果在多模态的那种大模型中,bert只是其中文本编码的一部分,但是无法转onnx,这个时候需要给多模态做tensorrt加速,那么这种情况下怎么处理呢?
NLP工具——doccano标注系统自动标注功能使用
dogpeople:
AttributeError: type object 'CustomRESTRequestModel' has no attribute 'model_json_schema' 博主您好,我在执行doccano task 遇到这个问题,有有什么方法解决吗
您愿意向朋友推荐“博客详情页”吗?
强烈不推荐
不推荐
一般般
推荐
强烈推荐
提交
最新文章
多模态——使用stable-video-diffusion将图片生成视频
NLP实践——LLM生成过程中防止重复循环
NLP实践——中文指代消解方案
2023年18篇
2022年17篇
2021年12篇
2020年7篇
目录
目录
分类专栏
实验记录
6篇
计算机视觉
6篇
生成模型
13篇
自然语言处理
47篇
杂谈
4篇
笔记
2篇
目录
评论 4
被折叠的 条评论
为什么被折叠?
到【灌水乐园】发言
查看更多评论
添加红包
祝福语
请填写红包祝福语或标题
红包数量
个
红包个数最小为10个
红包总金额
元
红包金额最低5元
余额支付
当前余额3.43元
前往充值 >
需支付:10.00元
取消
确定
下一步
知道了
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝
规则
hope_wisdom 发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额
0
抵扣说明:
1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。
余额充值