手撸nano-gpt

nano GPT

跟着youtube上AndrejKarpathy大佬复现一个简单GPT

1.数据集准备

很小的莎士比亚数据集

wget https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt

1.1简单的tokenize

数据和等下的模型较简单,所以这里用了个很简单的直接按照字母去分割的tokenize

复杂些的可以用**tiktoken**: openai在gpt2上用的。

with open('input.txt', 'r', encoding='utf-8') as f:
    text = f.read()

print(len(text))
#>>> 1115394

chars = sorted(list(set(text)))
vocab_size = len(chars)

stoi = {ch: i for i, ch in enumerate(chars)}
itos = {i: ch for i, ch in enumerate(chars)}

string = 'hii there'
decode(encode(string)) == string
#>>> True

1.2切分训练集

import torch

data = torch.tensor(encode(text), dtype=torch.long)
print(data.shape, data.dtype)
#>>> torch.Size([1115394]) torch.int64

n = int(0.9 * len(data))
train_data = data[:n]
val_data = data[n:]

1.3获取小批量

注意,target在切分的时候错开了一个位置。原因是如果原串是[1,2,3,4,5]。当我们的input是[1,2,3]的时候应该生成一个[1,2,3,4]。

实现如下

torch.manual_seed(1337)
batch_size, block_size = 4, 8

def get_batch(split):
  data = train_data if split == 'train' else val_data
  ix = torch.randint(len(data) - block_size, (batch_size, ))
  x = torch.stack([data[i: i+block_size] for i in ix])
  y = torch.stack([data[i+1:i+1+block_size] for i in ix])
  return x, y

xb, yb = get_batch('train')
print(xb.shape)
print(xb)

print('target:')
print(yb.shape)
print(yb)

for b in range(batch_size):
  for t in range(block_size):
    # print(xb[b: :t+1])
    context = xb[b, :t+1].tolist()
    target = yb[b, t]
    print(f'when input is {context}, target is {target}')

image-20240308150934834

2.模型定义

2.1模型代码

这里具体解释一下为什么inputs, target送入模型前要做reshape。因为F.cross_entropy规定了 input 的shape必须是 [N, C] 其中N是样本数C是类别数这里也就是我们的vocab_size。与之对应,我们的 target 的shape就应该是[N]。input 送入模型后我们会得到input中每一个位置的下一个位置的预测,如果原文本是 [1,2,3],input : [1,2] ,target : [2,3]。那么送入 input 后我们可能会得到[2, 2.7]然后用这个和target计算损失。

import torch
import torch.nn as nn
from torch.nn import functional as F

torch.manual_seed(1337)

class BigramLanguageModel(nn.Module):

  def __init__(self, vocab_size):
    super().__init__()
    self.token_embedding = nn.Embedding(vocab_size, vocab_size)

  def forward(self, inputs, target=None):
    # inputs: [B,L], target: [B,1]
    
    logits = self.token_embedding(inputs) #[B,L,C]
    if target is None: 
      loss = None
    else:
      B, T, C = logits.shape
      logits = logits.reshape(B*T, C)
      target = target.reshape(-1)
      loss = F.cross_entropy(logits, target)

    return logits, loss

  def generate(self, idx, max_new_tokens):
    # idx is [B, T] array of indices in the current context
    for _ in range(max_new_tokens):
      logits, loss = self(idx)
      # 关注最后一个位置
      logits = logits[:, -1, :] # [B, C]
      probs = F.softmax(logits, dim=-1) # [B, C]
      idx_next = torch.multinomial(probs, num_samples=1) # [B, 1]
      idx = torch.cat([idx, idx_next], dim=1)
    return idx


m = BigramLanguageModel(vocab_size)
logits, loss = m(xb, yb)
print(logits.shape)
print(loss)

idx = torch.zeros((1, 1), dtype=torch.long)
print(decode(m.generate(idx, max_new_tokens=100)[0].tolist()))

2.2优化器及训练

optimizer = torch.optim.AdamW(m.parameters(), lr=1e-3)

batch_size = 32
for steps in range(1000):
   xb, yb = get_batch('train')

   logits, loss = m(xb, yb)
   optimizer.zero_grad(set_to_none=True)
   loss.backward()
   optimizer.step()
print(loss.item())

2.3 生成

print(decode(m.generate(idx, max_new_tokens=300)[0].tolist()))

3.加入注意力机制

3.1单头注意力

class Head(nn.Module):
    def __init__(self, head_size):
        super().__init__()
        self.key = nn.Linear(n_embd, head_size)
        self.query = nn.Linear(n_embd, head_size)
        self.value = nn.Linear(n_embd, head_size)
        self.register_buffer('tril', torch.tril(torch.ones(block_size, block_size)))
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        B, T, C  = x.shape

        k = self.key(x)
        q = self.query(x)
        v = self.value(x)

        wei = q @ k.transpose(-2, -1) * C ** -0.5
        wei = wei.masked_fill(self.tril[:T, :T] == 0, float('-inf'))
        wei = F.softmax(wei, dim=-1)
        wei = self.dropout(wei)
        out = wei @ v
        return out

3.2多头注意力

class MultiHeadAttention(nn.Module):
    def __init__(self, num_heads, head_size):
        super().__init__()
        self.heads = nn.ModuleList(
            [Head(head_size) for _ in range(num_heads)]
        )
        self.proj = nn.Linear(n_embd, n_embd)
        self.dropout = nn.Dropout(dropout)
    def forward(self, x):
        out = torch.cat([head(x) for head in self.heads], dim=-1)
        out = self.dropout(self.proj(out))
        return out

3.3前馈神经网络

class FeedForward(nn.Module):
    def __init__(self, n_embed):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(n_embed, 4 * n_embed),
            nn.ReLU(),
            nn.Linear(4 * n_embed, n_embed)
        )
        self.dropout = nn.Dropout(dropout)
    def forward(self, x):
        return self.dropout(self.net(x))

3.4transformerBlock

这里实现的是一个简易版的,如果n_embd是32, n_head=4, 那么每个单独的头只会产生 [B, T, 8] 这个尺寸的信息,然后将4个头的信息在dim=-1这个维度拼接起来即可。

class Block(nn.Module):
    def __init__(self, n_embd, n_head):
        super().__init__()
        head_size = n_embd // n_head
        self.sa = MultiHeadAttention(n_head, head_size)
        self.ffwd = FeedForward(n_embd)
        self.ln1 = nn.LayerNorm(n_embd)
        self.ln2 = nn.LayerNorm(n_embd)
    def forward(self, x):
        x = x + self.sa(self.ln1(x))
        x = x + self.ffwd(self.ln2(x))
        return x

4.最终训练

加入注意力机制和扩大模型后我们得到了这样的模型以及超参数

参数

batch_size = 64
block_size = 256
max_iters = 5000
eval_interval = 500
lr = 3e-4
device = 'cuda' if torch.cuda.is_available() else 'cpu'
eval_iters = 200
n_embd = 384
n_head = 6
n_layer = 6
dropout = 0.2

模型

class BigramLanguageModel(nn.Module):

    def __init__(self, n_embd):
        super().__init__()
        self.token_embedding = nn.Embedding(vocab_size, n_embd)
        self.lm_head = nn.Linear(n_embd, vocab_size)
        self.position_embedding_table = nn.Embedding(block_size, n_embd)
        self.blocks = nn.Sequential(
            Block(n_embd, n_head=n_head),
            Block(n_embd, n_head=n_head),
            Block(n_embd, n_head=n_head),
            Block(n_embd, n_head=n_head),
            Block(n_embd, n_head=n_head),
            Block(n_embd, n_head=n_head),
            nn.LayerNorm(n_embd)
        )
        self.ffwd = FeedForward(n_embd)

    def forward(self, inputs, target=None):
        # inputs: [B,L], target: [B,L]
        B, T = inputs.shape

        tok_emb = self.token_embedding(inputs)  # [B,T,C]
        pos_emb = self.position_embedding_table(torch.arange(T, device=device)) # [T, C]
        x = tok_emb + pos_emb
        x = self.blocks(x)
        x = self.ffwd(x)
        logits = self.lm_head(x) # [B, T, C] C = vocab_size

        if target is None:
            loss = None
        else:
            B, T, C = logits.shape

            logits = logits.reshape(B * T, C)
            target = target.reshape(-1)
            loss = F.cross_entropy(logits, target)

        return logits, loss

    def generate(self, idx, max_new_tokens):
        # idx is [B, T] array of indices in the current context
        for _ in range(max_new_tokens):
            idx_cond = idx[:, -block_size:]
            logits, loss = self(idx_cond)
            # 关注最后一个位置
            logits = logits[:, -1, :]  # [B, C]
            probs = F.softmax(logits, dim=-1)  # [B, C]
            idx_next = torch.multinomial(probs, num_samples=1)  # [B, 1]
            idx = torch.cat([idx, idx_next], dim=1)
        return idx

在A800上训练可以得到如下结果

可以看到loss已经降的不错了,只不过说出来的话还不太合理hhh

step 0: train loss 4.1744, val loss 4.1743
step 500: train loss 1.9218, val loss 2.0212
step 1000: train loss 1.5678, val loss 1.7493
step 1500: train loss 1.4277, val loss 1.6303
step 2000: train loss 1.3384, val loss 1.5647
step 2500: train loss 1.2810, val loss 1.5380
step 3000: train loss 1.2325, val loss 1.5121
step 3500: train loss 1.1924, val loss 1.5010
step 4000: train loss 1.1506, val loss 1.4956
step 4500: train loss 1.1204, val loss 1.5051


Havingly made me been's wife.
Thy father's name be heard he will not say
Your undoubter'd prift, that's that sympirate.

KING RICHARD III:
Those palasion most pallars, these measures
Shame laceling may be invenged by my breast.

DUKE VINCENTIO:
Then, I think it, is approach'd lip.

PRINCENTIUS:
The

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

飞塔防火墙开局百篇——002.FortiGate上网配置——在路由模式下使用虚拟接口对(virtual-wire-pair)

在路由模式下使用虚拟接口对(virtual-wire-pair) 拓扑配置接口配置策略 使用方有透明模式下一进一出的这样需求的组网,可以在路由模式下使用虚拟接口对(virtual-wire-pair)替代。 登陆FortiGate防火墙界面,…

城市基础信息管理系统 (VB版电子地图源码/公交车线路图/超市平面图)-143-(代码+程序说明)

转载地址http://www.3q2008.com/soft/search.asp?keyword143 请访问 以下地址,查看最新版本, 新增加支持 建筑物 距离测量, 鸟瞰, 地图放大缩小, VB完善地图扩充程序(城市街道基础信息管理系统 )-362-(代码+) 这套系统印象深刻 因为,写了一…

12双体系Java学习之局部变量和作用域

局部变量 局部变量的作用域 参数变量

数据结构中的堆(Java)

文章目录 把普通数组转换大顶堆数组堆增删改查替换堆排序 把普通数组转换大顶堆数组 该方式适用索引为0起点的堆 在堆(Heap)这种数据结构中,节点被分为两类:叶子节点(Leaf Nodes)和非叶子节点(N…

面试旺季,鸿蒙开发岗位怎么能没有面试题刷呢?

一年一度的面试浪潮来袭,你是否也想着利用这次机会去实现,跳槽涨薪的梦呢?在往年这个时候基本就有许多的小伙伴跑找到我要相关的面试题进行刷题,或要简历模板对自己的简历进行优化。 今年我又整了点新鲜的面试题,如果…

Linux系统之ipcalc命令的基本使用

Linux系统之ipcalc命令的基本使用 一、ipcalc命令介绍二、ipcalc命令的使用帮助2.1 ipcalc命令的help帮助信息2.2 ipcalc命令的语法解释 三、ipcalc命令的基本使用3.1 计算子网掩码3.2 计算网络地址3.3 找出所对应的主机名3.4 计算子网详细信息 四、ipcalc命令使用注意事项 一、…

由于 Positive Technologies 的专业知识,Moxa 消除了工业无线转换器中的一个漏洞。

我们的专家在 NPort W2150A 和 W2250A 转换器中发现了该漏洞 - 这些设备可将工业控制器、仪表和传感器连接到本地 Wi-Fi 网络。Moxa 已根据负责任的披露政策通知了该威胁,并发布了软件更新。 🥷 攻击者可以完全访问这些设备。 Positive Technologies 公…

目标检测应用场景—数据集【NO.28】无人机红外目标检测数据集

写在前面:数据集对应应用场景,不同的应用场景有不同的检测难点以及对应改进方法,本系列整理汇总领域内的数据集,方便大家下载数据集,若无法下载可关注后私信领取。关注免费领取整理好的数据集资料!今天分享…

(二)运行自己的stable-diffusion

前面的步骤如https://datawhaler.feishu.cn/docx/BwjzdQPJRonFh8xeiSOcRUI3n8b所示 拷贝、解压文件后,进入到stable-diffusion-webui的文件夹中,文件如下: 启动: 运行效果: 由于生成了好几个图,所以…

为什么不要使用elasticsearch

互联网上有很多文章,都在讲为什么要使用elasticsearch,却很少有人讲为什么不要使用elasticsearch。作为深入研究elasticsearch四年,负责公司万亿级别检索的操盘手,借着这篇文章,给大家分享一下,为什么不要使…

nginx swrr负载均衡算法的二宗罪及其改进的思考

目录 1. swrr负载均衡算法的二宗罪1.1 第一宗罪: 共振引起系统崩溃1.2 第二宗罪: 吃CPU大户 2. 对swrr负载均衡算法的改进的思考2.1 “共振”问题的解决2.2 “吃CPU大户”问题的解决 1. swrr负载均衡算法的二宗罪 swrr是一种基于加权轮询的负载均衡算法。它根据服务器的权重来分…

一款 Windows C盘文件清理工具

推荐一款 Windows C盘清理工具 0. 引言1. 下载地址 0. 引言 Windows 在使用过程,C盘的空间会变得越来越少。 Windows在C盘放了很多缓存,临时文件,我们自己还不敢乱删。 今天试了1款工具,可以很方便的查看C盘各个文件夹的文件大小…

中间件 | RabbitMq - [AMQP 模型]

INDEX 1 全局示意2 依赖 1 全局示意 AMQP,即高级消息队列协议(Advanced Message Queuing Protocol),整体架构如下图 producer 发送消息给 rabbit mq brokerrabbit mq broker 分发消息给 consumer消费producer/consumer 都通过 …

【Echarts】曲线图上方显示数字以及自定义值,标题和副标题居中,鼠标上显示信息以及自定义信息

欢迎来到《小5讲堂》 大家好,我是全栈小5。 这是《前端》系列文章,每篇文章将以博主理解的角度展开讲解, 特别是针对知识点的概念进行叙说,大部分文章将会对这些概念进行实际例子验证,以此达到加深对知识点的理解和掌握…

蝙蝠避障:我生活中的一道光

盲人的世界,是无尽的黑暗。看不见光,看不见色彩,甚至看不见自己的手。但在这个黑暗的世界里,我找到了一个光明的出口:一款可以障碍物实时检测的名为蝙蝠避障的盲人软件。 这款软件就像是我的一双眼睛。它通过先进的激光…

探索HDFS读写流程、节点机制和数据完整性

目录 写在前面一、HDFS的读写流程1.1 HDFS写数据流程1.2 机架感知1.3 HDFS读数据流程1.4 小结 二、 NameNode和SecondaryNameNode2.1 NN和2NN工作机制2.2 Fsimage和Edits解析2.2.1 oiv查看Fsimage文件2.2.2 oev查看Edits文件 2.3 CheckPoint时间设置 三、DataNode3.1 DataNode工…

Spring Cloud Alibaba微服务从入门到进阶(二)

Spring Boot配置管理 1、application.properties 2、application.yml 1.内容格式比较: .properties文件,通过 . 来连接,通过 来赋值,结构上,没有分层的感觉,但比较直接。 .yml文件,通过 &…

攻防演练|某车企攻防小记

前言 专注于web漏洞挖掘、内网渗透、免杀和代码审计,感谢各位师傅的关注!网安之路漫长,与君共勉! 实习期间针对某车企开展的一次攻防演练,过程很曲折,当时的记录没有了只是简单的总结一下。 攻击路径 收…

Promise图解,Pass

10-优化代码_哔哩哔哩_bilibili

Linux运维:深入了解 Linux 目录结构

Linux运维:深入了解 Linux 目录结构 一、 Linux 目录结构与 Windows之间的主要区别二、Linux根目录结构三、常见目录及其作用 💖The Begin💖点点关注,收藏不迷路💖 一、 Linux 目录结构与 Windows之间的主要区别 1、根…