Réalisation d'une segmentation de mots chinois basée sur un modèle de champ aléatoire conditionnel (segmentation de mots chinois en Python)

Table des matières

Réaliser une segmentation de mots chinois basée sur un modèle de champ aléatoire conditionnel

apprentissage des caractéristiques des mots

1. Lire le corpus

2. Trouver la fonction 2 (R)

3. Trouver la caractéristique 3 (P)

4. Obtenir la fonction 4 (W)

participe de début

Code complet et captures d'écran du projet

code de projet

Capture d'écran des résultats

Ne pas imprimer les captures d'écran des fonctionnalités 2, 3 et 4

Capture d'écran après impression des fonctionnalités


Réaliser une segmentation de mots chinois basée sur un modèle de champ aléatoire conditionnel

Dans le domaine de la segmentation des mots chinois, la méthode basée sur l'étiquetage des mots a été largement utilisée. Le problème de la segmentation des mots peut être converti en un problème d'étiquetage des séquences. Désormais, l'effet de segmentation des mots est meilleur et le modèle d'étiquetage basé sur les champs aléatoires conditionnels (CRF ) est couramment utilisé. L'idée du modèle est que le modèle de champ aléatoire conditionnel correspond à un graphe non orienté , et les éléments de Y correspondent aux sommets du graphe non orienté un par un. Sous la condition X,  la distribution de probabilité conditionnelle de la variable aléatoire est conforme à la propriété de Markov du graphe, que l'on peut appeler un champ aléatoire conditionnel. Le modèle de champ aléatoire conditionnel est utilisé pour calculer la distribution de probabilité conjointe de la séquence d'édition entière compte tenu de la séquence d'observation qui doit être marquée, et résoudre la distribution de probabilité conjointe de la séquence de mots marquée dans la phrase, de manière à réaliser la segmentation des mots .

Amélioration de la segmentation des mots chinois basée sur le modèle de champ aléatoire conditionnel : https://blog.csdn.net/admiz/article/details/109882968

apprentissage des caractéristiques des mots

1. Lire le corpus

Utilisez la fonction open() de Python pour lire le fichier ic, définissez le code de lecture sur 'utf-8-sig' et ouvrez le corpus nommé "msr_training.utf8.ic", puis utilisez la fonction readlines() pour lire chaque ligne de le corpus en tant qu'élément de la liste wordFeed, la longueur wordFeedLen du corpus est obtenue via la fonction len( ). Le format de chaque élément de la liste wordFeed est : [A|B], "A" est un mot et "B" est l'état du mot. Le format des données est comme indiqué ci-dessous, le lien de téléchargement des données : https://download.csdn.net/download/admiz/13132232

2. Trouver la fonction 2 (R)

Afin de trouver la deuxième caractéristique, comptez d'abord le nombre total de fois qu'elle apparaît dans le corpus pour un certain mot, puis calculez son statut comme le début du mot (B), le milieu du mot (M), le fin du mot (E), et le mot dans un mot ( S) probabilité.

Avant d'effectuer des statistiques sur un certain mot, définissez d'abord la structure de données pour stocker les résultats sous forme de dictionnaire. " est la probabilité que le mot soit au début du mot, "M" est la probabilité que le mot soit dans le mot, "E" est la probabilité que le mot soit à la fin du mot, et "S" est la probabilité que le mot devienne un mot.

Le pseudo code de cette étape est le suivant :

je est un mot qui nécessite la fonctionnalité deux

Initialiser BDict en tant que dictionnaire vide

Initialiser B, M, E, S, BMESsum à 0

for j in range(wordFeedLen):
    if wordFeed[j][0] == i:
        if wordFeed[j][2] == 'B':
            B = B+1
        elif  wordFeed[j][2] == 'M':
            M = M+1
        elif  wordFeed[j][2] == 'E':
            E = E+1
        elif  wordFeed[j][2] == 'S':
            S = S+1
    BMESsum = B+M+E+S
    BDict[i] = [B/BMESsum,M/BMESsum,E/BMESsum,S/BMESsum]

3. Trouver la caractéristique 3 (P)

La troisième caractéristique est de calculer la probabilité de passer à l'état de mot suivant B, M, E, S lorsqu'il est dans l'état actuel B, M, E, S pour un mot. Comptez le nombre total d'occurrences d'un certain mot dans le texte intégral et calculez la probabilité que son état soit le début (B), le milieu (M), la fin (E) et le mot unique (S). Pour les 4 états, une matrice 4x4 est formée et les valeurs de la matrice sont les probabilités de transition entre elles.

Avant d'effectuer les statistiques pour un certain mot, la structure de données de stockage du résultat est supposée être un dictionnaire antérieur, et sa structure spécifique est la suivante :

{ UNE:[ [0,0,0,0] ,[0,0,0,0],[0,0,0,0],[0,0,0,0]]} , où "A" est le mot ciblé, le premier élément de la liste (c'est-à-dire la liste en gras) signifie : lorsque "A" est l'état B, la probabilité que le mot suivant soit B, M, E, S et l'élément [0,0,0 ,0] dans une correspondance biunivoque, et ainsi de suite.

Une partie du code de cette étape est la suivante :

(1) Créer et initialiser une matrice 4x4 (dictionnaire) de mots

def setDict2(testWord):   #testWord为句子每个字的列表
    testDict = { }
    for i in testWord:
        testDict[i]=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]  #[B,M,E,S]的四阶矩阵
    return testDict

(2) Fonction de jugement dans la caractéristique trois

def charCJudge(i,mark1): #i为要判断的字符 mark1为i的状态
    B=0
    M=0
    E=0
    S=0
    BMESsum=0
    for j in range(wordFeedLen):
    if i == wordFeed[j][0] and wordFeed[j][2]==mark1 and j+1<len(wordFeed):
        if wordFeed[j+1][2] == 'B':
            B=B+1
        elif  wordFeed[j+1][2] == 'M':
            M=M+1
        elif  wordFeed[j+1][2] == 'E' :
            E=E+1
        elif  wordFeed[j+1][2] == 'S' :
            S=S+1
        else:
            pass
     BMESsum=B+M+E+S
     if BMESsum>0 :
         return [B/BMESsum,M/BMESsum,E/BMESsum,S/BMESsum]
     elif BMESsum==0 :
         return [0,0,0,0]

(3) Calculer la valeur de la matrice de fréquence de transition

Définir la liste des phrases à segmenter par testWord

testDict=setDict2(testWord)
for i in testWord:
    j=0
    for mark1 in ['B','M','E','S']:
        testDict[i][j]=charCJudge(i,mark1)
        j=j+1

4. Obtenir la fonction 4 (W)

La quatrième fonctionnalité consiste à calculer le contenu du mot suivant dans l'état de B, M, E et S pour le mot qui nécessite une segmentation de mot, et à calculer la probabilité que deux mots apparaissent en même temps, c'est-à-dire à enregistrer le contexte de la relation entre le mot précédent et le mot suivant.

Avant d'effectuer des statistiques sur un certain mot, définissez d'abord la structure de données pour stocker les résultats sous forme de dictionnaire, et sa structure spécifique est la suivante :

{ A :{'B' :{C :[B0,B1]},'M' :{},'E' :{},'S' :{}}}, où "A" est le caractère cible , "B" est l'état du mot, et le dictionnaire dans "B" signifie que le mot "A" est dans l'état de "B", et la probabilité que le mot "C" apparaisse avant et après le mot "A " est B0, B1, respectivement. et ainsi de suite.

Le pseudo code de cette étape est le suivant :

(1) Créer et initialiser le dictionnaire de contexte

def setDict3(testWord): #testWord为句子每个字的列表
    testDict = { }
    for i in testWord:
        testDict[i]={'B':{},'M':{},'E':{},'S':{}}  #[B,M,E,S]的四阶矩阵
    return testDict

(2) Fonction de probabilité de relation de contexte dans la caractéristique quatre

def charDJudge(i,mark1): #i为要判断的字符 mark1为i的状态
    testDict = { }
    lastSum=0
    nextSum=0
    for j in range(wordFeedLen):
    #print(wordFeed[j][0])
        if i == wordFeed[j][0] and wordFeed[j][2]==mark1 and j+1<len(wordFeed):
            #特征四内的上文关系概率函数 
            if wordFeed[j-1][0] not in testDict:
                testDict[wordFeed[j-1][0]]=[1,0]
                lastSum=lastSum+1
            elif wordFeed[j-1][0] in testDict:
                testDict[wordFeed[j-1][0]][0]=testDict[wordFeed[j-1][0]][0]+1
                lastSum=lastSum+1
            #特征四内的下文关系概率函数        
            if wordFeed[j+1][0] not in testDict:
                testDict[wordFeed[j+1][0]]=[0,1]
                nextSum=nextSum+1
            elif wordFeed[j+1][0] in testDict:
                testDict[wordFeed[j+1][0]][1]=testDict[wordFeed[j+1][0]][1]+1
                nextSum=nextSum+1
    for key, value in testDict.items():
        testDict[key][0]=testDict[key][0]/lastSum
        testDict[key][1]=testDict[key][1]/nextSum
    return testDict

participe de début

Après avoir terminé l'apprentissage des caractéristiques des mots, la segmentation des mots chinois peut être réalisée à l'aide des valeurs de paramètres apprises. Par exemple, la segmentation des mots chinois est effectuée sur la phrase "La Grèce a une structure économique particulière" entrée par l'utilisateur.

La première étape consiste à convertir la "structure économique grecque est spéciale" saisie par l'utilisateur en une liste de caractères : ['Greece', 'Greek', 'of', 'Economic', 'Economy', 'Jie', 'Structure ', 'Comparer', 'Spécial', 'Spécial'].

La deuxième étape consiste à obtenir les caractéristiques correspondantes de chaque mot et à renvoyer la relation de mappage matriciel initiale entre le mot et l'état en fonction des informations d'état. Calculez les probabilités d'état de tous les caractères de la liste de caractères en B, M, E et S, et les problèmes suivants peuvent être résolus facilement. Selon la formule de Viterbi :

 

(Remarques : S est la valeur dans la matrice, R est la caractéristique deux, P est la caractéristique trois, "W avant" est la partie ci-dessus de la caractéristique quatre, "W après" est la partie suivante de la caractéristique quatre.)

Enfin, selon la formule, la matrice après avoir calculé la relation correspondante entre les mots et les états peut être obtenue.

Une partie du code de cette étape est la suivante :

 (1) Initialisez d'abord la liste de chaînes

 testList=['希', '腊', '的', '经', '济', '结', '构', '较', '特', '殊']  

(2) Obtenez les fonctionnalités 2, 3 et 4 en suivant les étapes de l'apprentissage des fonctionnalités 3.1 ci-dessus.

testCharaB=charaB(testList) #特征二 字符BMES矩阵
testCharaC=charaC(testList) #特征三 转移频率
testCharaD=charaD(testList) #特征四 上下文关联关系

(3) Calculer la matrice de correspondance mot-état et le chemin de retraçage

column=['B','M','E','S']
relaDict=setDict(testList)
wayList=[]  #保存回溯路径
a=0
b=0
c=0
d=0
for i in range(len(testList)):
    oneWaylist=[] #临时保存回溯路径

(4) Réaliser la formule de Viterbi

(e est la probabilité des quatre contextes caractéristiques du mot ciblé, et e est la probabilité des quatre contextes caractéristiques du mot)       

for j in range(len(column)):
    if i==0 :
        if testList[i+1] not in testCharaD[testList[i]][column[j]] :
            e=0
        else:
            e=testCharaD[testList[i]][column[j]][testList[i+1]][0]
        relaDict[testList[i]][j]= e + testCharaB[testList[i]][j]
    elif i>0 and i<len(testList)-1:
        a=testCharaC[testList[i-1]][0][j] * relaDict[testList[i-1]][0]
        b=testCharaC[testList[i-1]][1][j] * relaDict[testList[i-1]][1]
        c=testCharaC[testList[i-1]][2][j] * relaDict[testList[i-1]][2]
        d=testCharaC[testList[i-1]][3][j] * relaDict[testList[i-1]][3]
        if testList[i-1] not in testCharaD[testList[i]][column[j]] : #特征四上文
            e=0
        else:
            e=testCharaD[testList[i]][column[j]][testList[i-1]][0]
        if testList[i+1] not in testCharaD[testList[i]][column[j]] : #特征四下文
            f=0
        else:
            f=testCharaD[testList[i]][column[j]][testList[i+1]][1]
        relaDict[testList[i]][j]=max(a,b,c,d) + testCharaB[testList[i]][j]  + e + f
    elif i==len(testList)-1:
        a=testCharaC[testList[i-1]][0][j] * relaDict[testList[i-1]][0]
        b=testCharaC[testList[i-1]][1][j] * relaDict[testList[i-1]][1]
        c=testCharaC[testList[i-1]][2][j] * relaDict[testList[i-1]][2]
        d=testCharaC[testList[i-1]][3][j] * relaDict[testList[i-1]][3]
        if testList[i-1] not in testCharaD[testList[i]][column[j]] : #特征四上文
             e=0
        else:
            e=testCharaD[testList[i]][column[j]][testList[i-1]][0]
        relaDict[testList[i]][j]= max(a,b,c,d) + testCharaB[testList[i]][j] +e

Grâce au code ci-dessus, la matrice de correspondance mot-état et le chemin de retour en arrière peuvent être obtenus, et le résultat de marquage de chaque mot peut être obtenu, de manière à réaliser une segmentation de mot.

 

Code complet et captures d'écran du projet

code de projet

# -*- coding: utf-8 -*-
"""
Created on Fri Oct 18 08:36:46 2019

@author: JoeLiao‘s ASUS
"""


#全局变量
#读取语料库
wordFile=open("msr_training.utf8.ic",'r',encoding='utf-8')
wordFeed=wordFile.readlines() #[0]=字 [2]=标注
wordFile.close
wordFeedLen=len(wordFeed)
#print(wordFeed)

#创建字的列表
def setList(a):  #a为要分词的句子
    testWord = [ ]
    for i in a:
        testWord.append(i)
    return testWord


#创建及初始化矩阵(字典)
def setDict(testWord): #testWord为句子每个字的列表
    testDict = { }
    for i in testWord:
        testDict[i]=[0,0,0,0]  #[B,M,E,S]
    return testDict

####### 特征二 #######
#特征二 计算状态频率矩阵值
def  charaB(testWord): #testDict为每个字的字典
    testDict=setDict(testWord)
    for i in testWord:
        B=0
        M=0
        E=0
        S=0
        BMESsum=0
        for j in range(wordFeedLen):
            #print(wordFeed[j][0])
            if i == wordFeed[j][0]:
                if wordFeed[j][2] == 'B':
                    B=B+1
                elif  wordFeed[j][2] == 'M':
                    M=M+1
                elif  wordFeed[j][2] == 'E':
                    E=E+1
                elif  wordFeed[j][2] == 'S':
                    S=S+1
        BMESsum=B+M+E+S
        testDict[i]=[B/BMESsum,M/BMESsum,E/BMESsum,S/BMESsum]
    return testDict


####### 特征三 #######
#创建及初始化字的4x4的矩阵(字典)
def setDict2(testWord): #testWord为句子每个字的列表
    testDict = { }
    for i in testWord:
        testDict[i]=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]  #[B,M,E,S]的四阶矩阵
    return testDict

#特征三内的判断判断函数
def charCJudge(i,mark1): #i为要判断的字符 mark1为i的状态
        B=0
        M=0
        E=0
        S=0
        BMESsum=0
        for j in range(wordFeedLen):
            #print(wordFeed[j][0])
            if i == wordFeed[j][0] and wordFeed[j][2]==mark1 and j+1<len(wordFeed):
                if wordFeed[j+1][2] == 'B':
                    B=B+1
                elif  wordFeed[j+1][2] == 'M':
                    M=M+1
                elif  wordFeed[j+1][2] == 'E' :
                    E=E+1
                elif  wordFeed[j+1][2] == 'S' :
                    S=S+1
                else:
                    pass
        BMESsum=B+M+E+S
        if BMESsum>0 :
            return [B/BMESsum,M/BMESsum,E/BMESsum,S/BMESsum]
        elif BMESsum==0 :
            return [0,0,0,0]

#特征三 计算转移频率矩阵值
def  charaC(testWord):
    testDict=setDict2(testWord)
    for i in testWord:
        j=0
        for mark1 in ['B','M','E','S']:
            testDict[i][j]=charCJudge(i,mark1)
            j=j+1
            #print(i,j)
            #print(testDict)
    return testDict



####### 特征四 #######
#创建及初始化上下文字典
def setDict3(testWord): #testWord为句子每个字的列表
    testDict = { }
    for i in testWord:
        testDict[i]={'B':{},'M':{},'E':{},'S':{}}  #[B,M,E,S]的四阶矩阵
    return testDict

#特征四内的上下文关系概率函数
def charDJudge(i,mark1): #i为要判断的字符 mark1为i的状态
    testDict = { }
    lastSum=0
    nextSum=0
    for j in range(wordFeedLen):
    #print(wordFeed[j][0])
        if i == wordFeed[j][0] and wordFeed[j][2]==mark1 and j+1<len(wordFeed):
            #特征四内的上文关系概率函数 
            if wordFeed[j-1][0] not in testDict:
                testDict[wordFeed[j-1][0]]=[1,0]
                lastSum=lastSum+1
            elif wordFeed[j-1][0] in testDict:
                testDict[wordFeed[j-1][0]][0]=testDict[wordFeed[j-1][0]][0]+1
                lastSum=lastSum+1
            #特征四内的下文关系概率函数        
            if wordFeed[j+1][0] not in testDict:
                testDict[wordFeed[j+1][0]]=[0,1]
                nextSum=nextSum+1
            elif wordFeed[j+1][0] in testDict:
                testDict[wordFeed[j+1][0]][1]=testDict[wordFeed[j+1][0]][1]+1
                nextSum=nextSum+1
    for key, value in testDict.items():
        testDict[key][0]=testDict[key][0]/lastSum
        testDict[key][1]=testDict[key][1]/nextSum
    return testDict


#特征四计算特定的字与上下文关系
def  charaD(testWord):
    testDict=setDict3(testWord)
    for i in testWord:
        for mark1 in ['B','M','E','S']:
            testDict[i][mark1]=charDJudge(i,mark1)
    return testDict


####### 实现分词  #######

#返回分词结果
def getResult(signList):
    resultString=''
    for iList in signList:
        if iList[1]=='B':
            resultString=resultString+' '+iList[0]
        elif iList[1]=='M':
            resultString=resultString+iList[0]
        elif iList[1]=='E':
            resultString=resultString+iList[0]+' '
        elif iList[1]=='S':
            resultString=resultString+' '+iList[0]+' '
    return resultString

#返回分词标记转换
def trans(num):
    if num==0:
        return 'B'
    elif num==1:
        return 'M'
    elif num==2:
        return 'E'
    elif num==3:
        return 'S'

#字与状态对应关系计算
def separateWords(testString):
    testString=str1     #要测试的句子
    testList=setList(testString) #字符串列表
    #计算 特征二 特征三 特征四
    print('字符串列表:',testList,'\n')
    testCharaB=charaB(testList) #特征二 字符BMES矩阵
    #print('特征二:',testCharaB,'\n')
    testCharaC=charaC(testList) #特征三 转移频率
    #print('特征三',testCharaC,'\n')
    testCharaD=charaD(testList) #特征四 上下关联关系
    print('特征四',testCharaD,'\n')
    
    #生成字与状态对应关系矩阵值(字典)
    column=['B','M','E','S']
    relaDict=setDict(testList)
    wayList=[]
    a=0
    b=0
    c=0
    d=0
    for i in range(len(testList)):
        oneWaylist=[]
        for j in range(len(column)):
            #print(testList[i],column[j])
            if i==0 :
                if testList[i+1] not in testCharaD[testList[i]][column[j]] :
                    e=0
                else:
                    e=testCharaD[testList[i]][column[j]][testList[i+1]][0]
                relaDict[testList[i]][j]= e + testCharaB[testList[i]][j]
            elif i>0 and i<len(testList)-1:
                a=testCharaC[testList[i-1]][0][j] * relaDict[testList[i-1]][0]
                b=testCharaC[testList[i-1]][1][j] * relaDict[testList[i-1]][1]
                c=testCharaC[testList[i-1]][2][j] * relaDict[testList[i-1]][2]
                d=testCharaC[testList[i-1]][3][j] * relaDict[testList[i-1]][3]
                if testList[i-1] not in testCharaD[testList[i]][column[j]] : #特征四上文
                    e=0
                else:
                    e=testCharaD[testList[i]][column[j]][testList[i-1]][0]
                if testList[i+1] not in testCharaD[testList[i]][column[j]] : #特征四下文
                    f=0
                else:
                    f=testCharaD[testList[i]][column[j]][testList[i+1]][1]
                relaDict[testList[i]][j]=max(a,b,c,d) + testCharaB[testList[i]][j]  + e + f
            elif i==len(testList)-1:
                a=testCharaC[testList[i-1]][0][j] * relaDict[testList[i-1]][0]
                b=testCharaC[testList[i-1]][1][j] * relaDict[testList[i-1]][1]
                c=testCharaC[testList[i-1]][2][j] * relaDict[testList[i-1]][2]
                d=testCharaC[testList[i-1]][3][j] * relaDict[testList[i-1]][3]
                if testList[i-1] not in testCharaD[testList[i]][column[j]] : #特征四上文
                     e=0
                else:
                    e=testCharaD[testList[i]][column[j]][testList[i-1]][0]
                relaDict[testList[i]][j]= max(a,b,c,d) + testCharaB[testList[i]][j] +e
            #wayDict={'a':a,'b':b,'c':c,'d':d}
            findMax=[a,b,c,d]
            oneWaylist.append(findMax.index(max(findMax)))
        #print(oneWaylist)
        wayList.append(oneWaylist)
    print('\n关系矩阵:',relaDict,'\n\n回溯路径:',wayList)
    
    
    
    signList=[]
    lenList=[]
    #lenList=list(range(len(testList))).reverse()
    for i in range(len(testList)):
        lenList.append(i)
    
    lenList.reverse()
    
    for i in lenList:
        testWord=testList[i]
        if i == len(testList)-1:
            indexNum=relaDict[testWord].index(max(relaDict[testWord]))
            sign=trans(indexNum)
            signList.append([testWord,sign])
            nextIndexNum=wayList[i][indexNum]
        else:
            sign=trans(nextIndexNum)
            signList.append([testWord,sign])
            indexNum=relaDict[testWord].index(max(relaDict[testWord]))
            nextIndexNum=wayList[i][indexNum]
            
    
    signList.reverse()
    print("\n分词标记:",signList,'\n')
    print("分词原句:",testString,'\n')
    print("分词结果:",getResult(signList))



################主函数################
import time

#from multiprocessing.dummy import Pool as ThreadPool
#pool = ThreadPool(processes=8)

str1=input("请输入要分词的字符串:")
print('\n')
start = time.clock()
separateWords(str1)

            
#results2 = pool.map(separateWords, str1)
#pool.close()
#pool.join()

print('\n')

elapsed = (time.clock() - start)
print("分词用时:",elapsed,'秒')


#input('******回车后可退出界面******')

Capture d'écran des résultats

Les captures d'écran sans impression comportent deux, trois et quatre :

Capture d'écran après l'impression des fonctionnalités :

Fonctionnalité 4 (capture d'écran partielle)

 

Je suppose que tu aimes

Origine blog.csdn.net/admiz/article/details/109846185
conseillé
Classement