鱼C论坛

 找回密码
 立即注册
查看: 78|回复: 2

训练模型中如何增加梯度计算

[复制链接]
发表于 2024-11-3 13:14:17 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
class CustomModel(nn.Module):
    def __init__(self, cfg, config_path=None, pretrained=False):
        super().__init__()
        self.cfg = cfg
        if config_path is None:
            self.config = AutoConfig.from_pretrained(cfg.model, output_hidden_states=True)
            self.config.hidden_dropout = 0.
            self.config.hidden_dropout_prob = 0.
            self.config.attention_dropout = 0.
            self.config.attention_probs_dropout_prob = 0.
            self.config.add_pooling_layer = False 
        else:
            self.config = torch.load(config_path)
        if pretrained:
            self.model = AutoModel.from_pretrained(cfg.model, config=self.config)
        else:
            self.model = AutoModel.from_config(self.config)
        
        self.model.resize_token_embeddings(len(CFG.tokenizer))
        if self.cfg.gradient_checkpointing:
            self.model.gradient_checkpointing_enable()
        self.fc = nn.Linear(self.config.hidden_size, self.cfg.num_labels)
        self._init_weights(self.fc)
        
    def _init_weights(self, module):
        if isinstance(module, nn.Linear):
            module.weight.data.normal_(mean=0.0, std=self.config.initializer_range)
            if module.bias is not None:
                module.bias.data.zero_()
        elif isinstance(module, nn.Embedding):
            module.weight.data.normal_(mean=0.0, std=self.config.initializer_range)
            if module.padding_idx is not None:
                module.weight.data[module.padding_idx].zero_()
        elif isinstance(module, nn.LayerNorm):
            module.bias.data.zero_()
            module.weight.data.fill_(1.0)
        
    def feature(self, input_ids, attention_mask, token_type_ids ):
        outputs = self.model( input_ids, attention_mask, token_type_ids)
        last_hidden_states = outputs[0] 
        feature = last_hidden_states[:, 0, :] ## CLS token
        return feature

    def forward(self,  input_ids, attention_mask, token_type_ids):
        feature = self.feature( input_ids, attention_mask, token_type_ids)
        output = self.fc(feature)
        return output.squeeze(-1)
# ====================================================
# inference
# ====================================================
def inference_fn(test_loader, model, device):
    preds = []
    model.eval()
    model.to(device)
    tk0 = tqdm(test_loader, total=len(test_loader))
    for inputs in tk0:
        inputs["input_ids"] = inputs["input_ids"].to(device)
        inputs["attention_mask"] = inputs["attention_mask"].to(device)
        inputs['token_type_ids'] = inputs['token_type_ids'].to(device)
        
        with torch.no_grad():
            with torch.cuda.amp.autocast(
                enabled=True, dtype=torch.float16, cache_enabled=True
            ):
                y_preds = model(inputs["input_ids"], inputs["attention_mask"],inputs['token_type_ids']  )
        preds.append(y_preds.to("cpu").numpy().astype(np.float32))
    predictions = np.concatenate(preds)
    return predictions

上述代码不使用梯度更新仅适用于测试集,如何修改可将其用于训练集
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2024-11-3 13:14:23 | 显示全部楼层
要将上述代码用于训练集,你需要在模型的训练过程中计算梯度并更新模型参数。可以通过以下步骤实现:

1. **设置模型为训练模式**:使用`model.train()`。
2. **定义损失函数和优化器**:选择合适的损失函数(如`nn.CrossEntropyLoss()`)和优化器(如`torch.optim.AdamW()`)。
3. **计算损失并反向传播**:使用`loss.backward()`计算梯度。
4. **更新模型参数**:使用`optimizer.step()`更新参数。
5. **清零梯度**:在每个训练步骤开始前使用`optimizer.zero_grad()`清除上一步的梯度。

以下是如何修改代码以适用于训练集的示例:
# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate)

# 训练函数
def train_fn(train_loader, model, device, optimizer, criterion):
    model.train()
    model.to(device)
    tk0 = tqdm(train_loader, total=len(train_loader))
    for inputs, labels in tk0:
        inputs, labels = inputs.to(device), labels.to(device)
        
        # 清零梯度
        optimizer.zero_grad()
        
        # 前向传播
        outputs = model(inputs, inputs, inputs)
        
        # 计算损失
        loss = criterion(outputs, labels)
        
        # 反向传播
        loss.backward()
        
        # 更新参数
        optimizer.step()
        
        tk0.set_postfix(loss=loss.item())

在训练过程中,确保你传入了正确的`train_loader`,并且`inputs`和`labels`是从数据集中获取的。这样就可以在训练集上进行梯度计算和参数更新了。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-11-3 13:23:45 | 显示全部楼层
FishC 发表于 2024-11-3 13:14
要将上述代码用于训练集,你需要在模型的训练过程中计算梯度并更新模型参数。可以通过以下步骤实现:

1. ...
import gc,os
from tqdm.auto import tqdm # visualizing tool for progress

# They will be used to pick the best model.pt given to the valid loss
best_model_epoch, valid_loss_values = [],[] 
valid_loss_min = [1] # arbitrary loss I set here
def train(model,device,train_dataloader,valid_dataloader,epochs,loss_fn,optimizer,metric):

    for epoch in range(epochs):
        gc.collect() # memory cleaning垃圾回收机制,减少占用内存
        model.train()

        train_loss = 0
        train_step = 0
        pbar = tqdm(train_dataloader, total=len(train_dataloader))#tqdm参数是一个iterable

        for batch, labels in pbar: # you can also write like "for batch in tqdm(train_dataloader"
            optimizer.zero_grad() # initialize
            train_step += 1
            

            train_input_ids = batch['input_ids'].to(device)#batch是一个字典
            train_attention_mask = batch['attention_mask'].to(device)
            train_token_type_ids = batch['token_type_ids'].to(device)
            train_labels = labels.squeeze().to(device).long()#label真实值long()转化成一维张量
            
            # You can refer to the class "TweetsModel" for understand 
            # what would be logits
            logits = model(train_input_ids, train_attention_mask,train_token_type_ids).to(device)
            predictions = torch.argmax(logits, dim=1) # get an index from larger one
            detached_predictions = predictions.detach().cpu().numpy()
            
            loss = loss_fn(logits, train_labels)
            loss.backward() 
            optimizer.step()
            model.zero_grad()

            train_loss += loss.detach().cpu().numpy().item()

            pbar.set_postfix({'train_loss':train_loss/train_step})#设置进度条显示信息
        pbar.close()

        with torch.no_grad():
            model.eval()

            valid_loss = 0
            valid_step = 0
            total_valid_score = 0

            y_pred = [] # for getting f1_score that is a metric of the competition
            y_true = []

            pbar = tqdm(valid_dataloader)
            for batch,labels in pbar:
                valid_step += 1

                valid_input_ids = batch['input_ids'].to(device)
                valid_attention_mask = batch['attention_mask'].to(device)
                valid_token_type_ids = batch['token_type_ids'].to(device)
                valid_labels = labels.squeeze().to(device).long()

                logits = model(valid_input_ids, valid_attention_mask).to(device)
                predictions = torch.argmax(logits, dim=1)
                detached_predictions = predictions.detach().cpu().numpy()
                
                loss = loss_fn(logits, valid_labels)
                valid_loss += loss.detach().cpu().numpy().item()

                y_pred.extend(predictions.cpu().numpy())
                y_true.extend(valid_labels.cpu().numpy())

            valid_loss /= valid_step
            f1 = f1_score(y_true,y_pred)

            print(f'Epoch [{epoch+1}/{epochs}] Score: {f1}')
            print(f'Epoch [{epoch+1}/{epochs}] Valid_loss: {valid_loss}')

            if valid_loss < min(valid_loss_min):
                print('model improved!')
            else:
                print('model not improved')
    
            torch.save(model.state_dict(), f'epoch:{epoch+1}_model.pt')#state_dict 是一个字典对象,包含了模型的所有可学习参数(如权重和偏置)及其当前值
            print('save checkpoint!')
            valid_loss_min.append(valid_loss)
            print(f'valid_loss_min:{min(valid_loss_min)}')

        best_model_epoch.append(f'/kaggle/working/epoch:{epoch+1}_model.pt')
        valid_loss_values.append(valid_loss)
        print('='*100)

    select_best_model() # refer to below function
    print('Train/Valid Completed!!')
    del train_dataloader, valid_dataloader # memory cleaning
    gc.collect()

def select_best_model():
    best_model = best_model_epoch[np.array(valid_loss_values).argmin()]
    os.rename(best_model, best_model.split('.pt')[0] + '_best.pt')#重命名文件
上述代码train_dataloader要求是什么形状的?batch是一个字典
有如下报错
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[128], line 4
      1 print('Training Start!')
      2 print('=' * 100)
----> 4 train(model,
      5     device,
      6     train_dataloader,
      7     valid_dataloader,
      8     CFG.epochs,
      9     loss_fn,
     10     optimizer,
     11     metric)
     13 del model,train_dataloader, valid_dataloader
     14 gc.collect()

Cell In[127], line 17, in train(model, device, train_dataloader, valid_dataloader, epochs, loss_fn, optimizer, metric)
     14 train_step = 0
     15 pbar = tqdm(train_dataloader, total=len(train_dataloader))#tqdm参数是一个iterable
---> 17 for batch, labels in pbar: # you can also write like "for batch in tqdm(train_dataloader"
     18     optimizer.zero_grad() # initialize
     19     train_step += 1

ValueError: too many values to unpack (expected 2)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-21 18:43

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表