首页 > 其他分享 >一步一步微调小模型

一步一步微调小模型

时间:2024-08-11 19:28:03浏览次数:13  
标签:loss torch 一步 模型 微调 batch path model size

本文记录一下,使用自顶向下的编程法一步步编写微调小语言模型的代码。

微调一个语言模型,本质上是把一个已经预训练过的语言模型在一个新的数据集上继续训练。那么一次微调模型的任务,可以分为下面三个大个步骤(不包含evaluation):

  • 加载已经预训练好的模型和新的数据集
  • 预处理模型和数据集
  • 开始循环训练
# ======== imports ========
import torch 

# ======== config  =========

num_epochs = ...

# ========= Load model, tokenizer, dataset  =========

model = ...
tokenizer = ...
dataset = ...

# ========= Preprocessing  =========

train_dataloader = ...

# ========= Finetuning/Training  =========

def compute_loss(X, y): ...

optimizer = ...
scheduler = ...

for epoch in range(num_epochs):
   for batch in train_dataloader:
        # Training code here
        out = model(batch['X'])
        loss = compute_loss(out, batch['y'])
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()
        scheduler.step()

torch.save(model.state_dict(), save_path)

本文的目标是微调一个小规模的千问模型Qwen-0.5B,使之具有更强的数学思考能力,使用微软的数据集microsoft/orca-math-word-problems-200k。因此,准备步骤就是导入相应的模块,加载相应的模型和数据集:

# ======== imports ========
import torch
from torch.utils.data import DataLoader
from transformers import AutoModelForCausalLM, AutoTokenizer
from datasets import load_dataset, Dataset

# ======== config  =========
model_path = 'Qwen/Qwen2-0.5B'
data_path = 'microsoft/orca-math-word-problems-200k'
save_path = './Qwen2-0.5B-math-1'

# ========= Load model, tokenizer, dataset  =========
model = AutoModelForCausalLM.from_pretrained(model_path)
tokenizer = AutoTokenizer.from_pretrained(model_path)
dataset = load_dataset(data_path)

# ========= Preprocessing  =========

train_dataloader = ...

# ========= Finetuning  =========

def compute_loss(X, y): ...

optimizer = ...
scheduler = ...

for epoch in range(num_epochs):
   for batch in train_dataloader:
        # Training code here
        out = model(batch['X'])
        loss = compute_loss(out, batch['y'])
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()
        scheduler.step()

torch.save(model.state_dict(), save_path)

优化器和调度器也按照惯例,使用Adam(或者SGD)和Cosine

# ========= Finetuning  =========
from transformers import get_cosine_schedule_with_warmup 

def compute_loss(X, y): ...

optimizer = torch.optim.Adam(model.parameters(), lr=2e-4, betas=(0.9,0.99), eps=1e-5)
scheduler = get_cosine_schedule_with_warmup(optimizer, num_training_steps=100, num_warmup_steps=10)

for epoch in range(num_epochs):
   for batch in train_dataloader:
        # Training code here
        out = model(batch['X'])
        loss = compute_loss(out, batch['y'])
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()
        scheduler.step()

torch.save(model.state_dict(), save_path)

现在我们聚焦于怎么预处理数据,也就是获得train_dataloader

# ======== config  =========
num_epochs = 5  # 量力而行
batch_size = 8  # 量力而行

# ========= Preprocessing  =========
train_dataloader = DataLoader(dataset, batch_size=batch_size)

Qwen模型和GPT类似,都是根据一串token输入,预测下一个token。所以在训练中的X就应该是一串token,y则是一个token。但实际上,Qwen以及Huggingface的其他AutoModelForCausalLM的输出不止是一个token,而是一串token。准确来说AutoModelForCausalLM输出的logits是一个的型为(batch_size, sequence_length, config.vocab_size)三维张量。因为模型会把输入x中的每一个x[0:i]子串都当作输入来预测一下,所以相应的y也应当调整为每一个x[0:i]子串的后一个token

那么,train_dataloader里面的每个batch的X和y都是token,且基本上y可以看作x左移了一位,而y[-1]则是x这个输入序列的真实预测值

x, y = x[0:-1], x[1:len(x)]

现在的任务就是把数据集变换成我们想要的模样。考察数据集,发现每个样本有两个序列

>>>  print(dataset)
DatasetDict({
    train: Dataset({
        features: ['question', 'answer'],
        num_rows: 200035
    })
})

我们使用Qwentokenizer提供的方法,把这两个序列合并成一个x

from transformers import default_data_collator

def ds_generator():
    for item in ds['train']:
        messages = [
            {"role": "user", "content": item['question']},
            {"role": "system", "content": item['answer']}
        ]
        text = tokenizer.apply_chat_template(
            messages,
            tokenize=False,
            add_generation_prompt=True
        )
        model_inputs = tokenizer([text], return_tensors="pt", padding='max_length', truncation=True)
        yield {
          'x': model_inputs['input_ids'][0][:-1],
          'y': model_inputs['input_ids'][0][1:]
        }
dataset = Dataset.map(ds_generator)
train_dataloader = DataLoader(new_ds, batch_size=batch_size,  collate_fn=default_data_collator)

我们这里只考虑语言模型,所以模型常规的输出应该是logits,则compute_loss则是计算模型输出与真实label之间的交叉熵。

def compute_loss(X, y):
   '''
   X: (batch_size, seq_len, vocab_size)
   y: (batch_size, seq_len)
   '''
   return torch.nn.functional.cross_entropy(X.view(-1, X.shape[-1]), y.view(-1))

(其实Qwen模型如果在喂输入x时同时把y喂进去,那么返回值就包含了loss:loss = model(x, y).loss


完整的微调代码如下

from types import SimpleNamespace

import torch
from torch.utils.data import DataLoader
from transformers import AutoModelForCausalLM, AutoTokenizer
from transformers import default_data_collator, get_cosine_schedule_with_warmup
from datasets import load_dataset, Dataset

model_path = 'Qwen/Qwen2-0.5B'
data_path = 'microsoft/orca-math-word-problems-200k'
save_path = './Qwen2-0.5B-math-1'


device = 'cuda' if torch.cuda.is_available() else 'cpu'

config = SimpleNamespace(
  batch_size=8,
  epochs=5,
  eps=1e-5,
  lr=2e-4,
  model_max_length=2048
)

model = AutoModelForCausalLM.from_pretrained(model_path)
tokenizer = AutoTokenizer.from_pretrained(model_path, padding_side="right", model_max_length=config.model_max_length)
dataset = load_dataset(data_path)

def ds_generator():
    for item in ds['train']:
        messages = [
            {"role": "user", "content": item['question']},
            {"role": "system", "content": item['answer']}
        ]
        text = tokenizer.apply_chat_template(
            messages,
            tokenize=False,
            add_generation_prompt=True
        )
        model_inputs = tokenizer([text], return_tensors="pt", padding='max_length', truncation=True)
        yield {
          'x': model_inputs['input_ids'][0][:-1],
          'y': model_inputs['input_ids'][0][1:]
        }

dataset = Dataset.map(ds_generator)
train_dataloader = DataLoader(dataset, batch_size=config.batch_size,  collate_fn=default_data_collator)

def compute_loss(X, y):
   '''
   X: (batch_size, seq_len, vocab_size)
   y: (batch_size, seq_len)
   '''
   return torch.nn.functional.cross_entropy(X.view(-1, X.shape[-1]), y.view(-1))

optimizer = torch.optim.Adam(model.parameters(), lr=config.lr, betas=(0.9,0.99), eps=config.eps)
scheduler = get_cosine_schedule_with_warmup(optimizer, num_training_steps=100, num_warmup_steps=10)

for epoch in range(num_epochs):
   for batch in train_dataloader:
        # Training code here
        out = model(batch['x'])
        loss = compute_loss(out.logits, batch['y'])
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()
        scheduler.step()

torch.save(model.state_dict(), save_path)

标签:loss,torch,一步,模型,微调,batch,path,model,size
From: https://www.cnblogs.com/zrq96/p/18345298

相关文章

  • AI Native应用中的模型微调
    关注我,持续分享逻辑思维&管理思维&面试题;可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导;推荐专栏《10天学会使用asp.net编程AI大模型》,目前已完成所有内容。一顿烧烤不到的费用,让人能紧跟时代的浪潮。从普通网站,到公众号、小程序,再到AI大模型网站。干货满满。学成后可......
  • MLM掩码语言模型在实际应用中有哪些常见的挑战和解决方案
    关注我,持续分享逻辑思维&管理思维&面试题;可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导;推荐专栏《10天学会使用asp.net编程AI大模型》,目前已完成所有内容。一顿烧烤不到的费用,让人能紧跟时代的浪潮。从普通网站,到公众号、小程序,再到AI大模型网站。干货满满。学成后可......
  • 如何评估分类任务的模型性能
    二分类&多分类任务的评估指标对比多分类任务和二分类任务的评估指标在概念上有一些相似性,但由于多分类任务涉及三个或更多类别,因此在评估方法和指标上存在一些差异:二分类任务的评估指标:准确率(Accuracy):正确预测的样本数占总样本数的比例。精确度(Precision):预测为......
  • 大模型agent开发之prompt提示词模板
    提示词工程的建模在大模型对话agent的开发中有着重要的地位,好的提示词模板可以辅助大模型做出更加准确的预测,得到更加准确的答案。本文使用langchain进行agnent开发,langchain中封装了很多工具和方法其中就包括不同的prompt模板,接下来本文将详细介绍几种不同风格的prompt模板的使用......
  • 7-3FM模型
    FM算法全称为因子分解机(FactorizationMachine)。它是广告和推荐领域非常著名的算法,在线性回归模型上考虑了特征的二阶交互。适合捕捉大规模稀疏特征(类别特征)当中的特征交互。FM及其衍生的一些较有名的算法的简要介绍如下:FM(FactorizationMachine):在LR基础上用隐向量点......
  • (2-2)多模态模型与框架:多模态模型
    2.2 多模态模型多模态模型是一类能够处理和融合来自不同模态(如文本、图像、音频等)数据的机器学习模型,它们通过联合学习不同模态的特征,实现更丰富和准确的理解和生成任务。多模态模型在诸如视觉问答(VQA)、图文生成和跨模态检索等应用中表现出色,它们能够同时理解和关联图像和文......
  • (2-3)多模态模型与框架:预训练模型
    2.3 预训练模型预训练模型是通过在大规模未标记数据上进行学习而生成的模型,它们能够捕捉数据中的统计特性和语义信息。这些模型通常在通用任务上进行预训练,如语言模型的掩码语言建模或图像模型的自监督学习,然后在特定任务上进行微调,以提高性能和泛化能力。例如本章前面介绍......
  • 基于腾讯云高性能应用服务 HAI 搭建并使用 AI 模型 StableDiffusion 进行文生图
    基于腾讯云高性能应用服务HAI搭建并使用AI模型StableDiffusion进行文生图HAI是什么高性能应用服务HAI与传统GPU云服务器区别使用高性能应用服务HAI一键部署StableDiffusionAIGC创建高性能应用服务启动HAI实例进行文生图快速构建StableDiffusion文生......
  • 【Redis进阶】Redis单线程模型和多线程模型
    目录单线程为什么Redis是单线程处文件事件理器的结构文件处理器的工作流程总结文件事件处理器连接应答处理器命令请求处理器命令回复处理器多线程为什么引入多线程多线程架构多线程执行流程关于Redis的问题Redis为什么采用单线程模型Redis为什么要引入多线程呢......
  • LLaMA-Factory微调llama3之模型的合并,并采用llama.cpp量化成ollama支持的gguf格式模型
    上期我们已经成功的训练了模型,让llama3中文聊天版知道了自己的名字这次我们从合并模型开始,然后使用llama.cpp量化成gguf格式,并且调用api(1)前期准备上期链接: 基于LLaMA-Factory微调llama3成为一个角色扮演大模型,保姆级教学零基础,导出GGUF格式前篇-CSDN博客 首先根据上期......