Skip to content

baojunshan/nlp-fluency

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nlp-fluency

说明

  • 评估自然语言的流畅度的方法集
  • 包括ngrams, gpt, masked bert几种不同计算流畅度的方法;kenlm的方法可以参考苏神的博客
  • 欢迎star,issue以及PR
方法 介绍 模型 案例
ngrams 利用ngram计算得到下一个词的概率【单向滑窗】 百度网盘:no8i (基于thucnew摘要数据集训练); 也可以通过train_ngramslm.py用其他语料训练 案例
gpt 利用中文gpt计算得到下一个词的概率【单向】 百度网盘:qmzg ; 也可以访问 链接 获取其他gpt预训练的中文模型,或者自己训练 案例
bert 把句子中的词mask住,然后预测得到mask词的分布,进而得到该词的概率【双向】 百度网盘:ma3b ; 也可以访问链接 获取其他bert预训练的中文模型,或者自己训练 案例
albert 同bert,只是模型小了 百度网盘:q6pb ; 也可以访问链接 获取其他albert预训练的中文模型,或者自己训练 案例

用法

需要安装 torch, transformers,请自行安装即可。 使用案例可见example.py

函数:

  • score: 负值,越大越好
  • perplexity:正值,越小越好

输入:

  • 中文
  • 可以是一句话,也可以是一段话
  • ngram方法需要输入jieba切词的句子,详见下面的案例

测试语料

sentences = [
    "中国人的性情是总喜欢调和折中的,譬如你说,这屋子太暗,须在这里开一个窗,大家一定不允许的。但如果你主张拆掉屋顶他们就来调和,愿意开窗了。",
    "惟将终夜长开眼,报答平生未展眉",
    "我原以为,你身为汉朝老臣,来到阵前,面对两军将士,必有高论。没想到,竟说出如此粗鄙之语!",
    "人生当中成功只是一时的,失败却是主旋律,但是如何面对失败,却把人分成不同的样子,有的人会被失败击垮,有的人能够不断的爬起来继续向前,我想真正的成熟,应该不是追求完美,而是直面自己的缺憾,这才是生活的本质,罗曼罗兰说过,这个世界上只有一种真正的英雄主义,那就是认清生活的真相,并且仍然热爱它。难道向上攀爬的那条路不是比站在顶峰更让人热血澎湃吗?",
    "我在树上游泳。",
    "我在游泳池游泳。",
    "我游泳在游泳池。",
    "尤是为了,更佳大的,念,念,李是彼,更伟大的多,你只会用这种方法解决问题吗!",
]

ngrams

训练模型详见train_ngramslm.py

由于本模型使用清华摘要数据集训练,缺乏古诗文的语料,导致非白话文的部分ppl偏高,其他都比较准确,在语义的表现也不错,且不受长短句影响。

import jieba
import time
from models import NgramsLanguageModel


start_time = time.time()

model = NgramsLanguageModel.from_pretrained("./thucnews_lm_model")

print(f"Loading ngrams model cost {time.time() - start_time:.3f} seconds.")

for s in sentences:
    ppl = model.perplexity(
        x=jieba.lcut(s),   # 经过切词的句子或段落
        verbose=False,     # 是否显示详细的probability,default=False
    )
    print(f"ppl: {ppl:.5f} # {s}")

print(model.perplexity(jieba.lcut(sentences[-4]), verbose=True))

# Loading ngrams model cost 26.640 seconds.
#
# ppl: 8572.17074 # 中国人的性情是总喜欢调和折中的,譬如你说,这屋子太暗,须在这里开一个窗,大家一定不允许的。但如果你主张拆掉屋顶他们就来调和,愿意开窗了。
# ppl: 660033.44283 # 惟将终夜长开眼,报答平生未展眉
# ppl: 121955.03294 # 我原以为,你身为汉朝老臣,来到阵前,面对两军将士,必有高论。没想到,竟说出如此粗鄙之语!
# ppl: 6831.79220 # 人生当中成功只是一时的,失败却是主旋律,但是如何面对失败,却把人分成不同的样子,有的人会被失败击垮,有的人能够不断的爬起来继续向前,我想真正的成熟,应该不是追求完美,而是直面自己的缺憾,这才是生活的本质,罗曼罗兰说过,这个世界上只有一种真正的英雄主义,那就是认清生活的真相,并且仍然热爱它。难道向上攀爬的那条路不是比站在顶峰更让人热血澎湃吗?
# ppl: 12816.52860 # 我在树上游泳。
# ppl: 7122.96754 # 我在游泳池游泳。
# ppl: 61286.99997 # 我游泳在游泳池。
# ppl: 135742.90546 # 尤是为了,更佳大的,念,念,李是彼,更伟大的多,你只会用这种方法解决问题吗!
#
# ['我', '在'] | 0.00901780
# ['在', '树上'] | 0.00003544
# ['树上', '游泳'] | 0.00000059
# ['游泳', '。'] | 0.00019609
# l score: -13.64571794
# 12816.528602897242

bert

bert总体上比ngrams的方法好,albert虽然速度快,但是效果不理想

from models import MaskedBert, MaskedAlbert

model = MaskedAlbert.from_pretrained("/home/baojunshan/data/pretrained_models/albert_base_zh")

# model = MaskedBert.from_pretrained(
#     path="/home/baojunshan/data/pretrained_models/chinese_bert_wwm_ext_pytorch",
#     device="cpu",  # 使用cpu或者cuda:0,default=cpu
#     sentence_length=50,  # 长句做切句处理,段落会被切成最大不超过该变量的句子集,default=50
# )

for s in sentences:
    ppl = model.perplexity(
        x=" ".join(s),   # 每个字空格隔开或者输入一个list
        verbose=False,     # 是否显示详细的probability,default=False
        temperature=1.0,   # softmax的温度调节,default=1
        batch_size=100,    # 推理时的batch size,可根据cpu或gpu而定,default=100
    )
    print(f"ppl: {ppl:.5f} # {s}")

model.perplexity(sentences[-4], verbose=True)
# model.score(...) # 参数相同

# ppl: 4.20476 # 中国人的性情是总喜欢调和折中的,譬如你说,这屋子太暗,须在这里开一个窗,大家一定不允许的。但如果你主张拆掉屋顶他们就来调和,愿意开窗了。
# ppl: 71.91608 # 惟将终夜长开眼,报答平生未展眉
# ppl: 2.59046 # 我原以为,你身为汉朝老臣,来到阵前,面对两军将士,必有高论。没想到,竟说出如此粗鄙之语!
# ppl: 1.99123 # 人生当中成功只是一时的,失败却是主旋律,但是如何面对失败,却把人分成不同的样子,有的人会被失败击垮,有的人能够不断的爬起来继续向前,我想真正的成熟,应该不是追求完美,而是直面自己的缺憾,这才是生活的本质,罗曼罗兰说过,这个世界上只有一种真正的英雄主义,那就是认清生活的真相,并且仍然热爱它。难道向上攀爬的那条路不是比站在顶峰更让人热血澎湃吗?
# ppl: 10.55426 # 我在树上游泳。
# ppl: 4.38016 # 我在游泳池游泳。
# ppl: 6.56533 # 我游泳在游泳池。
# ppl: 22.52334 # 尤是为了,更佳大的,念,念,李是彼,更伟大的多,你只会用这种方法解决问题吗!
# 我 | 0.00039561
# 在 | 0.96003467
# 树 | 0.00347330
# 上 | 0.42612109
# 游 | 0.95590442
# 泳 | 0.17133135
# 。 | 0.74459237
# l score: -3.39975392

gpt

GPT效果不理想,抛开结果本身,用gpt做通顺度的方法,本身存在一定问题,在预测下一个字的概率时,总是把前面所有的词当做正确的来估计,这会对结果造成偏差。

from models import GPT

model = GPT.from_pretrained(
    path="/home/baojunshan/data/pretrained_models/chinese_gpt2_pytorch",
    device="cpu",
    sentence_length=50
)

for s in sentences:
    ppl = model.perplexity(
        x=" ".join(s),   # 每个字空格隔开或者输入一个list
        verbose=False,     # 是否显示详细的probability,default=False
        temperature=1.0,   # softmax的温度调节,default=1
        batch_size=100,    # 推理时的batch size,可根据cpu或gpu而定,default=100
    )
    print(f"ppl: {ppl:.5f} # {s}")

model.perplexity(sentences[-4], verbose=True)

ppl: 901.41065 # 中国人的性情是总喜欢调和折中的,譬如你说,这屋子太暗,须在这里开一个窗,大家一定不允许的。但如果你主张拆掉屋顶他们就来调和,愿意开窗了。
ppl: 7773.85606 # 惟将终夜长开眼,报答平生未展眉
ppl: 949.33750 # 我原以为,你身为汉朝老臣,来到阵前,面对两军将士,必有高论。没想到,竟说出如此粗鄙之语!
ppl: 906.79251 # 人生当中成功只是一时的,失败却是主旋律,但是如何面对失败,却把人分成不同的样子,有的人会被失败击垮,有的人能够不断的爬起来继续向前,我想真正的成熟,应该不是追求完美,而是直面自己的缺憾,这才是生活的本质,罗曼罗兰说过,这个世界上只有一种真正的英雄主义,那就是认清生活的真相,并且仍然热爱它。难道向上攀 爬的那条路不是比站在顶峰更让人热血澎湃吗?
ppl: 798.38110 # 我在树上游泳。
ppl: 729.68857 # 我在游泳池游泳。
ppl: 469.11313 # 我游泳在游泳池。
ppl: 927.94576 # 尤是为了,更佳大的,念,念,李是彼,更伟大的多,你只会用这种方法解决问题吗!
 | 0.00924169
 | 0.00345525
 | 0.00000974
 | 0.22259754
 | 0.00021145
 | 0.00004592| 0.00719284
l score: -9.64093376

计划

  • 实现ngrams,gpt,bert mask方法
  • 利用gan的判别器
  • 目前bert和gpt方法实现比较粗糙,速度较慢,之后会加速
  • 虽然bert和gpt的模型可以自己训练完再载入就好,但之后repo也会提供一个train的方法
  • 目前流畅度检测的方法都比较旧了,之后会尝试增加最新的一些方法(又要读论文了

引用

@misc{nlp-fluency,
  author = {Junshan Bao},
  title = {nlp-fluency},
  year = {2021},
  publisher = {GitHub},
  journal = {GitHub repository},
  howpublished = {\url{https://github.com/baojunshan/nlp-fluency}},
}