机器学算法基础:条件随机场
CONTENT
1. 概率无向图的马尔科夫性
2. 概率无向图的因子分解
3. 条件随机场的定义与形式
4. 条件随机场的基本问题
5. 代码实践
1. 概率无向图的马尔科夫性
-
成对马尔科夫性:在无向图中有两个没有链接的结点,结点对应两个随机变量。在其余点给定的条件下,这两个点表示的变量条件独立。
-
局部马尔科夫性:在无向图中有一个点v,与之连接的结点的集合称为w,其余点的集合为o,在w给定的条件下,v和o条件独立。
-
全局马尔科夫性:在无向图中两个结点集合A,B被结点集合C分开,聪明的你应该猜出来了,当C给定的条件下,A,B表示的变量条件独立。
2. 概率无向图的因子分解
团和最大团
团:无向图中任意两点都有边相连。
最大团:团中无法再添加任意节点使得任意两点都有边相连的团。

3. 条件随机场的定义与形式
3.1 定义
给定 \(X=(x_1,x_2,...,x_n)\) ,\(Y=(y_1,y_2,...,y_n)\) 均为线性链表示的随机变量序列,若在给随机变量序列 X 的条件下,随机变量序列 Y 的条件概率分布 \(P(Y|X)\) 构成条件随机场,即满足马尔可夫性:
\[ P(y_i|x_1,x_2,...,x_{i-1},y_1,y_2,...,y_{i-1},y_{i+1})
= P(y_i|x,y_{i-1},y_{i+1})
\]
则称为 P(Y|X) 为线性链条件随机场。
通过去除了隐马尔科夫算法中的观测状态相互独立假设,使算法在计算当前隐状态\(x_i\)时,会考虑整个观测序列,从而获得更高的表达能力,并进行全局归一化解决标注偏置问题。

3.2 参数化形式
\[p\left(y | x\right)=\frac{1}{Z\left(x\right)} \prod_{i=1}^{n} \exp \left(\sum_{i, k} \lambda_{k} t_{k}\left(y_{i-1}, y_{i}, x, i\right)+\sum_{i, l} \mu_{l} s_{l}\left(y_{i}, x, i\right)\right)
\]
其中:
\(Z(x)\) 为归一化因子,是在全局范围进行归一化,枚举了整个隐状态序列\(x_{1…n}\)的全部可能,从而解决了局部归一化带来的标注偏置问题。
\[Z(x)=\sum_{y} \exp \left(\sum_{i, k} \lambda_{x} t_{k}\left(y_{i-1}, y_{i}, x, i\right)+\sum_{i, l} \mu_{l} s_{l}\left(y_{i}, x, i\right)\right)
\]
\(t_k\) 为定义在边上的特征函数,转移特征,依赖于前一个和当前位置
\(s_1\) 为定义在节点上的特征函数,状态特征,依赖于当前位置。
3.3 简化形式
step 1
将转移特征和状态特征及其权值用统一的符号表示,设有\(k_1\)个转移特征,\(k_2\)个状态特征,\(K=k_1+k_2\),记
\[f_k(y_{i-1},y_i,x,i) = \begin{cases}t_k(y_{i-1},y_i,x,i),\quad k = 1,2,3, ..., K_1 \\s_l(y_i,x,i), \quad k = k_1+l; l = 1, 2, ... , K_2\end{cases}
\]
step 2
对转移与状态特征在各个位置i求和,记作
\[f_k(y,x) = \sum_{i=1}^{n}f_k(y_{i-1},y_i,x,i), k = 1, 2, ..., K
\]
step 3
将 \(\lambda_{x}\) 和 \(\mu_{l}\) 用统一的权重表示,记作
\[w_k = \begin{cases}\lambda_k, \quad k = 1, 2, ..., K_1 \\\mu_l, \quad k = K_1+l; l = 1, 2, ..., K_2\end{cases}
\]
step 4
转化后的条件随机场可表示为:
\[\begin{split}P(y\mid x) &= \frac{1}{Z(x)}exp\sum_{k=1}^{K}w_kf_k(y,x) \\Z(x) &= \sum_{y}exp\sum_{k=1}^{K}w_kf_k(y,x)\end{split}
\]
step 5
若 \(w\) 表示权重向量:
\[w = (w_1,w_2,...,w_K)^T
\]
以 \(F(y,x)\) 表示特征向量,即
\[F(y,x) = (f_1(y,x), f_2(y,x), ..., f_K(y,x))^T
\]
则,条件随机场写成内积形式为:
\[\begin{split}P_w(y\mid x) &= \frac{exp(w.F(y,x))}{Z_w(x)} \\Z_w(x) &= \sum_{y}exp(w.F(y,x))\end{split}
\]
3.4 矩阵形式
\[P_w(y\mid x) = \frac{1}{Z_w(x)}\prod_{i=1}^{n+1}M_i(y_{i-1}, y_i \mid x)
\]
4. 条件随机场的基本问题
条件随机场包含概率计算问题、学习问题和预测问题三个问题:
- 概率计算问题:已知模型的所有参数,计算观测序列 𝑌 出现的概率,常用方法:前向和后向算法;
- 学习问题:已知观测序列 𝑌,求解使得该观测序列概率最大的模型参数,包括隐状态序列、隐状态间的转移概率分布和从隐状态到观测状态的概率分布,常用方法:Baum-Wehch 算法;
- 预测问题:一直模型所有参数和观测序列 𝑌 ,计算最可能的隐状态序列 𝑋,常用算法:维特比算法。
5. 代码实践
import numpy as np
class CRF(object):
'''实现条件随机场预测问题的维特比算法
'''
def __init__(self, V, VW, E, EW):
'''
:param V:是定义在节点上的特征函数,称为状态特征
:param VW:是V对应的权值
:param E:是定义在边上的特征函数,称为转移特征
:param EW:是E对应的权值
'''
self.V = V
self.VW = VW
self.E = E
self.EW = EW
self.D = []
self.P = []
self.BP = []
return
def Viterbi(self):
'''
条件随机场预测问题的维特比算法,此算法一定要结合CRF参数化形式对应的状态路径图来理解,更容易理解.
'''
self.D = np.full(shape=(np.shape(self.V)), fill_value=.0)
self.P = np.full(shape=(np.shape(self.V)), fill_value=.0)
for i in range(np.shape(self.V)[0]):
if 0 == i:
self.D[i] = np.multiply(self.V[i], self.VW[i])
self.P[i] = np.array([0, 0])
print('self.V[%d]='%i, self.V[i], 'self.VW[%d]='%i, self.VW[i], 'self.D[%d]='%i, self.D[i])
print('self.P:', self.P)
pass
else:
for y in range(np.shape(self.V)[1]):
for l in range(np.shape(self.V)[1]):
delta = 0.0
delta += self.D[i-1, l]
delta += self.E[i-1][l,y]*self.EW[i-1][l,y]
delta += self.V[i,y]*self.VW[i,y]
print('(x%d,y=%d)-->(x%d,y=%d):%.2f + %.2f + %.2f='%(i-1, l, i, y, \
self.D[i-1, l], \
self.E[i-1][l,y]*self.EW[i-1][l,y], \
self.V[i,y]*self.VW[i,y]), delta)
if 0 == l or delta > self.D[i, y]:
self.D[i, y] = delta
self.P[i, y] = l
print('self.D[x%d,y=%d]=%.2f\n'%(i, y, self.D[i,y]))
print('self.Delta:\n', self.D)
print('self.Psi:\n', self.P)
N = np.shape(self.V)[0]
self.BP = np.full(shape=(N,), fill_value=0.0)
t_range = -1 * np.array(sorted(-1*np.arange(N)))
for t in t_range:
if N-1 == t:
self.BP[t] = np.argmax(self.D[-1])
else:
self.BP[t] = self.P[t+1, int(self.BP[t+1])]
self.BP += 1
print('最优状态路径为:', self.BP)
return self.BP
def CRF_manual():
S = np.array([[1,1],
[1,1],
[1,1]])
SW = np.array([[1.0, 0.5],
[0.8, 0.5],
[0.8, 0.5]])
E = np.array([[[1, 1],
[1, 0]],
[[0, 1],
[1, 1]]])
EW= np.array([[[0.6, 1],
[1, 0.0]],
[[0.0, 1],
[1, 0.2]]])
crf = CRF(S, SW, E, EW)
ret = crf.Viterbi()
print('最优状态路径为:', ret)
return
if __name__=='__main__':
CRF_manual()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!