机器学习深度学习—语言模型和数据集

news2024/11/23 22:13:11

👨‍🎓作者简介:一位即将上大四,正专攻机器学习的保研er
🌌上期文章:机器学习&&深度学习——文本预处理
📚订阅专栏:机器学习&&深度学习
希望文章对你们有所帮助

语言模型和数据集

  • 引入
  • 学习语言模型
  • 马尔可夫模型与n元语法
  • 自然语言统计
  • 读取长序列数据
    • 随机采样
    • 顺序分区
    • 包装
  • 小结

引入

假设长度为T的文本序列,那么语言模型的目标是估计序列的联合概率:
P ( x 1 , x 2 , . . . , x T ) P(x_1,x_2,...,x_T) P(x1,x2,...,xT)
在抽取一个词元:
x t 符合 P ( x t ∣ x t − 1 , . . . , x 1 ) x_t符合P(x_t|x_{t-1},...,x_1) xt符合P(xtxt1,...,x1)
的时候,一个理想的语言模型就能基于模型本身生成自然文本。
而从这样的模型中提取的样本要作为自然语言来传递,因此我们需要生成一个有意义的对话,也就是说,我们的设计需要理解文本,而不仅仅是生成语法合理的内容。
例如:“狗咬人”就比“人咬狗”看起来更正常;“我想吃老妈”看起来语法没问题,但是很emmm,而“我想吃,老妈”就看起来合理多了。

学习语言模型

从基本概率规则开始:
P ( x 1 , x 2 , . . . , x T ) = ∏ t = 1 T P ( x t ∣ x 1 , . . . , x t − 1 ) P(x_1,x_2,...,x_T)=\prod_{t=1}^TP(x_t|x_1,...,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)
而其中的P,也就是数据集中词的概率可以根据给定词的相对词频来计算,如:
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,x)分别是单个单词和连续单词对的出现次数。
但连续的单词出现的概率会低很多,特别可能有些是三个或更多的单词组合( 除非我们提供某种解决方案,来将这些单词组合指定为非零计数,否则将无法在语言模型中使用它们。如果数据集很小,或者单词非常罕见,那么这类单词出现一次的机会可能都找不到。)

马尔可夫模型与n元语法

之前讲过马尔可夫模型,比如一阶马尔可夫性质:
P ( x t + 1 ∣ x t , . . . , x 1 ) = P ( x t + 1 ∣ x t ) P(x_{t+1}|x_t,...,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 ) 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) P(x1,x2,x3,x4)=P(x1)P(x2)P(x3)P(x4)P(x1,x2,x3,x4)=P(x1)P(x2x1)P(x3x2)P(x4x3)P(x1,x2,x3,x4)=P(x1)P(x2x1)P(x3x1,x2)P(x4x2,x3)
上面上式分别被称为一元语法二元语法三元语法

自然语言统计

我们首先在《时光机器》数据集总构建词表,并打印前10个最常用单词:

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)
print(vocab.token_freqs[: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')
d2l.plt.show()

在这里插入图片描述
这个图可以发现,词频在以一种明确的方式迅速衰减。将前几个单词作为例外消除后,剩余的单词大致遵循双对数坐标图上的一条直线(单词满足着齐普夫定律),即第i个最常用单词的频率为:
n i ≈ 1 i α n_i≈\frac{1}{i^α} niiα1
等价于
l o g n i = − α l o g i + c logn_i=-αlogi+c logni=αlogi+c
其中α是刻画分布的质数,c是常数。
我们可以验证二元语法、三元语法等的频率是否和一元语法的频率表现出相同的行为方式:

# 二元语法
bigram_tokens = [pair for pair in zip(corpus[:-1], corpus[1:])]  # 第0位到最后一位以及第1位到最后一位组合起来就是二元组
bigram_vocab = d2l.Vocab(bigram_tokens)
# 三元语法
trigram_tokens = [triple for triple in zip(
    corpus[:-2], corpus[1:-1], corpus[2:]
)]
trigram_vocab = d2l.Vocab(trigram_tokens)
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'])
d2l.plt.show()

运行结果:
在这里插入图片描述
这张图可以看出:除了一元语法,单词序列也遵循齐普夫定律,区别也就是α更小(指数大小受序列长度影响)

读取长序列数据

由于文本序列可以是任意长的,于是任意长的序列可以被我们划分为具有相同时间步数的子序列。当训练我们的神经网络时,这样的小批量子序列将被输入到模型中。下图给出了n=5的情况:
在这里插入图片描述
可以看出,我们也可以选择任意偏移量来指示初始位置,所以我们有很好的自由度。
上图中的他们都一样好,但是我们如果只能选一个偏移量,那么用来训练网络的、所有可能的子序列的覆盖范围是有限的。
因此我们可以从随机偏移量开始划分序列,以同时获得覆盖性和随机性。

随机采样

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

import torch
import random
from d2l import torch as d2l

def seq_data_iter_random(corpus, batch_size, num_steps):  #@save
    """使用随机抽样生成一个小批量子序列"""
    # 从随机偏移量开始对序列进行分区,随机范围包括num_steps-1
    corpus = 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_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)

如果我们生成一个0到34的序列,批量大小为2,时间步为5,那么我们可以生成(35-1)/5=6个“特征-标签”子序列对。如果设置小批量大小为2,我们只能得到3个小批量。

my_seq = list(range(35))
for X, Y in seq_data_iter_random(my_seq, batch_size=2, num_steps=5):
    print('X: ', X, '\nY:', Y)

结果:

X: tensor([[22, 23, 24, 25, 26],
[27, 28, 29, 30, 31]])
Y: tensor([[23, 24, 25, 26, 27],
[28, 29, 30, 31, 32]])
X: tensor([[ 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16]])
Y: tensor([[ 8, 9, 10, 11, 12],
[13, 14, 15, 16, 17]])
X: tensor([[ 2, 3, 4, 5, 6],
[17, 18, 19, 20, 21]])
Y: tensor([[ 3, 4, 5, 6, 7],
[18, 19, 20, 21, 22]])

可以看出Y比起对应的X同位置都是多一个的。

顺序分区

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

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

基于相同的设置,通过顺序分区读取每个小批量的子序列的特征X和标签Y。 通过将它们打印出来可以发现: 迭代期间来自两个相邻的小批量中的子序列在原始序列中确实是相邻的(每个相邻X之间数据都是接在一起的)。

my_seq = list(range(35))
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_random
        else:
            self.data_iter_fn = d2l.seq_data_iter_sequential
        self.corpus, self.vocab = d2l.load_corpus_time_machine(max_tokens)
        self.batch_size, self.num_steps = batch_size, num_steps

    def __iter__(self):
        return self.data_iter_fn(self.corpus, self.batch_size, self.num_steps)

最后定义一个load_data_time_machine函数,同时返回数据迭代器和词表:

def load_data_time_machine(batch_size, num_steps, use_random_iter=False, max_tokens=10000):  #@save
    """返回时光机器数据集的迭代器和词表"""
    data_iter = SeqDataLoader(
        batch_size, num_steps, use_random_iter, max_tokens)
    return data_iter, data_iter.vocab

小结

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

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

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

相关文章

订单系统就该这么设计,稳的一批~

订单功能作为电商系统的核心功能,由于它同时涉及到前台商城和后台管理系统,它的设计可谓是非常重要的。就算不是电商系统中,只要是涉及到需要交易的项目,订单功能都具有很好的参考价值,说它是通用业务功能也不为过。今…

单元测试最终结果为Stopped状态(有报错后不继续往下执行)

之前跑了一下项目的单元测试,但是发现一旦有报错后单元测试就不继续往下执行,而且最终的结果是stopped状态,截图如下: 经过排查是因为项目启动的时候有如下的代码: 通过代码可以发现如果项目启动失败,则直接…

如何用python画动漫人物,python画卡通人物代码

大家好,小编来为大家解答以下问题,python画动漫人物代码 星空,如何用python画动漫人物,现在让我们一起来看看吧! 要寒假了,给孩子画一个卡通版蜘蛛侠 完整程序代码: from turtle import * speed…

鉴源实验室|公钥基础设施(PKI)在车联网中的应用

作者 | 付海涛 上海控安可信软件创新研究院汽车网络安全组 来源 | 鉴源实验室 01 PKI与车联网 1.1 PKI概述 公钥基础设施(PKI ,Public Key Infrastructure)是一种在现代数字环境中实现认证和加密的基本框架,主要用于保护网络交互和通信的安…

新手教程:5步掌握系统流程图绘制方法!

流程图通常用于管理、分析、设计许多不同领域的流程,是一个很有用的工具,能够帮助大家更轻松、更有效地解决问题。系统流程图是流程图的常见变体之一。 系统流程图是展示数据流以及决策如何影响周围事件的图表类型。 与其他类型的流程图一样,…

【沁恒蓝牙mesh】CH58x USB功能开发记录(一)

本文主要介绍基于【沁恒蓝牙mesh】CH58x USB功能,结合SDK提供的代码包分析USB的基本常识 【沁恒蓝牙mesh】CH58x USB功能开发记录(一) 1. USB基本常识1.1 **USB 设备类别:**1.2 **USB设备实现方法:**1.3 **CDC设备&…

【我们一起60天准备考研算法面试(大全)-第三十九天 39/60】【序列型DP】

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客,如有问题交流,欢迎评论区留言,一定尽快回复!(大家可以去看我的专栏,是所有文章的目录)   文章字体风格: 红色文字表示&#…

PDF Expert 3.3 for mac

PDF Expert是一款专业的PDF编辑和阅读工具。它可以帮助用户在Mac、iPad和iPhone等设备上查看、注释、编辑、填写和签署PDF文档。 以下是PDF Expert的特点: PDF编辑:PDF Expert提供了丰富的PDF编辑功能,包括添加、删除、移动、旋转、缩放、裁…

【2.2】Java微服务:Hystrix的详解与使用

目录 分布式系统面临问题 Hystrix概念 Hystrix作用 降级 什么是降级 order服务导入Hystrix依赖(简单判断原则:谁调用远程谁加) 启动类添加注解 业务方法添加注解(冒号里填回调方法名,回调方法返回兜底数据&…

DETR不需要多尺度或局部性设计

文章目录 DETR Doesn’t Need Multi-Scale or Locality Design摘要本文方法Box-to-Pixel Relative Position Bias其他改进 实验结果 DETR Doesn’t Need Multi-Scale or Locality Design 摘要 提出了一种改进的DETR检测器,使用单尺度特征映射和全局交叉注意计算&a…

RFID系统数据编码方式仿真实现

RFID 技术简介 射频识别技术(RFID,即,Radio Frequency Identification)是一种非接触自动识别技术,它利用无线通信的方式自动的从目标中读取信息。   典型的RFID射频识别系统包括标签和读写器两部分。   标签是一块集…

学术资源加速

以下为可以加速访问的学术资源地址: github.comgithubusercontent.comgithubassets.comhuggingface.co 编辑 /etc/network_turbo vim /etc/network_turbo 内容格式参考如下: export no_proxylocalhost,127.0.0.1 export http_proxyhttp://127.0.0.…

5,二叉树【p6-p7】

二叉树 5.1二叉树5.1.1例1:用递归和非递归两种方式实现二叉树的先序、中序、后序遍历5.1.1.1递归序的先序、中序、后序遍历先序遍历:中序遍历:后序遍历: 5.1.1.2非递归序的先序、中序、后序遍历先序遍历:中序遍历&…

基于Java+SpringBoot+Vue的时间管理系统设计与实现(源码+LW+部署文档等)

博主介绍: 大家好,我是一名在Java圈混迹十余年的程序员,精通Java编程语言,同时也熟练掌握微信小程序、Python和Android等技术,能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

Apache Paimon 学习笔记

本博客对应于 B 站尚硅谷教学视频 尚硅谷大数据Apache Paimon教程(流式数据湖平台),为视频对应笔记的相关整理。 1 概述 1.1 简介 Flink 社区希望能够将 Flink 的 Streaming 实时计算能力和 Lakehouse 新架构优势进一步结合,推…

《深度探索c++对象模型》第七章笔记

非原创,在学习 7 站在对象模型的尖端 On the Cusp of the Object Model 这一章讨论三个著名的C语言扩充性质,它们都会影响CH对象。它们分别是 template、exception handling (EH)和runtime type identification (RTTI) 模版、异常、通过运行…

vite项目中使用@代表根路径

1.配置vite.config.ts import { defineConfig } from vite import vue from vitejs/plugin-vue import path from pathexport default defineConfig({plugins: [vue()],resolve: {alias:{: path.resolve(__dirname, src) }} })2.报错path和__dirname 找不到模块“path”或其相…

K8S系列文章 之 编写自动化部署K8S脚本

介绍 通过ansible脚本shell实现自动化部署k8s基础集群(v1.25.0) 部署结构 1. 通过二进制部署包镜像安装k8s集群、目录etcd节点只支持1-3个节点、最多三个etcd节点 2. 因k8s版本相对较新、需要升级内核来支持后台程序、当前版本只支持Cento7,内核版本(5.19.4-1.el7…

本地构建包含java和maven的镜像

目录 1.前提条件 2.下载 2.1.创建Dockerfile 3.构建镜像 参考文章 1.前提条件 本地环境需要的系统和软件 win10 Docker Desktop Powershell 图1 Win10安装Docker后,直接在Powershell使用Docker命令 有些Developer不习惯win10系统,却想要使用Lin…

使用ResponseBodyAdvice封装统一返回值

目录 ResponseBodyAdvice 接口概述 ResponseBodyAdvice 快速使用 父pom文件 pom文件 ResponseDto MyResponseBodyAdvice DemoController 结果展示 ResponseBodyAdvice 接口概述 在实际项目中,我们经常需要在请求前后进行一些操作,比如&#xf…