• 句子通顺度预测和严重不通顺的识别


    一 背景:句子不通顺在应用场景中经常出现,但是可以参考的东西并不多,你是我最好的朋友  对比。你是我好的朋友,后一句明显不通顺,大量的数据想把这些低质的不通顺的句子识别出来,如果不借助工具和算法的力量,我们需要耗费大量的时间和精力也无法解决好这些比较常用的问题。

    二 句子通顺度的识别思路:

               (a)基于规则来解决这个问题:构建词表的方式,但是发现很难建立完善的语言规则也缺乏相关的语言学知识,实现这么完整的一套规则也不简单,其中发现实词是能够单独充当句子成分的词,有名词、动词、形容词、数词、量词、副词、代词、拟声词;虚词是不能单独充当句子成分的词,有介词、连词、  助词、语气词。

               (b)N-Gram:基于词来做参数量巨大,需要非常完善且高质量的语料库,而词的词性种类数目很小,基于词性来做就不会有基于词的困扰,而且基于词性来做直觉上更能体现搭配出现的关系。

               (c)深度学习学习句子通顺与否的特征,通过神经网络模型进行训练预测。

    三 算法实现:

    (1)n_gram调研:

    问题探索:经过对一些错误的句子分析之后,发现某些类型的词不应该拼接在一起,比如动词接动词(e.g.我打开听见),这种情况很少会出现在我们的生活中。

                       n-gram参考的博客:https://blog.csdn.net/weixin_33895604/article/details/93060891?utm_medium=distribute.pc_relevant_download.none-task-blog-searchFromBaidu-1.nonecase

                       n-gram的结果并没有想象的那么好,在样本正负比例2:1的情况下表现效果很差,结果极度偏向不通顺样本,设置阈值麻烦。

                       n-gram预测空缺词的方法:https://github.com/Jed-Z/ngram-text-prediction      https://www.jeddd.com/article/python-ngram-language-prediction.html    

                       n-gram的优化:样本正负样本比例需要调节,阈值划分需要其他方面的思考。

                       n-gram处理不通顺度的流程:以3-gram为例子,参考指标衡量方式:https://blog.csdn.net/dianyanxia/article/details/107592000    https://blog.csdn.net/hxxjxw/article/details/107722646    

                                   (1)建立n-gram词表,词表1分别为3个词滑动出现并且统计出现频次,词表2分别为2个词的滑动出现并且统计出现频次。

                                   (2)通过ppl指标进行test数据的预测观察结果

                                   (3)通过句子分词,句子分词后的词性分别计算句子出现的概率

                                   (4)句子越长ppl就会越高,句子越长,乘积后的概率越小,ppl反而越大    

                      n_gram算法实验结果:(1)1_gram和2_gram对比中,句子出现的联合概率的整体受到句子长度的影响非常大,句子越长ppl越大。

                                                     (2)2_gram中句子是否通顺:

                                                                              (a)对比一个句子中的2_gram的滑动窗口下,一个句子产生所有的2_gram的集合,用该集合中最差的出现概率代替整个句子的出现概率,最后求ppl.

                                                                              (b)对比一个句子中的2_gram的滑动窗口下,一个句子产生所有的2_gram的集合,用该集合中平均的出现概率代替整个句子的出现概率,最后求ppl.

                                                      (3)平滑处理:计算ppl过程中需要进行平滑处理,避免词表中不存在词频的数据,其概率就会为0.

                                                      (4)数据词表:数据量越大效果会越好,80w和400w的标题构建词表,产生的词典中词频有很大的差距,所以数据量越大越好。

    其实现如下:

    # !/usr/bin/python
    # -*- coding: UTF-8 -*-

    from __future__ import division
    import numpy as np
    from io import open
    import time
    import jieba
    import jieba.posseg as pseg
    from collections import Counter

    def text_filter(text):
    """
    文本过滤器:过滤掉文本数据中的标点符号和其他特殊字符
    :param text: 待过滤的文本
    :return: 过滤后的文本
    """
    result = str()
    for t in text:
    if t.isalnum():
    if t.isalpha():
    t = t.lower()
    result += t
    return result

    def slide_word(tf, l=2):
    """
    滑动取词器
    Input: text='abcd',l=2
    Output: ['ab','bc','cd']
    :param text: 过滤后的文本 (只包含小写数字/字母)
    :param l: 滑动窗口长度,默认为 5
    :return:
    """
    #tf = text_filter(text)
    result = list()
    if len(tf) <= l:
    result.append(tf)
    return result
    for i in range(len(tf)):
    word = tf[i:i + l]
    if len(word) < l:
    break
    result.append(word)
    return result

    def perplexity_n(sentence, bi_gram_dict, l):
    """
    function:
    计算句子的ppl平均出现概率
    params:
    sentence:待计算的句子
    bi_gram_dict:2个词组的字典
    l:2个词组的总数量
    """
    V = l
    l_p = [] # 概率初始值
    k = 1 # ngram 的平滑值,平滑方法:Add-k Smoothing (k<1)
    ll = slide_word(sentence)
    for i in ll:
    p = (bi_gram_dict.get(i, 0) + k) / (k + V)
    l_p.append(p)
    print(l_p)
    return 1 / np.min(l_p)

    def data_seg_word(path):
    """
    function:
    数据滑动拆分构建词典
    params:
    path:训练集
    """
    ngrams_list = [] # n元组(分子)
    l = 0
    count = 0
    with open(path, 'r', encoding='utf-8') as trainf:
    for line in trainf:
    if len(line.strip()) != 0:
    items = line.strip().split(' ')
    count += 1
    if len(items) == 2 and count:
    # print(items)
    ngrams = slide_word(items[1])
    l += len(ngrams)
    ngrams_list += ngrams

    # 返回字典
    ngrams_counter = Counter(ngrams_list)
    return ngrams_counter, l
    if __name__ == "__main__":
    #构建2元词典的过程
    ngrams_counter, l = data_seg_word()
    #计算句子的出现概率
    pro = perplexity_n(sentense, ngrams_counter, l)

    (2)cnn模型进行通顺度的分类:

                        (1)句子不通顺的样本部分来源:https://github.com/tracy-talent/curriculum/tree/master/NLP/smooth/smooth/input       

                        (2)样本增强的方式:抽取tp问答和自有内容的问答的标题,通过python脚本进行不同程度的截断,产生不通顺的样本

                        (3)cnn模型训练结果与其他模型对比结果:通过paddle 1.15版本实现在  https://aistudio.baidu.com/aistudio/projectdetail/71554?channelType=0&channel=0

                        (4)cnn模型总结发现:句子结尾带有呢,吗这种语气助词以及?,这样的标志更容易判断是通顺的。

                        (5)阈值的划分:通过不同的阈值划分出不通顺的部分,后期进行修改和补充。目前阈值的划分是0.5,可以结合具体情况进行阈值划分,0.4等等的划分。

     (3) 规则过滤: 

              a  句子去重长度大于2通常不通顺;b 特殊字的开头和结尾的单独出现;c 相同的词性不能共现 

              

    其代码实现如下:

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-

    import requests
    import re
    import json
    import jieba
    import jieba.posseg as pseg


    fuhao_word_set = set()
    def init_fuhao_word_dict(fuhao_word_path):
    """
    function:加载标点符号列表
    params: fuhao_word_path 标点符号存放列表
    """
    global fuhao_word_dict
    input_file = open(fuhao_word_path, "r")
    lines = input_file.readlines()
    input_file.close()
    for line in lines:
    fuhao_word_set.add(line.strip().decode("utf8"))
    white_fuhao_set_1 = set("""'"()[]{}‘’“”《》()【】{}"""[:])
    white_fuhao_set_2 = set("""'"()[]{}‘’“”《》()【】{}?,?,"""[:])
    white_fuhao_set_3 = set("""'"()[]{}‘’“”《》()【】{}??…"""[:])
    white_fuhao_set_3.add("...")
    white_fuhao_set_3.add("……")
    stop_word_set = set(["你好", "谢谢", "多谢"])

    def get_word_list_jieba(content):
    """
    funtion:对内容进行分词
    params:content 切词的内容
    """
    seg = jieba.cut(content, cut_all=False)
    return list(seg)

    def get_dis_fuhao_word_list(word_list):
    """
    function:去掉句子中冗余的标点符号
    params:word_list 分词结果
    """
    str_len = len(word_list)
    dis_word_list = []
    dis_word_num = 0
    fuhao_num = 0
    if str_len > 1:
    pro_idx = -1
    # pro_word = word_list[0]
    for i in range(0, str_len):
    if word_list[i] in fuhao_word_set:
    fuhao_num += 1
    if pro_idx == -1:
    pro_idx = i
    dis_word_list.append(word_list[i])
    else:
    if word_list[pro_idx] in fuhao_word_set and word_list[i] in fuhao_word_set:
    dis_word_num += 1
    else:
    dis_word_list.append(word_list[i])
    pro_idx = i
    else:
    pro_idx = -1
    dis_word_list.append(word_list[i])
    return dis_word_list, dis_word_num, fuhao_num

    kuohao_map = {")": "(", ")": "("}

    def format_fuhao_word_list(word_list):
    """
    function:返回标准化后的句子结果
    params:word_list 分词结果
    """
    str_len = len(word_list)
    str_idx = 0
    res_word_list = []
    word_kuohao_dict = dict()
    if str_len > 1:
    for i in range(0, str_len):
    if word_list[i] in fuhao_word_set:
    if str_idx == 0:
    if word_list[i] in white_fuhao_set_1:
    res_word_list.append(word_list[i])
    str_idx += 1
    else:
    pass
    else:
    if i == str_len - 1:
    if word_list[i] in white_fuhao_set_3:
    res_word_list.append(word_list[i])
    str_idx += 1
    else:
    if word_list[i] in white_fuhao_set_2:
    res_word_list.append(word_list[i])
    str_idx += 1
    else:
    res_word_list.append(" ")
    str_idx += 1
    else:
    res_word_list.append(word_list[i])
    str_idx += 1
    if len(res_word_list) > 0:
    for i in range(0, len(res_word_list)):
    if res_word_list[i] in ["(", "("]:
    word_kuohao_dict[res_word_list[i]] = i
    if res_word_list[i] in [")", ")"]:
    kuohao_tmp = kuohao_map.get(res_word_list[i])
    if kuohao_tmp in word_kuohao_dict:
    word_kuohao_dict.pop(kuohao_tmp)
    else:
    res_word_list[i] = " "
    if len(word_kuohao_dict) > 0:
    for j in word_kuohao_dict.values():
    res_word_list[j] = " "
    return res_word_list
    return word_list

    def merge(sentence, max_ngram_length=4):
    """
    function:去掉句子重复词
    params: max_ngram_length 控制滑动窗口
    """
    final_merge_sent = sentence
    max_ngram_length = min(max_ngram_length, len(sentence))
    for i in range(max_ngram_length, 0, -1):
    start = 0
    end = len(final_merge_sent) - i + 1
    ngrams = []
    while start < end:
    ngrams.append(final_merge_sent[start: start + i])
    start += 1
    result = []
    for cur_word in ngrams:
    result.append(cur_word)
    if len(result) > i:
    pre_word = result[len(result) - i - 1]
    if pre_word == cur_word:
    for k in range(i):
    result.pop()
    cur_merge_sent = ""
    for word in result:
    if not cur_merge_sent:
    cur_merge_sent += word
    else:
    cur_merge_sent += word[-1]
    final_merge_sent = cur_merge_sent
    if (len(final_merge_sent) - len(sentence)) > 2:
    return 1
    return 0

    def regular_ze(title):
    """
    title:输入的标题
    """
    r = ['这次', '这么', '这儿', '这个', '这样', '这些', '这么样', '这程子',
    '这阵儿', '这会儿', '这么点儿', '这么些', '这的', '这两天', '这其间',
    '这麽', '这夜', '这末', '这些儿', '这般个', '这荅', '这伙', '这搭里',
    '这边', '这会子', '这陀儿', '这么点', '这们', '这么着', '这早晚', '这么说',
    '这下子', '这埚里', '这等样', '这说', '这会', '这埚儿']
    for i in r:
    if title.endswith(i):
    return 1
    else:
    continue
    return 0

    def regular_zen(title):
    """
    title:输入的标题
    """
    r = ['怎样', '怎么', '怎的', '怎奈', '怎地', '怎着', '怎样着', '怎许'
    '怎麽着', '怎见得', '怎说', '怎当得', '怎麽', '怎生', '怎']
    for i in r:
    if title.endswith(i):
    return 1
    else:
    continue
    return 0

    def regular_h(title):
    """
    title:输入的标题
    """
    r = ['还', '还有']
    for i in r:
    if title.endswith(i):
    return 1
    else:
    continue
    return 0

    def identify_common(ll):
    """
    function:判断是否是完全相同的元素
    params:ll数据分词词性的列表
    """
    l = len(ll)
    flag = True
    for i in range(l):
    if i == (l - 1):
    break
    if ll[i] == ll[i + 1]:
    continue
    else:
    flag = False

    def regular(title):
    """
    title:输入的标题
    """
    if title.startswith('的'):
    return 1
    elif title.endswith('是'):
    return 1
    elif title.endswith('是不是'):
    return 0
    elif title.startswith('多少'):
    return 1
    elif title.endswith('可不可以'):
    return 0
    elif title.endswith('可以'):
    return 1
    elif title.endswith('在'):
    return 1
    elif '的佳' in title:
    return 1
    elif '问.' in title:
    return 1
    elif '问。' in title:
    return 1
    elif '问...' in title:
    return 1
    elif regular_ze(title) == 1:
    return 1
    elif regular_h(title) == 1:
    return 1
    elif merge(title, max_ngram_length=4) == 1:
    return 1
    else:
    pseg_cut = pseg.cut(title)
    count = 0
    r_flag = []
    for word, flag in pseg_cut:
    print(word, flag)
    r_flag.append(flag)
    print(r_flag)
    if identify_common(r_flag):
    return 1
    else:
    return 0

    if __name__ == '__main__': init_fuhao_word_dict(
    "") word_list = get_word_list_jieba(
    '') dis_word_list
    , dis_word_num, fuhao_num = get_dis_fuhao_word_list(word_list) res_word_list = format_fuhao_word_list(dis_word_list) res_word =

    ''.join(res_word_list) label = regular(res_word)


    (4)总结:

    
    

                           (1)bilstm模型可以用于句子通顺度的打分,使用之前最好对句子去掉末尾符号,在进行句子通顺度预测,阈值可以结合具体的需求在做调整,效果会提升很多。

    
    

                           (2)cnn模型准确率高于bilstm,但是对句子的顺序结构理解差很多,测试句子通顺度排序效果不是特别符合业务发展。

    
    

                           (3)规则和模型的巧妙结合,在具体的业务场景中可以思考的点。

    最后,欢迎大家来聊欧,一起探讨技术,代码可以参考,重要参考思想欧,注重实践,追求卓越。

  • 相关阅读:
    SQL SERVER 2005生成带数据的脚本文件 [work around]
    VB.NET窗体关闭事件
    Code::Blocks The open source, cross platform, free C++ IDE.
    VB Twips And Pixels 缇和像素
    JQuery 鼠标点击其它地方隐藏层
    Asp.net 基于Form的权限方法备忘
    JQuery Highcharts图表控件多样式显示多组数据
    ASP.NET中动态获取数据使用Highcharts图表控件
    使用windows服务和.NET FileSystemWatcher对象来监控磁盘文件目录的改变
    【JQuery插件】Select选择框的华丽变身
  • 原文地址:https://www.cnblogs.com/limingqi/p/14695283.html
Copyright © 2020-2023  润新知