花费7元训练自己的GPT 2模型

2023-10-27

在上一篇博客中,我介绍了用Tensorflow来重现GPT 1的模型和训练的过程。这次我打算用Pytorch来重现GPT 2的模型并从头进行训练。

GPT 2的模型相比GPT 1的改进并不多,主要在以下方面:

1. GPT 2把layer normalization放在每个decoder block的前面。

2. 最终的decoder block之后额外添加了一个layer normalization。

3. 残差层的参数初始化根据网络深度进行调节

4. 训练集采用了webtext(45GB),而不是之前采用的bookcorpus(5GB)

5. 更深的网络结构,最大的模型拥有15亿的参数,对比GPT 1是1.2亿的参数

GPT 2有以下四种不同深度的模型架构,如图:

以下我将用pytorch代码来搭建一个GPT 2的模型,以最小的GPT 2为例,采用bookcorpus的数据,在AutoDL平台的一个40G显存的A100显卡上进行训练,看看效果如何。

模型结构

整个模型的结构和GPT 1是基本一致的。

定义一个多头注意力模块,如以下代码:

class MHA(nn.Module):
    def __init__(self, d_model, num_heads, attn_pdrop, resid_pdrop):
        super().__init__()
        self.d_model = d_model
        self.num_heads = num_heads
        self.attn_pdrop = attn_pdrop
        self.resid_dropout = nn.Dropout(resid_pdrop)
        self.ln = nn.Linear(d_model, d_model*3)
        self.c_proj = nn.Linear(d_model, d_model)

    def forward(self, x):
        B, T, C = x.size()
        x_qkv = self.ln(x)
        q, k, v = x_qkv.split(self.d_model, dim=2)
        q = q.view(B, T, self.num_heads, C//self.num_heads).transpose(1, 2)
        k = k.view(B, T, self.num_heads, C//self.num_heads).transpose(1, 2)
        v = v.view(B, T, self.num_heads, C//self.num_heads).transpose(1, 2)
        y = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask=None, dropout_p=self.attn_pdrop if self.training else 0, is_causal=True)
        y = y.transpose(1, 2).contiguous().view(B, T, C)
        y = self.c_proj(y)
        y = self.resid_dropout(y)
        return y

这个模块接收一个输入数据,大小为(batch_size, seq_len, dimension),然后进行一个线性变换层,把数据映射为(batch_size, seq_len, dimension*3)的维度,这里的dimension*3表示的是qkv这三个值的拼接。接着就把这个数据切分为q,k,v三份,然后每份都把维度调整为(batch_size, seq_len, num_head, dimension/num_head),num_head表示这个自注意力模块包含多少个head。最后就可以调用scaled_dot_product_attention进行qk的相似度计算,进行缩放之后与v值相乘。Pytorch的这个函数提供了最新的flash attention的实现,可以大幅提升计算性能。最后就是对qkv的结果进行一个线性变换,映射为一个(batch_size, seq_len, dimension)的向量。

自注意力模块的输出结果,将通过一个Feed forward层进行计算,代码如下:

class FeedForward(nn.Module):
    def __init__(self, d_model, dff, dropout):
        super().__init__()
        self.ln1 = nn.Linear(d_model, dff)
        self.ln2 = nn.Linear(dff, d_model)
        self.dropout = nn.Dropout(dropout)
        self.layernorm = nn.LayerNorm(d_model)
        self.gelu = nn.GELU()

    def forward(self, x):
        x = self.ln1(x)
        x = self.gelu(x)
        x = self.ln2(x)
        x = self.dropout(x)
        return x

代码很简单,就是做了两次线性变换,第一次把维度扩充到dimension*4,第二次把维度恢复为dimension。

最后定义一个decoder block模块,把多头注意力模块和feed forward模块组合起来,代码如下:

class Block(nn.Module):
    def __init__(self, d_model, num_heads, dff, attn_pdrop, resid_pdrop, dropout):
        super().__init__()
        self.layernorm1 = nn.LayerNorm(d_model)
        self.attn = MHA(d_model, num_heads, attn_pdrop, resid_pdrop)
        self.layernorm2 = nn.LayerNorm(d_model)
        self.ff = FeedForward(d_model, dff, dropout)

    def forward(self, x):
        x = x + self.attn(self.layernorm1(x))
        x = x + self.ff(self.layernorm2(x))
        return x

有了decoder block之后,GPT 2的模型就是把这些block串起来,例如最小的GPT 2的模型结构是定义了12个decoder block。模型接收的是字符序列经过tokenizer之后的数字,然后把这些数字通过embedding层映射为向量表达,例如对每个token id,映射为784维度的一个向量。为了能在embedding的向量里面反映字符的位置信息,我们需要把字符的位置也做一个embedding,然后两个embedding相加。

输入数据经过embedding处理后,通过多个decoder block处理之后,数据的维度为(batch_size, seq_len, dimension), 我们需要通过一个权重维度为(dimension, vocab_size)的线性变换,把数据映射为(batch_size, seq_len, vocab_size)的维度。这里vocab_size表示tokenizer的单词表的长度,例如对于GPT 2所用的tokenizer,有50257个单词。对于输出数据进行softmax计算之后,我们就可以得到每个token的预测概率,从而可以和label数据,即真实的下一个token id进行比较,计算loss值。

GPT 2模型的代码如下:

class GPT2(nn.Module):
    def __init__(self, vocab_size, d_model, block_size, embed_pdrop, num_heads, dff, attn_pdrop, resid_pdrop, dropout, num_layer):
        super().__init__()
        self.token_embed = nn.Embedding(vocab_size, d_model, sparse=False)
        self.pos_embed = nn.Embedding(block_size, d_model, sparse=False)
        self.dropout_embed = nn.Dropout(embed_pdrop)
        #self.blocks = [Block(d_model, num_heads, dff, attn_pdrop, resid_pdrop, dropout) for _ in range(num_layer)]
        self.blocks = nn.ModuleList([Block(d_model, num_heads, dff, attn_pdrop, resid_pdrop, dropout) for _ in range(num_layer)])
        self.num_layer = num_layer
        self.block_size = block_size
        self.lm_head = nn.Linear(d_model, vocab_size, bias=False)
        self.token_embed.weight = self.lm_head.weight
        self.layernorm = nn.LayerNorm(d_model)

        self.apply(self._init_weights)

        # apply special scaled init to the residual projections, per GPT-2 paper
        for pn, p in self.named_parameters():
            if pn.endswith('c_proj.weight'):
                torch.nn.init.normal_(p, mean=0.0, std=0.02/math.sqrt(2 * num_layer))

    def _init_weights(self, module):
        if isinstance(module, nn.Linear):
            torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
            if module.bias is not None:
                torch.nn.init.zeros_(module.bias)
        elif isinstance(module, nn.Embedding):
            torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)

    def forward(self, x, targets=None):
        device = x.device
        b, t = x.size()
        pos = torch.arange(0, t, dtype=torch.long, device=device) 
        x = self.token_embed(x) + self.pos_embed(pos)
        x = self.dropout_embed(x)
        for block in self.blocks:
            x = block(x)
        x = self.layernorm(x)

        if targets is not None:
            logits = self.lm_head(x)
            loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1), ignore_index=-1)
        else:
            logits = self.lm_head(x[:, -1, :])
            loss = None

        return logits, loss

    def configure_optimizers(self, weight_decay, learning_rate, betas, device_type):
        # start with all of the candidate parameters
        param_dict = {pn: p for pn, p in self.named_parameters()}
        # filter out those that do not require grad
        param_dict = {pn: p for pn, p in param_dict.items() if p.requires_grad}
        # create optim groups. Any parameters that is 2D will be weight decayed, otherwise no.
        # i.e. all weight tensors in matmuls + embeddings decay, all biases and layernorms don't.
        decay_params = [p for n, p in param_dict.items() if p.dim() >= 2]
        nodecay_params = [p for n, p in param_dict.items() if p.dim() < 2]
        optim_groups = [
            {'params': decay_params, 'weight_decay': weight_decay},
            {'params': nodecay_params, 'weight_decay': 0.0}
        ]
        num_decay_params = sum(p.numel() for p in decay_params)
        num_nodecay_params = sum(p.numel() for p in nodecay_params)
        print(f"num decayed parameter tensors: {len(decay_params)}, with {num_decay_params:,} parameters")
        print(f"num non-decayed parameter tensors: {len(nodecay_params)}, with {num_nodecay_params:,} parameters")
        # Create AdamW optimizer and use the fused version if it is available
        fused_available = 'fused' in inspect.signature(torch.optim.AdamW).parameters
        use_fused = fused_available and device_type == 'cuda'
        extra_args = dict(fused=True) if use_fused else dict()
        optimizer = torch.optim.AdamW(optim_groups, lr=learning_rate, betas=betas, **extra_args)
        print(f"using fused AdamW: {use_fused}")

        return optimizer
    
    @torch.no_grad()
    def generate(self, idx, max_new_tokens, temperature=1.0, top_k=None, block_size=512):
        for _ in range(max_new_tokens):
            # if the sequence context is growing too long we must crop it at block_size
            idx_cond = idx if idx.size(1) <= block_size else idx[:, -block_size:]
            # forward the model to get the logits for the index in the sequence
            logits, _ = self(idx_cond)
            # pluck the logits at the final step and scale by desired temperature
            logits = logits / temperature
            # optionally crop the logits to only the top k options
            if top_k is not None:
                v, _ = torch.topk(logits, min(top_k, logits.size(-1)))
                logits[logits < v[:, [-1]]] = -float('Inf')
            # apply softmax to convert logits to (normalized) probabilities
            probs = F.softmax(logits, dim=-1)
            # sample from the distribution
            idx_next = torch.multinomial(probs, num_samples=1)
            # append sampled index to the running sequence and continue
            idx = torch.cat((idx, idx_next), dim=1)

        return idx

模型训练

定义好模型之后,我们就可以开始训练了。

首先我们需要准备训练数据集。GPT 2采用的是webtext,网上的一些公开网页数据来进行训练。在Huggingface上面有对应的一个公开数据集。不过考虑到我们的资源有限,我这次还是采用GPT 1所用的bookcorpus数据集来训练。

以下代码是下载huggingface的数据集,并用GPT 2的tokenizer来进行编码:

from datasets import load_dataset
from transformers import GPT2Tokenizer

dataset = load_dataset("bookcorpusopen", split="train")

block_size=513
tokenizer = GPT2Tokenizer.from_pretrained("gpt2")

def tokenize_function(examples):
    token_ids = [tokenizer(text) for text in examples["text"]]
    total_length = [len(t["input_ids"]) for t in token_ids]
    total_length = [(l//(block_size+1))*(block_size+1) for l in total_length]
    result = []
    label = []
 
    for i in range(len(total_length)):
        result.extend([token_ids[i]["input_ids"][j:j+block_size+1] for j in range(0, total_length[i], block_size+1)])
    return {"token_ids": result}
 
ds_test = ds['train'].select(range(10000))
 
tokenized_datasets = ds_test.map(
    tokenize_function, batched=True, num_proc=8, remove_columns=["title", "text"], batch_size=100
)
 
tokenized_datasets.save_to_disk("data/boocorpusopen_10000_512tokens")

GPT1采用的bookcorpus有7000多本书,huggingface的bookcorpusopen数据集有14000多本,这里我只采用了10000本书来构建数据集,对于每本书进行tokenizer转化后,每513个token写入为1条记录。这样我们在训练时,每条记录我们采用前1-512个token作为训练,取2-513个token作为label。

以下代码将读取我们处理好的数据集,并转化为pytorch的dataloader

from datasets import load_from_disk

dataset = load_from_disk("data/boocorpusopen_10000_512tokens")
dataset = dataset.with_format("torch")
dataloader = DataLoader(dataset, batch_size=args.batch_size, shuffle=True, num_workers=4)

然后我们就可以实例化一个GPT 2的model并开始训练,具体的代码可以见repo https://github.com/gzroy/gpt2_torch.git 里面的train.py文件。

如果在本地显卡上训练,对应12层的网络结构需要30多G的显存,我的显卡是2080Ti,只有11G显存,因此只能指定6层decoder。我们可以在autodl上面租用一个40G显存的A100显卡,价格是3.45元每小时,在这个显卡上开启半精度进行训练,大约1个小时可以跑10000个迭代,batch大小为64。我总共训练了2小时,最终在训练集上的Loss值为3.5左右,准确度为35%,花费为7元。

生成文本

最后我们可以基于这个训练了1个小时的GPT 2模型来测试一下,看生成文本的效果如何,如以下代码:

from transformers import GPT2Tokenizer
from model import GPT2
import torch
from torch.nn import functional as F
import argparse

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='gpt2 predict')
    parser.add_argument('--checkpoint_path', type=str, default='checkpoints/')
    parser.add_argument('--checkpoint_name', type=str, default='')
    parser.add_argument('--d_model', type=int, default=768)
    parser.add_argument('--block_size', type=int, default=512)
    parser.add_argument('--dff', type=int, default=768*4)
    parser.add_argument('--heads', type=int, default=12)
    parser.add_argument('--decoder_layers', type=int, default=6)
    parser.add_argument('--device', type=str, default='cuda')
    parser.add_argument('--input', type=str)
    parser.add_argument('--generate_len', type=int, default=100)
    parser.add_argument('--topk', type=int, default=5)
    args = parser.parse_args()

    tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
    vocab_size = len(tokenizer.get_vocab())
    model = GPT2(vocab_size, args.d_model, args.block_size, 0, args.heads, args.dff, 0, 0, 0, args.decoder_layers)
    model.to(args.device)
    model = torch.compile(model)
    checkpoint = torch.load(args.checkpoint_path+args.checkpoint_name)
    model.load_state_dict(checkpoint['model_state_dict'])

    token_id = tokenizer.encode(args.input)
    input_data = torch.reshape(torch.tensor(token_id, device=args.device), [1,-1])
    predicted = model.generate(input_data, args.generate_len, 1.0, args.topk, args.block_size)
    print("Generated text:\n-------------------")
    print(tokenizer.decode(predicted.cpu().numpy()[0]))

运行以下命令,给定一个文本的开头,然后让模型生成200字看看:

python predict.py --checkpoint_name model_1.pt --input 'it was saturday night, the street' --generate_len 200 --topk 10

生成的文本如下:

it was saturday night, the street lights blared and the street lights flickered on. A few more houses were visible.

The front door opened, and a large man stepped in and handed him one. He handed the man the keys and a small smile. It looked familiar, and then a little too familiar. The door was closed.

"Hey! You guys out there?" he said, his eyes wide.

"What are you up to?" the man asked.

"I'm just asking for you out in my office."

The man was about thirty feet away from them.

"I'm in a serious situation, but it's just the way you are."

He looked around at the man, the man looked up and down, and then his eyes met hers. He was a little older than he was, but his eyes were blue with red blood. He looked like a giant. His eyes were blue and red, and his jaw looked like a giant

可见生成的文本语法没有问题,内容上也比较连贯,上下文的逻辑也有关联。如果模型继续训练更长时间,相信生成文本的内容会更加好。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

花费7元训练自己的GPT 2模型 的相关文章

  • DeepSpeed 部署中bug以及解决方法

    text generation 1 can t find Rust compiler 在Linux上安装Rust 您可以使用curl或者类似包管理器的工具来安装Rust 使用curl命令安装Rust和Cargo curl proto htt
  • 使用gpt和mindshow快速制作PPT

    目录 准备工具 PPT制作大体流程 工具 步骤 获取PPT大纲 注意 要markdown格式 编辑 打开MindShow 找不到的可以私信我 编辑 创建ppt 编辑 选择ppt基本样式 编辑 点击下载 不过要提前登录一下就好 编辑 添加动画
  • 让GPT成为您的科研加速器丨GPT引领前沿与应用突破之GPT4科研实践技术与AI绘图

    GPT对于每个科研人员已经成为不可或缺的辅助工具 不同的研究领域和项目具有不同的需求 如在科研编程 绘图领域 1 编程建议和示例代码 无论你使用的编程语言是Python R MATLAB还是其他语言 都可以为你提供相关的代码示例 2 数据可
  • 惊爆GPT OpenAPI的调用以及API内的参数详解

    开篇 随着人工智能技术的飞速发展 自然语言处理技术 NLP 在过去几年也取得了突飞猛进的突破 在这个过程中 一个重要且可称为颠覆者的模型 GPT 3 第三代生成式预训练 Transformer 模型 的诞生 无疑大大加速了 NLP 领域的前
  • 开源了,我做了一个基于GPT的桌宠聊天系统:Pet-GPT!

    前言 最近chatgpt的热度高居不下 作为一个深度成谜者 发现大部分开发者在调用GPT的时候要不就是基于Tauri做本地窗口外接网页 要不就是web直接展示 在沉思苦想一段时间后 才发现好像没啥什么人用pyqt做啊 特别是没人用桌面宠物
  • gpt_academic使用注意事项

    不要开启360等安全卫士
  • GPT专业应用:生成实习报告

    正文共 1070 字 阅读大约需要 4 分钟 大学生必备技巧 您将在4分钟后获得以下超能力 快速生成实习报告 Beezy评级 B级 经过简单的寻找 大部分人能立刻掌握 主要节省时间 推荐人 Kim 编辑者 Linda 图片由Lexica 生
  • 【新手教程】Windows本地化安装、运行,部署Auto-GPT

    Windows安装 运行Auto GPT 第一 准备条件 OpenAI Key 请登录官网获取 sk RhLoBodCbL6AAlyuYeC8T3BlbkFJ5vJfX9P5Md504SmADtth 第二 环境搭建 2 1安装python
  • 国产版ChatGPT大盘点

    我们看到 最近 国内大厂开始密集发布类ChatGPT产品 一方面 是因为这是最近10年最大的趋势和机会 另一方面 国内的AI 不能别国外卡了脖子 那在类ChatGPT赛道上 哪些中国版的ChatGPT能快速顶上 都各有哪些困境需要突破呢 本
  • 关于自己对像Chat-GPT的反应速度感悟

    这几个月相信大家应该对ChatGPT都不陌生了吧 因为这个东西已经在各大社交媒体可以说是无限次曝光了 就连一些其他行业的 完全跟科技行业沾不上边的朋友们 都知道了 可想而知 这个是有多火了 而我之所以发表这个感悟 其实也是自己的一个反思吧
  • “GPT+健康医疗”赋能医疗行业“数智化”发展,景联文科技提供高质量医疗数据库

    近日 ChatGPT这个代表着通用版的大型语言模型以其出色的表现在全球互联网上引人注目 它所使用的GPT技术基础为人工智能应用开启了全新的世界 大模型时代已经到来 它已变成基础设施 变成算力 变成生产力 大模型可能有通用技术 但更重要的是如
  • Unlimited “使用GPT-4 ”!它来了!

    备注本文末尾附录注册登录过程 平台介绍 聊天机器Chat fore人front ai是一为款基于人主工智能的题聊天的机器人主平台 旨在帮菜助企 业提可以高客户服务质是量和一效率 该平款台利用自然语精言处理技术和机器致学习的算法 能够自牛动排
  • 基于开源大模型Vicuna-13B构建私有制库问答系统

    本教程专注在怎么使用已经开源的模型和项目 构建一个可以私有化部署的问答知识库 而且整体效果要有所保障 主要工作包括 选择基础模型 openAI claude 这些商用的 或者其他的开源的 这次我们选择 Vicuna 13B 开源有很多的知识
  • 使用 ChatGPT 辅助学习——为自己找一个老师

    我们每个人都有许多标签 例如高中生 成绩中等 文科 根据这些标签我和其他拥有相同标签的人分配了相同的教程 班级和老师 这可以带来效率上的提升 因为同一份教程 老师就可以服务几十上百人 而无须为每个人定制 但也正是这样造成了忽略个性的问题 而
  • 自然语言处理: 第七章GPT的搭建

    自然语言处理 第七章GPT的搭建 理论基础 在以transformer架构为框架的大模型遍地开花后 大模型的方向基本分成了三类分别是 decoder only架构 其中以GPT系列为代表 encoder only架构 其中以BERT系列为代
  • GPT专业应用:生成会议通知

    正文共 917 字 阅读大约需要 3 分钟 公务员 文秘必备技巧 您将在3分钟后获得以下超能力 快速生成会议通知 Beezy评级 B级 经过简单的寻找 大部分人能立刻掌握 主要节省时间 推荐人 Kim 编辑者 Linda 图片由Lexica
  • 文心千帆为你而来

    1 前言 3月16号百度率先发布了国内第一个人工智能大语言模型 文心一言 文心一言的发布在业界引起了不小的震动 而文心一言的企业服务则由文心千帆大模型平台提供 文心千帆大模型平台是百度智能云打造出来的一站式大模型开发与应用平台 提供包括文心
  • GPT系列训练与部署——GPT2环境配置与模型训练

    本文为博主原创文章 未经博主允许不得转载 本文为专栏 Python从零开始进行AIGC大模型训练与推理 系列文章 地址为 https blog csdn net suiyingy article details 130169592 Colo
  • GPT与ArcGISPro结合编程,地理空间分析,图绘制、渲染

    在地学领域 ArcGIS几乎成为了每位科研工作者作图 数据分析的必备工具 而ArcGIS Pro3除了良好地继承了ArcMap强大的数据管理 制图 空间分析等能力 还具有二三维融合 大数据 矢量切片制作及发布 任务工作流 时空立方体等特色功
  • 如何快速申请GPT账号?

    详情点击链接 如何快速申请GPT账号 一OpenAI 1 最新大模型GPT 4 Turbo 2 最新发布的高级数据分析 AI画图 图像识别 文档API 3 GPT Store 4 从0到1创建自己的GPT应用 5 模型Gemini以及大模型

随机推荐

  • Mac电脑如何删除磁盘及双系统分区?

    对于一些新手来说 在使用Mac电脑时可能会选择对硬盘进行分区或者安装双系统 但是 如果后期不需要这些分区时 如何删除它们呢 首先在应用程序中找到实用工具并打开文件夹 然后选择磁盘工具打开 在左侧选中需要修改的磁盘 接着在右侧上方菜单中点击
  • 【当LINUX系统出现网络问题时该如何排查】

    当LINUX出现网络问题时该如何排查 具体问题具体分析 遵循相应的排查思路 一 网络不通时需要进行的处理 1 检测链路是否连通 2 网卡是否正常启用 3 检测路由与网关的配置 4 DNS工作状况 5 检测是否可以正常路由到远程主机 6 检查
  • selenium无登录状态爬取Boss直聘

    BOSS是我很早就实现数据爬取的网站 那会直接用request即可 最近再次尝试以前的代码发现 它做了一些反爬处理 当你直接访问例如https www zhipin com c101210100 b 西湖区 query 数据分析杭州这样的网
  • C++模板基础(五)

    函数模板 函数模板的 完全 特化 template lt gt void f int template lt gt void f int 并不引入新的 同名 名称 只是为某个模板针对特定模板实参提供优化算法 函数模板的特化本质上是实例化 有
  • SQL Server不允许保存更改的解决方法

    点击上面的 工具 选项 在选项对话框中 点击 设计器 表设计器和数据库设计器 去掉 阻止保存要求重新创建表的更改 前面的勾 然后确定 好啦 再去试试吧 应该可以正常修改表的结构啦 o
  • 【NLP】第 2 章 : Transformers简介

    2017 年 12 月左右 发表了一篇题为 Attention Is All You Need 的开创性论文 这篇论文彻底改变了 NLP 领域在未来时代的面貌 本文描述了转换器和所谓的序列到序列架构 序列到序列 或 Seq2Seq 神经网络
  • Excel2013 利用phonetic函数将多行数据合并到同一单元格中

    场景 有一列邮箱数据 现在需要将他们合并到同一个单元格内 且邮箱之间要用英文的逗号隔开 以前五条邮箱为例 利用phonetic函数实现这种合并 合并结果 其中 E列是添加的辅助列
  • Python 调用Sikuli Jar包

    Python 调用Sikuli Python 目录 Sikuli简介 简要说明 环境设置 第一种 Jpype 第二种 Pyjnius 结论 目录 Sikuli简介 Sikuli是由MIT 麻省理工学院 研究团队发布的一种图形化编程技术 编程
  • 实现SSM简易商城项目的商品查询功能

    实现SSM简易商城项目的商品查询功能 介绍 在SSM Spring SpringMVC MyBatis 框架下 我们可以轻松地实现一个简易商城项目 本博客将重点介绍如何实现商品查询功能 帮助读者了解并掌握该功能的开发过程 步骤 1 创建数据
  • LeetCode-1306. Jump Game III

    Given an array of non negative integers arr you are initially positioned at start index of the array When you are at ind
  • 用Flask和Vue制作一个单页应用(自己学习)

    这里以一个简单的例子 展示如何把前端页面的增删改查请求 传递到后端进行数据的操作 一 https zhuanlan zhihu com p 311323583 二 https zhuanlan zhihu com p 311510196 三
  • 王者荣耀s15服务器维护,王者荣耀s15赛季更新全部内容

    原标题 王者荣耀s15赛季更新全部内容 王者荣耀S14很快就要结束了 体验服的版本更新也已经放出来进行测试了 大家都对新赛季的改动非常期待 究竟会有哪些英雄成为新的版本之子 哪些英雄会沦为下水道呢 以下均为体验服内容 不代表最终版本数据 p
  • 栈和队列 Stack and Queue

    Stack and Queue Stack and Queue Linked List Implementation ListNode Stack Queue Array Implementation Stack Queue Stack a
  • 又一巨头宣布入局AIGC,一口气开源数个模型,还道出了它的变现之道

    金磊 发自 凹非寺量子位 公众号 QbitAI AIGC AI生成内容 这个概念在今年可以说是火得一塌糊涂 例如Stable Diffusion 只要对它说一句话 唰唰唰 地就能秒生成画作 再如最近大火的ChatGPT 对答如流堪比人类 简
  • 学习day59

    昨天学了插槽 但是没有即笔记了 今天的是vuex 总体来说 vuex就是一个共享单车 每个人都可以使用他 也可也对他进行反馈 即把一个数据列为vuex 然后每个组件可以使用这个对象 也可也反过来反馈他 这一个设计是将A组件的一个数据作为公共
  • git报错

    Git报错总结 一 git remote add origin git code aliyun com account TestProject git发生报错 报错信息如下 error remote origin already exist
  • 输入三个字符串,要求找出其中的最大者

    解题思路 设一个二维的字符数组 大小为3 20 每一行存放一个字符串 字符串比较用strcmp 字符串复制用strcpy include
  • Shell脚本编程教程

    1 Shell脚本语言的基本结构 1 1 Shell脚本的用途 自动化常用命令 执行系统管理和故障排除 创建简单的应用程序 处理文本或文件 1 2 Shell脚本基本结构 Shell脚本编程 是基于过程式 解释执行的语言 编程语言的基本结构
  • jquery获取select选中的值

    误区 一直以为jquery获取select中option被选中的文本值 是这样写的 s text 获取所有option的文本值 实际上应该这样 s option selected text 获取选中的option的文本值 获取select中
  • 花费7元训练自己的GPT 2模型

    在上一篇博客中 我介绍了用Tensorflow来重现GPT 1的模型和训练的过程 这次我打算用Pytorch来重现GPT 2的模型并从头进行训练 GPT 2的模型相比GPT 1的改进并不多 主要在以下方面 1 GPT 2把layer nor