手撸nano-gpt

news2024/11/22 10:37:55

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

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

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

相关文章

解析Perl爬虫代码:使用WWW__Mechanize__PhantomJS库爬取stackoverflow.com的详细步骤

在这篇文章中,我们将探讨如何使用Perl语言和WWW::Mechanize::PhantomJS库来爬取网站数据。我们的目标是爬取stackoverflow.com的内容,同时使用爬虫代理来和多线程技术以提高爬取效率,并将数据存储到本地。 Perl爬虫代码解析 首先&#xff0…

神经网络线性量化方法简介

可点此跳转看全篇 目录 神经网络量化量化的必要性量化方法简介线性对称量化线性非对称量化方法神经网络量化 量化的必要性 NetworkModel size (MB)GFLOPSAlexNet2330.7VGG-1652815.5VGG-1954819.6ResNet-50983.9ResNet-1011707.6ResNet-15223011.3GoogleNet271.6InceptionV38…

【机器学习300问】34、决策树对于数值型特征如果确定阈值?

还是用之前的猫狗二分类任务举例(这个例子出现在【机器学习300问】第33问中),我们新增一个数值型特征(体重),下表是数据集的详情。如果想了解更多决策树的知识可以看看我之前的两篇文章: 【机器…

spring启动时如何自定义日志实现

一、现象 最近在编写传统的springmvc项目时,遇到了一个问题:虽然在项目的web.xml中指定了log4j的日志启动监听器Log4jServletContextListener,且开启了日志写入文件,但是日志文件中只记录业务代码中我们声明了日志记录器的日志&a…

CPU设计实战-协处理器访问指令的实现

目录 一 协处理器的作用与功能 1.计数寄存器和比较寄存器 2.Status寄存器 3.Cause寄存器(标号为13) 4.EPC寄存器(标号为14) 5.PRId寄存器(标号为15) 6.Config 寄存器(标号为16)-配置寄存器 二 协处理器的实现 三 协处理器访问指令说明 四 具体实现 1.译码阶段 2.执行…

3/12/24交换排序、插入排序、选择排序、归并排序

目录 交换排序 冒泡排序 快速排序 插入排序 直接插入排序 选择排序 简单选择排序 堆排序 归并排序 各种排序的时间复杂度、空间复杂度、稳定性和复杂度 快排真题2016 选排真题2022 排序算法分为交换类排序、插入类排序、选择类排序、归并类排序。 交换排序 交换排…

【智能算法】哈里斯鹰算法(HHO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.代码实现4.参考文献 1.背景 2019年,Heidari 等人受到哈里斯鹰捕食行为启发,提出了哈里斯鹰算法(Harris Hawk Optimization, HHO)。 2.算法原理 2.1算法思想 根据哈里斯鹰特性,HHO分为探索-…

新智元 | Stable Diffusion 3技术报告流出,Sora构架再立大功!生图圈开源暴打Midjourney和DALL·E 3?

本文来源公众号“新智元”,仅用于学术分享,侵权删,干货满满。 原文链接:Stable Diffusion 3技术报告流出,Sora构架再立大功!生图圈开源暴打Midjourney和DALLE 3? 【新智元导读】Stability AI放…

chrome浏览器插件content.js和background.js还有popup都是什么,怎么通讯

popup 在用户点击扩展程序图标时(下图中的下载图标),都可以设置弹出一个popup页面。而这个页面中自然是可以包含运行的js脚本的(比如就叫popup.js)。它会在每次点击插件图标——popup页面弹出时,重新载入。…

如何阅读“计算机界三大神书”之一 ——SICP

《计算机程序的构造和解释》(Structure and Interpretation of Computer Programs,简记为SICP)是MIT的基础课教材,出版后引起计算机教育界的广泛关注,对推动全世界大学计算机科学技术教育的发展和成熟产生了很大影响。…

plantUML使用指南之序列图

文章目录 前言一、序列图1.1 语法规则1.1.1 参与者1.1.2 生命线1.1.3 消息1.1.4 自动编号1.1.5 注释1.1.6 其它1.1.7 例子 1.2 如何画好 参考 前言 在软件开发、系统设计和架构文档编写过程中,图形化建模工具扮演着重要的角色。而 PlantUML 作为一种强大且简洁的开…

springboot265基于Spring Boot的库存管理系统

基于Spring Boot库存管理系统 Inventory Meanagement System based on Spring Boot 摘 要 当下,如果还依然使用纸质文档来记录并且管理相关信息,可能会出现很多问题,比如原始文件的丢失,因为采用纸质文档,很容易受潮…

《vtk9 book》 官方web版 第3章 - 计算机图形基础 (3 / 5)

3.8 演员几何 我们已经看到了光照属性如何控制演员的外观,以及相机如何结合变换矩阵将演员投影到图像平面上。剩下的是定义演员的几何形状,以及如何将其定位在世界坐标系中。 建模 计算机图形学研究中的一个重要主题是建模或表示物体的几何形状。…

二分查找【详解】

本期介绍🍖 主要介绍:二分查找的简单思路,为什么必须在有序的前提下才能使用二分查找,该怎么用C程序来实现二分查找,二分查找的局限性👀。 文章目录 1. 题目2. 思路3. 前提条件4. 编写程序 1. 题目 在一个有…

Android Studio开发项目——记账簿应用

项目资源: 百度网盘链接:https://pan.baidu.com/s/1zN9lrIypi1t_QpuoBcdBNQ?pwdxj5h 提取码:xj5h 项目设计内容 1.基本功能描述 电子记账本是一种在线财务管理工具,用于帮助用户记录和管理他们的收入与支出。以下是电…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的水果新鲜程度检测系统(深度学习模型+UI界面代码+训练数据集)

摘要:开发水果新鲜程度检测系统对于提高农业产量和食品加工效率具有重大意义。本篇博客详细介绍了如何利用深度学习构建一个水果新鲜程度检测系统,并提供了完整的实现代码。该系统基于强大的YOLOv8算法,并结合了YOLOv7、YOLOv6、YOLOv5的对比…

ChatGPT 结合实际地图实现问答式地图检索功能基于Function calling

ChatGPT 结合实际地图实现问答式地图检索功能基于Function calling ChatGPT结合实际业务,主要是研发多函数调用(Function Calling)功能模块,将自定义函数通过ChatGPT 问答结果,实现对应函数执行,再次将结果…

打卡学习kubernetes——了解kubernetes组成及架构

目录 1 什么是kubernetes 2 kubernetes组件 3 kubernetes架构 1 什么是kubernetes kubernetes是一个旨在自动部署、扩展和运行应用容器的开源平台。目标是构建一个生态系统,提供组件和工具以减轻在公共和私有云中运行应用程序的负担。 kubernetes是&#xff1a…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的跌倒检测系统详解(深度学习模型+UI界面代码+训练数据集)

摘要:本研究介绍了一个基于深度学习和YOLOv8算法的跌倒检测系统,并对比分析了包括YOLOv7、YOLOv6、YOLOv5在内的早期版本性能。该系统可在多种媒介如图像、视频文件、实时视频流中准确识别跌倒事件。文内详解了YOLOv8的工作机制,并提供了相应…

一套plm系统大约多少钱?彩虹PLM系统

一套PLM(产品生命周期管理)系统的价格因多个因素而异,包括企业规模、需求复杂性、系统功能、技术支持和厂商选择等。一般来说,面向小型和微型企业的PLM产品,其价位在5万元~15万元左右;面向中型企业的中端PL…