原文地址:https://medium.com/analytics-vidhya/a-comprehensive-guide-to-build-your-own-language-model-in-python-5141b3917d6d

文章开头便引用了一句话:We tend to look through language and not realize how much power language has. 我们往往低估了语言的力量。文本摘要抽取,文本生成,文本自动填充这些任务都依赖于Language Model (LM),事实上,LM是大部分NLP任务的基石,本篇文章就带我们由浅入深,亲自实践LM去了解它的广度与深度。

什么是LM?

A language model learns to predict the probobality of a sequence of words. LM学习预测一个词序列出现的概率。如何理解一个词序列出现的概率?以一个机器翻译的例子来说明,在机器翻译任务中,通常是给你一个词序列,让你把它转换成另一个词序列,你需要估计转换后的词序列的概率分布,概率最高的那个词序列就是一个理想的翻译结果。比如以下两个词序列:the cat is small 和 small the is cat,很明显第一个词序列出现的概率更高。当模型能够学习到语言的规律(词序列的概率分布)时,就可以解决很多NLP任务。

LM的类别

有两种类型的LM:

1. Statistical Language Model 统计语言模型。 这类LM利用一些传统的统计模型如N-gram, HMM,或者一些特定的统计规则来学习词的概率分布。

2. Neural Language Model 神经语言模型。利用神经网络来建模的语言模型。

下面将分别介绍这两类语言模型。

构建一个N-gram Model

N-gram 就是一个长度为N的词序列,可以通过下面这个例子来理解N-gram:

“I love reading blogs about data science on Analytics Vidhya.”

这个句子中可以抽出1-gram有"I","love","reading"等等由一个词构成的单元,2-gram包括"I love","love reading","reading blogs"等由两个连续词构成的序列。一个N-gram Model可以预测自然语言中一个长度为N的词序列出现的概率。

为了预测长度为N的语言序列的概率,即构建该N-gram Model,需要使用链式法则来获取N个词出现的联合概率分布:

p(w1...ws) = p(w1) . p(w2 | w1) . p(w3 | w1 w2) . p(w4 | w1 w2 w3) ..... p(wn | w1...wn-1)

可以发现为了求得联合概率分布,需要计算若干条件概率。这些条件概率都是在给定history的条件下来预测下一个词,即下图所示:

可以发现当历史很长时,若考虑所有的历史词p(wn | w1...wn-1),会使得模型空间过大,模型过于复杂,同时也会有数据稀疏等问题。因此,通常使用马尔科夫假设,使得下一个要预测的词只与当前一个词有关,与其他历史词无关,从而使得条件概率简化为p(wn | wn-1)。

下面来实操构建一个N-gram Language Model。使用的Reuters数据集共包含10,788篇新闻文档,1,300,000个词。使用以下代码可以构建一个language model:

# code courtesy of https://nlpforhackers.io/language-models/from nltk.corpus import reuters
from nltk import bigrams, trigrams
from collections import Counter, defaultdict# Create a placeholder for model
model = defaultdict(lambda: defaultdict(lambda: 0))# Count frequency of co-occurance
for sentence in reuters.sents():for w1, w2, w3 in trigrams(sentence, pad_right=True, pad_left=True):model[(w1, w2)][w3] += 1# Let's transform the counts to probabilities
for w1_w2 in model:total_count = float(sum(model[w1_w2].values()))for w3 in model[w1_w2]:model[w1_w2][w3] /= total_count

代码的逻辑非常简明清晰,首先统计出所有文章中出现的三元组(w1, w2, w3),统计出每一对(w1, w2)在给定的条件下,w3的概率。基于这个模型,就可以不断得预测下一个即将出现的词。注意这里与之前说的马尔科夫假设不同,多考虑了一个历史词p(wn | w1...wn-1)=p(wn | wn-2wn-1)。

可以看到以"today the"为起始的两个词,采用不同的阈值生成的句子具有一定的可读性。这个N-gram模型与 Google、Alexa 和 Apple 等公司用于语言建模的基本原理相同。

N-gram Model的局限性

1. N-gram模型通常当N取值越大效果越好,但是随着N的增加计算的代价也会大幅增加,对内存资源的消耗是指数级增加的。

2. N-gram模型是离散化地建模语言模型,对于没有在语料中共同出现的词,联合概率为0。

构建一个Neural Language Model

深度学习在很多NLP任务上都取得了很好的表现,比如摘要生成,机器翻译。这些任务都是基于LM的,因而有很多研究开始致力于使用深度神经网络来建模LM。使用Neural LM可以建模字符级别(character level)或者词级别(word level)的LM,下面以字符级别的LM为例。

首先对问题进行描述,Neural LM要求通过给定的语料训练一个LM,随后在给定text的基础上生成后续的内容,使得其符合给定语料的风格同时满足语法要求。

下面尝试构建Neural LM,给定语料是独立宣言,引入需要的package,并读取独立宣言文本:

import numpy as np
import pandas as pd
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, GRU, Embedding
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpointfile_name = "Declaration_of_Independence.txt"
data_text = ""
for line in open(file_name):data_text += line.strip()

随后对文本进行简单的过滤,过滤方式就是1)将大写字母都转换成小写字母。2)将's结尾的单词去掉's 3)去掉标点 4)去掉长度小于3的单词:

import redef text_cleaner(text):# lower case textnewString = text.lower()newString = re.sub(r"'s\b","",newString)# remove punctuationsnewString = re.sub("[^a-zA-Z]", " ", newString) long_words=[]# remove short wordfor i in newString.split():if len(i)>=3:                  long_words.append(i)return (" ".join(long_words)).strip()# preprocess the text
data_new = text_cleaner(data_text)

预处理完成就可以构建训练用的输入序列了。问题定义为给定30个字符,预测下一个字符。因此输入序列就是分别以每一个位置为起始位置,取长度为31的子序列作为训练用的输入序列:

def create_seq(text):length = 30sequences = list()for i in range(length, len(text)):# select sequence of tokensseq = text[i-length:i+1]# storesequences.append(seq)print('Total Sequences: %d' % len(sequences))return sequences# create sequences
sequences = create_seq(data_new)

下面将训练数据的字符序列转换成id,由于共有26个字母,因此id字典长度也为26

# create a character mapping index
chars = sorted(list(set(data_new)))
mapping = dict((c, i) for i, c in enumerate(chars))def encode_seq(seq):sequences = list()for line in seq:# integer encode lineencoded_seq = [mapping[char] for char in line]# storesequences.append(encoded_seq)return sequences# encode the sequences
sequences = encode_seq(sequences)

此时的输入序列变成了若干长度为31的id list,id范围为0~25。下面划分训练集和验证集,验证集占比为10%:

from sklearn.model_selection import train_test_split# vocabulary size
vocab = len(mapping)
sequences = np.array(sequences)
# create X and y
X, y = sequences[:,:-1], sequences[:,-1]
# one hot encode y
y = to_categorical(y, num_classes=vocab)
# create train and validation sets
X_tr, X_val, y_tr, y_val = train_test_split(X, y, test_size=0.1, random_state=42)print('Train shape:', X_tr.shape, 'Val shape:', X_val.shape)

输出为Train shape: (6345, 30) Val shape: (706, 30)。下面来构建模型,模型由简单的三层组成。维度为50的embedding层,隐含层维度为150的GRU层以及以softmax为激活函数的全连接层:

# define model
model = Sequential()
model.add(Embedding(vocab, 50, input_length=30, trainable=True))
model.add(GRU(150, recurrent_dropout=0.1, dropout=0.1))
model.add(Dense(vocab, activation='softmax'))
print(model.summary())# compile the model
model.compile(loss='categorical_crossentropy', metrics=['acc'], optimizer='adam')
# fit the model
model.fit(X_tr, y_tr, epochs=100, verbose=2, validation_data=(X_val, y_val))

训练初始和稳定的loss和accuracy变化如下:

模型训练完成之后,可以根据给定的前几个单词,生成后面的单词:

# generate a sequence of characters with a language model
def generate_seq(model, mapping, seq_length, seed_text, n_chars):in_text = seed_text# generate a fixed number of charactersfor _ in range(n_chars):# encode the characters as integersencoded = [mapping[char] for char in in_text]# truncate sequences to a fixed lengthencoded = pad_sequences([encoded], maxlen=seq_length, truncating='pre')# predict characteryhat = model.predict_classes(encoded, verbose=0)# reverse map integer to characterout_char = ''for char, index in mapping.items():if index == yhat:out_char = charbreak# append to inputin_text += charreturn in_text

注意这里有一个问题在于将序列输入到模型之前要在序列最前面加入padding的字符,这个可能与训练不符,实际使用GRU生成的时候应该采用变长序列的方式,使用for循环逐个通过GRU。下面这个inference的例子可以验证训练得到的模型效果:

可以发现模型是非常敏感的,对于不同的介词of/for,或者是加了一个空格,生成的结果都是不一样的。另外,生成的句子并没有出现在训练语料中,说明模型的确是在训练过程中理解了英语的语法规则。

使用GPT-2做自然语言生成

2019年2月,OpenAI使用大规模语料训练了基于Transformer的语言模型名叫GPT-2。GPT-2是一个基于Transformer decoder的生成式语言模型,在互联网上40GB语料上训练得到,GPT-2论文见

  • OpenAI’s GPT-2: A Simple Guide to Build the World’s Most Advanced Text Generator in Python

下面将基于PyTorch-Transformers来使用GPT-2。PyTorch-Transformers包括许多SOTA的预训练模型,文章建议使用Google Colab来运行示例代码:

# Import required libraries
import torch
from pytorch_transformers import GPT2Tokenizer, GPT2LMHeadModel# Load pre-trained model tokenizer (vocabulary)
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')# Encode a text inputs
text = "What is the fastest car in the"
indexed_tokens = tokenizer.encode(text)# Convert indexed tokens in a PyTorch tensor
tokens_tensor = torch.tensor([indexed_tokens])# Load pre-trained model (weights)
model = GPT2LMHeadModel.from_pretrained('gpt2')# Set the model in evaluation mode to deactivate the DropOut modules
model.eval()# If you have a GPU, put everything on cuda
tokens_tensor = tokens_tensor.to('cuda')
model.to('cuda')# Predict all tokens
with torch.no_grad():outputs = model(tokens_tensor)predictions = outputs[0]# Get the predicted next sub-word
predicted_index = torch.argmax(predictions[0, -1, :]).item()
predicted_text = tokenizer.decode(indexed_tokens + [predicted_index])# Print the predicted word
print(predicted_text)

代码中使用预训练好的gpt-2模型来预测what is the fastest car in the __ 这个词,google上预测的答案是"world",模型预测结果为:

结果和Google给的query suggestion一致,说明gpt-2的效果很强。

之前使用gpt-2的方式是给定context预测下一个词,下面感受一下使用gpt-2根据给定文字生成一篇文章的能力。下面是给定的文字:

Two roads diverged in a yellow wood,
And sorry I could not travel both
And be one traveler, long I stood
And looked down one as far as I could
To where it bent in the undergrowth;

这段文字是诗歌“The Road Not Taken”的第一段,下面使用PyTorch-Transformers写好的脚本直接来生成后面的段落。直接在google colab上运行下面的命令:

!git clone https://github.com/huggingface/pytorch-transformers.git
!python pytorch-transformers/examples/pytorch/text-generation/run_generation.py \--model_type=gpt2 \--length=100 \--model_name_or_path=gpt2 \

注意这里与原文的命令有差别,代码仓库的结构有所变动。最终生成的结果如下:

Two roads diverged in a yellow wood, And sorry I could not travel both And be one traveler, long I stood And looked down one as far as I could To where it bent in the undergrowth; He was no man who could resist, Nor had any one in his own reach To laugh at his vanity; The only thing which could lull him, for he could not remember, The complete unspoken confession of the suffering of Mr. Ford's face, and his mournful agony. And glad I saw them yet. In a darkness beneath the fell moon I at last heard the sigh, Until the moon lifted from the earth's right bethought. Every man rushes before death, When death brings about

感觉比较通顺,意境与第一段也是贴合的。可见gpt-2模型容量之大(后面gpt-3更厉害)

结语

以上经历了关于LM的全面的了解,我们讨论了什么是LM,以及如何使用最新的NLP框架来使用它们。结果令人印象深刻!

我的总结

这篇笔记的确让人由浅入深地理解了什么是Language Model,并实打实地教会大家如何手动搭建一个statistical LM以及Neural LM,后续我会更新一些常见的LM的具体细节介绍。

语言模型(LM)介绍及实操相关推荐

  1. 重置已清账项目及两种不同重置方式(只重置、重置并冲销)介绍和实操-FBRA

    文章目录 一.重置已清账项目介绍 1.1 重置方式:只重置 1.2 重置方式:重置并冲销 二.业务实操 2.1 验证是否可以直接冲销-FB08 2.2 重置方式:只重置 2.3 重置方式:重置并冲销 ...

  2. 信而泰OLT使用介绍-网络测试仪实操

    一.OLT产品介绍 1.概述 PON作为FTTX网络发展的核心技术,局端设备OLT尤其重要. 本文档中主要介绍OLT的功能特性.业务配置 2.基本功能特性 2.1大容量和高集成度 ZXA10 C300 ...

  3. goaccess的安装、使用及介绍【实操完整版】

    goaccess的安装.使用及介绍 goaccess的安装.使用及介绍 一.安装goaccess 二.启动 1.第一种,用定时任务,定时生成html 2.第二种,改配置文件,让goaccess在后台运 ...

  4. 【iVX 开发 - 入门】UI 组件介绍及实操详解

    本文导读 写在前面 iVX 各 UI 组件及属性面板详解 1. 图片组件 2. 图片序列组件 3. 文本组件 4. 输入框组件 5. 富文本/富文本编辑器组件 6. 地图组件 7. 二维码组件 写在前 ...

  5. GIS基础测量、地形分析、位置分析、空间分析功能介绍与实操应用

    通知 入门级.进阶级一.二.三期.高阶级一期已完成,大家可进入公众号"图新地球"查看底部菜单:2022教程,获得软件直播课程的相关资料,包括直播讲解.直播PPT.直播的示例数据. ...

  6. 大数据框架介绍与实操

    文章目录 一. 大数据开源框架汇总简介 1.1 hadoop 1.2 hdfs 1.3 yarn 1.4 mapreduce 1.5 spark 1.6 hbase 1.7 zookeeper 1.8 ...

  7. win2012 磁盘阵列 介绍和实操

    2012存储的基本概念: 1.raid类型:raid0,raid1,raid4,raid10 2.磁盘类型:mbr模式(早期)(0-2g).GPT(大于2g) 一.raid1: 1.概述 又称为mir ...

  8. 【网络工程】6、防火墙介绍及配置实操

    接上篇<5.路由器介绍及配置实操> 之前我们讲解了网络设备路由器的介绍,以及完成了路由器的相关配置实操.本篇我们来讲解防火墙的基础知识以及相应的实操案例. 一.什么是防火墙? 防火墙是一个 ...

  9. 实操|风控规则的监控与自动化策略生成

    风险策略分析工作是风险管理的重要工作内容,其工作内容需要涉及风控领域中多个环节及细节内容,包含贷前策略调整.策略分析调优.贷中业务监控.贷后策略调整等模块内容. 监控这些风控策略,我们用得最多的就是各 ...

最新文章

  1. 【简洁易懂】为什么判断素数时只需要循环到该数的平方根
  2. 计算机硬件教学设计高中信息,重大版信息技术七上《计算机硬件系统》教学设计.doc...
  3. oracle比mysql查询快的原因_Oracle查询速度慢的原因总结
  4. 腾讯Q3财报看点:净利近10年来首次下滑 为硬科技持续“烧钱”
  5. OpenCV2.3.1 VS 安装
  6. 写博客是一种乐趣,一种需要培养的乐趣,Java程序员最大的悲哀是什么
  7. java properties null_正确使用Java Properties - Java综合 - Java - JavaEye...
  8. 计算机在线考试报名系统软件,全国计算机软考报名官网
  9. 自创RTSP 服务器 用多款客户端软件测试接入可以,唯独VLC接入不了
  10. 西游记不单单讲的是故事(2) ------ 摘自 吴闲云的《煮酒探西游》
  11. php在线拍照代码,PHP+Javascript实现在线拍照功能实例_php技巧
  12. 视觉三维重建核心算法讲解和代码实现(sfm构建稀疏地图和mvs构建稠密地图)...
  13. 重磅!首届倍增科学技术研究院高级研究员研讨会胜利召开
  14. H5 图片自动适应高度
  15. ICPC North America Qualifier 2017 B.Bumped! (分层图 + spfa)
  16. c语言运算符 amp 的意思,C语言运算符是什么意思
  17. 这些中国顶级黑客带来的价值远比负面影响多!
  18. XBox One 升级后显示黑屏
  19. 永远不要和沙雕一样的人去争论,争论最后你会发现你也是一个沙雕
  20. 阿里图标字体库的动态使用Android

热门文章

  1. git merge原理
  2. 解决Matlab deembedsparams函数报错
  3. Minecraft钻石矿生成定位器③(版本:1.16和1.17的Java版本)
  4. slf4j如何进行logback配置呢?
  5. 【图像分类】卷积神经网络之AlexNet网络模型实现花卉图像识别(附代码和数据集,PyTorch框架)
  6. sql 视图 排序 实例
  7. RPN网络(区域候选网络源码)
  8. [RHEL5企业级Linux服务攻略]--第4季 DNS服务全攻略
  9. 通过GPS获取位置信息
  10. Oracle添加字段