《动手学深度学习 Pytorch版》 8.3 语言模型和数据集

8.3.1 学习语言模型

依靠在 8.1 节中对序列模型的分析,可以在单词级别对文本数据进行词元化。基本概率规则如下:

P ( x 1 , x 2 , … , x T ) = ∏ t = 1 T P ( x t ∣ x 1 , … , x t − 1 ) P(x_1,x_2,\dots,x_T)=\prod^T_{t=1}P(x_t|x_1,\dots,x_{t-1}) P(x1,x2,,xT)=t=1TP(xtx1,,xt1)

例如,包含了四个单词的一个文本序列的概率是:

P ( d e e p , l e a r n i n g , i s , f u n ) = P ( d e e p ) P ( l e a r n i n g ∣ d e e p ) P ( i s ∣ d e e p , l e a r n i n g ) P ( f u n ∣ d e e p , l e a r n i n g , i s ) P(deep,learning,is,fun)=P(deep)P(learning|deep)P(is|deep,learning)P(fun|deep,learning,is) P(deep,learning,is,fun)=P(deep)P(learningdeep)P(isdeep,learning)P(fundeep,learning,is)

语言模型就是要计算单词的概率,以及给定前面几个单词后出现某个单词的条件概率。这些概率本质上就是语言模型的参数。

假设训练数据集是一个大型的文本语料库。训练数据集中词的概率可以根据给定词的相对词频来计算。对于频繁出现的单词可以统计单词“deep”在数据集中的出现次数,然后将其除以整个语料库中的单词总数。接下来尝试估计

P ^ ( l e a r n i n g ∣ d e e p ) = n ( d e e p , l e a r n i n g ) n ( d e e p ) \hat{P}(learning|deep)=\frac{n(deep,learning)}{n(deep)} P^(learningdeep)=n(deep)n(deep,learning)

其中 n ( x ) n(x) n(x) n ( x , x ′ ) n(x,x') n(x,x) 分别是单个单词和连续单词对的出现次数。

对于一些不常见的单词组合,要想找到足够的出现次数来获得准确的估计可能都不容易。如果数据集很小,或者单词非常罕见,那么这类单词出现一次的机会可能都找不到。这里一种常见的策略是执行某种形式的拉普拉斯平滑(Laplace smoothing),具体方法是在所有计数中添加一个小常量。用 n n n 表示训练集中的单词总数,用 m m m 表示唯一单词的数量。例如通过:

P ^ ( x ) = n ( x ) + ϵ 1 / m n + ϵ 1 P ^ ( x ′ ∣ x ) = n ( x , x ′ ) + ϵ 2 P ^ ( x ′ ) n ( x ) + ϵ 2 P ^ ( x " ∣ x , x ′ ) = n ( x , x ′ , x " ) + ϵ 3 P ^ ( x " ) n ( x , x ′ ) + ϵ 3 \begin{align} \hat{P}(x)&=\frac{n(x)+\epsilon_1/m}{n+\epsilon_1}\\ \hat{P}(x'|x)&=\frac{n(x,x')+\epsilon_2\hat{P}(x')}{n(x)+\epsilon_2}\\ \hat{P}(x"|x,x')&=\frac{n(x,x',x")+\epsilon_3\hat{P}(x")}{n(x,x')+\epsilon_3} \end{align} P^(x)P^(xx)P^(x"∣x,x)=n+ϵ1n(x)+ϵ1/m=n(x)+ϵ2n(x,x)+ϵ2P^(x)=n(x,x)+ϵ3n(x,x,x")+ϵ3P^(x")

其中 ϵ 1 \epsilon_1 ϵ1 ϵ 2 \epsilon_2 ϵ2 ϵ 3 \epsilon_3 ϵ3 是超参数。例如当 ϵ 1 = 0 \epsilon_1=0 ϵ1=0 时,不应用平滑;当 ϵ 1 \epsilon_1 ϵ1 接近无穷大时, P ^ ( x ) \hat{P}(x) P^(x) 基金均匀概率分布 1 / m 1/m 1/m

上述方案也存在问题,模型很容易变得无效,原因如下:

  • 需要存储所有的计数;

  • 完全忽略了单词的意思。例如,“猫”(cat)和“猫科动物”(feline)可能出现在相关的上下文中,但是想根据上下文调整这类模型其实是相当困难的。

  • 长单词序列大部分是没出现过的,因此一个模型如果只是简单地统计先前“看到”的单词序列频率,那么模型面对这种问题肯定是表现不佳的。

8.3.2 马尔可夫模型与 n 元语法

如果 P ( x t + 1 ∣ x t , … , x 1 ) = P ( x t + 1 ∣ x t ) P(x_{t+1}|x_t,\dots,x_1)=P(x_{t+1}|x_t) P(xt+1xt,,x1)=P(xt+1xt),则序列上的分布满足一阶马尔可夫性质。阶数越高则对应的依赖关系就越长。这种性质可以推导出许多可以应用于序列建模的近似公式:

P ( x 1 , x 2 , x 3 , x 4 ) = P ( x 1 ) P ( x 2 ) P ( x 3 ) P ( x 4 ) P ( x 1 , x 2 , x 3 , x 4 ) = P ( x 1 ) P ( x 2 ∣ x 1 ) P ( x 3 ∣ x 2 ) P ( x 4 ∣ x 3 ) P ( x 1 , x 2 , x 3 , x 4 ) = P ( x 1 ) P ( x 2 ∣ x 1 ) P ( x 3 ∣ x 1 , x 2 ) P ( x 4 ∣ x 2 , x 3 ) \begin{align} P(x_1,x_2,x_3,x_4)&=P(x_1)P(x_2)P(x_3)P(x_4)\\ P(x_1,x_2,x_3,x_4)&=P(x_1)P(x_2|x_1)P(x_3|x_2)P(x_4|x_3)\\ P(x_1,x_2,x_3,x_4)&=P(x_1)P(x_2|x_1)P(x_3|x_1,x_2)P(x_4|x_2,x_3) \end{align} P(x1,x2,x3,x4)P(x1,x2,x3,x4)P(x1,x2,x3,x4)=P(x1)P(x2)P(x3)P(x4)=P(x1)P(x2x1)P(x3x2)P(x4x3)=P(x1)P(x2x1)P(x3x1,x2)P(x4x2,x3)

通常,涉及一个、两个和三个变量的概率公式分别被称为一元语法(unigram)、二元语法(bigram)和三元语法(trigram)模型。

以下将对模型进行更好的设计。

8.3.3 自然语言统计

import random
import torch
from d2l import torch as d2l
tokens = d2l.tokenize(d2l.read_time_machine())
corpus = [token for line in tokens for token in line]  # 将文本行拼接到一起
vocab = d2l.Vocab(corpus)
vocab.token_freqs[:10]  # 打印前10个频率最高的单词
[('the', 2261),('i', 1267),('and', 1245),('of', 1155),('a', 816),('to', 695),('was', 552),('in', 541),('that', 443),('my', 440)]
freqs = [freq for token, freq in vocab.token_freqs]
d2l.plot(freqs, xlabel='token: x', ylabel='frequency: n(x)',xscale='log', yscale='log')


在这里插入图片描述

频率最高的词都是停用词(stop words),可以被过滤掉。但它们本身仍然是有意义的,我们仍然会在模型中使用它们。

此外,还有个明显的问题是词频衰减的速度相当地快。从词频图看到,词频衰减大致遵循双对数坐标图上的一条直线。这意味着单词的频率满足齐普夫定律(Zipf’s law),即第 i i i 个最常用单词的频率 n i n_i ni 为:

n i ∝ 1 i α n_i\propto\frac{1}{i^\alpha} niiα1

可以等价为

log ⁡ n i = − α log ⁡ i + c \log{n_i}=-\alpha\log{i}+c logni=αlogi+c

其中 α \alpha α 是刻画分布的指数, c c c 是常数。

所以,上面通过计数统计和平滑来建模单词是不可行的,因为这样建模的结果会大大高估尾部(也就是所谓的不常用单词)的频率。

下面尝试一下二元语法的频率是否与一元语法的频率表现出相同的行为方式。

bigram_tokens = [pair for pair in zip(corpus[:-1], corpus[1:])]  # 优雅 实在优雅
bigram_vocab = d2l.Vocab(bigram_tokens)
bigram_vocab.token_freqs[:10]
[(('of', 'the'), 309),(('in', 'the'), 169),(('i', 'had'), 130),(('i', 'was'), 112),(('and', 'the'), 109),(('the', 'time'), 102),(('it', 'was'), 99),(('to', 'the'), 85),(('as', 'i'), 78),(('of', 'a'), 73)]

可以看到二元语法大部分也是两个停用词组成的。下面的三元语法就好些。

trigram_tokens = [triple for triple in zip(corpus[:-2], corpus[1:-1], corpus[2:])]
trigram_vocab = d2l.Vocab(trigram_tokens)
trigram_vocab.token_freqs[:10]
[(('the', 'time', 'traveller'), 59),(('the', 'time', 'machine'), 30),(('the', 'medical', 'man'), 24),(('it', 'seemed', 'to'), 16),(('it', 'was', 'a'), 15),(('here', 'and', 'there'), 15),(('seemed', 'to', 'me'), 14),(('i', 'did', 'not'), 14),(('i', 'saw', 'the'), 13),(('i', 'began', 'to'), 13)]
bigram_freqs = [freq for token, freq in bigram_vocab.token_freqs]
trigram_freqs = [freq for token, freq in trigram_vocab.token_freqs]
d2l.plot([freqs, bigram_freqs, trigram_freqs], xlabel='token: x',ylabel='frequency: n(x)', xscale='log', yscale='log',legend=['unigram', 'bigram', 'trigram'])


在这里插入图片描述

从这张一元语法、二元语法和三元语法的直观对比图可以看到:

  • 除了一元语法词,单词序列似乎也遵循齐普夫定律,指数的大小受序列长度的影响。

  • 词表中 n 元组的数量并没有那么大,这说明语言中存在相当多的结构,这些结构给了我们应用模型的希望;

  • 很多 n 元组很少出现,这使得拉普拉斯平滑非常不适合语言建模。作为代替,我们将使用基于深度学习的模型。

8.3.4 读取长序列数据

长序列不能被模型一次性全部处理时,依然采用第一节的拆分序列方法。不同的是,步长不选择固定的而是从随机偏移量开始划分序列,以同时获得覆盖性(coverage)和随机性(randomness)。

随机采样

在随机采样中,每个样本都是在原始的长序列上任意捕获的子序列。 在迭代过程中,来自两个相邻的、随机的、小批量中的子序列不一定在原始序列上相邻。

def seq_data_iter_random(corpus, batch_size, num_steps):  #@save"""使用随机抽样生成一个小批量子序列"""corpus = corpus[random.randint(0, num_steps - 1):]  # 从头随机截一下,保证第一个序列的随机性num_subseqs = (len(corpus) - 1) // num_steps  # 计算序列数initial_indices = list(range(0, num_subseqs * num_steps, num_steps))  # 获取各序列起始下标random.shuffle(initial_indices)  # 进行打乱def data(pos):# 返回从pos位置开始的长度为num_steps的序列return corpus[pos: pos + num_steps]num_batches = num_subseqs // batch_size  # 计算组数for i in range(0, batch_size * num_batches, batch_size):# 在这里,initial_indices包含子序列的随机起始索引initial_indices_per_batch = initial_indices[i: i + batch_size]  # 截取当前组各序列的启示下标X = [data(j) for j in initial_indices_per_batch]  # 获取序列作为数据Y = [data(j + 1) for j in initial_indices_per_batch]  # 获取下一个序列作为标签yield torch.tensor(X), torch.tensor(Y)
my_seq = list(range(35))  # 生成一个从0到34的序列
for X, Y in seq_data_iter_random(my_seq, batch_size=2, num_steps=5):print('X: ', X, '\nY:', Y)
X:  tensor([[18, 19, 20, 21, 22],[13, 14, 15, 16, 17]]) 
Y: tensor([[19, 20, 21, 22, 23],[14, 15, 16, 17, 18]])
X:  tensor([[ 8,  9, 10, 11, 12],[ 3,  4,  5,  6,  7]]) 
Y: tensor([[ 9, 10, 11, 12, 13],[ 4,  5,  6,  7,  8]])
X:  tensor([[23, 24, 25, 26, 27],[28, 29, 30, 31, 32]]) 
Y: tensor([[24, 25, 26, 27, 28],[29, 30, 31, 32, 33]])

顺序分区

在小批量的迭代过程中保留了拆分的子序列的顺序,可以保证两个相邻的小批量中的子序列在原始序列上也是相邻的。

def seq_data_iter_sequential(corpus, batch_size, num_steps):  #@save"""使用顺序分区生成一个小批量子序列"""offset = random.randint(0, num_steps)  # 随机首序列的起始下标num_tokens = ((len(corpus) - offset - 1) // batch_size) * batch_size  # 计算总词源数Xs = torch.tensor(corpus[offset: offset + num_tokens])  # 获取词元起始下标Ys = torch.tensor(corpus[offset + 1: offset + 1 + num_tokens])  # 获取对应的下一个词元的起始下标Xs, Ys = Xs.reshape(batch_size, -1), Ys.reshape(batch_size, -1)  # 利用矩阵操作分组num_batches = Xs.shape[1] // num_steps  # 计算组数for i in range(0, num_steps * num_batches, num_steps):X = Xs[:, i: i + num_steps]  # 顺序获取各组作为数据Y = Ys[:, i: i + num_steps]  # 获取下一个序列作为标签yield X, Y
for X, Y in seq_data_iter_sequential(my_seq, batch_size=2, num_steps=5):print('X: ', X, '\nY:', Y)
X:  tensor([[ 5,  6,  7,  8,  9],[19, 20, 21, 22, 23]]) 
Y: tensor([[ 6,  7,  8,  9, 10],[20, 21, 22, 23, 24]])
X:  tensor([[10, 11, 12, 13, 14],[24, 25, 26, 27, 28]]) 
Y: tensor([[11, 12, 13, 14, 15],[25, 26, 27, 28, 29]])

将上述两个采样函数包装到一个类中,再定义一个返回数据迭代器和词表的 load 函数。

class SeqDataLoader:  #@save"""加载序列数据的迭代器"""def __init__(self, batch_size, num_steps, use_random_iter, max_tokens):if use_random_iter:self.data_iter_fn = d2l.seq_data_iter_randomelse:self.data_iter_fn = d2l.seq_data_iter_sequentialself.corpus, self.vocab = d2l.load_corpus_time_machine(max_tokens)self.batch_size, self.num_steps = batch_size, num_stepsdef __iter__(self):return self.data_iter_fn(self.corpus, self.batch_size, self.num_steps)
def load_data_time_machine(batch_size, num_steps,  #@saveuse_random_iter=False, max_tokens=10000):"""返回时光机器数据集的迭代器和词表"""data_iter = SeqDataLoader(batch_size, num_steps, use_random_iter, max_tokens)return data_iter, data_iter.vocab

练习

(1)假设训练数据集中有 10 万个单词。一个四元语法需要存储多少个词频和相邻多词频率?

这应该不好说吧。


(2)我们如何对一系列对话建模?

不会,略。


(3)一元语法、二元语法和三元语法的齐普夫定律的指数是不一样的,能设法估计么?

不会,略。


(4)想一想读取长序列数据的其他方法?

固定最大长度,截取多余的部分


(5)考虑一下我们用于读取长序列的随机偏移量。

a. 为什么随机偏移量是个好主意?

b. 它真的会在文档的序列上实现完美的均匀分布吗?

c. 要怎么做才能使分布更均匀?

总比从头到尾顺着读好。


(6)如果我们希望一个序列样本是一个完整的句子,那么这在小批量抽样中会带来怎样的问题?如何解决?

不会,略。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/104978.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniapp 运行到 app 报错 Cannot read property ‘nodeName‘ of null

uniapp 运行到某一个页面&#xff0c;报错&#xff0c;h5没有问题 Unhandled error during execution of scheduler flush. This is likely a Vue internals bug. Please open an issue at https://new-issue.vuejs.org/?repovuejs/coreat <GuiPagecustomHeadertruecustomF…

docker 安装oracle

拉取镜像 拉取oracle_11g镜像 拉取oracle镜像(oracle 11.0.2 64bit 企业版 实例名: helowin) Oracle主要在Docker基础上安装&#xff0c;安装环境注意空间和内存&#xff0c;Oracle是一个非常庞大的一个软件&#xff0c; 建议使用网易镜像或阿里镜像网站这里以oracle 11.0.2…

力扣-461.汉明距离

Method 1 直接比较x&#xff0c;y二进制中的每一位&#xff0c;如果不同则cnt加一&#xff0c;并且x&#xff0c;y每次右移一位 class Solution { public:int hammingDistance(int x, int y) {int cnt 0;while(x > 0 && y > 0) {if((x & 1) ! (y & 1)…

当涉及到API接口数据分析时,主要可以从以下几个方面展开

当涉及到API接口数据分析时&#xff0c;主要可以从以下几个方面展开&#xff1a; 请求分析&#xff1a;可以统计每个API接口的请求次数、请求成功率、失败率等基础指标。这些指标可以帮助你了解API接口的使用情况&#xff0c;比如哪个API接口被调用的次数最多&#xff0c;哪个…

“之江创客”跨境电商赛区决赛暨浙南新电商发展论坛圆满落幕

9月26日&#xff0c;由商务部中国国际电子商务中心指导&#xff0c;浙江省商务厅等十个部门主办&#xff0c;浙江省电子商务促进中心、温州市商务局、苍南县人民政府承办的“之江创客”2023全球电子商务创业创新大赛跨境电商赛区决赛暨浙南新电商发展论坛在苍南圆满落幕。浙江省…

用 Three.js 创建一个酷炫且真实的地球

接下来我会分步骤讲解&#xff0c;在线示例在数字孪生平台。 先添加一个球体 我们用threejs中的SphereGeometry来创建球体&#xff0c;给他贴一张地球纹理。 let earthGeo new THREE.SphereGeometry(10, 64, 64) let earthMat new THREE.MeshStandardMaterial({map: albed…

结构体对齐规则

1.第一个成员在结构体变量偏移量为0的地址处。 2.其他成员变量对齐到某个数字(对齐数)的整数倍的地址处。(对齐数编译器默认的一个对齐数与该成员大小的较小值&#xff09;注意&#xff1a;目前有且只有VS编译器有默认为8. 3.结构体总大小为最大对齐数的整数倍。 4.如果嵌套…

常用的软件项目管理工具一览

软件项目管理工具是帮助团队成功管理和完成软件开发项目的软件程序和应用程序。根据项目及其规模和复杂性&#xff0c;可以使用各种各样的这些工具来协助完成任务&#xff0c;从任务跟踪和调度&#xff0c;到项目报告&#xff0c;到版本控制和协作。 项目经理对软件项目的整体成…

qgis c++ api 整体框架详解

文章目录 整体架构QGis库官方文档编译生成的库 core地图和图层矢量图层(Vector layers)图层要素符号图层要素要素渲染(feature renderer)符号(symbol) 坐标映射数据源(data provider) Raster layers图层符号数据源坐标映射 core库其他有用类 guiQgsMapCanvasQgsMapToolQgsLayer…

计算机视觉和机器视觉有什么区别?

人工智能是一个概念性术语&#xff0c;涵盖了若干特定技术。本文中&#xff0c;我们将探讨机器视觉&#xff08;MV&#xff09;和计算机视觉&#xff08;CV&#xff09;。二者都涉及可视化输入的摄取和解释&#xff0c;因此&#xff0c;了解这些重叠技术的优势、约束和最佳应用…

Vue 绑定style和class

在应用界面中&#xff0c;某些元素的样式是动态的。class 与 style 绑定就是专门用来实现动态样式效果的技术。 如果需要动态绑定 class 或 style 样式&#xff0c;可以使用 v-bind 绑定。 绑定 class 样式【字符串写法】 适用于&#xff1a;类名不确定&#xff0c;需要动态指…

leetcode:1929. 数组串联(python3解法)

难度&#xff1a;简单 给你一个长度为 n 的整数数组 nums 。请你构建一个长度为 2n 的答案数组 ans &#xff0c;数组下标 从 0 开始计数 &#xff0c;对于所有 0 < i < n 的 i &#xff0c;满足下述所有要求&#xff1a; ans[i] nums[i]ans[i n] nums[i] 具体而言&am…

Mongodb----部署副本集 实现读写分离

使用软件&#xff1a; xshell7 vmware16 centos8 nosql booster 1 部署副本集 推荐方案&#xff1a; 为了降低资源分配&#xff0c;这里仅使用一台服务器&#xff0c;但是分配3个端口&#xff08;27017、27018、27019&#xff09;来分别实现 主节点、副本节点…

K8S:配置资源管理 Secret和configMap

配置资源管理 Secret和configMap [TOC](配置资源管理 Secret和configMap)一、Secret1.Secret概念2.Secret的类型3.secret的三种参数4.Pod 的3种方式来使用secret5.Secret创建及案例 二.ConfigMap1.ConfigMap概念2.ConfigMap功能及应用场景3.ConfigMap创建及案例 三、总结1.secr…

基于SSM+Vue的线上学习网站

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

springboot 通过url下载文件并上传到OSS

DEMO流程 传入一个需要下载并上传的url地址下载文件上传文件并返回OSS的url地址 springboot pom文件依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w…

论文阅读:

来源&#xff1a;公众号看到一篇文章 原文&#xff1a;https://arxiv.org/pdf/2301.04275.pdf 代码&#xff1a;GitHub - fengluodb/LENet: LENet: Lightweight And Efficient LiDAR Semantic Segmentation Using Multi-Scale Convolution Attention 0、摘要 基于LiDAR的语义…

Linux:【Kafka四】集群介绍与单机搭建

目录 环境简介 一、搭建kafka集群 1.1、复制出两个kafka的配置文件 1.2、修改配置文件中的如下属性 二、启动kafka集群 三、可校验kafka三个节点是否均启动成功 四、查看集群中主题的分区和副本 4.1、新建一个包含了分区和副本的主题 4.2、查看该主题的详细信息 五、…

linux下安装ffmpeg的详细教程、ffmpeg is not installed

1、下载解压 wget http://www.ffmpeg.org/releases/ffmpeg-6.0.tar.gz tar -zxvf ffmpeg-6.0.tar.gz 2、 进入解压后目录,输入如下命令/usr/local/ffmpeg为自己指定的安装目录 cd ffmpeg-6.0 ./configure --prefix/usr/local/ffmpeg make sudo make install 3、配置变量 v…

kaggle新赛:写作质量预测大赛【数据挖掘】

赛题名称&#xff1a;Linking Writing Processes to Writing Quality 赛题链接&#xff1a;https://www.kaggle.com/competitions/linking-writing-processes-to-writing-quality 赛题背景 写作过程中存在复杂的行为动作和认知活动&#xff0c;不同作者可能采用不同的计划修…