NLP学习笔记:实现简单的单词纠错功能

一、知识储备

  1.单词的编辑距离

    这个概念表示的就是一个单词经过几次编辑形成新的单词。这个编辑包含了增加,删除,替换三种。例如 apple 的编辑距离为 1 的单词可以有 aapple bapple pple aple bpple 等,这就是所谓的编辑距离。如果需要得到编辑距离为2的单词。只需要在编辑距离为 1 的基础上再次调用函数。python的实现代码如下

功能,生成所有的编辑距离为 1 的单词。

def generate_candidates(word):
    """
    :param word: 给定的输入单词
    :return: 返回所有候选集合
    """
    ##生成编辑距离为 1 的单词  1)insert  2)delete 3)replace

    #假设添加的字母如下
    letters = 'abcdefghijklmnopqrstuvwxyz'
    #将单词进行拆分,明确可以插入字母的位置
    splits =  [(word[:i],word[i:]) for i in range(len(word)+1)]
    #插入操作
    inserts = [L+c+R for L,R in splits   for c in letters]
    #删除操作  删除右边部分的第一个字母
    deletes = [L+R[1:] for L,R in splits if R]
    #替换操作  将右边的第一个字符替换
    replaces = [L+c+R[1:] for L,R in splits if R for c in letters]
    #生成的所有结果
    candidates = set(inserts+deletes+replaces)
    #将不再词库的单词去掉     下边的词库为vocab 的 txt文件,后边也会用到
    result = [word for word in candidates if word in vocab]
    return result

  2.语言模型

    目前,我学习到的语言模型是比较简单的 n-gream 。 这个涉及到了一些概率论的知识。举个例子说明一下:句子 I like learning NLP。我们需要计算 like 在I  之后出现的概率,我们选取n-gream 中 n 为2 的情况,即 Bi-gream。P(like| I )=P(I | like)*P(like) /P(I)  这个公式就是贝叶斯的公式。 P(Wi | Wi-1)=P(Wi-1 | PW)*P(W)/P(Wi-1)

计算的这个概率即为出现这种组合的概率。同理,如果 n = 3 则计算 P(Wi | Wi-1 Wi-2)的概率。这种联合概率的计算是概率论的知识。

以下是根据nltk库构建语言模型的代码。

#导入nltk
from nltk.corpus import reuters
#读取语料库
categories = reuters.categories()
corpus = reuters.sents(categories=categories)

#构建语言模型 ,使用bigram
term_count = {}
bigram_count = {}
for doc in corpus:
    doc=['<s>']+doc
    for i in range(0,len(doc)-1):
        # [i,i+1]
        term = doc[i]
        bigram = doc[i:i+2]

        if term in term_count:
            term_count[term]+=1
        else:
            term_count[term]=1
        bigram = ' '.join(bigram)
        if bigram in bigram_count:
            bigram_count[bigram]+=1
        else:
            bigram_count[bigram]=1
print(term_count)
print(bigram_count )

  3.平滑操作

    目前我学习的平滑操作包括 add-one smoothing ; add-n smoothing ;good turning smoothing 。简单介绍一下 add-one smoothing 。 当我们训练语言模型的的时候,有些单词没有出现在训练的数据中,所以会有单词的概率为 0 ,这样就会导致部分的计算结果为0 ,会影响最后的效果。所以,为了效果更好一点,需要在计算概率的时候,分子加1 ,分母加上所有单词出现频率。P = P(Wi-1|Wi)+1 / P(Wi)+V 。这样最终结果的效果会好一点。

 

二、所有的代码如下

用到的数据集为

 

 

放到百度网盘

链接:https://pan.baidu.com/s/1pZbTOx9C8Y1xqV9rUWF4Eg
提取码:mir6

import numpy as np
#读入词典库
vocab=set([line.rstrip()  for line in open("data/vocab.txt")])
# print(vocab)

#生成所有首选集合
def generate_candidates(word):
    """
    :param word: 给定的输入单词
    :return: 返回所有候选集合
    """
    ##生成编辑距离为 1 的单词  1)insert  2)delete 3)replace

    #假设添加的字母如下
    letters = 'abcdefghijklmnopqrstuvwxyz'
    #将单词进行拆分,明确可以插入字母的位置
    splits =  [(word[:i],word[i:]) for i in range(len(word)+1)]
    #插入操作
    inserts = [L+c+R for L,R in splits   for c in letters]
    #删除操作  删除右边部分的第一个字母
    deletes = [L+R[1:] for L,R in splits if R]
    #替换操作  将右边的第一个字符替换
    replaces = [L+c+R[1:] for L,R in splits if R for c in letters]
    #生成的所有结果
    candidates = set(inserts+deletes+replaces)
    #将不再词库的单词去掉
    result = [word for word in candidates if word in vocab]
    return result

#导入nltk
from nltk.corpus import reuters
#读取语料库
categories = reuters.categories()
corpus = reuters.sents(categories=categories)

#构建语言模型 ,使用bigram
term_count = {}
bigram_count = {}
for doc in corpus:
    doc=['<s>']+doc
    for i in range(0,len(doc)-1):
        # [i,i+1]
        term = doc[i]
        bigram = doc[i:i+2]

        if term in term_count:
            term_count[term]+=1
        else:
            term_count[term]=1
        bigram = ' '.join(bigram)
        if bigram in bigram_count:
            bigram_count[bigram]+=1
        else:
            bigram_count[bigram]=1
print(term_count)

#用户犯错的概率  channel probability
channel_pro = {}
#读入写错的概率
for line in open('data/spell-errors.txt'):
    items = line.split(":")
    correct = items[0].strip()
    mistacks = [errorword.strip() for errorword in items[1].strip().split(",")]
    channel_pro[correct] = {}
    for mis in mistacks:
        channel_pro[correct][mis]=1.0/len(mistacks)

V = len(term_count.keys())

file = open("data/testdata.txt",'r')
for line in file:
    items = line.rstrip().split("\t")
    sentence = items[2].split()
    for word in sentence:
        if word not in vocab:
            #找到不再词典里的单词    s生成候选集合 然后   计算分数  score = P(correct)*P(mis|correct)= log(correct)+log(mis|correct)
            candidates = generate_candidates(word)
            probs = []
            for candi in candidates:
                prob = 0
                if candi in channel_pro and word in channel_pro[candi]:
                    prob += np.log(channel_pro[candi][word])
                else:
                    prob += np.log(0.00001)
                #计算语言模型概率  I like apply  computer   计算apply的语言模型概率
                idx = items[2].index(word)+1
                if items[2][idx-1] in bigram_count and candi in bigram_count[items[2][idx-1]]:
                    #  P = P(apply|like)+1 / P(like) + V
                    prob += np.log(
                        (bigram_count[items[2][idx-1]][candi] +1.0) /
                        (term_count[items[2][idx-1]]+ V)
                    )
                else:
                    prob += np.log(1.0/V)  #平滑操作
                probs.append(prob)
            max_idx = probs.index(max(probs))
            print(word , candidates[max_idx])

 

 

 

  

posted @ 2020-08-08 10:40  星际毁灭  阅读(391)  评论(0编辑  收藏  举报