POSタグ付け問題雑音チャネルモデルとビタビアルゴリズム

英語コーパスを考えると、多くの文章がありますが、言葉はすでに、行っている/単語の前に、品詞の単語を表し、すべての単語の後ろの図に示すように、ピリオドで区切られています。

文Sのために、文章中の単語の各\(W_i \)音声の対応する部分によって示される\(z_i \) 質問は今、各単語に対して生成された定型文S」を与えること、である(w'_i \)\スピーチの(z'_i \)\

確率ように要求される(| S)、P(Z \ \) 最大\(Z \) ベイズの定理により得ることができる
\ [\開始{ALIGN *} &= \ FRAC {P | P(S Z) (S | Z)P(Z )} {P(S)} \\&\ propto P(S | Z)・P(Z)\\&= P(W_1、W_2、...、W_N | Z_1、 Z_2、...、Z_N)・P (Z_1、Z_2、...、Z_N)\\&= \ prod_ {I = 1} ^ {N} P(w_i | z_i)・P(Z_1)P(Z_2 | Z_1)···P(Z_N | Z_ {N-1})\\&= \ prod_ {I = 1} ^ {N} P(w_i | z_i)・P(Z_1)\ prod_ {J = 2} ^ NP(z_j | Z_ {J
-1})\端{ALIGN *} \] 前記次の2つの仮定を用いて式相互誘導の二列:

  1. HMMその仮定\(w_i \)のみ\(z_i \)他のすべての単語や互いの音声に独立に関連付けられています。したがって\(P(W_1、...、 W_N | Z_1、...、Z_Nは)\) のように簡略化することができる(\ prod_ {i = 1から\ | z_i)\} ^ {N} P(w_i)
  2. バイグラムのための言語モデルを前提としています。したがって\(P(Z_1、...、 Z_N)\) のように書くことができる\(P(Z_1)P(Z_2 | Z_1)P···(Z_N |。Z_ {} 1-N)\) すなわち\(P (Z_1)\ prod_ {J = 2} ^ NP(z_j | Z_ {J-1})\)

でも確率の多数によって、全体の発現は、最終的に多重にプラスの変化、上記の考え方に基づいて、式の最終結果でしょう、これを避けるために、我々は対数方式を採用取ることができ、浮動小数点数のアンダーフローにつながる可能性が
\ [\開始{ALIGN *} P (Z | S)&=(\ prod_ {I = 1} ^ {N} P(w_iログ| z_i)・P(Z_1)\ prod_ {J = 2} ^ NP(z_j | Z_ {J-1}) \\&= \ sum_ {I = 1} ^ Nlog(P(w_i | z_i))+のlogP(Z_1)+ \ sum_ {J = 2} ^ NlogP(z_j | Z_ {J -1})\端{ALIGN * } \]

パラメータを決定

最終的確率関数は、3つの可変パラメータが含まれて、次の意味が説明されています

最初のパラメータ:\(A = P(W_i | z_i)\)

パラメータ\(A \)音声の所定の部分に、表す\(z_i \)場合、対応するワードである\(W_i \)すべてが音声としてマークされていることを条件付き確率である\(z_i \)ワードの、ワード\(w_i \)割合
\ | [P(w_i z_i) = \ FRAC { z_i音声の数のw_i音声} {}は、単語z_i \の総数である]
所与の音声NNが今与える場合、例えばつまり、食べ高いリンゴよりも確かにある単語の確率に対応する(名詞)、\(| NN)P(りんご > Pは、(食べる|)NN \)

後の計算を容易にするために、我々パラメーター\(A \)空間に格納された値N行M列の行列 Nは、音声コーパス、コーパス内のM個の異なる単語の数の異なる部品の数です。行列の各行は、品詞を表し、各列はワード行列要素表す\(A_ {ijは} \)のような音声の全ての部分を表し、\(I \)ワード、ワード\(J \)の割合(すなわち、条件確率)、それはすなわち、同じ行の全てと1のすべての確率の総和、見ることができる\(\ sum_ {J = 1 } ^ {Ma_ IJ} = 1 \)

第一の大きさを定義、行列Aは、非常に単純な演算(N \回M \)\すべてゼロ行列を、次いでコーパスにおける各列を通して单词/词性、マトリクスは行対応する「音声の現在の横断部分」および「現在の反復に対応ワード値「列の位置プラス1

最後に、正規化、これまでマトリックスにカウント値を記憶し、私たちは確率を必要としているので、それは行要素の和で除算され、各要素のために使用することができます

パラメータ得\(Aは\)に示すように行列の形で一般的です。

二番目のパラメータ:\(\ = PI P(z_i)\)

パラメータ\(\ PI \)品詞を表す文である\(z_i \)確率、発話文で、すなわち全ての計算\(z_i \)の割合
\ [P(z_i)= \ FRAC { 文音声{}音声のz_i文総数} \]の数である
VB(動詞)よりも高く、例えば、典型的には文が確率NN(名詞)である、すなわち\(P(NN)> P (VB)\)

パラメータ\(\ PI \)の値が範囲内に格納することができる長さの\(N \)ベクター、で\(N \)音声コーパスの異なる部分の数です。推論、このベクトルの要素は、すなわち、1であることができる\(\ sum_ {i = 1 } ^ NP(I)= 1 \)

まず、0と初期化ベクトル(\ \ PI \) 長さ\(N \) 各列コーパスを介して单词/词性ワードは、それが期間、感嘆符、疑問符、および他の句読点終了であるかどうかを確認する前に、裁判官によると、文章中の現在の単語かどうかを決定します。それは文の場合は、音声の現在の一部を削除し、値位置ベクトル「発言に、現在の反復」とそれに対応するコスト1

最後に、割合(確率)を得るために、各要素ベクトルのすべての要素の合計を割ることによって正規化

第三个参数:\(B=P(z_i|z_{i-1})\)

参数\(B\)表示给定前驱词性为\(z_{i-1}\),当前词性为\(z_i\)的条件概率,即计算在前驱词性为\(z_{i-1}\)(前驱词性,当前词性)组合对中,当前词性为\(z_i\)的组合对的占比
\[ P(z_i|z_{i-1})=\frac{当前词性为z_{i-1}且前驱词性为z_i的bigram数量}{前驱词性为z_i的bigram总数} \]
举例来说,对于给定的前驱词性VB(动词),当前词性为NN(名词)的概率要高于VB(动词),即\(P(NN|VB)>P(VB|VB)\)

参数\(B\)是一个\(N\times N\)的矩阵\(N\)为语料库中不同词性的数量。矩阵的行表示前驱词性,列表示当前词性,矩阵元素\(b_{ij}\)表示前驱词性为\(i\)时,当前词性为\(j\)的条件概率,由此可知同一行中所有元素之和为1,即\(\sum_{j=1}^Nb_{ij}=1\)

矩阵\(B\)的计算很简单,首先定义一个大小为\(N\times N\)的全0方阵。然后遍历语料库,统计词性序列的bigram,将方阵中对应的"前驱词性"行和"当前词性"列位置的数值加1

最后进行归一化,用每个元素除以所在行元素之和,即得到所在行占比(概率)

tag2id, id2tag = {}, {} # tag2id: {"VB":0,...}, id2tag: {0:"VB",...}
word2id, id2word = {}, {}

for line in open('traindata.txt'):
    items = line.split('/')
    word, tag = items[0], items[1].rstrip() # 去掉换行符
    
    if word not in word2id:
        word2id[word] = len(word2id)
        id2word[len(id2word)] = word
    if tag not in tag2id:
        tag2id[tag] = len(tag2id)
        id2tag[len(id2tag)] = tag

N = len(tag2id) # number of tags
M = len(word2id) # number of words
print(N, M)

# define pi, A, B
import numpy as np

pi = np.zeros(N) # pi[i] 表示tag i出现在句子开头的概率, vector
A = np.zeros((N, M)) # A[i][j] 表示给定tag i, 出现word j的概率, matrix
B = np.zeros((N, N)) # B[i][j] 表示前一个是tag i, 后一个是tag j的概率, matrix

pre_tag = -1 # 记录前一个tag的id
for line in open('traindata.txt'):
    items = line.split('/')
    wordid, tagid = word2id[items[0]], tag2id[items[1].rstrip()]
    if pre_tag == -1: # 这意味着是句子的开始
        pi[tagid] += 1
        A[tagid][wordid] += 1
        pre_tag = tagid
    else: # 不是句子开头
        A[tagid][wordid] += 1
        B[pre_tag][tagid] += 1
        
    if items[0] == '.':
        pre_tag = -1
    else:
        pre_tag = tagid

        
# normalize
pi /= sum(pi) # probability
for i in range(N):
    A[i] /= sum(A[i])
    B[i] /= sum(B[i])

计算最优解

通过前面的分析,我们已经确定了三个参数及其取值空间,接下来可以用暴力枚举的方法测试出使得目标函数最大的参数取值,但时间复杂度太高,不建议采用

通过分析,我们发现这是一个最优化问题,而且问题的求解可以分为\(T\)个步骤(\(T\)为测试集的文本长度),每个步骤求解方式相同,符合动态规划算法的应用场景


\[ score=\sum_{i=1}^TlogP(w_i|z_i)+logP(z_1)+\sum_{j=2}^TlogP(z_j|z_{j-1}) \]
我们的目标是对于给定的文本\(S=w_1w_2...w_T\),给这\(T\)个单词分别赋予一个词性(有\(N\)个可选词性),使得score的值最大。score的计算过程描述如下

图中黑点给出了一个示例的标记方案(如同一条路径):

  • \(w_1\)被标记为\(pos_2\)
  • \(w_2\)被标记为\(pos_1\)
  • \(w_3\)被标记为\(pos_3\)
  • ...
  • \(w_T\)被标记为\(pos_T\)

该路径的score值为
\[ \begin{align*} score &= logP(w_1|pos_2)+logP(pos_2)\\ &+logP(w_2|pos_1)+logP(pos_1|pos_2)\\ &+logP(w_3|pos_3)+logP(pos_3|pos_1)\\ &+...\\ &+logP(w_T|pos_1)+logP(pos_1|pos_3) \end{align*} \]
从上式可以看出,score的求解过程分为\(T\)个步骤,每个步骤有\(N\)种选择。因为我们可以定义一个\(T\times N\)的二维数组DP,为了描述的方便,我们假设数组的下标从0开始,其中元素DP[i][j]表示从第一个单词开始,当计算到第\(i\)个单词(第\(i\)步)且将词性标记为\(j\)时的最优路径(最大概率)

状态转移方程为
\[ \begin{align*} DP[i][j]=max(&\\ &dp[i-1][0]+logB[0][j]+logA[j][w_i在词典中的下标],\\ &dp[i-1][1]+logB[1][j]+logA[j][w_i在词典中的下标],\\ &dp[i-1][2]+logB[2][j]+logA[j][w_i在词典中的下标],\\ &...\\ &dp[i-1][N-1]+logB[N-1][j]+logA[j][w_i在词典中的下标],\\ ) \end{align*} \]

最终答案(最大的概率值),就是max(DP[T-1][0],DP[T-1][1],...,DP[T-1][N-1])。但是光有概率不够,我们还需要记录,这个概率是通过怎样的路径过来的,这个路径就是每个词的词性。因此我们还需要另外建立一个\(T\times N\)的二维数组,用于记录最优的词性选择路径

viterbi算法部分的代码如下

def log(v):
    if v == 0:
        return np.log(v + 0.0000001)
    return np.log(v)

def viterbi(x, pi, A, B):
    """
    x: user input string/sentence
    pi: initial probability of tags
    A: 给定tag,每个单词出现的概率
    B: tag之间的转移概率
    """
    x = [word2id[word] for word in x.split(" ")]
    T = len(x)
    dp = np.zeros((T, N))
    path = np.array([[0 for x in range(N)] for y in range(T)]) # T*N
    for j in range(N): # basecase for dp algorithm
        dp[0][j] = log(pi[j]) + log(A[j][x[0]])
    
    for i in range(1, T): # every words
        for j in range(N): # every tags
            dp[i][j] = -9999999
            for k in range(N): # 从每个k可以到达j
                score = dp[i-1][k] + log(B[k][j]) + log(A[j][x[i]])
                if score > dp[i][j]:
                    dp[i][j] = score
                    path[i][j] = k
    # print best tag sequence
    best_seq = [0] * T # best_seq = [1, 5, 0, 3, 55, ...]
    # step1: 找出最后一个单词的词性
    best_seq[T-1] = np.argmax(dp[T-1]) # 求出最大值所在下标
    
    # step2: 通过从后往前的循环以此求出每个单词的词性
    for i in range(T-2, -1, -1):
        best_seq[i] = path[i + 1][best_seq[i + 1]]
        
    # step3: print
    for i in range(len(best_seq)):
        print(id2tag[best_seq[i]])

# Test
x = "Social Security number , passport number and details about the services provided for the payment"
viterbi(x, pi, A, B)

おすすめ

転載: www.cnblogs.com/mathor/p/12409110.html