NLP-分词

一、中文分词-难点

一、中文分词的3大难点

1、没有统一的标准

  • 目前中文分词没有统一的标准,也没有公认的规范
  • 不同的公司和组织各有各的方法和规则
  • 例如人名,在哈工大标准中姓和名是分开的,但在Hanlp(Han Language Processing汉语言处理包)中是合在一起的
  • 需要根据不同的需求制定不同的分词标准

2、歧义如何划分

  • 歧义:对同一个待切分字符序列存在多个分词结果
  •  例:“乒乓球拍卖完了”有两种不同的分词方式,含义也不同
  •  乒乓/球拍/卖完了
  •  乒乓球/拍卖/完了

3、新词如何识别

  • 信息爆炸的时代,三天两头就会冒出来一堆新词
  • 蓝瘦香菇
  • 泰裤辣
  • 奥利给
  • 酱紫
#jieba添加新词
jieba.add_word("蓝瘦香菇") #定义新词汇
str_6 = jieba.lcut("我看了这个电视剧有点蓝瘦香菇")
print(str_6)
# jieba删除本地新词
jieba.del_word("蓝瘦香菇")
str_8 = jieba.lcut("我看了这个电视剧有点蓝瘦香菇")
print(str_8)

二、基于词典匹配的分词方法

将待分词的中文文本根据一定规则切分和调整,然后跟词典中的词语进行匹配,匹配
成功则按照词典的词分词,匹配失败通过调整或者重新选择
代表方法:正向最大匹配、反向最大匹配法、双向最大匹配

正向最大匹配、反向最大匹配法、双向最大匹配的共同缺点:

  • 对词表极为依赖,如果没有词表,则无法进行;如果词表中缺少需要的词,结果也不会正确
  • 切分过程中不会关注整个句子表达的意思,只会将句子看成一个个片段
  • 如果文本中出现一定的错别字,会造成一连串影响
  • 对于人名等的无法枚举实体词无法有效的处理

一、中文分词-正向最大匹配

1、分词步骤:

1、收集一个词表;2、对于一个待分词的字符串、从前向后寻找最长的,在此表中出现的词,在词边界做切分;3、从切分处重复步骤2、直到字符串末尾

例:北京大学生前来报道

北京          生前

北京大学   前来

大学          报道

大学生

划分得:北京大学 / 生前 / 来 / 报道(不能确保划分结果的正确)

2、实现方式一

1、找出词表中最大词长度

2、从字符串开头开始选取最大词长度的窗口、检查窗口内的词是否在词表中

3、如果在词表中,在词边界处进行切分,之后移动到词边界处,重复步骤2

4、如果不在词表中,窗口右边界回退一个字符,之后检查窗口词是否在词表中

  • 切分过程
  • 北京大学生前来报道
  • 北京大学生前来报
  • 北京大学生前来报道
  • 北京大学生前来报道
  • 北京大学生前来报道
  • 北京大学生前来报
  • 北京大学生前报道
  • 北京大学生前来报道

#分词方法:最大正向切分的第一种实现方式

import re
import time

#加载词典
def load_word_dict(path):
    max_word_length = 0
    word_dict = {}  #用set也是可以的。用list会很慢
    with open(path, encoding="utf8") as f:
        for line in f:
            word = line.split()[0]
            word_dict[word] = 0
            max_word_length = max(max_word_length, len(word))
    return word_dict, max_word_length

#先确定最大词长度
#从长向短查找是否有匹配的词
#找到后移动窗口
def cut_method1(string, word_dict, max_len):
    words = []
    while string != '':
        lens = min(max_len, len(string))
        word = string[:lens]
        while word not in word_dict:
            if len(word) == 1:
                break
            word = word[:len(word) - 1]
        words.append(word)
        string = string[len(word):]
    return words

#cut_method是切割函数
#output_path是输出路径
def main(cut_method, input_path, output_path):
    word_dict, max_word_length = load_word_dict("dict.txt")
    writer = open(output_path, "w", encoding="utf8")
    start_time = time.time()
    with open(input_path, encoding="utf8") as f:
        for line in f:
            words = cut_method(line.strip(), word_dict, max_word_length)
            writer.write(" / ".join(words) + "\n")
    writer.close()
    print("耗时:", time.time() - start_time)
    return


string = "北京大学生前来报道"
word_dict, max_len = load_word_dict("dict.txt")
print(cut_method1(string, word_dict, max_len))

main(cut_method1, "corpus.txt", "cut_method1_output.txt")

3、实现方式二(前缀字典)

1、从前向后进行查找

2、如果窗口内的词是一个词前则继续扩大窗口

3、如果窗口内的词不是一个词前缀,则记录已经发现的词,并将窗口移动到词边界

{     0代表不是一个词,但是词的前缀     1代表是一个词

“北”:0,

“北京”:1,

“北京大”:0,

“北京大学”:1,                                           

“北京大学生”:1,                                       

“大”:0,

“大学”:0,

“大学生”:1

}     

  • 切分过程:
  • 北京大学生前来报道                                                 
  • 北京大学生前来报道                                                 
  • 北京大学生前来报道                                                 
  • 北京大学生前来报道                                                 
  • 北京大学生前来报道                                                 
  • 北京大学生前来报道
  • 北京大学生前来报道
  • 北京大学生前来报
  • 北京大学生前报道
#分词方法最大正向切分的第二种实现方式

import re
import time
import json

#加载词前缀词典
#用0和1来区分是前缀还是真词
#需要注意有的词的前缀也是真词,在记录时不要互相覆盖
def load_prefix_word_dict(path):
    prefix_dict = {}
    with open(path, encoding="utf8") as f:
        for line in f:
            word = line.split()[0]
            for i in range(1, len(word)):
                if word[:i] not in prefix_dict: #不能用前缀覆盖词
                    prefix_dict[word[:i]] = 0  #前缀
            prefix_dict[word] = 1  #词
    return prefix_dict


#输入字符串和字典,返回词的列表
def cut_method2(string, prefix_dict):
    if string == "":
        return []
    words = []  # 准备用于放入切好的词
    start_index, end_index = 0, 1  #记录窗口的起始位置
    window = string[start_index:end_index] #从第一个字开始
    find_word = window  # 将第一个字先当做默认词
    while start_index < len(string):
        #窗口没有在词典里出现
        if window not in prefix_dict or end_index > len(string):
            words.append(find_word)  #记录找到的词
            start_index += len(find_word)  #更新起点的位置
            end_index = start_index + 1
            window = string[start_index:end_index]  #从新的位置开始一个字一个字向后找
            find_word = window
        #窗口是一个词
        elif prefix_dict[window] == 1:
            find_word = window  #查找到了一个词,还要在看有没有比他更长的词
            end_index += 1
            window = string[start_index:end_index]
        #窗口是一个前缀
        elif prefix_dict[window] == 0:
            end_index += 1
            window = string[start_index:end_index]
    #最后找到的window如果不在词典里,把单独的字加入切词结果
    if prefix_dict.get(window) != 1:
        words += list(window)
    else:
        words.append(window)
    return words


#cut_method是切割函数
#output_path是输出路径
def main(cut_method, input_path, output_path):
    word_dict = load_prefix_word_dict("dict.txt")
    writer = open(output_path, "w", encoding="utf8")
    start_time = time.time()
    with open(input_path, encoding="utf8") as f:
        for line in f:
            words = cut_method(line.strip(), word_dict)
            writer.write(" / ".join(words) + "\n")
    writer.close()
    print("耗时:", time.time() - start_time)
    return


string = "王羲之草书《平安帖》共有九行"
# string = "你到很多有钱人家里去看"
# string = "金鹏期货北京海鹰路营业部总经理陈旭指出"
# string = "伴随着优雅的西洋乐"
# string = "非常的幸运"
prefix_dict = load_prefix_word_dict("dict.txt")
# print(cut_method2(string, prefix_dict))
# print(json.dumps(prefix_dict, ensure_ascii=False, indent=2))
main(cut_method2, "corpus.txt", "cut_method2_output.txt")

二、中文分词-反向最大匹配

  • 北 京 大 学 生 前 来 报 到
  • 正向匹配:    北京大学 /  生前 / 来 / 报到
  • 反向匹配:     北京 / 大学生 / 前来 / 报到                      
  • 友好的哥谭市民
  • 正向匹配:    友好 / 的哥 / 谭 / 市民
  • 反向匹配:    友好 / 的 / 哥谭 / 市民

三、中文分词-双向最大匹配

思路:同时进行正向最大切分,和负向最大切分,之后比较两者结果,决定切分方式。

比较方式:

1.单字词:词表中可以有单字,从分词的角度,我们也会把它称为一个词

2.非字典词:未在词表中出现过的词,一般都会被分成单字

3.词总量:不同切分方法得到的词数可能不同

  • 我们在野生动物园玩
  • 正向最大匹配法: “我们/在野/生动/物/园/玩”
  • 词典词3个,单字字典词为2,非词典词为1。
  • 逆向最大匹配法: “我们/在/野生动物园/玩”
  • 词典词2个,单字字典词为2,非词典词为0。

词表:我们 在野 生动 动物 野生动物园 野生 动物园 在 玩 园

三、基于统计的分词方法

在给定大量已经分词的文本的前提下,利用统计机器学习模型学习词语切分的规律(称为
训练),从而实现对未知文本的切分
常用的算法:HMM、CRF、N-gram

四、基于深度学习的分词方法

直接以最基本的向量化原子特征作为输入,经过多层非线性变换,输出层就可以很好的预测
当前字的标记或下一个动作。在深度学习的框架下,仍然可以采用基于子序列标注的方式,
或基于转移的方式,以及半马尔科夫条件随机场 算法:LSTM+CRF、BiLSTM+CRF

一、基于机器学习

上 海 自 来 水 来 自 海 上 对于每一个字,我们想知道它是不是一个词的边界                  

| | | | | | ||

0     1    0     0     1     0    1    1    1

蓝色表示不是词边界,红色表示是词边界

问题转化为:对于句子中的每一个字,进行二分类判断,正类表示这句话中,它是词边界,负类表示它不是词边界

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天黑速遛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值