前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用hmmlearn框架实现中文分词

使用hmmlearn框架实现中文分词

作者头像
Cyril-KI
发布2022-09-19 14:33:26
4160
发布2022-09-19 14:33:26
举报
文章被收录于专栏:KI的算法杂记

隐马尔可夫模型(Hidden Markov Model,HMM)是统计模型,它用来描述一个含有隐含未知参数的马尔可夫过程。其难点是从可观察的参数中确定该过程的隐含参数。然后利用这些参数来作进一步的分析,例如模式识别。

HMM中文分词原理: 对于一个词语,比如“我爱吃饭”,每个字有都对应的状态,状态一共四种:B、M、E、S。其中B表示开始,M表示中间,E表示结尾,S表示单独一个字。因此上述四个字的隐状态为:“BMME”。

使用hmmlearn实现中文分词,我们要解决的核心问题就是计算三大矩阵:初始概率矩阵、转移概率矩阵以及发射概率矩阵。

  • 初始概率矩阵是一个1 X 4维的矩阵,我们用pi表示。pi[0]就是初始时B的概率,后面三个依次类推。怎么算这四个值呢?我们遍历训练集中每一个句子,如果该句子第一个词语长度大于等于2,那说明该句子是以B开头的,则pi[0]++;如果句子开头只有一个字,则pi[3]++。很显然,句子开头不可能是M或者E。遍历完之后,矩阵中每个数再除以所有数之和即可(算概率)。
  • 转义概率矩阵A是一个4 X 4维的矩阵。A[0, 1]表示当前状态是B而下一状态是M的概率。具体计算方法:我们遍历所有句子,对每一个句子,我们找出B后面跟着的M的个数,以及B后面跟着的E的个数等等。遍历完成之后同样每一行除以该行总和。
  • 发射概率矩阵B是一个4 X 65536(unicode)的矩阵。B[3, 25000]表示unicode编码为25000的汉字状态为B的概率。计算方式为:遍历训练集中每一个字,利用ord(x)返回编码,如果该字隐状态为B,则B[0, ord(x)]++,以此类推。最后同样每一行除以该行所有数据之和。当然,按理说也可以不用unicode编码,我刚开始是这样做的:找到所有汉字的集合(不重复),大概有25000的样子,然后从0-24999编号。但是这样做的话时间代价很大,直接计算ord(x)显然更快。

具体步骤

1.数据处理。读取txt文件,把每一个字的隐状态都表示出来:

代码语言:javascript
复制
def load_data():
     data = open('HMM/TrainData.txt', encoding='utf-8')
     file = []
     for line in data.readlines():
         file.append(line.strip().split(' '))
     res = []
     for i in range(len(file)):
         for j in range(len(file[i])):
             for k in range(len(file[i][j])):
                 res.append(file[i][j][k])
     real_file = []
     for i in range(len(file)):
         x = []
         for j in range(len(file[i])):
             if len(file[i][j]) == 1:
                 x.append('S')
             elif len(file[i][j]) == 2:
                 x.append('BE')
             else:
                 str = 'B'
                 for k in range(len(file[i][j])-2):
                     str += 'M'
                 str += 'E'
                 x.append(str)
         real_file.append(x)

     return file, real_file

file表示所有词语的集合,是一个二维列表,每个列表表示一句话,每句话又被分成了词语。real_file与file一一对应,只不过是编码BMES的集合。

2.计算三大矩阵:

代码语言:javascript
复制
def hMM():
     file, data = load_data()    # lens表示一个出现了多少个字
     # print(list_set)
     states = ['B', 'M', 'E', 'S']   # 分别表示开始中间结尾以及单个字
     A = np.zeros((4, 4))
     B = np.zeros((4, 65536))   # 编码表示汉字
     pi = np.zeros(4)     # 初始状态

     for i in range(len(data)):
         if data[i][0][0] == 'B':  # 开头只能是B或者S
             pi[0] += 1
         if data[i][0][0] == 'S':
             pi[3] += 1

     pi /= np.sum(pi)   # 初始状态

     for i in range(len(data)):
         for j in range(len(data[i])):
             for k in range(len(data[i][j])):
                 B[states.index(data[i][j][k]), ord(file[i][j][k])] += 1   # 隐状态为data[i][j][k]时对应汉字file[i][j][k]
             if len(data[i][j]) == 1 and j + 1 < len(data[i]):
                 if data[i][j+1][0] == 'B':  # S后面接B
                     A[3, 0] += 1
                 if data[i][j+1][0] == 'S':  # S后面接S
                     A[3, 3] += 1
                 continue
             A[0, 1] += data[i][j].count('BM')
             A[0, 2] += data[i][j].count('BE')
             A[1, 2] += data[i][j].count('ME')
             if j + 1 < len(data[i]) and data[i][j + 1][0] == 'B':
                 A[2, 0] += 1
             if j + 1 < len(data[i]) and data[i][j + 1][0] == 'S':
                 A[2, 3] += 1

     for i in range(4):
         if np.sum(A[i]) != 0:
             A[i] = A[i] / np.sum(A[i])

     for i in range(4):
         B[i] /= np.sum(B[i])

3.模型训练

代码语言:javascript
复制
model = hmm.MultinomialHMM(n_components=len(states))
model.startprob_ = pi
model.emissionprob_ = B
model.transmat_ = A

4.测试。对测试集中每一句话,对其中每一个字找到它的unicode码集合,然后利用上面训练好的模型对该集合进行解码。

代码语言:javascript
复制
if __name__ == '__main__':
     print('请稍候...')
     model = hMM()
     dataset = []
     data = open('HMM/TestData.txt', 'r+')
     for line in data.readlines():
         temp = line.strip().split('\t')
         file = []
         for x in temp:
             file.append(x)
         dataset.append(file)   #处理数据
     data = np.array(dataset)
     for k in range(4):    #四句话
         print('分词前:', str(data[k]))
         datas = []
         x = str(*data[k])   #变成字符串
         for j in x:
             datas.append(ord(j))  #寻找汉字的编码,进行decode
         xd = np.asarray(datas).reshape(-1, 1)
         pre = model.predict(xd)
         final = []
         for p, q in enumerate(pre):
             if q == 0:
                 t = p
             elif q == 2:
                 final.append(x[t:p + 1])
             elif q == 3:
                 final.append(x[p])
         print("分词后:", '/'.join(final))
         print('\n')

5.输出

代码语言:javascript
复制
分词前:['长春市长春节讲话。']
分词后: 长春/市长/春节/讲话/。


分词前:['他说的确实在理.']
分词后: 他/说/的/确实/在理


分词前:['我有一台电脑。']
分词后: 我有/一台/电脑/。

完整代码

代码语言:javascript
复制
# -*- coding: utf-8 -*-
"""
@Time :2021/1/2 16:27
@Author :KI
@File :HMM.py
@Motto:Hungry And Humble

"""
from hmmlearn import hmm
import numpy as np


def load_data():
    data = open('HMM/TrainData.txt', encoding='utf-8')
    file = []
    for line in data.readlines():
        file.append(line.strip().split(' '))
    res = []
    for i in range(len(file)):
        for j in range(len(file[i])):
            for k in range(len(file[i][j])):
                res.append(file[i][j][k])
    real_file = []
    for i in range(len(file)):
        x = []
        for j in range(len(file[i])):
            if len(file[i][j]) == 1:
                x.append('S')
            elif len(file[i][j]) == 2:
                x.append('BE')
            else:
                str = 'B'
                for k in range(len(file[i][j]) - 2):
                    str += 'M'
                str += 'E'
                x.append(str)
        real_file.append(x)
    return file, real_file


# 训练
def hMM():
    file, data = load_data()  # lens表示一个出现了多少个字
    # print(list_set)
    states = ['B', 'M', 'E', 'S']  # 分别表示开始中间结尾以及单个字
    A = np.zeros((4, 4))
    B = np.zeros((4, 65536))  # 编码表示汉字,不用顺序
    pi = np.zeros(4)  # 初始状态

    for i in range(len(data)):
        if data[i][0][0] == 'B':  # 开头只能是B或者S
            pi[0] += 1
        if data[i][0][0] == 'S':
            pi[3] += 1

    pi /= np.sum(pi)  # 初始状态

    for i in range(len(data)):
        for j in range(len(data[i])):
            for k in range(len(data[i][j])):
                B[states.index(data[i][j][k]), ord(file[i][j][k])] += 1  # 隐状态为data[i][j][k]时对应汉字file[i][j][k]
            if len(data[i][j]) == 1 and j + 1 < len(data[i]):
                if data[i][j + 1][0] == 'B':  # S后面接B
                    A[3, 0] += 1
                if data[i][j + 1][0] == 'S':  # S后面接S
                    A[3, 3] += 1
                continue
            A[0, 1] += data[i][j].count('BM')
            A[0, 2] += data[i][j].count('BE')
            A[1, 2] += data[i][j].count('ME')
            if j + 1 < len(data[i]) and data[i][j + 1][0] == 'B':
                A[2, 0] += 1
            if j + 1 < len(data[i]) and data[i][j + 1][0] == 'S':
                A[2, 3] += 1

    for i in range(4):
        if np.sum(A[i]) != 0:
            A[i] = A[i] / np.sum(A[i])

    for i in range(4):
        B[i] /= np.sum(B[i])
    # 训练模型
    model = hmm.MultinomialHMM(n_components=len(states))
    model.startprob_ = pi
    model.emissionprob_ = B
    model.transmat_ = A
    return model


if __name__ == '__main__':
    print('请稍候...')
    model = hMM()
    dataset = []
    data = open('HMM/TestData.txt', 'r+')
    for line in data.readlines():
        temp = line.strip().split('\t')
        file = []
        for x in temp:
            file.append(x)
        dataset.append(file)  # 处理数据
    data = np.array(dataset)
    for k in range(4):  # 四句话
        print('分词前:', str(data[k]))
        datas = []
        x = str(*data[k])  # 变成字符串
        for j in x:
            datas.append(ord(j))  # 寻找汉字的编码,进行decode
        xd = np.asarray(datas).reshape(-1, 1)
        pre = model.predict(xd)
        final = []
        for p, q in enumerate(pre):
            if q == 0:
                t = p
            elif q == 2:
                final.append(x[t:p + 1])
            elif q == 3:
                final.append(x[p])
        print("分词后:", '/'.join(final))
        print('\n')
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-01-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 KI的算法杂记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 具体步骤
  • 5.输出
  • 完整代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档