53 语言模型(和之后用来训练语言模型的数据集)_by《李沐:动手学深度学习v2》pytorch版

系列文章目录


文章目录

  • 系列文章目录
  • 理论部分
    • 使用计数来建模
    • N元语法
    • 总结
  • 代码
    • 读取长序列数据
      • 随机采样
      • 顺序分区
    • 小结
    • 练习


理论部分

在上一部分中,我们了解了如何将文本数据映射为词元,以及将这些词元可以视为一系列离散的观测,例如单词或字符。
假设长度为 T T T的文本序列中的词元依次为 x 1 , x 2 , … , x T x_1, x_2, \ldots, x_T x1,x2,,xT。于是, x t x_t xt 1 ≤ t ≤ T 1 \leq t \leq T 1tT)可以被认为是文本序列在时间步 t t t处的观测或标签。在给定这样的文本序列时,语言模型(language model)的目标是估计序列的联合概率

P ( x 1 , x 2 , … , x T ) . P(x_1, x_2, \ldots, x_T). P(x1,x2,,xT).

例如,只需要一次抽取一个词元 x t ∼ P ( x t ∣ x t − 1 , … , x 1 ) x_t \sim P(x_t \mid x_{t-1}, \ldots, x_1) xtP(xtxt1,,x1),一个理想的语言模型就能够基于模型本身生成自然文本。与猴子使用打字机完全不同的是,从这样的模型中提取的文本都将作为自然语言(例如,英语文本)来传递。只需要基于前面的对话片断中的文本,就足以生成一个有意义的对话。显然,我们离设计出这样的系统还很遥远,因为它需要“理解”文本,而不仅仅是生成语法合理的内容。

尽管如此,语言模型依然是非常有用的。例如,短语“to recognize speech”和“to wreck a nice beach”读音上听起来非常相似。
这种相似性会导致语音识别中的歧义,但是这很容易通过语言模型来解决,因为第二句的语义很奇怪。同样,在文档摘要生成算法中,“狗咬人”比“人咬狗”出现的频率要高得多,或者“我想吃奶奶”是一个相当匪夷所思的语句,而“我想吃,奶奶”则要正常得多。

使用计数来建模

在这里插入图片描述

N元语法

在这里插入图片描述

总结

  1. 语言模型估计文本序列的联合概率
  2. 使用统计方法时常采用n元语法

代码

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]
#print(corpus)
vocab = d2l.Vocab(corpus)
print(vocab.token_freqs[:10])
print(list(vocab.token_to_idx.items())[:10])
[('the', 2261), ('i', 1267), ('and', 1245), ('of', 1155), ('a', 816), ('to', 695), ('was', 552), ('in', 541), ('that', 443), ('my', 440)]
[('<unk>', 0), ('the', 1), ('i', 2), ('and', 3), ('of', 4), ('a', 5), ('to', 6), ('was', 7), ('in', 8), ('that', 9)]

正如我们所看到的,(最流行的词)看起来很无聊,这些词通常(被称为停用词)(stop words),比如the、and、of,因此可以被过滤掉。
尽管如此,它们本身仍然是有意义的,我们仍然会在模型中使用它们。
此外,还有个明显的问题是词频衰减的速度相当地快。
例如,最常用单词的词频对比,第 10 10 10个还不到第 1 1 1个的 1 / 5 1/5 1/5
为了更好地理解,我们可以[画出的词频图]:

freqs = [freq for token, freq in vocab.token_freqs] # 把频率提取出来放到一个list中
d2l.plot(freqs, xlabel='token: x', ylabel='frequency: n(x)',xscale='log', yscale='log')

在这里插入图片描述
通过此图我们可以发现:词频以一种明确的方式迅速衰减。
将前几个单词作为例外消除后,剩余的所有单词大致遵循双对数坐标图上的一条直线。
这意味着单词的频率满足齐普夫定律(Zipf’s law),
即第 i i i个最常用单词的频率 n i n_i ni为:

n i ∝ 1 i α , n_i \propto \frac{1}{i^\alpha}, niiα1,
:eqlabel:eq_zipf_law

等价于

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)]

这里值得注意:在十个最频繁的词对中,有九个是由两个停用词组成的,
只有一个与“the time”有关。
我们再进一步看看三元语法的频率是否表现出相同的行为方式。

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'])

在这里插入图片描述
这张图非常令人振奋!原因有很多:

  1. 除了一元语法词,单词序列似乎也遵循齐普夫定律,
    尽管公式 :eqref:eq_zipf_law中的指数 α \alpha α更小
    (指数的大小受序列长度的影响);
  2. 词表中 n n n元组的数量并没有那么大,这说明语言中存在相当多的结构,
    这些结构给了我们应用模型的希望;
  3. 很多 n n n元组很少出现,这使得拉普拉斯平滑非常不适合语言建模。
    作为代替,我们将使用基于深度学习的模型。

读取长序列数据

由于序列数据本质上是连续的,因此我们在处理数据时需要解决这个问题。
我们以一种相当特别的方式做到了这一点:当序列变得太长而不能被模型一次性全部处理时,我们可能希望拆分这样的序列方便模型读取。

在介绍该模型之前,我们看一下总体策略。
假设我们将使用神经网络来训练语言模型,模型中的网络一次处理具有预定义长度(例如 n n n个时间步)的一个小批量序列。
现在的问题是如何[随机生成一个小批量数据的特征和标签以供读取。]

首先,由于文本序列可以是任意长的,例如整本《时光机器》(The Time Machine),于是任意长的序列可以被我们划分为具有相同时间步数的子序列。
当训练我们的神经网络时,这样的小批量子序列将被输入到模型中。
假设网络一次只处理具有 n n n个时间步的子序列。下图中画出了从原始文本序列获得子序列的所有不同的方式,其中 n = 5 n=5 n=5,并且每个时间步的词元对应于一个字符。
请注意,因为我们可以选择任意偏移量来指示初始位置,所以我们有相当大的自由度。
在这里插入图片描述

因此,我们应该从上图中选择哪一个呢?事实上,他们都一样的好。
然而,如果我们只选择一个偏移量,那么用于训练网络的、所有可能的子序列的覆盖范围将是有限的。因为定住一个偏移量后,相当于从上图中选择一行(即一种情况)来反复使用,在一个epoch中每个子序列(数据)可能要被使用多次(比如一个epoch要循环两遍文章数据),不如使用随即偏移量,来使用更多种不同数据。
因此,我们可以从随机偏移量开始划分序列,以同时获得覆盖性(coverage)和随机性(randomness)。
下面,我们将描述如何实现随机采样(random sampling)和]顺序分区(sequential partitioning)策略。

随机采样

(在随机采样中,每个样本都是在原始的长序列上任意捕获的子序列。)
在迭代过程中,来自两个相邻的、随机的、小批量中的子序列不一定在原始序列上相邻。
对于语言建模,目标是基于到目前为止我们看到的词元来预测下一个词元,因此标签是移位了一个词元的原始序列。
下面的代码每次可以从数据中随机生成一个小批量。
在这里,参数batch_size指定了每个小批量中子序列样本的数目,参数num_steps是每个子序列中预定义的时间步数。

def seq_data_iter_random(corpus, batch_size, num_steps):  #@save"""使用随机抽样生成一个小批量子序列"""# 从随机偏移量开始对序列进行分区,随机范围包括num_steps-1corpus = corpus[random.randint(0, num_steps - 1):]# 减去1,是因为我们需要考虑标签num_subseqs = (len(corpus) - 1) // num_steps# 长度为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_sizefor 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)
X:  tensor([[21, 22, 23, 24, 25],[11, 12, 13, 14, 15]]) 
Y: tensor([[22, 23, 24, 25, 26],[12, 13, 14, 15, 16]])
X:  tensor([[ 6,  7,  8,  9, 10],[16, 17, 18, 19, 20]]) 
Y: tensor([[ 7,  8,  9, 10, 11],[17, 18, 19, 20, 21]])
X:  tensor([[ 1,  2,  3,  4,  5],[26, 27, 28, 29, 30]]) 
Y: tensor([[ 2,  3,  4,  5,  6],[27, 28, 29, 30, 31]])

顺序分区

在迭代过程中,除了对原始序列可以随机抽样外,我们还可以[保证两个相邻的小批量中的子序列在原始序列上也是相邻的]。
这种策略在基于小批量的迭代过程中保留了拆分的子序列的顺序,因此称为顺序分区。

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_sizeXs = 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_stepsfor 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

基于相同的设置,通过顺序分区[读取每个小批量的子序列的特征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([[ 1,  2,  3,  4,  5],[17, 18, 19, 20, 21]]) 
Y: tensor([[ 2,  3,  4,  5,  6],[18, 19, 20, 21, 22]])
X:  tensor([[ 6,  7,  8,  9, 10],[22, 23, 24, 25, 26]]) 
Y: tensor([[ 7,  8,  9, 10, 11],[23, 24, 25, 26, 27]])
X:  tensor([[11, 12, 13, 14, 15],[27, 28, 29, 30, 31]]) 
Y: tensor([[12, 13, 14, 15, 16],[28, 29, 30, 31, 32]])

现在,我们[将上面的两个采样函数包装到一个类中],以便稍后可以将其用作数据迭代器。

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)

[最后,我们定义了一个函数load_data_time_machine,它同时返回数据迭代器和词表],因此可以与其他带有load_data前缀的函数 类似地使用。

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

小结

  • 语言模型是自然语言处理的关键。
  • n n n元语法通过截断相关性,为处理长序列提供了一种实用的模型。
  • 长序列存在一个问题:它们很少出现或者从不出现。
  • 齐普夫定律支配着单词的分布,这个分布不仅适用于一元语法,还适用于其他 n n n元语法。
  • 通过拉普拉斯平滑法可以有效地处理结构丰富而频率不足的低频词词组。
  • 读取长序列的主要方式是随机采样和顺序分区。在迭代过程中,后者可以保证来自两个相邻的小批量中的子序列在原始序列上也是相邻的。

练习

  1. 假设训练数据集中有 100 , 000 100,000 100,000个单词。一个四元语法需要存储多少个词频和相邻多词频率?
  2. 我们如何对一系列对话建模?
  3. 一元语法、二元语法和三元语法的齐普夫定律的指数是不一样的,能设法估计么?
  4. 想一想读取长序列数据的其他方法?
  5. 考虑一下我们用于读取长序列的随机偏移量。
    1. 为什么随机偏移量是个好主意?
    2. 它真的会在文档的序列上实现完美的均匀分布吗?
    3. 要怎么做才能使分布更均匀?
  6. 如果我们希望一个序列样本是一个完整的句子,那么这在小批量抽样中会带来怎样的问题?如何解决?

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

.bixi勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复

导言 在当今数字化时代&#xff0c;勒索软件已成为企业和个人面临的重大安全威胁。.bixi勒索病毒作为其中一种新型恶意软件&#xff0c;以其快速加密文件的能力和高效传播机制引发了广泛关注。该病毒不仅能够迅速锁定用户的关键数据&#xff0c;还常常在感染后施加极大的心理压…

邀请功能的实现分析

邀请功能 功能分析 场景&#xff1a;项目中出现用户邀请其他用户加入群组的功能 需求&#xff1a;用户点击生成邀请链接可以生成一个url&#xff0c;将这个url分享给其他用户&#xff0c;其他用户点击后对用户登录状态进行校验&#xff0c;校验通过即可加入群组&#xff0c;未…

江协科技STM32学习- P16 实验-TIM输出比较(PWD驱动LED呼吸灯,舵机,直流电机)

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

如何访问字符串中某个字符

在Java中&#xff0c;你可以使用字符串的charAt(int index)方法来查看字符串中第index个位置的字符。index是从0开始的&#xff0c;也就是说&#xff0c;字符串的第一个字符的索引是0&#xff0c;第二个字符的索引是1&#xff0c;以此类推。如果索引超出了字符串的长度&#xf…

IO多路转接:select、poll、epoll

目录 非阻塞读取 fcntl函数 I/O多路转接之select select函数 fd_set结构 select的模拟实现 select的优缺点 I/O多路转接之poll poll函数 struct pollfd结构体 poll函数的使用示例 poll的模拟实现 poll的优缺点 I/O多路转接之epoll epoll的三个系统调用 epoll的…

当你在Linux系统中使用MySQL命令行工具查询数据库时,如果中文显示为问号(?)或其他乱码,简单解决办法。(2)

文章目录 1、问题出现2、解决办法 1、问题出现 2、解决办法 mysql -u [username] -p --default-character-setutf8 [database_name]rootab66508d9441:/# mysql -uroot -p123456 --default-character-setutf8 tingshu_album mysql: [Warning] Using a password on the command …

Redis 字符串类型的典型应用场景

目录 1. 缓存功能 2. 计数功能 3. 共享会话&#xff08;Session&#xff09; 4. 手机验证码 前言 这里将详细介绍 Redis 字符串类型在实际开发中的几个典型应用场景&#xff0c;并提供相应的伪代码示例。 1. 缓存功能 场景描述 在许多Web应用中&#xff0c;数据通常需要…

使用AVL树实现Map

一、数组在裂变扩容时可能会出现环、在数组元素转为链表之后选择尾插法插入节点、数组到链表到AVL到RBT的转换 1、数组在裂变扩容时链表中的节点计算出来的位置可能也会发生变化&#xff0c;在多线程情况下调整节点位置可能会出现环。 2、数组中的数组元素转为链表后插入新节点…

在大模型训练中,为什么GPU 通常比 CPU 更重要

在大模型训练中&#xff0c;GPU 通常比 CPU 更重要&#xff0c;原因主要有以下几点&#xff1a; 一、并行计算能力 GPU 拥有强大的并行计算能力。在大模型训练中&#xff0c;需要处理海量的数据和复杂的计算任务。例如&#xff0c;深度学习模型中的矩阵运算、卷积运算等&…

13. 了解人工智能可能存在的偏见

这篇文章没有太多技术和代码细节&#xff0c;更多的是作为一份有趣的报告。 这里没有任何模型会被训练。 这篇文章也为生成式人工智能导论课程中 HW8: Safety Issues of Generative AI 提供中文引导。 代码文件下载 文章目录 为什么人工智能存在偏见&#xff1f;动手试试加载模…

算法_BFS解决多源最短路问题---持续更新

文章目录 前言引入矩阵题目要求题目解析代码如下 飞地的数量题目要求题目解析代码如下 地图中的最高点题目要求题目解析代码如下 地图分析题目要求题目解析代码如下 前言 本文将会向你介绍有关宽度优先搜索&#xff08;BFS&#xff09;解决多源最短路问题的相关题型&#xff1…

故障诊断│GWO-DBN灰狼算法优化深度置信网络故障诊断

1.引言 随着人工智能技术的快速发展&#xff0c;深度学习已经成为解决复杂问题的热门方法之一。深度置信网络&#xff08;DBN&#xff09;作为深度学习中应用比较广泛的一种算法&#xff0c;被广泛应用于分类和回归预测等问题中。然而&#xff0c;DBN的训练过程通常需要大量的…

机器人速度雅可比矩阵(机器人动力学)

博途PLC矩阵求逆 矩阵求逆 博图SCL_博图矩阵运算-CSDN博客文章浏览阅读839次。本文介绍如何用C语言实现矩阵求逆的过程,详细解析了相关代码,适合线性代数和编程爱好者学习。https://rxxw-control.blog.csdn.net/article/details/122367883 1、二自由度平面关节机器人速度雅…

项目第十二弹:功能联调

项目第十二弹&#xff1a;功能联调 一、发布订阅功能测试1.生产者2.消费者3.演示4.持久化信息查看1.消息2.SQLite3数据库 二、持久化恢复测试1.代码2.gc3.演示 三、虚拟机和信道隔离测试1.责任划分2.如何测试3.生产者4.消费者5.演示 一、发布订阅功能测试 我们直接上TOPIC交换…

MySQL中的逻辑条件

逻辑条件组合两个比较条件的结果来产生一个基于这些条件的单个的结果&#xff0c;或者逆转一个单个条件的结果。当所有条件的结果为真时&#xff0c;返回行。 SQL的三个逻辑运算符是&#xff1a; AND、OR、NOT 可以在WHERE子句中用AND和OR运算符使用多个条件。 示例一&#…

惊爆!高通要收购英特尔,巨头也会被时代抛弃!

今天看到的外媒消息&#xff0c;高通要收购英特尔&#xff0c;看到消息的时候&#xff0c;其实&#xff0c;还是挺吃惊的。 高通是移动芯片的王者&#xff0c;英特尔是 PC 芯片的王者。当然了&#xff0c;英特尔这个可能需要再加上两个字&#xff1a;曾经的 PC 芯片王者。 其实…

植物大战僵尸【源代码分享+核心思路讲解】

植物大战僵尸已经正式完结&#xff0c;今天和大家分享一下&#xff0c;话不多说&#xff0c;直接上链接&#xff01;&#xff01;&#xff01;&#xff08;如果大家在运行这个游戏遇到了问题或者bug&#xff0c;那么请私我谢谢&#xff09; 大家写的时候可以参考一下我的代码思…

在VMware16中安装Windows 10:完整教程

在VMware中安装Windows 10&#xff1a;完整教程 1.安装环境准备2.创建虚拟机 1.安装环境准备 1.虚拟机: VMware-workstation-full-16.2.2-19200509 2.系统镜像:win10 2.创建虚拟机 1.自定义 2.下一步 3.稍后安装系统 3.默认下一步 4.虚拟机取名和选择存放路径(按需更改…

利士策分享,江西新余悲剧背后的深思:安全与责任的重构

利士策分享&#xff0c;江西新余悲剧背后的深思&#xff1a;安全与责任的重构 在这个信息瞬息万变的时代&#xff0c;每一次突发事件都能迅速触动社会的神经&#xff0c; 而江西新余近期发生的悲剧&#xff0c;更是让我们在悲痛之余&#xff0c;不得不深刻反思安全管理与社会…

AVL树与红黑树

目录 AVL树 AVL树节点的定义 AVL树的插入 AVL树的旋转 右单旋 左单旋 左右双旋 右左双旋 AVL树的验证 AVL树的性能 红黑树 红黑树的性质 红黑树节点的定义 红黑树结构 红黑树的插入操作 按照二叉搜索的树规则插入新节点 检测新节点插入后&#xff0c;红黑树的性…