萱仔大模型学习记录4-BERT_lora实战

        这两天把 bert+lorade 代码已经改顺了,成功将这个lora应用到那个天池官方的新闻文本分类项目中,训练的结果较好,例如F1能达到0.90以上。(我第一个尝试做lora实践的原因还是我比较缺少资源,虽然有一些薅羊毛来的资源,但还是远远不够的,希望我入职之后能够快乐用卡嘿嘿嘿)

        话又说回来,Lora的优点在高效的模型微调和参数量减少上。由于在大模型中,微调所有参数的成本非常高,特别是在内存和计算资源方面。Lora通过添加低秩的矩阵,使得只需要微调少量参数,而不是整个模型。由于Lora只引入了一些低秩的矩阵进行训练,相较于微调整个模型,它大大减少了训练时间和计算资源。

        Lora可以应用于各种不同类型的模型,包括Transformer模型、卷积神经网络等,具有很高的适应性。Lora在保留预训练模型的原始参数的同时,只对特定任务引入改动,有助于保留模型的泛化能力,从而避免过拟合。

        Lora方法允许在微调新任务时保留预训练模型的原始参数不变,这意味着我们可以利用预训练模型的知识,而不需要从头开始训练。这样就可以用比较小的代价去处理我自己的任务,虽然bert本身不算一个特别巨大的模型,但是作为实践,还是可以尝试吧lora引入进去当作一个实践,从简单到困难需要循序渐进嘛。

        由于我已经理顺了一次lora的原理,对于个人简单来说就是外接一个新的A和B矩阵,来训练新的小小矩阵去×原本的大大模型,这样只训练小矩阵的成本就能大大降低,而且由于训练关注的也是更加值得关注的部分,模型的精度常常不降反升,用在自己的任务上非常的方便。

接下来是代码部分:

---------------------------------------------------------------------------------------------------------------

第一种代码(通用demo,方便后续自己修改的小小demo):

  • 定义低秩矩阵 A 和 B。
  • 替换原始的全连接层权重矩阵 W。
  • 仅微调 A 和 B 矩阵。
import torch
import torch.nn as nn
from transformers import BertModel, BertTokenizer

class BertWithLoRA(nn.Module):
    def __init__(self, num_classes, r=8):  # r为低秩矩阵的秩
        super(BertWithLoRA, self).__init__()
        self.bert = BertModel.from_pretrained('bert-base-uncased')
        self.num_classes = num_classes
        hidden_size = self.bert.config.hidden_size

        # 定义低秩矩阵
        self.A = nn.Parameter(torch.randn(hidden_size, r))
        self.B = nn.Parameter(torch.randn(r, hidden_size))
        self.classifier = nn.Linear(hidden_size, num_classes)

    def forward(self, input_ids, attention_mask, token_type_ids):
        outputs = self.bert(input_ids, attention_mask, token_type_ids)
        pooled_output = outputs[1]

        # 应用低秩矩阵变换
        low_rank_output = torch.matmul(pooled_output, self.A)
        low_rank_output = torch.matmul(low_rank_output, self.B)

        logits = self.classifier(low_rank_output)
        return logits

# 加载模型和分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertWithLoRA(num_classes=2)  # 假设我需要完成的是二分类任务

optimizer = torch.optim.Adam(model.parameters(), lr=1e-6)
criterion = nn.CrossEntropyLoss()

inputs = tokenizer("Example sentence", return_tensors='pt')
labels = torch.tensor([1])  # 示例标签

model.train()
optimizer.zero_grad()
outputs = model(**inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()

第二个代码(我自己完成前面章节提到的新闻文本分类任务的代码,供大家参考和自己学习记录使用): 

import torch
from torch.utils.data import DataLoader
from transformers import AdamW, BertTokenizer
import pandas as pd
import os
from sklearn.model_selection import train_test_split
from sklearn.metrics import precision_recall_fscore_support, accuracy_score
import openpyxl
from openpyxl import Workbook


def evaluate_model(model, dataloader, device):
    model.eval()
    preds = []
    true_labels = []

    for batch in dataloader:
        batch = tuple(t.to(device) for t in batch)
        token_ids, attn_masks, token_type_ids, labels = batch

        with torch.no_grad():
            logits = model(token_ids, attn_masks, token_type_ids)

        preds.extend(torch.argmax(logits, axis=1).cpu().numpy())
        true_labels.extend(labels.cpu().numpy())

    precision, recall, f1, _ = precision_recall_fscore_support(true_labels, preds, average='weighted')
    acc = accuracy_score(true_labels, preds)
    #记录一下我的验证的结果
    return {
        'accuracy': acc,
        'precision': precision,
        'recall': recall,
        'f1': f1,
        'preds': preds  
    }

# 保存模型和分词器
def save_model_and_tokenizer(model, tokenizer, dir_path):
    os.makedirs(dir_path, exist_ok=True)
    torch.save(model.state_dict(), os.path.join(dir_path, 'model.pth'))
    tokenizer.save_pretrained(dir_path)

# 保存指标和模型的函数
def save_metrics(epoch, avg_train_loss, metrics, best_f1, model, val_dataloader, device, tokenizer):
    # 每隔五轮就保存一次训练的模型
    if epoch % 5 == 0:
        save_model_and_tokenizer(model, tokenizer, f'./model_weights/epoch_{epoch}')

    if metrics['f1'] > best_f1:  #这里保存了我训练完的模型,最好的那个模型
        best_f1 = metrics['f1']
        save_model_and_tokenizer(model, tokenizer, './model_weights/best_model')

    val_metrics = evaluate_model(model, val_dataloader, device)
    predictions = val_metrics['preds']
    val_results = pd.DataFrame({'text': val_texts, 'label': val_labels, 'prediction': predictions})
    val_results.to_csv(f'./val_results/epoch_{epoch}.csv', index=False)

    # 更新指标
    print(f"Epoch: {epoch}, Loss: {avg_train_loss:.4f}, Accuracy: {metrics['accuracy']:.4f}, Precision: {metrics['precision']:.4f}, Recall: {metrics['recall']:.4f}, F1: {metrics['f1']:.4f}")

df = pd.read_csv('./data_news/train_set.csv', sep='\t')
train_texts, val_texts, train_labels, val_labels = train_test_split(df['text'].tolist(), df['label'].astype(int).tolist(), test_size=0.2)

train_dataset = XuanDataset(train_texts, train_labels, max_length=128)
val_dataset = XuanDataset(val_texts, val_labels, max_length=128)

train_dataloader = DataLoader(train_dataset, batch_size=8, shuffle=True)
val_dataloader = DataLoader(val_dataset, batch_size=8)

model = BertWithLoRA(num_classes=16)
device = 'cuda' if torch.cuda.is_available() else 'cpu'
model.to(device)


tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')

os.makedirs('./model_weights', exist_ok=True)
os.makedirs('./val_results', exist_ok=True)

excel_file = 'metrics1.xlsx'
if not os.path.exists(excel_file):
    wb = Workbook()
    ws = wb.active
    ws.title = 'Metrics'
    ws.append(['Epoch', 'Loss', 'Accuracy', 'Precision', 'Recall', 'F1'])
    wb.save(excel_file)

optimizer = AdamW(model.parameters(), lr=2e-5)
epochs = 100  # 训练轮数
best_f1 = 0.0

for epoch in range(1, epochs + 1):
    model.train()
    total_loss = 0

    for batch in train_dataloader:
        batch = tuple(t.to(device) for t in batch)
        token_ids, attn_masks, token_type_ids, labels = batch

        optimizer.zero_grad()
        logits = model(token_ids, attn_masks, token_type_ids)
        loss = nn.CrossEntropyLoss()(logits, labels)
        loss.backward()
        optimizer.step()

        total_loss += loss.item()

    avg_train_loss = total_loss / len(train_dataloader)
    
    # 验证模型
    metrics = evaluate_model(model, val_dataloader, device)
    
    wb = openpyxl.load_workbook(excel_file)
    ws = wb['Metrics']
    ws.append([epoch, avg_train_loss, metrics['accuracy'], metrics['precision'], metrics['recall'], metrics['f1']])
    wb.save(excel_file)

    # 保存模型和打印评价指标
    save_metrics(epoch, avg_train_loss, metrics, best_f1, model, val_dataloader, device, tokenizer)

结果数据就如下啦:

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值