Chatgpt论文笔记——GPT1详细解读与可运行的代码

这篇具有很好参考价值的文章主要介绍了Chatgpt论文笔记——GPT1详细解读与可运行的代码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

论文:https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf
时间:2018年6月
贡献:

提出了大规模数据上无监督预训练然后在目标任务上有监督finetune的范式。

具体实现

当时由于NLP领域不存在像图像领域中ImageNet那样百万级别标注的数据(并且图像的像素包含了比句子更丰富的信息,百万级别的图像标注数据相当于千万级别的句子标注数据),所以当时NLP的发展比较缓慢。本文相当于开疆拓土采用了在大规模数据上进行无监督预训练然后再目标任务上进行有监督finetune的尝试。
最后实验的效果是在12个NLP任务上,9个取得了超过SOTA的效果:
Chatgpt论文笔记——GPT1详细解读与可运行的代码

模型结构

GPT的模型结构核心组建是transformer的decoder模块,为什么用transformer而不用经典的RNN或者LSTM,GRU之类呢?因为作者在论文中说到,相比于RNN,transformer学到的特征更加的稳健一些,这个可能还是跟transformer里面的self attention有关,它更加的结构化并且可以学习了token和token之间的关系,对句子的理解更加的深刻。
完整的GPT1模型结构也比较简单:
Chatgpt论文笔记——GPT1详细解读与可运行的代码
整体采用了12个transformer的decoder模块构成,其实这里说的decoder给我造成了很多误解,我记得transformer的decoder部分长这样:
Chatgpt论文笔记——GPT1详细解读与可运行的代码
但是看GPT论文的结构又是transformer的encoder的样子:
Chatgpt论文笔记——GPT1详细解读与可运行的代码
所以一直没明白为啥说用的是decoder,仔细看了下别人实现的代码才发现了,主要是GPT仅仅用了单向的transformer,也就是mask multi head self attention,也就是transformer的decoder模块的这部分:
Chatgpt论文笔记——GPT1详细解读与可运行的代码
但是整个结构还是还是encoder一样的,只是MHA这个地方用了mask所以说成了用了decoder部分。
至于为什么用带Mask的MHA呢?

GPT中因为要完成语言模型的训练,也就要求Pre-Training预测下一个词的时候只能够看见当前以及之前的词,这也是GPT放弃原本Transformer的双向结构转而采用单向结构的原因。

代码

没有代码是不完整的,直接上模型结构的代码

import torch
import torch.nn as nn


class ScaledDotProductAttention(nn.Module):
    def __init__(self, d_k, attn_pdrop):
        super(ScaledDotProductAttention, self).__init__()
        self.d_k = d_k

        self.dropout = nn.Dropout(attn_pdrop)

    def forward(self, q, k, v, attn_mask):
        # |q| : (batch_size, n_heads, q_len, d_k)
        # |k| : (batch_size, n_heads, k_len, d_k)
        # |v| : (batch_size, n_heads, v_len, d_v)
        # |attn_mask| : (batch_size, n_heads, q_len, k_len)

        attn_score = torch.matmul(q, k.transpose(-1, -2)) / (self.d_k ** 0.5)
        attn_score.masked_fill_(attn_mask, -1e9)
        # |attn_scroe| : (batch_size, n_heads, q_len, k_len)

        attn_weights = nn.Softmax(dim=-1)(attn_score)
        attn_weights = self.dropout(attn_weights)
        # |attn_weights| : (batch_size, n_heads, q_len, k_len)

        output = torch.matmul(attn_weights, v)
        # |output| : (batch_size, n_heads, q_len, d_v)

        return output, attn_weights


class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, n_heads, attn_pdrop):
        super(MultiHeadAttention, self).__init__()
        self.n_heads = n_heads
        self.d_k = self.d_v = d_model // n_heads

        self.WQ = nn.Linear(d_model, d_model)
        self.WK = nn.Linear(d_model, d_model)
        self.WV = nn.Linear(d_model, d_model)
        self.scaled_dot_product_attn = ScaledDotProductAttention(self.d_k, attn_pdrop)
        self.linear = nn.Linear(n_heads * self.d_v, d_model)

    def forward(self, Q, K, V, attn_mask):
        # |Q| : (batch_size, q_len(=seq_len), d_model)
        # |K| : (batch_size, k_len(=seq_len), d_model)
        # |V| : (batch_size, v_len(=seq_len), d_model)
        # |attn_mask| : (batch_size, q_len, k_len)
        batch_size = Q.size(0)

        q_heads = self.WQ(Q).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2)
        k_heads = self.WK(K).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2)
        v_heads = self.WV(V).view(batch_size, -1, self.n_heads, self.d_v).transpose(1, 2)
        # |q_heads| : (batch_size, n_heads, q_len, d_k), |k_heads| : (batch_size, n_heads, k_len, d_k), |v_heads| : (batch_size, n_heads, v_len, d_v)

        attn_mask = attn_mask.unsqueeze(1).repeat(1, self.n_heads, 1, 1)
        # |attn_mask| : (batch_size, n_heads, q_len, k_len)
        attn, attn_weights = self.scaled_dot_product_attn(q_heads, k_heads, v_heads, attn_mask)
        # |attn| : (batch_size, n_heads, q_len, d_v)
        # |attn_weights| : (batch_size, n_heads, q_len, k_len)

        attn = attn.transpose(1, 2).contiguous().view(batch_size, -1, self.n_heads * self.d_v)
        # |attn| : (batch_size, q_len, n_heads * d_v)
        outputs = self.linear(attn)
        # |outputs| : (batch_size, q_len, d_model)

        return outputs, attn_weights


class PositionWiseFeedForwardNetwork(nn.Module):
    def __init__(self, d_model, d_ff):
        super(PositionWiseFeedForwardNetwork, self).__init__()

        self.linear1 = nn.Linear(d_model, d_ff)
        self.linear2 = nn.Linear(d_ff, d_model)
        self.gelu = nn.GELU()

        nn.init.normal_(self.linear1.weight, std=0.02)
        nn.init.normal_(self.linear2.weight, std=0.02)

    def forward(self, inputs):
        # |inputs| : (batch_size, seq_len, d_model)

        outputs = self.gelu(self.linear1(inputs))
        # |outputs| : (batch_size, seq_len, d_ff)
        outputs = self.linear2(outputs)
        # |outputs| : (batch_size, seq_len, d_model)

        return outputs


class DecoderLayer(nn.Module):
    def __init__(self, d_model, n_heads, d_ff, attn_pdrop, resid_pdrop):
        super(DecoderLayer, self).__init__()

        self.mha = MultiHeadAttention(d_model, n_heads, attn_pdrop)
        self.dropout1 = nn.Dropout(resid_pdrop)
        self.layernorm1 = nn.LayerNorm(d_model, eps=1e-5)

        self.ffn = PositionWiseFeedForwardNetwork(d_model, d_ff)
        self.dropout2 = nn.Dropout(resid_pdrop)
        self.layernorm2 = nn.LayerNorm(d_model, eps=1e-5)

    def forward(self, inputs, attn_mask):
        # |inputs| : (batch_size, seq_len, d_model)
        # |attn_mask| : (batch_size, seq_len, seq_len)

        attn_outputs, attn_weights = self.mha(inputs, inputs, inputs, attn_mask)
        attn_outputs = self.dropout1(attn_outputs)
        attn_outputs = self.layernorm1(inputs + attn_outputs)
        # |attn_outputs| : (batch_size, seq_len, d_model)
        # |attn_weights| : (batch_size, n_heads, q_len(=seq_len), k_len(=seq_len))

        ffn_outputs = self.ffn(attn_outputs)
        ffn_outputs = self.dropout2(ffn_outputs)
        ffn_outputs = self.layernorm2(attn_outputs + ffn_outputs)
        # |ffn_outputs| : (batch_size, seq_len, d_model)

        return ffn_outputs, attn_weights


class TransformerDecoder(nn.Module):
    def __init__(self, vocab_size, seq_len, d_model, n_layers, n_heads, d_ff, embd_pdrop, attn_pdrop, resid_pdrop,
                 pad_id):
        super(TransformerDecoder, self).__init__()
        self.pad_id = pad_id

        # layers
        self.embedding = nn.Embedding(vocab_size, d_model)
        self.dropout = nn.Dropout(embd_pdrop)
        self.pos_embedding = nn.Embedding(seq_len + 1, d_model)
        self.layers = nn.ModuleList(
            [DecoderLayer(d_model, n_heads, d_ff, attn_pdrop, resid_pdrop) for _ in range(n_layers)])

        nn.init.normal_(self.embedding.weight, std=0.02)

    def forward(self, inputs):
        # |inputs| : (batch_size, seq_len)
        positions = torch.arange(inputs.size(1), device=inputs.device, dtype=inputs.dtype).repeat(inputs.size(0), 1) + 1
        position_pad_mask = inputs.eq(self.pad_id)
        positions.masked_fill_(position_pad_mask, 0)
        # |positions| : (batch_size, seq_len)

        outputs = self.dropout(self.embedding(inputs)) + self.pos_embedding(positions)
        # |outputs| : (batch_size, seq_len, d_model)

        attn_pad_mask = self.get_attention_padding_mask(inputs, inputs, self.pad_id)
        # |attn_pad_mask| : (batch_size, seq_len, seq_len)
        subsequent_mask = self.get_attention_subsequent_mask(inputs).to(device=attn_pad_mask.device)
        # |subsequent_mask| : (batch_size, seq_len, seq_len)
        attn_mask = torch.gt((attn_pad_mask.to(dtype=subsequent_mask.dtype) + subsequent_mask), 0)
        # |attn_mask| : (batch_size, seq_len, seq_len)

        attention_weights = []
        for layer in self.layers:
            outputs, attn_weights = layer(outputs, attn_mask)
            # |outputs| : (batch_size, seq_len, d_model)
            # |attn_weights| : (batch_size, n_heads, seq_len, seq_len)
            attention_weights.append(attn_weights)

        return outputs, attention_weights

    def get_attention_padding_mask(self, q, k, pad_id):
        attn_pad_mask = k.eq(pad_id).unsqueeze(1).repeat(1, q.size(1), 1)
        # |attn_pad_mask| : (batch_size, q_len, k_len)

        return attn_pad_mask

    def get_attention_subsequent_mask(self, q):
        bs, q_len = q.size()
        subsequent_mask = torch.ones(bs, q_len, q_len).triu(diagonal=1)
        # |subsequent_mask| : (batch_size, q_len, q_len)

        return subsequent_mask


class GPT(nn.Module):
    def __init__(self,
                 vocab_size,
                 seq_len=512,
                 d_model=768,
                 n_layers=12,
                 n_heads=12,
                 d_ff=3072,
                 embd_pdrop=0.1,
                 attn_pdrop=0.1,
                 resid_pdrop=0.1,
                 pad_id=0):
        super(GPT, self).__init__()

        self.decoder = TransformerDecoder(vocab_size, seq_len, d_model, n_layers, n_heads, d_ff,
                                          embd_pdrop, attn_pdrop, resid_pdrop, pad_id)

    def forward(self, inputs):
        # |inputs| : (batch_size, seq_len)

        outputs, attention_weights = self.decoder(inputs)
        # |outputs| : (batch_size, seq_len, d_model)
        # |attention_weights| : [(batch_size, n_heads, seq_len, seq_len)] * n_layers

        return outputs, attention_weights



if __name__ == '__main__':
    model = GPT(vocab_size=10000)
    print(model)
    input = torch.ones(16, 128).long()
    out = model(input)
    print(out[0].shape)

看着很长,其实代码很简单,就是翻译这张图:
Chatgpt论文笔记——GPT1详细解读与可运行的代码
首先GPT这个类就是定义了12层transformer的decoder构成的:
Chatgpt论文笔记——GPT1详细解读与可运行的代码
vocab_size是词典的大小,就比如说英文一共有10w个单词,那么vocab_size就是10w。用来配合nn.embedding
模块把单词抽成embedding。
然后每个decoder里面就是对着图写代码了,其中比较核心的就是两个mask:
Chatgpt论文笔记——GPT1详细解读与可运行的代码文章来源地址https://www.toymoban.com/news/detail-503115.html

  • 第一个mask很好理解,因为句子的长度不一样,比如【good morning】和【nice to meet you】,一个长度是2一个是4,这样两个句子没办法组成训练数据,所以一般会把短的padding一下成【good morning pad pad】这样就长度一样可以组成训练数据了,图上第一个pad mask就是用于【good morning pad pad】这个里面那些是pad的部分,然后不参与self attention的计算。
  • 第二个mask就是之前我困惑的地方为啥叫decoder,decoder用的是带mask的MHA,这个mask就是图上的第二个框,他把某个单词后的单词都进行的mask。举个例子,还是【nice to meet you】,对于nice单词它的mask就是【0,1,1,1】,对于meet单词,它的mask就是【0,0,0,1】,这样从左往右相当于去了全1矩阵的上三角的1,所以这里的mask用pytorch的triu实现的:
    subsequent_mask = torch.ones(bs, q_len, q_len).triu(diagonal=1)

到了这里,关于Chatgpt论文笔记——GPT1详细解读与可运行的代码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Visual ChatGPT原理解读——大模型论文阅读笔记四

    Visual ChatGPT原理解读——大模型论文阅读笔记四

    论文:https://arxiv.org/abs/2303.04671 代码:https://github.com/microsoft/TaskMatrix 如图所示,用户上传一张黄花的图像并输入一个复杂的语言指令“请根据该图像的预测深度生成一朵红花,然后逐步使其像卡通一样”。 在交互管理器的帮助下,Visual ChatGPT 开始了相关视觉基础模型的执行

    2024年02月09日
    浏览(12)
  • 建议收藏:超详细ChatGPT(GPT 4.0)论文润色指南+最全提示词/咒语

    建议收藏:超详细ChatGPT(GPT 4.0)论文润色指南+最全提示词/咒语

    在这篇文章中,我将分享如何利用ChatGPT 4.0辅助论文写作的技巧,并根据网上的资料和最新的研究补充更多好用的咒语技巧。 本篇文章持续更新,祝大家写作顺利,如果对你有帮助,欢迎转发、推荐、分享! 温馨提示: 如果严肃对待这件事,请用GPT-4,别用GPT-3.5! 咒语心法

    2023年04月09日
    浏览(40)
  • 升级篇:超详细ChatGPT(GPT 4.0)论文润色指南+最全提示词/咒语

    升级篇:超详细ChatGPT(GPT 4.0)论文润色指南+最全提示词/咒语

    在这篇文章中,我将分享如何利用ChatGPT 4.0辅助论文写作的技巧,并根据网上的资料和最新的研究补充更多好用的咒语技巧。 本篇文章持续更新,祝大家写作顺利,如果对你有帮助,欢迎转发、推荐、分享! 温馨提示: 如果严肃对待这件事,请用GPT-4,别用GPT-3.5! 咒语心法

    2023年04月09日
    浏览(16)
  • 【论文笔记】——从transformer、bert、GPT-1、2、3到ChatGPT

    【论文笔记】——从transformer、bert、GPT-1、2、3到ChatGPT

    18年有bert和gpt这两个语言模型,分别源自transformer的编码器和解码器,都是无监督方式训练的 GPT-1用的是无监督预训练+有监督微调 GPT-2用的是纯无监督预训练。提升了网络层数和训练数据量 GPT-3沿用了GPT-2的纯无监督预训练,但是数据大了好几个量级 InstructGPT在GPT-3上用来自人

    2024年02月09日
    浏览(9)
  • 【YOLO系列】YOLOv3论文超详细解读(翻译 +学习笔记)

    【YOLO系列】YOLOv3论文超详细解读(翻译 +学习笔记)

    YOLOv3(《Yolov3:An incremental improvement》)是Joseph Redmon大佬关于YOLO系列的最后一篇,由于他反对将YOLO用于军事和隐私窥探,2020年2月宣布停止更新YOLO。  YOLOv3在YOLOv2的基础上改良了网络的主干,利用多尺度特征图进行检测,改进了多个独立的Logistic regression分类器来取代softmax来

    2024年02月07日
    浏览(19)
  • 【YOLO系列】YOLOv2论文超详细解读(翻译 +学习笔记)

    【YOLO系列】YOLOv2论文超详细解读(翻译 +学习笔记)

    时隔一年,YOLOv2隆重登场,新的YOLO版本论文叫《YOLO9000: Better, Faster, Stronger》,作者 Joseph Redmon 和 Ali Farhadi 在 YOLOv1 的基础上,进行了大量改进,提出了 YOLOv2 和 YOLO9000,重点解决YOLOv1召回率和定位精度方面的不足。 论文原文:[1612.08242] YOLO9000: Better, Faster, Stronger (arxiv.org) 项

    2023年04月08日
    浏览(11)
  • 【YOLO系列】YOLOv7论文超详细解读(翻译 +学习笔记)

    【YOLO系列】YOLOv7论文超详细解读(翻译 +学习笔记)

    终于读到传说中的YOLOv7了~≖‿≖✧ 这篇是在美团的v6出来不到一个月就高调登场,作者还是我们熟悉的AB大神(对,就是v4那个),读起来又是“熟悉”的感觉(贯穿了我的整个五一假期(╯-_-)╯╧╧)。 其实关于YOLOv7的网络结构还有很多细节值得深入研究,以及代码

    2024年02月02日
    浏览(13)
  • 【YOLO系列】YOLOv1论文超详细解读(翻译 +学习笔记)

    【YOLO系列】YOLOv1论文超详细解读(翻译 +学习笔记)

    从这篇开始,我们将进入YOLO的学习。YOLO是目前比较流行的目标检测算法,速度快且结构简单,其他的目标检测算法如RCNN系列,以后有时间的话再介绍。 本文主要介绍的是YOLOV1,这是由以Joseph Redmon为首的大佬们于2015年提出的一种新的目标检测算法。它与之前的目标检测算法

    2024年02月04日
    浏览(28)
  • 经典神经网络论文超详细解读(六)——DenseNet学习笔记(翻译+精读+代码复现)

    经典神经网络论文超详细解读(六)——DenseNet学习笔记(翻译+精读+代码复现)

    上一篇我们介绍了ResNet:经典神经网络论文超详细解读(五)——ResNet(残差网络)学习笔记(翻译+精读+代码复现) ResNet通过短路连接,可以训练出更深的CNN模型,从而实现更高的准确度。今天我们要介绍的是 DenseNet(《Densely connected convolutional networks》) 模型,它的基本

    2024年02月03日
    浏览(39)
  • 经典神经网络论文超详细解读(八)——ResNeXt学习笔记(翻译+精读+代码复现)

    经典神经网络论文超详细解读(八)——ResNeXt学习笔记(翻译+精读+代码复现)

    今天我们一起来学习何恺明大神的又一经典之作:  ResNeXt(《Aggregated Residual Transformations for Deep Neural Networks》) 。这个网络可以被解释为 VGG、ResNet 和 Inception 的结合体,它通过重复多个block(如在 VGG 中)块组成,每个block块聚合了多种转换(如 Inception),同时考虑到跨层

    2024年02月03日
    浏览(14)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包