日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

中文词性标注详解

發(fā)布時間:2023/12/16 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 中文词性标注详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

所謂的詞性標(biāo)注在NLP領(lǐng)域是一個應(yīng)用非常廣泛的技術(shù),總的來說,詞性標(biāo)注所解決的問題就是說,給定一句話 sss,我們將sss進行分詞操作,可以將 sss 分成 nnn 個詞,那么 sss 可以表示成:s=w1w2...wns = {w_1}{w_2}...{w_n}s=w1?w2?...wn?,我們將這 nnn 個詞每一個詞標(biāo)注一個詞性那么這句話詞性可以表示成 t=z1z2...znt = {z_1}{z_2}...{z_n}t=z1?z2?...zn?。在這個前提下,我們給定一句新的話術(shù) s′=w1′w2′...wn′s' = {w_1}'{w_2}'...{w_n}'s=w1?w2?...wn?,如何自動給這句話的每個詞打上標(biāo)簽?zāi)?#xff1f;也就是求出 t′=z1′z2′...zn′t' = {z_1}'{z_2}'...{z_n}'t=z1?z2?...zn?,這就是詞性標(biāo)注的作用。如圖所示就是詞性標(biāo)注的示意圖:

這個問題可以用概率的知識來解釋,我們可以把詞性標(biāo)注的問題建立一個概率的模型。在給定一個句子的前提下,求序列標(biāo)注的概率我們可以表示成 p(t∣s)p(t|s)p(ts),我們需要做的就是將這個概率最大化,在 p(t∣s)p(t|s)p(ts) 取得最大值的時候,所得到的標(biāo)注序列 ttt,就是我們要得到的結(jié)果,接下來我們就要最大化這個概率。
好了,我們來看 p(t∣s)p(t|s)p(ts) 這個概率表達(dá)式,運用貝葉斯定理,我們可以得到如下的等式: p(t∣s)=p(s∣t)p(t)p(t|s) = p(s|t)p(t)p(ts)=p(st)p(t),那么我們可以將 p(t∣s)p(t|s)p(ts) 拆成兩項相乘的形式,前面一項是一個 translation model,后面一項是 language model。我們繼續(xù)將這個表達(dá)式進行展開,可以得到:
p(t∣s)=p(s∣t)p(t)=p(w1w2...wn∣z1z2...zn)p(z1z2...zn)p(t|s) = p(s|t)p(t) = p({w_1}{w_2}...{w_n}|{z_1}{z_2}...{z_n})p({z_1}{z_2}...{z_n})p(ts)=p(st)p(t)=p(w1?w2?...wn?z1?z2?...zn?)p(z1?z2?...zn?)
接下來,我們可以將上述表達(dá)式進行展開操作,因為:
p(w1w2...wn∣z1z2...zn)p(z1z2...zn)=p(w1w2...wn?z1z2...zn)p({w_1}{w_2}...{w_n}|{z_1}{z_2}...{z_n})p({z_1}{z_2}...{z_n}) = p({w_1}{w_2}...{w_n} \cdot {z_1}{z_2}...{z_n})p(w1?w2?...wn?z1?z2?...zn?)p(z1?z2?...zn?)=p(w1?w2?...wn??z1?z2?...zn?)
然后我們可以繼續(xù)將表達(dá)式進行分解:
p(w1w2...wn?z1z2...zn)=p(w1∣w2...wnz1z2...zn)p(w2...wnz1z2...zn)p({w_1}{w_2}...{w_n} \cdot {z_1}{z_2}...{z_n}) = p({w_1}|{w_2}...{w_n}{z_1}{z_2}...{z_n})p({w_2}...{w_n}{z_1}{z_2}...{z_n})p(w1?w2?...wn??z1?z2?...zn?)=p(w1?w2?...wn?z1?z2?...zn?)p(w2?...wn?z1?z2?...zn?)
由馬爾科夫假設(shè)我們可以得到:
p(w1∣w2...wnz1z2...zn)=p(w1∣z1)p({w_1}|{w_2}...{w_n}{z_1}{z_2}...{z_n}) = p({w_1}|{z_1})p(w1?w2?...wn?z1?z2?...zn?)=p(w1?z1?)
那么可以得到:
p(w1w2...wn?z1z2...zn)=p(w1∣z1)p(w2...wnz1z2...zn)p({w_1}{w_2}...{w_n} \cdot {z_1}{z_2}...{z_n}) = p({w_1}|{z_1})p({w_2}...{w_n}{z_1}{z_2}...{z_n})p(w1?w2?...wn??z1?z2?...zn?)=p(w1?z1?)p(w2?...wn?z1?z2?...zn?)
同理以此類推,我們可以得到一個最終的表達(dá)式:
p(w1w2...wn?z1z2...zn)=∏i=1np(wi∣zi)?p(z1)p(z2∣z1)p(z3∣z2)...p(zn∣zn?1)p({w_1}{w_2}...{w_n} \cdot {z_1}{z_2}...{z_n}) = \prod\limits_{i = 1}^n {p({w_i}|{z_i})} \cdot p({z_1})p({z_2}|{z_1})p({z_3}|{z_2})...p({z_n}|{z_{n - 1}})p(w1?w2?...wn??z1?z2?...zn?)=i=1n?p(wi?zi?)?p(z1?)p(z2?z1?)p(z3?z2?)...p(zn?zn?1?)
在這個表達(dá)式中我們可以清楚地看到一些特性,∏i=1np(wi∣zi)\prod\limits_{i = 1}^n {p({w_i}|{z_i})}i=1n?p(wi?zi?) 就表示發(fā)射概率,后面的 p(z2∣z1)p(z3∣z2)...p(zn∣zn?1)p({z_2}|{z_1})p({z_3}|{z_2})...p({z_n}|{z_{n - 1}})p(z2?z1?)p(z3?z2?)...p(zn?zn?1?) 表示狀態(tài)轉(zhuǎn)移概率,p(z1)p({z_1})p(z1?) 表示初始的狀態(tài)概率。我們最后需要最大化這三項表達(dá)式的乘積。用表達(dá)式可以寫成:
z^=arg?max?p(z∣s)=arg?max?∏i=1np(wi∣zi)∏j=2np(zj∣zj?1)p(z1)\hat z = \arg \max p(z|s) = \arg \max \prod\limits_{i = 1}^n {p({w_i}|{z_i})} \prod\limits_{j = 2}^n {p({z_j}|{z_{j - 1}})} p({z_1})z^=argmaxp(zs)=argmaxi=1n?p(wi?zi?)j=2n?p(zj?zj?1?)p(z1?)
為了方便求解,我們將表達(dá)式進行求對數(shù)操作:
z^=arg?max?log?[∏i=1np(wi∣zi)∏j=2np(zj∣zj?1)p(z1)]\hat z = \arg \max\log[\prod\limits_{i = 1}^n {p({w_i}|{z_i})} \prod\limits_{j = 2}^n {p({z_j}|{z_{j - 1}})} p({z_1})]z^=argmaxlog[i=1n?p(wi?zi?)j=2n?p(zj?zj?1?)p(z1?)]
然后我們可以將乘積的形式變成求和的形式,也就是最終我們求解的目標(biāo)函數(shù):
z^=arg?max?z[∑i=1nlog?p(wi∣zi)+log?p(zi)+∑j=2nlog?p(zj∣zj?1)]\hat z = \mathop {\arg \max }\limits_z [\sum\limits_{i = 1}^n {\log p({w_i}|{z_i})} + \log p({z_i}) + \sum\limits_{j = 2}^n {\log p({z_j}|{z_{j - 1}})} ]z^=zargmax?[i=1n?logp(wi?zi?)+logp(zi?)+j=2n?logp(zj?zj?1?)]
在我之前的博客 隱馬爾科夫模型(HMM)算法的理解與超詳細(xì)推導(dǎo) 中有詳細(xì)介紹過HMM,這里我們用 AAA 表示發(fā)射概率,也就是 p(wi∣zi)p({w_i}|{z_i})p(wi?zi?)BBB 表示狀態(tài)轉(zhuǎn)移概率 p(zi∣zi?1)p({z_i}|{z_{i - 1}})p(zi?zi?1?),然后 π\(zhòng)piπ 表示初始狀態(tài) p(z1)p({z_1})p(z1?)。
好了,理清了整個中文詞性標(biāo)注的邏輯之后,我們首先要求 A、B、πA、B、\piAB、π,然后根據(jù)維特比算法求得 zzz 序列的最大值,將我們z 的序列解碼出來,這就是整個詞性標(biāo)注的流程和邏輯。首先我們來看一下訓(xùn)練的數(shù)據(jù),我是在網(wǎng)上下載的人民日報詞性標(biāo)注的訓(xùn)練集,這個訓(xùn)練集網(wǎng)上很多地方可以下載到,然后都是從人民日報中的摘要進行詞性標(biāo)注出來的,內(nèi)容如圖所示:

里面已經(jīng)分好詞并且標(biāo)注好詞性了,每一句話就是一行,總共是兩萬多條。接下來我們來看發(fā)射概率矩陣 AAA

再來看看狀態(tài)轉(zhuǎn)移概率矩陣 BBB:

好了,有了AAABBB,我們暫時把初始概率矩陣 π\(zhòng)piπ 先不管,我們來建立詞典和標(biāo)簽映射,代碼如下:

tag2id, id2tag = {}, {} word2id, id2word = {}, {}for line in open('dataset/pos_tag_dataset.txt', encoding='utf-8'):if line:for items in line.split(' '):item = items.split('/')if len(item)==2:word, tag = item[0], item[1].rstrip()if word not in word2id:word2id[word] = len(word2id)id2word[len(id2word)] = wordif tag not in tag2id:tag2id[tag] = len(tag2id)id2tag[len(id2tag)] = tagM = len(word2id) N = len(tag2id)

這里 MMM 表示詞典的大小,也就是詞的數(shù)量。NNN 表示詞性的數(shù)量,我們整個詞庫有55317個詞和45中詞性,如圖所示:

然后我們可以開始構(gòu)建 A、B、πA、B、\piA、B、π,代碼如下所示:

# 構(gòu)建 pi A B import numpy as np pi = np.zeros(N) A = np.zeros((N, M)) B = np.zeros((N, N))

接下來,我們需要為 A、B、πA、B、\piA、Bπ 填值了,首先我們需要統(tǒng)計發(fā)射矩陣出現(xiàn)的次數(shù)和轉(zhuǎn)移矩陣出現(xiàn)的次數(shù)以及初始狀態(tài)的次數(shù),
我們只需要解析 tag 的前后關(guān)系和 tag 與 word 的關(guān)系,代碼如下所示:

for line in open('dataset/pos_tag_dataset.txt',encoding='utf-8'):if line:prev_tag = ''for items in line.split(' '):item = items.split('/')if len(item)==2:wordId, tagId = word2id[item[0]], tag2id[item[1].rstrip()]if prev_tag == '': # 句子的開始pi[tagId] += 1A[tagId][wordId] += 1else:A[tagId][wordId] += 1B[tag2id[prev_tag]][tagId] += 1if item[0] == '。':prev_tag = ''else:prev_tag = item[1]

這樣的代碼很簡潔,也很容易理解,我們統(tǒng)計好詞頻之后,再將我們的 A、B、πA、B、\piA、B、π 進行歸一化,就是我們的發(fā)射概率矩陣,狀態(tài)轉(zhuǎn)移概率矩陣和初始矩陣了,代碼如下所示:

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

然后我們可以看看 A、B、πA、B、\piABπ 的結(jié)果如下:



然后我們可以定義一個 log 函數(shù),當(dāng)我們概率為零的時候,需要加入一個平滑:

def log(x):if x == 0:return np.log(0.00001)else:return np.log(x)

接下來我們就要開始用維特比算法進行解碼操作,所謂的維特比算法,就是運用我們動態(tài)規(guī)劃的算法思想。整體的流程如下所示:

在圖中橫坐標(biāo)是我們分詞之后的詞,縱坐標(biāo)是我們的45個不同的詞性,我們需要的就是找到一條路徑從第一個詞到最后一個詞,使得路徑所對應(yīng)的目標(biāo)函數(shù)最大,我們就取這條路徑所對應(yīng)的詞性序列作為最后這個話術(shù)的詞性標(biāo)注。在圖中,我們可以看到紅色、橘黃色和綠色的線都可以成為序列標(biāo)注的路徑。那么我們總共可以有 N=45nN = {45^n}N=45n 種路徑,如果我們要把 45n{45^n}45n 種路徑全部遍歷一遍,那將是災(zāi)難級別的時間復(fù)雜度,所以這里我們使用維特比算法,運用動態(tài)規(guī)劃的思想來解決這個問題。
我們每向前走一步,就記錄當(dāng)前的最佳的路徑,直到最后。走完最后一個詞之后,我們可以取出整個流程的最佳路徑,這就是維特比算法的大體流程,代碼如下所示:

def viterbi(x, pi, A, B):'''x: 輸入的句子pi:初始狀態(tài)A:發(fā)射概率B:轉(zhuǎn)移概率'''seg_list = jieba.cut(x)x = [word2id[word] for word in seg_list]T = len(x)dp = np.zeros((T,N)) # dp[i][j]:w1,w2,...,wT, 假設(shè)wi的tag是第j個tag# basecase for dp algorithmpointer = np.array([[0 for x in range(N)] for y in range(T)]) # T*Nfor j in range(N):dp[0][j] = log(pi[j]) + log(A[j][x[0]])for i in range(1, T): # 詞語for j in range(N): # 詞性dp[i][j] = float("-inf")for k in range(N): # 從每一個k詞性到j(luò)score = dp[i-1][k] + log(B[k][j]) + log(A[j][x[i]])if score > dp[i][j]:dp[i][j] = scorepointer[i][j] = k# decoding: 把最好的tag seq打印出來best_seq = [0 for _ in range(T)]# step1: 找出對應(yīng)于最后一個單詞的詞性best_seq[T-1] = np.argmax(dp[T-1])# step2: 通過從后到前的循環(huán)依次求出每個單詞的詞性for i in range(T-2, -1, -1):best_seq[i] = pointer[i+1][best_seq[i+1]]# 到目前為止 best_seq存放了對應(yīng)于x的詞性序列for i in range(len(best_seq)):print(id2tag[best_seq[i]])

最后我們就可以打印出,每句話的最佳序列標(biāo)注,我們來測試一個:

這個序列和我們訓(xùn)練數(shù)據(jù)中的進行對比:

結(jié)果上來看基本上沒有問題,但是要注意的是:這里用的是 jieba 分詞,很多分詞之后的結(jié)果和我們訓(xùn)練數(shù)據(jù)中的結(jié)果不一樣,所以很多情況下在詞典中找不到 jieba 分詞之后的詞的 id,所以我們再標(biāo)注訓(xùn)練數(shù)據(jù)的時候盡量應(yīng)該和我們分詞的結(jié)果一致,這樣就不會有問題了。
這就是從最底層實現(xiàn)了整個中文詞性標(biāo)注的流程,希望在詞性標(biāo)注方面對大家有所幫助,詳細(xì)代碼可以參考 GitHub ,謝謝。

總結(jié)

以上是生活随笔為你收集整理的中文词性标注详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。