免费建站推广,交换链接是什么意思,凡科建站的建站后如何管理,建立一个网站需要什么技术文章目录 引言1. 什么是语言模型2. 语言模型的主要用途2.1 言模型-语音识别2.2 语言模型-手写识别2.3 语言模型-输入法 3. 语言模型的分类4. N-gram语言模型4.1 N-gram语言模型-平滑方法4.2 ngram代码4.3 语言模型的评价指标4.4 两类语言模型的对比 5. 神经网络语言模型6. 语言… 文章目录 引言1. 什么是语言模型2. 语言模型的主要用途2.1 言模型-语音识别2.2 语言模型-手写识别2.3 语言模型-输入法 3. 语言模型的分类4. N-gram语言模型4.1 N-gram语言模型-平滑方法4.2 ngram代码4.3 语言模型的评价指标4.4 两类语言模型的对比 5. 神经网络语言模型6. 语言模型的应用6.1 语言模型的应用-话者分离6.2 语言模型的应用-文本纠错6.3 语言模型的应用-数字归一化6.4 语言模型的应用-文本打标 7. 总结 引言
语言模型是一种用于预测文本序列中下一个词或字符的概率分布的模型。它可以捕获语言结构的某些方面如语法、句式和上下文信息。传统的语言模型通常使用N-gram方法或隐藏马尔可夫模型但这些模型往往不能捕捉到长距离依赖和复杂的语义信息。
1. 什么是语言模型
通俗来讲 语言模型评价一句话是否“合理”或“是人话” 数学上讲 P(今天天气不错) P(今错不天天气) 语言模型用于计算文本的成句概率
2. 语言模型的主要用途
2.1 言模型-语音识别
语音识别声音 - 文本声音本质是一种波 将波按时间段切分很多帧如25ms一段之后进行声学特征提取将每一帧转化成一个向量以声学特征提取后的向量为输入经过声学模型预测得到音素 音素与拼音类似但要考虑声调音素序列对应多条文本序列由语言模型挑选出成句概率最高的序列使用beam search或维特比的方式解码语音识别示意图
2.2 语言模型-手写识别 识别模型将图片中文字转化为候选汉字一般分定位和识别两步再有语言模型挑选出成句概率最高的序列
2.3 语言模型-输入法
输入即为拼音序列每个拼音自然的有多个候选汉字根据语言模型挑选高概率序列输入法是一个细节繁多的任务在语言模型这一基础算法上需要考虑常见的打字手误常见误读拼音缩略中英混杂输出符号用户习惯等能力手写输入法语音输入法同理
3. 语言模型的分类
基于统计语言模型
对于一份语料进行词频、词序、词共现的统计计算相关概率得到语言模型代表N-gram语言模型
基于神经网络的语言模型
根据设定好的网络结构使用语料进行模型训练代表LSTM语言模型BERT等
自回归auto regressive语言模型
在训练时由上文预测下文或反过来单向模型仅使用单侧序列信息代表N-gramELMO
自编码auto encoding语言模型
在训练时预测序列中任意位置的字符双向模型吸收上下文信息代表BERT
4. N-gram语言模型
N-gram语言模型是一种基础的语言模型用于预测下一个词或字符出现的概率基于前N-1个词或字符。该模型将文本看作是词或字符的有序序列并假设第n个词仅与前N-1个词相关。
比如在一个bigram2-gram模型中每个词的出现只依赖于它前面的一个词。例如我吃之后是苹果的概率可以表示为P(苹果|我吃)。
优点
计算简单只需要统计词频和条件词频。实现容易不需要复杂的算法。
缺点
稀疏性问题随着N的增加模型需要的存储空间急剧增加而大多数N-gram组合在实际数据中可能并不存在。上下文限制只能捕捉到N-1个词的上下文信息。
尽管有这些局限性N-gram模型由于其简单和高效仍然在很多应用场景中被广泛使用如拼写检查、语音识别和机器翻译等。 如何计算成句概率 用S代表句子w代表单个字或词S w1w2w3w4w5…wnP(S) P(w1,w2,w3,w4,w5…wn)成句概率 - 词W1~Wn按顺序出现的概率P(w1,w2,w3,…,wn) P(w1)P(w2|w1)P(w3|w1,w2)…P(wn|w1,…,wn-1) 以字为单位 P(今天天气不错) P(今)*P(天|今) *P(天|今天) *P(气|今天天) *P(不|今天天气) *P(错|今天天气不) 以词为单位 P(今天 天气 不错) P(今天)*P(天气|今天) *P(不错|今天 天气) 如何计算P(今天) P(今天) Count(今天) / Count_total 语料总词数P(天气|今天) Count(今天 天气) / Count(今天)P(不错|今天 天气) Count(今天 天气 不错) / Count(今天 天气)二元组今天 天气 2 gram三元组今天 天气 不错 3 gram 困难句子太多了 对任意一门语言N-gram数量都非常庞大无法穷举需要简化 马尔科夫假设 P(wn|w1,…,wn-1) ≈ P(wn|wn-3,wn-2,wn-1)假设第n个词出现的概率仅受其前面有限个词影响P(今天天气不错) P(今)*P(天|今) *P(天|今天) *P(气|天天) *P(不|天气) *P(错|气不) 马尔科夫假设的缺陷 影响第n个词的因素可能出现在前面很远的地方 long distance dependency 例我读过关于马尔科夫的生平的书 我看过关于马尔科夫的生平的电影 我听过关于马尔科夫的生平的故事 影响第n个词的因素可能出现在其后面影响第n个词的因素可能并不在文中 但是马尔科夫假设下依然可以得到非常有效的模型 语料 今天 天气 不错 P(今天) 3 / 12 1/4 明天 天气 不错 P(天气|今天) 2 / 3 今天 天气 不行 P(不错|今天 天气) 1 / 2 今天 是 晴天 P(不错|天气) 2/3 3 gram模型 P(今天 天气 不错) P(今天)*P(天气|今天) *P(不错|今天 天气) 1 / 12 2 gram模型 P(今天 天气 不错) P(今天)*P(天气|今天) *P(不错|天气) 1 / 9
语料 今天 天气 不错 P(今天) 3 / 12 1/4 明天 天气 不错 P(天气|今天) 2 / 3 今天 天气 不行 P(不错|今天 天气) 1 / 2 今天 是 晴天 P(不错|天气) 2/3
问题如何给出语料中没出现过的词或ngram概率 P(今天 天气 糟糕) P(今天)*P(天气|今天) *P(糟糕|天气)
平滑问题smoothing理论上说任意的词组合成的句子概率都不应当为零如何给没见过的词或ngram分配概率即为平滑问题也称折扣问题discounting
4.1 N-gram语言模型-平滑方法
回退backoff
当三元组a b c不存在时退而寻找b c二元组的概率 P(c | a b) P(c | b) * Bow(ab) Bow(ab)称为二元组a b的回退概率 回退概率有很多计算方式甚至可以设定为常数 回退可以迭代进行如序列 a b c d P(d | a b c) P(d | b c) * Bow(abc) P(d | bc) P(d | c) * Bow(bc) P(d | c ) P(d) * Bow©
P(word)不存在如何处理 加1平滑 add-one smooth 对于1gram概率P(word) Count(word)1/Count(total_word)V V为词表大小 对于高阶概率同样可以
将低频词替换为 预测中遇到的未见过的词也用代替 一语成谶 - 一语成 P(|一 语 成) 这是一种nlp处理未登录词OOV的常见方法
插值 受到回退平滑的启发在计算高阶ngram概率是同时考虑低阶的ngram概率值以插值给出最终结果
实践证明这种方式效果有提升 λ 可以在验证集上调参确定
4.2 ngram代码
import math
from collections import defaultdictclass NgramLanguageModel:def __init__(self, corpusNone, n3):self.n nself.sep _ # 用来分割两个词没有实际含义只要是字典里不存在的符号都可以self.sos sos #start of sentence句子开始的标识符self.eos eos #end of sentence句子结束的标识符self.unk_prob 1e-5 #给unk分配一个比较小的概率值避免集外词概率为0self.fix_backoff_prob 0.4 #使用固定的回退概率self.ngram_count_dict dict((x 1, defaultdict(int)) for x in range(n))self.ngram_count_prob_dict dict((x 1, defaultdict(int)) for x in range(n))self.ngram_count(corpus)self.calc_ngram_prob()#将文本切分成词或字或tokendef sentence_segment(self, sentence):return sentence.split()#return jieba.lcut(sentence)#统计ngram的数量def ngram_count(self, corpus):for sentence in corpus:word_lists self.sentence_segment(sentence)word_lists [self.sos] word_lists [self.eos] #前后补充开始符和结尾符for window_size in range(1, self.n 1): #按不同窗长扫描文本for index, word in enumerate(word_lists):#取到末尾时窗口长度会小于指定的gram跳过那几个if len(word_lists[index:index window_size]) ! window_size:continue#用分隔符连接word形成一个ngram用于存储ngram self.sep.join(word_lists[index:index window_size])self.ngram_count_dict[window_size][ngram] 1#计算总词数后续用于计算一阶ngram概率self.ngram_count_dict[0] sum(self.ngram_count_dict[1].values())return#计算ngram概率def calc_ngram_prob(self):for window_size in range(1, self.n 1):for ngram, count in self.ngram_count_dict[window_size].items():if window_size 1:ngram_splits ngram.split(self.sep) #ngram :a b cngram_prefix self.sep.join(ngram_splits[:-1]) #ngram_prefix :a bngram_prefix_count self.ngram_count_dict[window_size - 1][ngram_prefix] #Count(a,b)else:ngram_prefix_count self.ngram_count_dict[0] #count(total word)# word ngram_splits[-1]# self.ngram_count_prob_dict[word | ngram_prefix] count / ngram_prefix_countself.ngram_count_prob_dict[window_size][ngram] count / ngram_prefix_countreturn#获取ngram概率其中用到了回退平滑回退概率采取固定值def get_ngram_prob(self, ngram):n len(ngram.split(self.sep))if ngram in self.ngram_count_prob_dict[n]:#尝试直接取出概率return self.ngram_count_prob_dict[n][ngram]elif n 1:#一阶gram查找不到说明是集外词不做回退return self.unk_probelse:#高于一阶的可以回退ngram self.sep.join(ngram.split(self.sep)[1:])return self.fix_backoff_prob * self.get_ngram_prob(ngram)#回退法预测句子概率def calc_sentence_ppl(self, sentence):word_list self.sentence_segment(sentence)word_list [self.sos] word_list [self.eos]sentence_prob 0for index, word in enumerate(word_list):ngram self.sep.join(word_list[max(0, index - self.n 1):index 1])prob self.get_ngram_prob(ngram)# print(ngram, prob)sentence_prob math.log(prob)return 2 ** (sentence_prob * (-1 / len(word_list)))if __name__ __main__:corpus open(sample.txt, encodingutf8).readlines()lm NgramLanguageModel(corpus, 3)print(词总数:, lm.ngram_count_dict[0])print(lm.ngram_count_prob_dict)print(lm.calc_sentence_ppl(e f g b d))
4.3 语言模型的评价指标
困惑度 perplexity PPL值与成句概率成反比
一般使用合理的目标文本来计算PPL若PPL值低则说明成句概率高也就说明由此语言模型来判断该句子的合理性高这样是一个好的语言模型
另一种PPL用对数求和代替小数乘积 本质是相同的与成句概率呈反比思考PPL越小语言模型效果越好这一结论是否正确成句概率是个相对值
4.4 两类语言模型的对比 5. 神经网络语言模型
Bengio et al. 2003与ngram模型相似使用前n个词预测下一个词输出在字表上的概率分布得到了词向量这一副产品 随着相关研究的发展隐含层模型结构的复杂度不断提升DNN - CNN/RNN - LSTM/GRU - transformer Devlin et al. 2018 BERT 诞生主要特点不再使用预测下一个字的方式训练语言模型转而预测文本中被随机遮盖的某个字这种方式被称为MLM(masked language model)实际上这种方式被提出的时间非常早并非bert原创
代码
#coding:utf8import torch
import torch.nn as nn
import numpy as np
import math
import random
import os
import re
import matplotlib.pyplot as plt
基于pytorch的rnn语言模型
class LanguageModel(nn.Module):def __init__(self, input_dim, vocab):super(LanguageModel, self).__init__()self.embedding nn.Embedding(len(vocab) 1, input_dim)self.layer nn.RNN(input_dim, input_dim, num_layers2, batch_firstTrue)self.classify nn.Linear(input_dim, len(vocab) 1)self.dropout nn.Dropout(0.1)self.loss nn.functional.cross_entropy#当输入真实标签返回loss值无真实标签返回预测值def forward(self, x, yNone):x self.embedding(x) #output shape:(batch_size, sen_len, input_dim)x, _ self.layer(x) #output shape:(batch_size, sen_len, input_dim)x x[:, -1, :] #output shape:(batch_size, input_dim)x self.dropout(x)y_pred self.classify(x) #output shape:(batch_size, input_dim)if y is not None:return self.loss(y_pred, y)else:return torch.softmax(y_pred, dim-1)#读取语料获得字符集
#输出一份
def build_vocab_from_corpus(path):vocab set()with open(path, encodingutf8) as f:for index, char in enumerate(f.read()):vocab.add(char)vocab.add(UNK) #增加一个unk token用来处理未登录词writer open(vocab.txt, w, encodingutf8)for char in sorted(vocab):writer.write(char \n)return vocab#加载字表
def build_vocab(vocab_path):vocab {}with open(vocab_path, encodingutf8) as f:for index, line in enumerate(f):char line[:-1] #去掉结尾换行符vocab[char] index 1 #留出0位给pad tokenvocab[\n] 1return vocab#加载语料
def load_corpus(path):return open(path, encodingutf8).read()#随机生成一个样本
#从文本中截取随机窗口前n个字作为输入最后一个字作为输出
def build_sample(vocab, window_size, corpus):start random.randint(0, len(corpus) - 1 - window_size)end start window_sizewindow corpus[start:end]target corpus[end]# print(window, target)x [vocab.get(word, vocab[UNK]) for word in window] #将字转换成序号y vocab[target]return x, y#建立数据集
#sample_length 输入需要的样本数量。需要多少生成多少
#vocab 词表
#window_size 样本长度
#corpus 语料字符串
def build_dataset(sample_length, vocab, window_size, corpus):dataset_x []dataset_y []for i in range(sample_length):x, y build_sample(vocab, window_size, corpus)dataset_x.append(x)dataset_y.append(y)return torch.LongTensor(dataset_x), torch.LongTensor(dataset_y)#建立模型
def build_model(vocab, char_dim):model LanguageModel(char_dim, vocab)return model#计算文本ppl
def calc_perplexity(sentence, model, vocab, window_size):prob 0model.eval()with torch.no_grad():for i in range(1, len(sentence)):start max(0, i - window_size)window sentence[start:i]x [vocab.get(char, vocab[UNK]) for char in window]x torch.LongTensor([x])target sentence[i]target_index vocab.get(target, vocab[UNK])if torch.cuda.is_available():x x.cuda()pred_prob_distribute model(x)[0]target_prob pred_prob_distribute[target_index]prob math.log(target_prob, 10)return 2 ** (prob * ( -1 / len(sentence)))def train(corpus_path, save_weightTrue):epoch_num 10 #训练轮数batch_size 128 #每次训练样本个数train_sample 10000 #每轮训练总共训练的样本总数char_dim 128 #每个字的维度window_size 6 #样本文本长度vocab build_vocab(vocab.txt) #建立字表corpus load_corpus(corpus_path) #加载语料model build_model(vocab, char_dim) #建立模型if torch.cuda.is_available():model model.cuda()optim torch.optim.Adam(model.parameters(), lr0.001) #建立优化器for epoch in range(epoch_num):model.train()watch_loss []for batch in range(int(train_sample / batch_size)):x, y build_dataset(batch_size, vocab, window_size, corpus) #构建一组训练样本if torch.cuda.is_available():x, y x.cuda(), y.cuda()optim.zero_grad() #梯度归零loss model(x, y) #计算losswatch_loss.append(loss.item())loss.backward() #计算梯度optim.step() #更新权重print(\n第%d轮平均loss:%f % (epoch 1, np.mean(watch_loss)))if not save_weight:returnelse:base_name os.path.basename(corpus_path).replace(txt, pth)model_path os.path.join(model, base_name)torch.save(model.state_dict(), model_path)return#训练corpus文件夹下的所有语料根据文件名将训练后的模型放到莫得了文件夹
def train_all():for path in os.listdir(corpus):corpus_path os.path.join(corpus, path)train(corpus_path)if __name__ __main__:# build_vocab_from_corpus(corpus/all.txt)# train(corpus.txt, True)train_all()6. 语言模型的应用
6.1 语言模型的应用-话者分离
根据说话内容判断说话人常用于语言识别系统中判断录音对话中角色如客服对话录音判断坐席或客户根据不同腔调判断说话人翻译腔 这倒霉的房子里竟然有蟑螂你可以想象到吗这真是太可怕了港台腔你这个人怎么可以这个样子东北味 我不稀得说你那些事儿就拉倒了本质上为文本分类任务
对于每个类别使用类别语料训练语言模型对于一个新输入的文本用所有语言模型计算成句概率选取概率最高的类别为预测类别 相比一般文本分类模型如贝叶斯rf神经网络等优势每个类别模型互相独立样本不均衡或样本有错误对其他模型没有影响可以随时增加新的类别而不影响旧的类别的效果效果上讲一般不会有显著优势效率上讲一般会低于统一的分类模型
6.2 语言模型的应用-文本纠错
纠正文本中的错误如我今天去了天暗门看人民英雄记念碑我今天去了天安门看人民英雄纪念碑错误可能是同音字或形近字等
对每一个字建立一个混淆字集合计算整句话成句概率用混淆字集合中的词替代原句中的字重新计算概率选取得分最高的一个候选句子如果这个句子比原句的得分增长超过一定的阈值对下一个字重复步骤3-4直到句子末尾 这种方式有一些缺陷
无法解决多字少字问题阈值的设置非常难把握如果设置过大达不到纠错效果如果设置过小造成大量替换有可能改变句子的原意混淆字字表难以完备语言模型的领域性会影响修改结果连续的错字会大幅提升纠错难度
一般工业做法限定一个修改白名单只判断特定的字词是否要修改如限定只对所有发音为shang wu的片段计算是否修改为“商务”其余一概不做处理对于深度学习模型而言错别字是可以容忍的所以纠错本身的重要性在下降一般只针对展示类任务
6.3 语言模型的应用-数字归一化
将一个文本中的数字部分转化成对读者友好的样式常见于语言识别系统后展示文本时使用如秦皇岛港煤炭库存量在十一月初突然激增从四百五十四点九万吨增加到七百七十三点四万吨打破了一九九九年以来的记录秦皇岛港煤炭库存量在11月初突然激增从454.9万吨增加到773.4万吨打破了1999年以来的记录
找到数字形式符合规范的文本作为原始语料用正则表达式找到数字部分任意形式将数字部分依照其格式替换为阿拉伯数字汉字数字汉字连读等token使用带token文本训练语言模型对于新输入的文本同样使用正则表达式找到数字部分之后分别带入各个token使用语言模型计算概率选取概率最高的token最为最终数字格式按照规则转化后填入原文本 6.4 语言模型的应用-文本打标 本质为序列标注任务可以依照类似方式处理分词、文本加标点、文本段落切分等任务分词或切分段落只需要一种token打标点时可以用多种分隔token代表不同标点
7. 总结
语言模型的核心能力是计算成句概率依赖这一能力可以完成大量不同类型的NLP任务。基于统计的语言模型和基于神经网络的语言模型各有使用的场景大体上讲基于统计的模型优势在于解码速度而神经网络的模型通常效果更好。单纯通过PPL评价语言模型是有局限的通过下游任务效果进行整体评价更好。深入的理解一种算法有助于发现更多的应用方式。看似简单甚至错误的假设也能带来有意义的结果事实上这是简化问题的常见方式。