2022 Sohu Campus NLP Algoritmo Competição Primeiro Lugar Programa de Análise de Sentimentos Compreensão e Reaparecimento

Índice

1. Compreensão da competição e programa

Defeitos da linha de base

plano de primeiro lugar

Alterações de dimensão de dados

Em segundo lugar, a implementação do código

código número um

swa - peso médio

código de linha de base

3. Exibição do efeito

O primeiro plano:

a、adamW + swa

b、sgd + swa 

esquema de linha de base


        Em Zhihu, vi o compartilhamento e o compartilhamento do plano de análise de sentimentos em primeiro lugar na Competição de Algoritmo de PNL do Campus Sohu 2022. Senti que o plano era muito simples e elegante e, ao mesmo tempo, tinha uma pitada de aprendizado imediato ( estritamente falando, não foi um aprendizado imediato), e o efeito foi muito bom. Embora eles também tenham dado idéias e códigos mais detalhados baseados em pytorch-lightning em seu compartilhamento de soluções, mas alguns detalhes não são claros o suficiente, e o código não é fácil de entender, então faça uma explicação mais clara no blog E compartilhe de forma mais concisa (melhor entender o código baseado em tocha).

1. Compreensão da competição e programa

        A tarefa desta competição é - Polaridade de sentimento de descrição de texto orientada a entidade e análise de intensidade de cor. A polaridade e a intensidade emocional são divididas em cinco situações: extremamente positiva, positiva, neutra, negativa e extremamente negativa. O concorrente precisa analisar a polaridade emocional e a intensidade da entidade a partir da perspectiva da descrição do texto para cada objeto da entidade.

Os dados são os seguintes:

{"id": 7410, "content": "Com um Nets tão incrível, os fãs e especialistas podem ter grandes expectativas para ele nos playoffs? Portanto, ao longo da temporada, as previsões de todos são razoáveis. A temporada deste ano As finais da Conferência Leste de os playoffs ainda devem ser os mesmos do ano passado, e os Nets e Bucks ainda devem se encontrar conforme programado. Os Nets Big Three de hoje não são o máximo, mas também são fáceis de "cozinhar o Ding Jie Niu", o caminho para os Bulls ainda devem ser Por muito tempo, a NBA ainda é o palco onde a superestrela fala!", "entity": {"Nets": 1, "Playoffs": 0}}
{"id": 88679, "content": "2014.09 Membro do Comitê Permanente do Comitê do Partido da Província de Hainan, Secretário do Comitê do Partido Municipal de Danzhou e Vice-Secretário do Comitê de Trabalho da Zona de Desenvolvimento Econômico de Yangpu 2014.10 Membro do Comitê Permanente do Comitê do Partido Provincial de Hainan e Secretário do Comitê do Partido Municipal de Sanya 2016.11 Membro do Comitê Permanente do Comitê do Partido Provincial de Hainan e Secretário do Comitê do Partido Municipal de Haikou inspecionado em setembro de 2019.", "entity": {"Secretário do Comitê do Partido Municipal": 0, "Comitê do Partido da Província de Hainan": 0}}

Para o texto do conteúdo e a entidade fornecida nos dados acima, analise as cores emocionais contidas no conteúdo, respectivamente. Obviamente, esta é uma tarefa de classificação. Quando vi essa pergunta, a solução que passou pela minha mente foi exatamente a mesma que a linha de base que eles deram:

[CLS]conteúdo[SEP]entidade_0[SEP]

[CLS]conteúdo[SEP]entidade_1[SEP]

[CLS]conteúdo[SEP]entidade_2[SEP]

......

[CLS]conteúdo[SEP]entity_n[SEP]

Depois de splicing o conteúdo e cada entidade de acordo com o acima, ele é enviado para o modelo bert para extrair o vetor de sentença, e depois passa pelo classificador. Isso completa a tarefa. Esse esquema também é usado na competição. Diz-se que o efeito não é muito satisfatório. Confira o plano para o primeiro lugar na competição:

Defeitos da linha de base

Conforme mostrado na figura abaixo (citando a figura no compartilhamento do plano do autor do concurso)

 Como os dados de entidade de cada parte dos dados não são iguais, um esquema de splicing como a linha de base fará com que o modelo veja o texto do conteúdo de forma diferente, o que pode ter um impacto no efeito final; ao mesmo tempo, cada parte dos dados é copiou o número de entidades, resultando em muitos dados de treinamento e baixa eficiência. Outro problema é que a seleção do vetor de sentença obtido pelo modelo também terá um certo erro.No esquema de linha de base, são usados ​​cls ou todos os tokens embeddings para o meanPooling, o que também terá um certo impacto no resultado final; o por último é que cada entidade é emendada individualmente, parece um pouco enfraquecendo a conexão entre cada entidade, o que terá um certo impacto no resultado final.

plano de primeiro lugar

Conforme mostrado na figura acima (referente à imagem compartilhada pelo autor do concurso), as entidades em cada dado são emendadas com [MASK], e depois emendadas com o texto do conteúdo usando [SEP], para que uma classificação possa ser construída com eficiência em um dado A tarefa não precisa ser repetida várias vezes para cada dado como a linha de base. Ao mesmo tempo, a escolha do vetor da última frase também é evitada aqui, e a incorporação correspondente a [MASK] é usada diretamente como a incorporação de classificação de cada emoção da entidade. A introdução de [MASK] neste esquema também tem uma sugestão de aprendizado imediato, e o autor disse que o efeito é melhor. Por outro lado, não é um aprendizado estrito de prompt, não precisa prever qual é o token específico em [Mask] e depois fazer o mapeamento da classe, ou seja, não precisa fazer a construção do Prompt answer mapeamento de espaço (Verbalizer), basta fazer uma construção de modelo de prompt (Template).

Em geral, esse esquema é realmente mais elegante e, claro, o efeito é melhor, o que faz com que as pessoas se sintam um pouco refrescantes à primeira vista. É claro que, se você ler mais artigos (aprendizagem rápida), poderá pensar em soluções semelhantes. Alguns detalhes implementados no código - a transformação da dimensão da matriz, fornecem uma descrição mais clara e é mais fácil entender todo o esquema.

Alterações de dimensão de dados

um lote de dados

[CLS]content_0[SEP]entity_0_0[MASK]entity_0_1[MASK]entity_0_2[MASK][SEP]

[CLS]content_1[SEP]entity_1_0[MASK]entity_1_1[MASK][SEP]

[CLS]content_2[SEP]entity_2_0[MASK][SEP]

[CLS]content_3[SEP]entity_3_0[MASK]entity_3_1[MASK][SEP]

[CLS]content_4[SEP]entity_4_0[MASK]entity_4_1[MASK]entity_4_2[MASK][SEP]

......

[CLS]content_(batch_size-1)[SEP]entity_(batch_size-1)_0[MASK]entity_(batch_size-1)_1[MASK][SEP]

 Após o tokenizer, o token é mapeado para o id correspondente ao dicionário. É necessário registrar os input_ids, Attention_mask, mask_tokens, entity_count e label de cada dado. A dimensão correspondente muda da seguinte forma

input_ids:[lote, seq_length]

[

[101,******,102,**,103,**,103,102,,0,0,0,0,0],

[101,*******,102,**,103,**,103,102],

[101,******,102,**,103,102,0,0,0,0,0],

......

[101,*****,102,**,103,**,103,**,103,**,103,0,0]

]

atenção_mask:[lote, seq_length]

[

[1,******,1,**,1,**,1,1,,0,0,0,0,0],

[1,******,1,**,1,**,1,1],

[1,******,1,**,1,1,0,0,0,0,0],

......

[1,******,1,**,1,**,1,**,1,**,1,0,0]

]

mask_tokens:[lote,seq_length]

[

[0,******,0,**,1,**,1,0,,0,0,0,0,0],

[0,******,0,**,1,**,1,0],

[0,********,0,**,1,0,0,0,0,0,0],

......

[0,*****,0,**,1,**,1,**,1,**,1,0,0]

]

A etiqueta é mantida por lista

[

[-2,2],

[1,2],

[-2],

......

[2, -2, 0, -1]

]

Se o número de entidades no lote for m, então a matriz do rótulo é [m]

[-2,2,1,2,...,2,-2,0,-1]

O resultado obtido após input_ids+attention_mask passa por bert:

# m indica que existem m entidades no lote
is_masked = inputs['is_masked'].bool()
inputs = {k: v para k, v em inputs.items() se k em ["input_ids", "attention_mask"]}
outputs = self.bert(**inputs,return_dict=True, output_hidden_states=True)
# [lote, comprimento_seq, 768]
outputs = outputs.last_hidden_state
# [m,768]
masked_outputs = outputs[is_masked]
# [m,5]
logits = self.classifier(masked_outputs)

Em segundo lugar, a implementação do código

código número um

O autor deu o código baseado em pytorch-lightning. Acho que o encapsulamento é relativamente alto e não é fácil de entender. Com base nisso, implementei uma versão do código baseada em torch:

Código de modelo

from transformers import BertPreTrainedModel,BertModel
import torch.nn as nn

class SentiClassifyBertPrompt(BertPreTrainedModel):
    def __init__(self,config):
        super(SentiClassifyBertPrompt,self).__init__(config)
        self.bert = BertModel(config=config)
        self.classifier = nn.Sequential(
            nn.Linear(config.hidden_size, config.hidden_size),
            nn.LayerNorm(config.hidden_size),
            nn.LeakyReLU(),
            nn.Dropout(p=config.dropout),
            nn.Linear(config.hidden_size, config.output_dim),
        )

    def forward(self,inputs):
        # m表示batch内有m个实体
        is_masked = inputs['is_masked'].bool()
        inputs = {k: v for k, v in inputs.items() if k in ["input_ids", "attention_mask"]}
        outputs = self.bert(**inputs,return_dict=True, output_hidden_states=True)
        # [batch, seq_length, 768]
        outputs = outputs.last_hidden_state
        # [m,768]
        masked_outputs = outputs[is_masked]
        # [m,5]
        logits = self.classifier(masked_outputs)
        return logits

código de carregamento de dados

import torch
from torch.utils.data import Dataset
from tqdm import tqdm
import json
class DataReader(Dataset):
    def __init__(self,file_path,tokenizer,max_langth):
        self.file_path = file_path
        self.tokenizer = tokenizer
        self.max_length = max_langth
        self.data_list = self.texts_tokeniztion()
        self.allLength = len(self.data_list)

    def texts_tokeniztion(self):
        with open(self.file_path,'r',encoding='utf-8') as f:
            lines = f.readlines()

        res = []
        for line in tqdm(lines,desc='texts tokenization'):
            line_dic = json.loads(line.strip('\n'))
            content = line_dic['content']
            entity = line_dic['entity']
            prompt_length = 0
            prompts = ""
            label = []
            en_count = len(entity)
            for k,v in entity.items():
                prompt_length += len(k) + 1
                #标签化为 0-4的整数
                label.append(v+2)
                prompts += k +"[MASK]"
            #直接最大长度拼接
            content = content[0:self.max_length-prompt_length-1-10]
            text = content + "[SEP]" + prompts
            input_ids,attention_mask,masks = self.text2ids(text)

            input_ids = torch.tensor(input_ids,dtype=torch.long)
            attention_mask = torch.tensor(attention_mask,dtype=torch.long)
            masks = torch.tensor(masks, dtype=torch.long)
            #记录每条数据有多少个实体,方便推理的时候batch推理
            en_count = torch.tensor(en_count,dtype=torch.long)

            temp = []
            temp.append(input_ids)
            temp.append(attention_mask)
            temp.append(masks)
            temp.append(label)
            temp.append(en_count)
            res.append(temp)


        return res

    def text2ids(self,text):
        inputs = self.tokenizer(text)
        input_ids = inputs['input_ids']
        attention_mask = inputs['attention_mask']
        masks = [ int(id==self.tokenizer.mask_token_id)  for id in input_ids]
        return input_ids, attention_mask, masks


    def __getitem__(self, item):
        input_ids = self.data_list[item][0]
        attention_mask = self.data_list[item][1]
        masks = self.data_list[item][2]
        label = self.data_list[item][3]
        en_count = self.data_list[item][4]
        return input_ids, attention_mask, masks, label, en_count

    def __len__(self):
        return self.allLength

Código de treinamento do modelo

from data_reader.reader import DataReader
import torch
from torch.utils.data import DataLoader
from transformers import BertTokenizer,BertConfig
from torch.optim import AdamW
from model import SentiClassifyBertPrompt
from torch.optim.swa_utils import AveragedModel, SWALR
from torch.nn.utils.rnn import pad_sequence
from log.log import  Logger
from tqdm import tqdm
import torch.nn.functional as F
import os
os.environ['CUDA_VISIBLE_DEVICES'] = "1"

def collate_fn(batch):
    input_ids, attention_mask, masks, label, en_count = zip(*batch)
    input_ids = pad_sequence(input_ids,batch_first=True,padding_value=0)
    attention_mask = pad_sequence(attention_mask,batch_first=True,padding_value=0)
    masks = pad_sequence(masks, batch_first=True, padding_value=0)
    labels = []
    for ele in label:
        labels.extend(ele)
    labels = torch.tensor(labels,dtype=torch.long)
    en_count = torch.stack(en_count,dim=0)
    return input_ids, attention_mask, masks, labels, en_count


def dev_validation(dev_loader,device,model):
    total_correct = 0
    total = 0
    model.eval()
    with torch.no_grad():
        for step, batch in enumerate(tqdm(dev_loader, desc="dev_validation")):
            batch = [t.to(device) for t in batch]
            inputs = {"input_ids": batch[0], "attention_mask": batch[1], "is_masked": batch[2]}
            label = batch[3]
            logits = model(inputs)
            preds = torch.argmax(logits,dim=1)

            correct = (preds==label).sum()
            total_correct += correct
            total += label.size()[0]

    acc = total_correct/total
    return acc

def set_seed(seed = 1):
    torch.cuda.manual_seed_all(seed)
    torch.manual_seed(seed)
    torch.backends.cudnn.deterministic = True



if __name__ == '__main__':
    set_seed()
    log_level = 10
    log_path = "logs/train_bert_prompt_AdamW_swa.log"
    logger = Logger(log_name='train_bert_prompt', log_level=log_level, log_path=log_path).logger

    pretrain_model_path = "./pretrained_models/chinese-bert-wwm-ext"
    batch_size = 16
    epochs = 10
    tokenizer = BertTokenizer.from_pretrained(pretrain_model_path)
    config = BertConfig.from_pretrained(pretrain_model_path)
    config.dropout = 0.2
    config.output_dim = 5
    config.batch_size = batch_size
    device = "cuda" if torch.cuda.is_available() else "cpu"
    model = SentiClassifyBertPrompt.from_pretrained(config=config,pretrained_model_name_or_path = pretrain_model_path)
    model.to(device)
    optimizer = AdamW(params=model.parameters(),lr=1e-6)

    # 随机权重平均SWA,实现更好的泛化
    swa_model = AveragedModel(model=model,device=device)
    # SWA调整学习率
    swa_scheduler = SWALR(optimizer, swa_lr=1e-6)

    train_dataset = DataReader(tokenizer=tokenizer, max_langth=512, file_path='./data/train_split.txt')
    train_loader = DataLoader(dataset=train_dataset, shuffle=True, batch_size=batch_size, collate_fn=collate_fn)

    dev_dataset = DataReader(tokenizer=tokenizer, max_langth=512, file_path='./data/dev_split.txt')
    dev_loader = DataLoader(dataset=dev_dataset, shuffle=True, batch_size=batch_size, collate_fn=collate_fn)



    for epoch in range(epochs):
        model.train()
        for step,batch in enumerate(tqdm(train_loader,desc="training")):
            batch = [ t.to(device) for t in batch]
            inputs = {"input_ids":batch[0],"attention_mask":batch[1],"is_masked":batch[2]}
            label = batch[3]
            logits = model(inputs)
            loss = F.cross_entropy(logits,label)

            loss.backward()
            optimizer.step()
            optimizer.zero_grad()

        swa_model.update_parameters(model)
        swa_scheduler.step()

        acc = dev_validation(dev_loader,device,model)
        swa_acc = dev_validation(dev_loader,device,swa_model)
        logger.info('Epoch %d acc is %.6f'%(epoch,acc))
        logger.info('Epoch %d swa_acc is %.6f' % (epoch, swa_acc))


O diretório do projeto é o seguinte

swa - peso médio

Existe um truque de treinamento - swa - peso médio no código de treinamento acima, que eu não vi e usei antes. É necessário mencionar que sua ideia central é o último modelo retido no processo de treinamento, não a verificação. O modelo com o melhor efeito no conjunto é o peso médio dos modelos treinados por todas as épocas, de modo que o modelo treinado tenha a melhor capacidade de generalização e o melhor efeito . Não precisamos implementar como calcular a média de pesos por nós mesmos. O Torch já possui um processo e código padronizados. O efeito específico precisa ser verificado por experimentos (algumas pessoas disseram que sgd+swa é eficaz).

    ......
    optimizer = AdamW(params=model.parameters(),lr=1e-6)
    # 随机权重平均SWA,实现更好的泛化
    swa_model = AveragedModel(model=model,device=device)
    # SWA调整学习率
    swa_scheduler = SWALR(optimizer, swa_lr=1e-6)
    for epoch in range(epochs):
        model.train()
        for step,batch in enumerate(tqdm(train_loader,desc="training")):
            ......
            #正常训练
            logits = model(inputs)
            loss = F.cross_entropy(logits,label)
            loss.backward()
            optimizer.step()
            optimizer.zero_grad()
        #每个epoch后swa_model模型更新参数
        swa_model.update_parameters(model)
        #调整学习率
        swa_scheduler.step()

código de linha de base

Para simplesmente verificar o efeito, também executei o esquema de linha de base, o código é o seguinte:

import torch
from torch.utils.data import Dataset
from tqdm import tqdm
import json
from transformers import BertPreTrainedModel,BertModel
import torch.nn as nn
class SentiClassifyBert(BertPreTrainedModel):
    def __init__(self,config):
        super(SentiClassifyBert,self).__init__(config)
        self.bert = BertModel(config=config)
        self.classifier = nn.Sequential(
            nn.Linear(config.hidden_size, config.hidden_size),
            nn.LayerNorm(config.hidden_size),
            nn.LeakyReLU(),
            nn.Dropout(p=config.dropout),
            nn.Linear(config.hidden_size, config.output_dim),
        )

    def forward(self,inputs):
        inputs = {k: v for k, v in inputs.items() if k in ["input_ids", "attention_mask"]}
        outputs = self.bert(**inputs,return_dict=True, output_hidden_states=True)
        outputs = outputs.last_hidden_state
        cls_output = outputs[:,0:1,:].squeeze()
        logits = self.classifier(cls_output)
        return logits

class DataReader(Dataset):
    def __init__(self,file_path,tokenizer,max_langth):
        self.file_path = file_path
        self.tokenizer = tokenizer
        self.max_length = max_langth
        self.data_list = self.texts_tokeniztion()
        self.allLength = len(self.data_list)

    def texts_tokeniztion(self):
        with open(self.file_path,'r',encoding='utf-8') as f:
            lines = f.readlines()

        res = []
        for line in tqdm(lines,desc='texts tokenization'):
            line_dic = json.loads(line.strip('\n'))
            content = line_dic['content']
            entity = line_dic['entity']
            for k,v in entity.items():
                # 直接最大长度拼接
                content = content[0:self.max_length - len(k) - 1 - 10]
                text = content + "[SEP]" + k
                input_ids, attention_mask, masks = self.text2ids(text)

                input_ids = torch.tensor(input_ids, dtype=torch.long)
                attention_mask = torch.tensor(attention_mask, dtype=torch.long)
                label = torch.tensor(v+2, dtype=torch.long)
                temp = []
                temp.append(input_ids)
                temp.append(attention_mask)
                temp.append(label)
                res.append(temp)

        return res

    def text2ids(self,text):
        inputs = self.tokenizer(text)
        input_ids = inputs['input_ids']
        attention_mask = inputs['attention_mask']
        masks = [ int(id==self.tokenizer.mask_token_id)  for id in input_ids]
        return input_ids, attention_mask, masks


    def __getitem__(self, item):
        input_ids = self.data_list[item][0]
        attention_mask = self.data_list[item][1]
        label = self.data_list[item][2]
        return input_ids, attention_mask, label



from data_reader.reader import DataReader
import torch
from torch.utils.data import DataLoader
from transformers import BertTokenizer,BertConfig
from torch.optim import AdamW,SGD
from model import SentiClassifyBert
from torch.optim.swa_utils import AveragedModel, SWALR
from torch.nn.utils.rnn import pad_sequence
from log.log import  Logger
from tqdm import tqdm
import torch.nn.functional as F
import os
os.environ['CUDA_VISIBLE_DEVICES'] = "0"

def collate_fn(batch):
    input_ids, attention_mask,  label = zip(*batch)
    input_ids = pad_sequence(input_ids,batch_first=True,padding_value=0)
    attention_mask = pad_sequence(attention_mask,batch_first=True,padding_value=0)
    label = torch.stack(label,dim=0)
    return input_ids, attention_mask, label


def dev_validation(dev_loader,device,model):
    total_correct = 0
    total = 0
    model.eval()
    with torch.no_grad():
        for step, batch in enumerate(tqdm(dev_loader, desc="dev_validation")):
            batch = [t.to(device) for t in batch]
            inputs = {"input_ids": batch[0], "attention_mask": batch[1]}
            label = batch[2]
            logits = model(inputs)
            preds = torch.argmax(logits,dim=1)

            correct = (preds==label).sum()
            total_correct += correct
            total += label.size()[0]

    acc = total_correct/total
    return acc

def set_seed(seed = 1):
    torch.cuda.manual_seed_all(seed)
    torch.manual_seed(seed)
    torch.backends.cudnn.deterministic = True



if __name__ == '__main__':
    set_seed()
    log_level = 10
    log_path = "logs/train_bert_adamW_swa_20220718.log"
    logger = Logger(log_name='train_bert', log_level=log_level, log_path=log_path).logger

    pretrain_model_path = "./pretrained_models/chinese-bert-wwm-ext"
    batch_size = 16
    epochs = 20
    tokenizer = BertTokenizer.from_pretrained(pretrain_model_path)
    config = BertConfig.from_pretrained(pretrain_model_path)
    config.dropout = 0.2
    config.output_dim = 5
    config.batch_size = batch_size
    device = "cuda" if torch.cuda.is_available() else "cpu"
    model = SentiClassifyBert.from_pretrained(config=config,pretrained_model_name_or_path = pretrain_model_path)
    model.to(device)
    optimizer = AdamW(params=model.parameters(),lr=1e-6)
    # optimizer = SGD(params=model.parameters(), lr=1e-5,momentum=0.9)

    # 随机权重平均SWA,实现更好的泛化
    swa_model = AveragedModel(model=model,device=device)
    # SWA调整学习率
    swa_scheduler = SWALR(optimizer, swa_lr=1e-6)

    train_dataset = DataReader(tokenizer=tokenizer, max_langth=512, file_path='./data/train_split.txt')
    train_loader = DataLoader(dataset=train_dataset, shuffle=True, batch_size=batch_size, collate_fn=collate_fn)

    dev_dataset = DataReader(tokenizer=tokenizer, max_langth=512, file_path='./data/dev_split.txt')
    dev_loader = DataLoader(dataset=dev_dataset, shuffle=True, batch_size=batch_size, collate_fn=collate_fn)



    for epoch in range(epochs):
        model.train()
        for step,batch in enumerate(tqdm(train_loader,desc="training")):
            batch = [ t.to(device) for t in batch]
            inputs = {"input_ids":batch[0],"attention_mask":batch[1]}
            label = batch[2]
            logits = model(inputs)
            loss = F.cross_entropy(logits,label)

            loss.backward()
            optimizer.step()
            optimizer.zero_grad()

        swa_model.update_parameters(model)
        swa_scheduler.step()

        acc = dev_validation(dev_loader,device,model)
        swa_acc = dev_validation(dev_loader,device,swa_model)
        logger.info('Epoch %d acc is %.6f'%(epoch,acc))
        logger.info('Epoch %d swa_acc is %.6f' % (epoch, swa_acc))


No treinamento, o conjunto de treinamento de cerca de 9W de dados é dividido em 1W de dados como o conjunto de verificação, e o chinês-bert-wwm-ext é usado como modelo de pré-treinamento para treinar 20 épocas; os efeitos do SGD e AdamW otimizadores são comparados; Os efeitos da linha de base e do esquema de primeiro lugar são comparados; é claro, se o efeito do swa é bom ou não, não pode ser concluído porque não há um conjunto de testes.

3. Exibição do efeito

O primeiro plano:

a、adamW + swa

 A precisão no conjunto de validação foi de 0,929579 em 20 épocas usando o otimizador AdamW; swa foi de 0,928673 - é menos claro como ele funciona no conjunto de teste.

b、sgd + swa 

Em termos de precisão, a convergência de sgd é relativamente lenta. A taxa de precisão de 19 epcohs é menor para atingir o valor mais alto, e a taxa de precisão não é tão alta quanto AdamW, que é apenas 89,7. No entanto, parece que tem não convergiu totalmente. Demorou muito, parece que um otimizador inteligente como o AdamW é mais adequado para pessoas como eu, que não são muito boas em ajustar os parâmetros do otimizador. 

esquema de linha de base

Em comparação, o efeito da linha de base é um pouco pior. A solução do primeiro lugar é realmente eficaz. Há dois pontos principais. Primeiro, não há splicing repetido, o que causa mudanças na distribuição dos dados. Ao mesmo tempo, o modelo pode ser melhor para aprender a relação direta entre entidades; A segunda é que a escolha do vetor de sentença é mais apropriada. Nem cls nem meanPooling são selecionados para incorporação, mas a incorporação correspondente a [MASK] é mais precisa. Esta é essencialmente a mudança de aprendizado imediato e aplicado aqui. A diferença entre o pré-treinamento e o ajuste fino é menor, a incorporação extraída é mais precisa e todos os efeitos são bons.

O programa é elegante e vale a pena aprender e aprender!

Artigo de referência

2022 Sohu Campus NLP Algoritmo Competição Análise de Sentimentos Compartilhamento do Esquema do Primeiro Prêmio

Competição de Algoritmo de Análise de Sentimento do Campus Sohu 2022

Acho que você gosta

Origin blog.csdn.net/HUSTHY/article/details/125809156
Recomendado
Clasificación