第十一章 原理篇:transformer模型入门

文章介绍了词向量的Embedding概念,强调了顺序和相关性的重要性。接着,详细阐述了Transformer架构,包括self-attention和multi-headattention的工作原理,以及position-embedding如何引入位置信息。还通过CBOW模型展示了词向量的训练过程,并提供了PyTorch实现的代码示例。

说在前面的话:

找工作面试不是特别顺利。进了目标公司的二面,但是一面面试官问的一些“新技术”问题答得不太好,尤其是transformer相关的。这一点确实是自己的问题,在工作后总是面向业务学习,对很多算法都是处于“听说过但没用过”的状态。VIT/SWIN/Convnext之前明明都专门去学习过,但是因为没有深入了解,所以也没有留下比较深刻的记忆。
加油啊!

参考教程:

从Embedding开始

词的向量化

transformer最初主要还是用于NLP任务中。在NLP任务中,对于一个文本的输入,通常会使用embedding对它进行一个向量化的表示。
embedding的目标,就是用一组向量来刻画现有的数据集。那么就对这个embedding有了一点要求。

  • 要有词的先后顺序。
  • 相似的词在向量空间的表达应该也是相似的。
  • 同一类的词在空间内应该是相近的。

第一个要求很好理解,比如你可以说“这个房子里有一条狗”但是你不能说“这条狗里有一个房子”。顺序的改变是会影响文本的含义的,因此我们希望对文本中单词进行编码时,能记录他们的位置信息。
第二个要求举个例子,“月亮”和“月球”两个词虽然组成它们的文字不一样,但是它们都是“月”的一个称呼,意思也都是“月”。因此我们希望它们的向量也比较相近。
第三个要求是限制同一类词的距离,比如说“篮球,足球,泰山”。那么篮球和足球作为一种体育用品,并且都是球类,它们两者在向量空间内肯定是比它们和泰山的距离要近的。泰山和这两个词本来就是八竿子打不着,如果它距离篮球比足球距离篮球还要近,这就很违背常人的逻辑印象。

同时呢,我们也希望得到的这个embedding的向量表示的维度也相对高一点,维度高意味着信息更多,那么在使用这个向量表示时,我们得到的结果也会相对更可靠一些。

词向量模型

我们的词向量,也是是文本中词汇的embedding化表示,是可以通过多种方法实现的。比如说简单的one-hot编码,又或者使用频率表示。但是这两种方法都是有缺陷的。

  • 使用one-hot编码,假如你的词库非常大,那么为了表示一个单词,就需要一个极其长的向量表示,造成极大的内存浪费。
  • 频率是通过用某单词在文章中出现次数除以文章的单词个数获得的,它的优点是简单快速,但是忽略了词的位置信息和相关性。

我们可以通过所谓的词向量模型来获得词向量。
词向量模型是一种需要训练的模型,使用这个模型,可以帮助我们获得对词库中的单词的向量化表达。这种模型的输入也是一个embedding,可以理解成对指定单词的一个标志,用于把单词区分开来。而它的输出结果是一个概率,表示在给定输入的情况下,最可能返回的单词是什么。

当然我们想要的并不是这个输出的结果,而是藏在模型中间的embedding。

整体的过程可以理解为,我们想要的embedding是这个模型的一个中间结果,它会随着模型的更新迭代而变化,我们判断这个embedding是否好用的根据是模型的输出结果是否准确。

而在这个任务中,模型的输入是给定的文本编码,模型的输出是目标单词出现的概率。所以它其实是一个多分类的问题,最终的概率是由softmax函数得到的。在你的分类目标很多的情况下,softmax计算会很麻烦,因此又有一个优化的方法:

输入A和B两个单词,判断这两个词是不是前后邻居。

这样这个任务就变成了二分类的任务。

过程介绍

数据的获取

常用的方法是:
使用滑动窗口构建输入数据。
在这里插入图片描述
从别的地方偷了一张图。这里就给出了一个很直观的例子。
在上面一节我们说词向量的模型输入是给定的文本编码,输出是目标单词的概率。而图中就是一个用前文预测下文的例子,它的前文单词数长度为2,也就说说用前两个词预测第三个词。
对于一段文本,从第一个单词开始,用连续的两个词预测下一个词,所以才会有inputs是“thou shalt”,output(target)是“not”。

具体input和output是如何获得的,还是由使用者自行决定的。除了用前n个词预测下一个词外,也可以使用前n个加后n个词,来预测中间的词。

word2vec

常用的两种模型介绍

  • CBOW:输入目标单词前面和后面的n个词,进行单词本身的预测。
  • skip-gram:输入一个单词,预测它前n个词和后n个词。

下方左边就是CBOW的例子,右边是Skip-gram的例子。
在这里插入图片描述
两个模型的原理是相似的。

对于CBOW,我们传入四个单词的编码(这里的编码不是我们想要的embedding,只是为了区分不同单词的标号),通过模型获得四个单词的embedding,对这个embedding进行处理,并用于预测四个单词正中间的单词,获得“目标单词为词库中的第n个词的概率”,最大概率对应的单词就是我们的输出。

对于Skip-gram,我们传入中间单词的编码,通过模型获得embedding,再用于预测它周围的四个单词。

代码介绍

torch.nn.Embedding()

torch.nn.Embedding(num_embeddings, embedding_dim, padding_idx=None, max_norm=None, norm_type=2.0, scale_grad_by_freq=False, sparse=False, _weight=None, _freeze=False, device=None, dtype=None)

先来看一下这个class中的参数都有什么用。

  • 第一个参数 num_embeddings:代表你的embedding有多少个,也可以等价于你期望的词库大小。
  • 第二个参数embedding_dim是你希望得到的embedding向量的维度。
  • padding_idx:可以指定不用于梯度更新的index的位置。也就是在模型训练的过程中,别的位置会随着backward更新,而这个位置不会。
  • max-norm:用于对你的vector进行归一化
  • p-norm:归一化使用L2还是L1。
  • _weight: 你可以自己传入一个weights,这里的weight就是生成的embedding矩阵的值,如果这个weight和你预设的大小不一致,也会重新生成。

它的前向传播得到的结果是

def forward(self, input: Tensor) -> Tensor:
        return F.embedding(
            input, self.weight, self.padding_idx, self.max_norm,
            self.norm_type, self.scale_grad_by_freq, self.sparse)

对于在前向传播中使用的函数F.embedding,它的解释是This module is often used to retrieve word embeddings using indices. The input to the module is a list of indices, and the embedding matrix, and the output is the corresponding word embeddings. 也就是说我们传入的input是一组index,F.embedding会根据这个index去我们的weight中取vector。
比如你用的index是[1,3,5],返回结果就是self.weights的第二行,第四行和第六行,三行长度为embedding_dim的向量。

代码实现

代码实现可以直接参考pytorch tutorial中给出的例子,在这里会对代码进行解释。

下面是一个CBOW的例子

首先获取我们的数据。完成输入输出对的构建。

context_size = 2   # 代表我们会使用前面和后面各两个单词在预测中间的词。
raw_text = """...""".split()  # 原始文章的string,这里因为字数太多没有列出来,可以直接替换成自己的
vocab = set(raw_text) # 文章中都使用了那些单词,这个也就是我们的词库
vocab_size = len(vocab) # 我们的词库大小

word_to_ix = {
   
   word:i for i, word in enumerate(vocab)} # 这里构建了一个字典,该字典是key是单词,value是单词的index。

# 下一步来构建我们的dataset
for i in range(context_size, len(raw_text) - context_size):
# 遍历整个文章
	context = (
	[raw_text[i-j-1] for j in range(context_size)]  # 第i个单词的前context_size个词
	+ [raw_text[i+j+1] for j in range(context_size)] # 第i个单词的后context_size个词
	)
	target = raw_text[i] # 第i个单词
	data.append((context, target)) # 将我们的input和target按对放入data中

然后定义我们的模型。
在这里给出一个forward流程的更详细的介绍。
假如我们输入的是[2,1,3,4]。target是0。预设的vocab_size = 500, embedding_dim = 64。
那么我们期望得到的一个整个词库的embedding大小就是500*64,也就是nn.Embedding中weights的大小。
现在输入[2,1,3,4],经过第一步self.embeddings(inputs),得到的结果大小为4*64。然后求和并用view(1,-1)后,得到的结果为1*64,也就是四个embedding的融合。
经过两个线性层后,得到的out大小是1*500。

class CBOW(nn.Module):
	# 定义CBOW类,继承nn.Module
    def __init__(self, vocab_size, embedding_dim):
        super(CBOW,self).__init__()
		# vocab_size: 我们的词库的大小,因为输出结果是在每个词上的概率,所以如果直接用softmax求概率,我们的输出维度应该=词库大小。
		# embedding_dim:你自己决定的embedding向量的长度。
		self.embeddings = nn.Embedding(vocab_size, embedding_dim) # 我们想要的embedding结果在这里,它可以看作是一个高为vocab_size, 高为embedding_dim
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值