GPT2

GPT2

bing

GPT2 和 BERT 的区别主要有以下几点:

  • GPT2 是基于 Transformer 的解码器模块构建的,而 BERT 是基于 Transformer 的编码器模块构建的。
  • GPT2 是单向的语言模型,只能利用左侧的上下文信息,而 BERT 是双向的语言模型,可以同时利用左右两侧的上下文信息。
  • GPT2 只有预训练阶段,没有微调阶段,而 BERT 有预训练和微调两个阶段。
  • GPT2 的输入向量只包括 token embedding 和 position embedding,而 BERT 的输入向量还包括 segment embedding。
  • GPT2 的参数量更大,最大的模型有 15 亿个参数,而 BERT 最大的模型有 3 亿个参数。
  • GPT2 适合做生成式任务,如文本生成、机器翻译等,而 BERT 适合做预测任务,如文本分类、命名实体识别等。

GPT2 是一个基于 Transformer 的解码器模块的单向语言模型,它的目标是根据给定的文本序列,预测下一个最可能的单词。它使用了自注意力(self-attention)机制,可以捕捉文本中的长距离依赖关系和语义信息。

GPT2 不是一个理解语义或逻辑的模型,它只是根据统计规律和语言习惯来生成文本。

对于你给出的例子 “cat eats bat, who wins”,GPT2 可能会根据语法和常识来生成一些可能的续写,比如:

  • cat eats bat, who wins? The cat, of course.
  • cat eats bat, who wins? No one wins, it’s a tragedy.

它只是根据它在大量文本中学习到的概率分布来生成一些看起来合理的文本。GPT2 也不能判断哪个续写是100%正确或合适的

from transformers import GPT2Tokenizer, GPT2LMHeadModel
import torch

# 初始化一个分词器和一个模型
tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
model = GPT2LMHeadModel.from_pretrained("gpt2")

# 将例句转换为 token,并添加结束符
text = "cat eats bat, who wins"
tokens = tokenizer.encode(text, add_special_tokens=True)

# 将 token 转换为张量,并放入模型中
output = model(torch.tensor([tokens]))

# 从输出中获取最新一个 token 的概率分布,并取最大值作为预测结果
probs = output.logits[0, -1, :]
predicted_index = torch.argmax(probs).item()
# 将预测结果转换为对应的 token,并解码为文本
predicted_text = tokenizer.decode(tokens + [predicted_index])

#unformat_tokens
predicted = tokenizer.convert_ids_to_tokens([predicted_index])[0]

这些 token 通过一个字节对编码的词汇表映射为一个整数,最后将这些整数作为输入向量传入 Transformer 的解码器模块。在 Transformer 的解码器模块中,这些输入向量会经过多层的自注意力和全连接层,最终输出一个log概率分布,表示下一个最可能的 token。

  • GPT2 是在一个 40GB 的名为 WebText 的数据集上训练的,这个数据集是从互联网上爬取的各种文本,包括新闻、博客、论坛、书籍等。这个数据集包含了丰富的知识和风格,使得 GPT2 能够学习到不同领域和主题的语言规律和常识。
  • GPT2 有不同规模的版本,从小号到特大号,参数量从 1.17 亿到 15.4 亿不等。参数量越大,模型越能够存储更多的信息和细节,从而提高生成文本的质量和多样性。特大号的 GPT2 会造成一些伦理和社会问题,因此没有完全公开。
  • GPT2 不仅可以做文本生成,还可以做一些其他的自然语言处理任务,如文本摘要、阅读理解、情感分析



对于一个句子,不同注意力头可能会关注句法结构、语义关系、情感倾向等等,增强表达能力和泛化能力。另外,因为每个注意力头的维度都比原来的词嵌入维度小,能降低计算。

embed_dim = 768 # 词嵌入的维度,也是查询、键和值向量的维度
num_heads = 12 # 注意力头的数量
max_len = 512 # 最大序列长度

class PositionalEncoding(nn.Module):
  def __init__(self, embed_dim, max_len):
    super().__init__()
    self.pe = torch.zeros(max_len, embed_dim) # 初始化一个位置编码矩阵,形状为 (max_len, embed_dim)
    position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1) # 创建一个位置向量,形状为 (max_len, 1)
    div_term = torch.exp(torch.arange(0, embed_dim, 2).float() * (-math.log(10000.0) / embed_dim)) # 创建一个分母向量,形状为 (embed_dim / 2,)
    self.pe[:, 0::2] = torch.sin(position * div_term) # 偶数位置使用 sin 函数
    self.pe[:, 1::2] = torch.cos(position * div_term) # 奇数位置使用 cos 函数
    self.pe = self.pe.unsqueeze(0).transpose(0, 1) # 增加一个批次维度,并将位置维度和词嵌入维度交换,最终形状为 (max_len, 1, embed_dim)

  def forward(self, x):return x + self.pe[:x.size(0), :] # 将位置编码与词嵌入相加,注意只取输入序列长度对应的位置编码部分



class MultiHeadAttention(nn.Module):
  def __init__(self, embed_dim, num_heads):
    super().__init__()
    self.embed_dim = embed_dim # 词嵌入的维度
    self.num_heads = num_heads # 注意力头的数量
    self.depth = embed_dim // num_heads # 每个注意力头的维度
    assert embed_dim % num_heads == 0 # 确保词嵌入的维度可以被注意力头的数量整除

    self.wq,self.wk,self.wv,self.dense = nn.Linear(embed_dim, embed_dim) # 用于生成查询,键向,值,向量、对拼接后的向量进行线性变换的层

def split_heads(self, x, batch_size):
  x = x.view(batch_size, -1, self.num_heads, self.depth) # 将输入张量 x 重塑为 (batch_size, seq_len, num_heads, depth) 的形状
  return x.transpose(1, 2) # 交换第二个维度和第三个维度,得到 (batch_size, num_heads, seq_len, depth) 的形状

def forward(self, query, key, value, mask):
  batch_size = query.size(0) # 获取批次大小

  query = self.wq(query) # 将查询输入通过线性层,得到形状为 (batch_size, seq_len_q, embed_dim) 的查询向量
  key = self.wk(key) # 将键输入通过线性层,得到形状为 (batch_size, seq_len_k, embed_dim) 的键向量
  value = self.wv(value) # 将值输入通过线性层,得到形状为 (batch_size, seq_len_v, embed_dim) 的值向量

  query = self.split_heads(query, batch_size) # 将查询向量分裂成多个注意力头,得到形状为 (batch_size, num_heads, seq_len_q, depth) 的张量
  key = self.split_heads(key, batch_size) # 将键向量分裂成多个注意力头,得到形状为 (batch_size, num_heads, seq_len_k, depth) 的张量
  value = self.split_heads(value, batch_size) # 将值向量分裂成多个注意力头,得到形状为 (batch_size, num_heads,


nn.MultiHeadAttention 的输入有:

  • query: 查询张量,形状为 (L, N, E) 或 (N, L, E),其中 L 是目标序列长度,N 是批大小,E 是嵌入维度。
  • key: 键张量,形状与查询张量相同。
  • value: 值张量,形状与查询张量相同。
  • key_padding_mask: 可选的布尔类型张量,形状为 (N, S),其中 N 是批大小,S 是源序列长度。如果提供,则对于批中的每个元素,对于填充位置设置为 True 的键值将被忽略。当给出此参数时,查询张量的第一个维度必须等于键填充掩码的第一个维度。对于批中的每个元素,键填充掩码中设置为 True 的位置必须设置为 False 在 attn_mask 中。默认为 None。

nn.MultiHeadAttention 的输出有:

  • attn_output: 注意力输出张量,形状与查询张量相同。
  • attn_output_weights: 注意力权重张量,形状为 (N * num_heads, L, S),其中 N 是批大小,num_heads 是平行注意力头的数量,L 是目标序列长度,S 是源序列长度。如果 average_attn_weights 为 True,则返回平均后的注意力权重张量,形状为 (N, L, S)。


Report Page