语言模型 python实现uni-gram和bi-gram 使用ppl困惑度对比模型效果 山东大学2019 NLP实验1

语言模型

  • 实验目的
  • 实验内容
  • 数据集
        • Example:
  • 实验环境
  • 实验思路
        • train
        • test
  • 代码实现
      • 一元语法
        • train
        • test
      • 二元语法
        • Train
        • test
  • 测试结果

实验目的

理解并实践语言模型

实验内容

  1. 用python 编程实践语言模型(uni-gram 和bi-gram) ,加入平滑技术。
  2. 计算测试集中句子的perplexity,对比uni-gram 和bi-gram 语言模型效果。

数据集

  • train_LM.txt
  • test_LM.txt

Example:

(每行数据是一段对话,句子间用__eou__分隔)
How much can I change 100 dollars for ? __eou__ What kind of currency do you
want ? __eou__ How much will it be in Chinese currency ? __eou__ That’s 680 Yuan .
__eou__
What kind of account do you prefer ? Checking account or savings account ?
__eou__ I would like to open a checking account . __eou__ Ok , please just fill out
this form and show us your ID card . __eou__ Here you are . __eou__

实验环境

  1. python 3.7.1
  2. nltk
    • 安装过程:
      1. pip install nltk
      2. 进入python命令行模式
      3. 执行以下命令
        import nltk
        nltk.download()
        
        下载安装所有文件

实验思路

train

  1. 对训练集进行预处理
    • 全部转化为小写
    • 去标点
    • 分句(使用bi-gram时,句尾句首词不会组成二元组,保证句子间独立性)
      *在bi-gram中,将词组成二元组,并且储存以wi为前缀的bi-gram的种类数量(例:词表中只存在go to和go back两种组合,则wgo的值为2)
  2. 训练
    • 以词作为基元,对每个分句进行分词,并统计每个基元在整个数据集中出现的频数
    • 加入未登录词,并将频数置为0(注意维护wi为前缀的bi-gram的种类数量)
    • 对数据进行平滑并利用频数计算词的频率,以此代替词的概率(此次实验采用加一平滑法)

test

  1. 计算测试集中每个句子的perplexity
    注:因概率值极小,为了减小误差,将先累乘再取对数的运算转化成先取对数再累加
  2. 对测试集句子的perplexity取平均值

代码实现

from nltk.tokenize import word_tokenize
from nltk import bigrams, FreqDist
from math import log
# 读取数据 小写 替换符号 分句 
dataset = open("train_LM.txt", 'r+', encoding='utf-8').read().lower()\
                .replace(',',' ').replace('.',' ').replace('?',' ').replace('!',' ')\
                .replace(':',' ').replace(';',' ').replace('<',' ').replace('>',' ').replace('/',' ')\
                .split("__eou__")
testset = open("test_LM.txt", 'r+', encoding='utf-8').read().lower()\
                .replace(',',' ').replace('.',' ').replace('?',' ').replace('!',' ')\
                .replace(':',' ').replace(';',' ').replace('<',' ').replace('>',' ').replace('/',' ')\
                .split("__eou__")

一元语法

train

unigramsDist = FreqDist()  # uni-gram词频数字典
for i in dataset:
    sWordFreq = FreqDist(word_tokenize(i))  # 每一句的词频数字典
    for j in sWordFreq:
        if j in unigramsDist:
            unigramsDist[j] += sWordFreq[j]
        else:
            unigramsDist[j] = sWordFreq[j]

test

# 加入未登录词
for i in testset:
    word = word_tokenize(i)  # 每一句的词频数字典
    for j in word:
        if j not in unigramsDist:
            unigramsDist[j] = 0
# 频数转化为频率  使用加一平滑法   unigramsDist.B()表示每个词都加一后的增加量
s = unigramsDist.N() + unigramsDist.B()
unigramsFreq = FreqDist()
for i in unigramsDist:
    unigramsFreq[i] = (unigramsDist[i] + 1) / s
ppt = []
for sentence in testset:
    logprob = 0
    wt = 0
    for word in word_tokenize(sentence):
        if word in unigramsFreq:
            logprob += log(unigramsFreq[word],2)
            wt += 1
    if wt > 0:
        ppt.append([sentence,pow(2,-(logprob/wt))])
temp = 0
for i in ppt:
    temp += i[1]
print("一元语法模型的困惑度:", temp/len(ppt))
一元语法模型的困惑度: 885.5469372058856

二元语法

Train

w2gram = {}     # 可能存在的以w为开头的2-gram的种类数量
bigramsDist = FreqDist()
for sentence in dataset:
    sWordFreq = FreqDist(bigrams(word_tokenize(sentence)))
    for j in sWordFreq:
        if j in bigramsDist:
            bigramsDist[j] += sWordFreq[j]
        else:
            bigramsDist[j] = sWordFreq[j]
            if j[0] in w2gram:
                w2gram[j[0]] += 1
            else:
                w2gram[j[0]] = 1

test

# 加入未登录词
# 由于将每种未出现的2-gram一一列举会生成vacab size * vocab size大小的bigramsDist,为节省时间和空间,此处只加入test中出现的2-gram
for sentence in testset:
    word = bigrams(word_tokenize(sentence))
    for j in word:
        if j not in bigramsDist:
            bigramsDist[j] = 0
            
            if j[0] in w2gram:
                w2gram[j[0]] += 1
            else:
                w2gram[j[0]] = 1
# 频数转化为频率  使用加一平滑法
history = {}    # 以w为历史的2-gram的数量和
for i in bigramsDist:
    if i[0] in history:
        history[i[0]] += bigramsDist[i]
    else:
        history[i[0]] = bigramsDist[i]
bigramsFreq = FreqDist()
for i in bigramsDist:
    bigramsFreq[i] = (bigramsDist[i] + 1) / (history[i[0]] + w2gram[i[0]])
ppt = []
for sentence in testset:
    logprob = 0
    wt = 0
    for word in bigrams(word_tokenize(sentence)):
        if word in bigramsFreq:
            logprob += log(bigramsFreq[word],2)
            wt += 1
    if wt > 0:
        ppt.append([sentence,pow(2,-(logprob/wt))])
temp = 0
for i in ppt:
    temp += i[1]
print("二元语法模型的困惑度:", temp/len(ppt))
二元语法模型的困惑度: 68.3362351830629

测试结果

一元语法模型的困惑度: 885.5469372058856
二元语法模型的困惑度: 68.3362351830629

你可能感兴趣的:(语言模型 python实现uni-gram和bi-gram 使用ppl困惑度对比模型效果 山东大学2019 NLP实验1)