GVKun编程网logo

使用Pytorch和BERT进行多标签文本分类(pytorch 多标签分类)

12

对于想了解使用Pytorch和BERT进行多标签文本分类的读者,本文将是一篇不可错过的文章,我们将详细介绍pytorch多标签分类,并且为您提供关于5个简单的步骤使用Pytorch进行文本摘要总结、B

对于想了解使用Pytorch和BERT进行多标签文本分类的读者,本文将是一篇不可错过的文章,我们将详细介绍pytorch 多标签分类,并且为您提供关于5个简单的步骤使用Pytorch进行文本摘要总结、BERT 实现多标签文本分类:强大模型的应用与展望、Multi label 多标签分类问题(Pytorch,TensorFlow,Caffe)、PyTorch 使用TorchText进行文本分类的有价值信息。

本文目录一览:

使用Pytorch和BERT进行多标签文本分类(pytorch 多标签分类)

使用Pytorch和BERT进行多标签文本分类(pytorch 多标签分类)

介绍

自然语言处理(NLP)是一种将非结构化文本处理成有意义的知识的人工智能技术。NLP解决了分类、主题建模、文本生成、问答、推荐等业务问题。虽然TF/IDF矢量化或其他高级词嵌入(如GLOVE和Word2Vec)在此类NLP业务问题上表现出了良好的性能,但这些模型存在局限性就是使用一个向量对词进行编码而不考虑上下文的不同含义。因此,当试图解决理解用户意图所需的问题时,这些模型可能不能很好地执行。一个例子是,当用户与自动聊天机器人交互时,它试图理解用户查询的意图并准确地提供响应。

对于这种情况,NLP中的另一个例子是从下面两个句子中解码上下文意义。

  • A thieve robbed a bank.

  • He went to river bank.

  • 从以上两种表述中,人们很容易就能看出“bank”有两种不同的含义;然而,机器不能区分,因为上面提到的词嵌入使用相同的标记“bank”,而不管他们的上下文意义。为了克服这一挑战,谷歌从Transformers (BERT)模型开发了最先进的双向编码器表示。

    BERT是什么?

    BERT是在8亿单词的图书语料库和2500万单词的英语维基百科上训练的预训练模型。在BERT中,“bank”将有两个不同的含义,因为它们的上下文差异。在保持NLP任务的高性能的同时并不会降低模型构建的训练时间。并且可以从BERT中提取新的语言特征用于模型预测。与RNN、LSTM、CNN等深度学习模型相比,BERT的发展速度要快得多。作为高层次的理解,BERT有两种不同的架构变体:BERT base和BERT large。第一个变型有12个Transformers 块,12个注意头,1.1亿参数,后一个变型有24个Transformers ,16个注意头,3.4亿参数。它在使用过程中完成了两个NLP的任务:遮蔽语言建模和下一句预测。

    数据集

    从此处(https://datahack.analyticsvidhya.com/contest/janatahack-independence-day-2020-ml-hackathon/#ProblemStatement)获取数据集,该数据集可用于研究论文的主题建模的多标签分类对比。对比的目的是从大型的科学文章在线存档中尽可能地容易找到相关的文章。我选择此数据集的原因是,尽管有许多关于二进制分类的Twitter情绪讨论BERT和Pytorch的文章,但很少找到有关处理多类问题的。并且有很多共享代码可能无法正常工作。

    查看如下的代码我建议具备python,NLP,深度学习和Pytorch框架的基础知识。必须使用Google帐户才能使用Google Colab帐户。

    处理数据的方法

    在传统的NLP机器学习问题中,我们倾向于清除不需要的文本,例如删除停用词,标点符号,删除符号和数字等。但是,在BERT中,不需要执行此类预处理任务,因为BERT使用了这些 单词的顺序和位置,以了解用户输入的意图。

    ML / DL工程师应该从不同方面探索数据集,以真正了解他们手中的数据类型,这是一个好习惯。NLP的典型功能是单词计数,动词计数,形容词计数,数字计数,标点符号计数,双字母组计数,三字组计数等。为简便起见,我已展示了如何对单词计数列进行计数,其中单个标题中使用的总单词数将被计算在内。您可能还需要处理类似于TITLE的Abstract列,以及ABSTRACT和TITLE的组合。

    下面的命令创建“ WORD_COUNT”列。

     df_raw[''WORD_COUNT''] = df_raw[''TITLE''].apply(lambda x: len(x.split())

    这将生成“ WORD_COUNT”的分布图,即标题的长度。

    如您所见,文章标题的大部分以10个单词为中心,这是预期的结果,因为TITLE应该简短,简洁且有意义。

    由于我将仅使用“ TITLE”和“ target_list”,因此我创建了一个名为df2的新数据框。df2.head()命令显示训练数据集中的前五个记录。如您所见,两个目标标签被标记到最后的记录,这就是为什么这种问题称为多标签分类问题的原因。

     df2 = df_raw[[''TITLE'', ''target_list'']].copy()
     
     df2.head()

    同时,设置将用于模型训练的参数。由于我更喜欢使用2*base数字,因此最大长度设置为16,这涵盖了大部分“ TITLE”长度。训练和有效批处理大小设置为32。epoch为4,因为它很容易在几个epoch上过度拟合。我从lr=0.00001开始学习。您可以随意尝试不同的值以提高准确性。

     # Sections of config
     # Defining some key variables that will be used later on in the training
     MAX_LEN = 16
     TRAIN_BATCH_SIZE = 32
     VALID_BATCH_SIZE = 32
     EPOCHS = 4
     LEARNING_RATE = 1e-05
     tokenizer = BertTokenizer.from_pretrained(''bert-base-uncased'')

    让我们创建一个称为“ CustomDataset”的通用类。Class从我们的原始输入特征生成张量,并且Pytorch张量可以接受class的输出。它期望具有上面定义的“ TITLE”,“ target_list”,max_len,并使用BERT toknizer.encode_plus函数将输入设置为数字矢量格式,然后转换为张量格式返回。

     class CustomDataset(Dataset):
        def __init__(self, dataframe, tokenizer, max_len):
            self.tokenizer = tokenizer
            self.data = dataframe
            self.title = dataframe[''TITLE'']
            self.targets = self.data.target_list
            self.max_len = max_len
     
        def __len__(self):
            return len(self.title)
     
     
     
        def __getitem__(self, index):
            title = str(self.title[index])
            title = " ".join(title.split())
     
            inputs = self.tokenizer.encode_plus(
                title,
                None,
                add_special_tokens=True,
                max_length=self.max_len,
                padding=''max_length'',
                return_token_type_ids=True,
                truncation=True
     
            )
     
            ids = inputs[''input_ids'']
            mask = inputs[''attention_mask'']
            token_type_ids = inputs["token_type_ids"]
            return {
                ''ids'': torch.tensor(ids, dtype=torch.long),
                ''mask'': torch.tensor(mask, dtype=torch.long),
                ''token_type_ids'': torch.tensor(token_type_ids, dtype=torch.long),
                ''targets'': torch.tensor(self.targets[index], dtype=torch.float)
     
            }

    数据集的80%用于模型训练,而20%用于验证。测试数据集完全用于测试目的。

     train_size = 0.8
     train_dataset = df2.sample(frac=train_size,random_state=200)
     valid_dataset = df2.drop(train_dataset.index).reset_index(drop=True)
     train_dataset = train_dataset.reset_index(drop=True)
     
     
     print("FULL Dataset: {}".format(df2.shape))
     print("TRAIN Dataset: {}".format(train_dataset.shape))
     print("TEST Dataset: {}".format(valid_dataset.shape))
     
     
     training_set = CustomDataset(train_dataset, tokenizer, MAX_LEN)
     validation_set = CustomDataset(valid_dataset, tokenizer, MAX_LEN)

    我们已经讨论了将张量准备为输入特征的大部分基础工作。现在,构建BERT模型很容易。由于来自模型的冗长输出,我已简化为仅显示模型。我已使用dropout 0.3来随机减少特征,以最大程度地减少第2层的过拟合。第3层采用了768维特征,这些特征是从使用BERT的第2层输出的。它返回6个特征,这是对目标列表的最终预测。

     class BERTClass(torch.nn.Module):
        def __init__(self):
            super(BERTClass, self).__init__()
            self.l1 = transformers.BertModel.from_pretrained(''bert-base-uncased'')
            self.l2 = torch.nn.Dropout(0.3)
            self.l3 = torch.nn.Linear(768, 6)
     
        def forward(self, ids, mask, token_type_ids):
            _, output_1= self.l1(ids, attention_mask = mask, token_type_ids = token_type_ids)
            output_2 = self.l2(output_1)
            output = self.l3(output_2)
            return output
     
     model = BERTClass()
     model.to(device)

    BCE损失函数用于找出模型预测值与实际目标值之间的误差。使用Adam优化器。损失功能请参见下文。

     def loss_fn(outputs, targets):
        return torch.nn.BCEWithLogitsLoss()(outputs, targets)
     
     optimizer = torch.optim.Adam(params = model.parameters(), lr=LEARNING_RATE)

    同时我还创建了检查点,可以在训练期间保存最佳模型。当需要从停下来的地方继续训练时,这将有助于减少训练时间。创建检查点可以节省时间,以便从头开始进行重新训练。如果您对从最佳模型生成的输出感到满意,则不需要进一步的微调,则可以使用模型进行推断。

     def load_ckp(checkpoint_fpath, model, optimizer):
     
        """
        checkpoint_path: path to save checkpoint
        model: model that we want to load checkpoint parameters into      
        optimizer: optimizer we defined in previous training
     
        """
        # load check point
        checkpoint = torch.load(checkpoint_fpath)
        # initialize state_dict from checkpoint to model
        model.load_state_dict(checkpoint[''state_dict''])
        # initialize optimizer from checkpoint to optimizer
        optimizer.load_state_dict(checkpoint[''optimizer''])
        # initialize valid_loss_min from checkpoint to valid_loss_min
        valid_loss_min = checkpoint[''valid_loss_min'']
        # return model, optimizer, epoch value, min validation loss
        return model, optimizer, checkpoint[''epoch''], valid_loss_min.item()
     import shutil, sys  
     
     def save_ckp(state, is_best, checkpoint_path, best_model_path):
     
        """
        state: checkpoint we want to save
        is_best: is this the best checkpoint; min validation loss
        checkpoint_path: path to save checkpoint
        best_model_path: path to save best model
        """
        f_path = checkpoint_path
        # save checkpoint data to the path given, checkpoint_path
        torch.save(state, f_path)
        # if it is a best model, min validation loss
        if is_best:
            best_fpath = best_model_path
            # copy that checkpoint file to best path given, best_model_path
            shutil.copyfile(f_path, best_fpath)
     def train_model(start_epochs, n_epochs, valid_loss_min_input,
                    training_loader, validation_loader, model,
                    optimizer, checkpoint_path, best_model_path):
     
      # initialize tracker for minimum validation loss
      valid_loss_min = valid_loss_min_input
      for epoch in range(start_epochs, n_epochs+1):
        train_loss = 0
        valid_loss = 0
        model.train()
        print(''############# Epoch {}: Training Start   #############''.format(epoch))
        for batch_idx, data in enumerate(training_loader):
            #print(''yyy epoch'', batch_idx)
            ids = data[''ids''].to(device, dtype = torch.long)
            mask = data[''mask''].to(device, dtype = torch.long)
            token_type_ids = data[''token_type_ids''].to(device, dtype = torch.long)
            targets = data[''targets''].to(device, dtype = torch.float)
            outputs = model(ids, mask, token_type_ids)
            optimizer.zero_grad()
            loss = loss_fn(outputs, targets)
            #if batch_idx%5000==0:
              #   print(f''Epoch: {epoch}, Training Loss: {loss.item()}'')
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            #print(''before loss data in training'', loss.item(), train_loss)
            train_loss = train_loss + ((1 / (batch_idx + 1)) * (loss.item() - train_loss))
            #print(''after loss data in training'', loss.item(), train_loss)
        print(''############# Epoch {}: Training End     #############''.format(epoch))
        print(''############# Epoch {}: Validation Start   #############''.format(epoch))
        ######################    
        # validate the model #
        ######################
        model.eval()
        with torch.no_grad():
          for batch_idx, data in enumerate(validation_loader, 0):
                ids = data[''ids''].to(device, dtype = torch.long)
                mask = data[''mask''].to(device, dtype = torch.long)
                token_type_ids = data[''token_type_ids''].to(device, dtype = torch.long)
                targets = data[''targets''].to(device, dtype = torch.float)
                outputs = model(ids, mask, token_type_ids)
                loss = loss_fn(outputs, targets)
                valid_loss = valid_loss + ((1 / (batch_idx + 1)) * (loss.item() - valid_loss))
                val_targets.extend(targets.cpu().detach().numpy().tolist())
                val_outputs.extend(torch.sigmoid(outputs).cpu().detach().numpy().tolist())
     
          print(''############# Epoch {}: Validation End     #############''.format(epoch))
          # calculate average losses
          #print(''before cal avg train loss'', train_loss)
          train_loss = train_loss/len(training_loader)
          valid_loss = valid_loss/len(validation_loader)
          # print training/validation statistics
          print(''Epoch: {} \tAvgerage Training Loss: {:.6f} \tAverage Validation Loss: {:.6f}''.format(
                epoch,
                train_loss,
                valid_loss
                ))
          # create checkpoint variable and add important data
          checkpoint = {
                ''epoch'': epoch + 1,
                ''valid_loss_min'': valid_loss,
                ''state_dict'': model.state_dict(),
                ''optimizer'': optimizer.state_dict()
          }
     
            # save checkpoint
          save_ckp(checkpoint, False, checkpoint_path, best_model_path)
          ## TODO: save the model if validation loss has decreased
          if valid_loss <= valid_loss_min:
            print(''Validation loss decreased ({:.6f} --> {:.6f}). Saving model ...''.format(valid_loss_min,valid_loss))
            # save checkpoint as best model
            save_ckp(checkpoint, True, checkpoint_path, best_model_path)
            valid_loss_min = valid_loss
        print(''############# Epoch {} Done   #############\n''.format(epoch))
      return model

    “train_model”被创建来训练模型,“checkpoint_path”是训练模型的参数将被保存为每个epoch,“best_model”是最好的模型将被保存的地方。

     checkpoint_path = ''/content/drive/My Drive/NLP/ResearchArticlesClassification/checkpoint/current_checkpoint.pt''
     
     best_model = ''/content/drive/My Drive/NLP/ResearchArticlesClassification/best_model/best_model.pt''
     
     trained_model = train_model(1, 4, np.Inf, training_loader, validation_loader, model,
                          optimizer,checkpoint_path,best_model)

    训练结果如下:

     ############# Epoch 1: Training Start   #############
     ############# Epoch 1: Training End     #############
     ############# Epoch 1: Validation Start   #############
     ############# Epoch 1: Validation End     #############
     Epoch: 1 Avgerage Training Loss: 0.000347 Average Validation Loss: 0.001765
     Validation loss decreased (inf --> 0.001765). Saving model ...
     ############# Epoch 1 Done   #############
     
     ############# Epoch 2: Training Start   #############
     ############# Epoch 2: Training End     #############
     ############# Epoch 2: Validation Start   #############
     ############# Epoch 2: Validation End     #############
     Epoch: 2 Avgerage Training Loss: 0.000301 Average Validation Loss: 0.001831
     ############# Epoch 2 Done   #############
     
     ############# Epoch 3: Training Start   #############
     ############# Epoch 3: Training End     #############
     ############# Epoch 3: Validation Start   #############
     ############# Epoch 3: Validation End     #############
     Epoch: 3 Avgerage Training Loss: 0.000263 Average Validation Loss: 0.001896
     ############# Epoch 3 Done   #############
     
     ############# Epoch 4: Training Start   #############
     ############# Epoch 4: Training End     #############
     ############# Epoch 4: Validation Start   #############
     ############# Epoch 4: Validation End     #############
     Epoch: 4 Avgerage Training Loss: 0.000228 Average Validation Loss: 0.002048
     ############# Epoch 4 Done   #############

    因为我只执行了4个epoch,所以完成得很快,我将threshold设置为0.5。你可以试试这个阈值,看看是否能提高结果。

     val_preds = (np.array(val_outputs) > 0.5).astype(int)
     val_preds
     array([[0, 0, 1, 0, 0, 0],
            [0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0],
            ...,
            [0, 0, 1, 0, 0, 0],
            [0, 1, 0, 0, 0, 0],
            [1, 0, 0, 0, 0, 0]])

    让我们将精确度和F1得分定义为模型性能的指标。F1将被用于评估。

     accuracy = metrics.accuracy_score(val_targets, val_preds)
     f1_score_micro = metrics.f1_score(val_targets, val_preds, average=''micro'')
     f1_score_macro = metrics.f1_score(val_targets, val_preds, average=''macro'')
     print(f"Accuracy Score = {accuracy}")
     print(f"F1 Score (Micro) = {f1_score_micro}")
     print(f"F1 Score (Macro) = {f1_score_macro}")

    使用混淆矩阵和分类报告,以可视化我们的模型如何正确/不正确地预测每个单独的目标。

     from sklearn.metrics import multilabel_confusion_matrix as mcm, classification_report
     cm_labels = [''Computer Science'', ''Physics'', ''Mathematics'',
            ''Statistics'', ''Quantitative Biology'', ''Quantitative Finance'']
     cm = mcm(val_targets, val_preds)
     print(classification_report(val_targets, val_preds))

    模型预测的准确率为76%。F1得分低的原因是有六个类的预测,通过结合“TITLE”和“ABSTRACT”或者只使用“ABSTRACT”来训练可以提高它。我对这两个案例都进行了训练,发现“ABSTRACT”特征本身的F1分数比标题和标题与抽象相结合要好得多。在没有进行超参数优化的情况下,我使用测试数据进行推理,并在private score中获得0.82分。

    有一些事情可以做,以提高F1成绩。一个是微调模型的超参数,你可能想要实验改变学习速率,退出率和时代的数量。在对模型微调的结果满意之后,我们可以使用整个训练数据集,而不是分成训练和验证集,因为训练模型已经看到了所有可能的场景,使模型更好地执行。

    你可以在谷歌Colab查看这个项目源代码

    https://colab.research.google.com/drive/1SPxxEW9okgnbMdk1ORlfSQI4rjV2tVW_#scrollTo=EJQRHd7VVMap

    作者:Kyawkhaung

    原文地址:https://kyawkhaung.medium.com/multi-label-text-classification-with-bert-using-pytorch-47011a7313b9

    deephub翻译组


    本文分享自微信公众号 - DeepHub IMBA(deephub-imba)。
    如有侵权,请联系 support@oschina.cn 删除。
    本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

    5个简单的步骤使用Pytorch进行文本摘要总结

    5个简单的步骤使用Pytorch进行文本摘要总结

    介绍

    文本摘要是自然语言处理(NLP)的一项任务,其目的是生成源文本的简明摘要。不像摘录摘要,摘要不仅仅简单地从源文本复制重要的短语,还要提出新的相关短语,这可以被视为释义。摘要在不同的领域产生了大量的应用,从书籍和文献,科学和研发,金融研究和法律文件分析。

    到目前为止,对抽象摘要最有效的方法是在摘要数据集上使用经过微调的transformer模型。在本文中,我们将演示如何在几个简单步骤中使用功能强大的模型轻松地总结文本。我们将要使用的模型已经经过了预先训练,所以不需要额外的训练:)

    让我们开始吧!

    步骤1:安装Transformers库

    我们要用的库是Huggingface实现的Transformers 。如果你不熟悉Transformers ,你可以继续阅读我之前的文章。

    要安装变压器,您可以简单地运行:

     pip install transformers

    注意需要事先安装Pytorch。如果您还没有安装Pytorch,请访问Pytorch官方网站并按照说明安装它。

    步骤2:导入库

    成功安装transformer之后,现在可以开始将其导入到Python脚本中。我们也可以导入os来设置GPU在下一步使用的环境变量。注意,这是完全可选的,但如果您有多个gpu(如果您使用的是jupiter笔记本),这是防止错误的使用其他gpu的一个好做法。

     from transformers import pipeline
     import os

    步骤3:设置使用的GPU和模型

    如果你决定设置GPU(例如0),那么你可以如下图所示:

     os.environ["CUDA_VISIBLE_DEVICES"] = "0"

    现在,我们准备好选择要使用的摘要模型了。Huggingface提供两种强大的摘要模型使用:BART (BART -large-cnn)和t5 (t5-small, t5-base, t5-large, t5- 3b, t5- 11b)。你可以在他们的官方paper(BART paper, t5 paper)上了解更多。

    要使用在CNN/每日邮报新闻数据集上训练的BART模型,您可以通过Huggingface的内置管道模块直接使用默认参数:

     summarizer = pipeline("summarization")

    如果你想使用t5模型(例如t5-base),它是在c4 Common Crawl web语料库进行预训练的,那么你可以这样做:

     summarizer = pipeline("summarization", model="t5-base", tokenizer="t5-base", framework="tf")

    步骤4:输入文本进行总结

    现在,在我们准备好我们的模型之后,我们可以开始输入我们想要总结的文本。想象一下,我们想从MedicineNet的一篇文章中总结以下关于COVID-19疫苗的内容:

    One month after the United States began what has become a troubled rollout  of a national COVID vaccination campaign, the effort is finally  gathering real steam.

    Close to a million doses — over 951,000, to be more exact — made their way  into the arms of Americans in the past 24 hours, the U.S. Centers for  Disease Control and Prevention reported Wednesday. That’s the largest  number of shots given in one day since the rollout began and a big jump  from the previous day, when just under 340,000 doses were given, CBS News reported.

    That number is likely to jump quickly after the federal government on  Tuesday gave states the OK to vaccinate anyone over 65 and said it would release all the doses of vaccine it has available for distribution.  Meanwhile, a number of states have now opened mass vaccination sites in  an effort to get larger numbers of people inoculated, CBS News reported.

    我们定义变量:

     text = """One month after the United States began what has become a troubled rollout of a national COVID vaccination campaign, the effort is finally gathering real steam.
     Close to a million doses -- over 951,000, to be more exact -- made their way into the arms of Americans in the past 24 hours, the U.S. Centers for Disease Control and Prevention reported Wednesday. That''s the largest number of shots given in one day since the rollout began and a big jump from the previous day, when just under 340,000 doses were given, CBS News reported.
     That number is likely to jump quickly after the federal government on Tuesday gave states the OK to vaccinate anyone over 65 and said it would release all the doses of vaccine it has available for distribution. Meanwhile, a number of states have now opened mass vaccination sites in an effort to get larger numbers of people inoculated, CBS News reported."""

    步骤4:总结

    最后,我们可以开始总结输入的文本。这里,我们声明了希望汇总输出的min_length和max_length,并且关闭了采样以生成固定的汇总。我们可以通过运行以下命令来实现:

     summary_text = summarizer(text, max_length=100, min_length=5, do_sample=False)[0][''summary_text'']
     print(summary_text)

    我们得到总结文本:

    Over 951,000 doses of vaccine given in one day in the past 24 hours, CDC says . That’s the largest number of shots given in a month since the  rollout began . The federal government gave states the OK to vaccinate  anyone over 65 on Tuesday . A number of states have now opened mass  vaccination sites in an effort to get more people inoculated, CBS News  reports .

    从总结的文本中可以看出,该模型知道24小时相当于一天,并聪明地将美国疾病控制与预防中心(U.S. Centers for Disease Control and Prevention)缩写为CDC。此外,该模型成功地从第一段和第二段链接信息,指出这是自上个月开始展示以来给出的最大次数。我们可以看到,该摘要模型的性能相当不错。

    最后把所有这些放在一起,这里是jupyter notebook形式的整个代码:

    https://gist.github.com/itsuncheng/f3c4dde81ac4651383c4480958da4f8e#file-summarization-ipynb


    Lewis, Mike, et al. “Bart: Denoising sequence-to-sequence pre-training for natural language generation, translation, and comprehension.” arXiv preprint arXiv:1910.13461 (2019).

    Raffel, Colin, et al. “Exploring the limits of transfer learning with a unified text-to-text transformer.” arXiv preprint arXiv:1910.10683 (2019).

    作者:Raymond Cheng

    原文地址:https://towardsdatascience.com/abstractive-summarization-using-pytorch-f5063e67510

    deephub翻译组


    本文分享自微信公众号 - DeepHub IMBA(deephub-imba)。
    如有侵权,请联系 support@oschina.cn 删除。
    本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

    BERT 实现多标签文本分类:强大模型的应用与展望

    BERT 实现多标签文本分类:强大模型的应用与展望

    【直播预告】程序员逆袭 CEO 分几步?

    在自然语言处理领域,文本分类是常见任务之一。然而,传统的文本分类通常只处理单标签分类问题,即每个文本只能被标记为一个特定的类别。但在现实生活中,很多文本可能同时属于多个类别,这就涉及到多标签分类问题。近年来,随着深度学习技术的发展,尤其是 BERT 模型的广泛应用,多标签文本分类取得了显著的进步。 BERT(Bidirectional Encoder Representations from Transformers)是一种基于 Transformer 的深度双向模型,它可以捕获文本的上下文信息并生成高质量的词向量。由于其强大的表示能力,BERT 被广泛应用于各种 NLP 任务,包括文本分类。

    在多标签文本分类中,一个重要的挑战是如何处理标签之间的相关性。由于标签之间可能存在相互依赖的关系,一个文本可能同时属于多个类别。传统的单标签分类方法无法有效地处理这种情况。而 BERT 通过生成词向量能够捕获词之间的关系,这使得它可以很好地处理多标签分类问题。

    为了实现多标签文本分类,一种常见的方法是修改 BERT 的输出层以产生多个输出。每个输出对应一个标签,然后使用 softmax 函数将这些输出转换为概率分布。在训练过程中,我们使用交叉熵损失来优化模型,使得对于每个标签,模型预测的概率分布接近于真实的标签分布。

    另外,为了充分利用标签之间的相关性信息,可以使用标签嵌入技术。这种方法将每个标签表示为一个向量,这些向量在训练过程中与文本一起被 BERT 学习。然后,在预测阶段,模型不仅考虑文本内容,还考虑标签之间的关系。这使得模型能够更好地理解文本并预测其可能的标签。

    除了上述方法外,还可以使用其他技术来提高多标签分类的性能。例如,可以使用元学习技术来微调 BERT 模型以适应特定任务的数据分布。另外,可以利用集成学习技术将多个 BERT 模型组合起来以提高预测精度。

    在实际应用中,多标签文本分类在许多领域都有广泛的应用。例如,在情感分析中,一个文本可能同时包含正面和负面的情感;在主题分类中,一个文本可能属于多个主题;在垃圾邮件检测中,一封邮件可能同时包含正常邮件和垃圾邮件的特征。通过使用 BERT 实现多标签文本分类,我们可以更准确地处理这些任务并提高模型的泛化能力。

    总之,BERT 由于其强大的表示能力为多标签文本分类提供了新的可能性。通过修改 BERT 的输出层、使用标签嵌入技术和结合其他技术,我们可以构建高效的多标签分类模型。这不仅提高了分类精度,而且更好地满足了实际应用的需求。随着深度学习技术的不断发展,我们有理由相信多标签文本分类将在更多领域得到应用并取得更大的成功。

    Multi label 多标签分类问题(Pytorch,TensorFlow,Caffe)

    Multi label 多标签分类问题(Pytorch,TensorFlow,Caffe)

    适用场景:一个输入对应多个label,或输入类别间不互斥

    调用函数:

    1. Pytorch使用torch.nn.BCEloss

    2. Tensorflow使用tf.losses.sigmoid_cross_entropy

    3. Caffe使用SigmoidCrossEntropyLoss

    在output和target之间构建binary cross entropy,其中i为每一个类。

     

    以pytorch为例:Caffe,TensorFlow版本类比,输入均为相同形式的向量

    m = nn.Sigmoid()
    loss = nn.BCELoss()
    input = autograd.Variable(torch.randn(3), requires_grad=True)
    target = autograd.Variable(torch.FloatTensor(3).random_(2))
    output = loss(m(input), target)
    output.backward()

    注意target的形式,要写成01编码形式,eg:如果同时为第一类和第三类则,[1, 0, 1]

    主要是结合sigmoid来使用,经过classifier分类过后的输出为(batch_size,num_class)为每个数据的标签, 标签不是one-hot的主要体现在sigmoid输出之后,仍然为(batch_size,num_class),对于一个实例,它的各个label的分数加起来不一定等于1,bceloss在每个类维度上求cross entropy loss然后加和求平均得到,这里就体现了多标签的思想。

    [CVPR2015] Is object localization for free? – Weakly-supervised learning with convolutional neural networks这篇论文里设计了针对多标签问题的loss,传统的类别分类不适用,作者把这个任务视为多个二分类问题,loss function和分类的分数如下:

     

    PyTorch 使用TorchText进行文本分类

    PyTorch 使用TorchText进行文本分类

    要查看图文并茂版教程,请移步: http://studyai.com/pytorch-1.4/beginner/text_sentiment_ngrams_tutorial.html

    本教程演示如何在 torchtext 中使用文本分类数据集,包括

    - AG_NEWS,
    - SogouNews,
    - DBpedia,
    - YelpReviewPolarity,
    - YelpReviewFull,
    - YahooAnswers,
    - AmazonReviewPolarity,
    - AmazonReviewFull
    

    此示例演示如何使用 TextClassification 数据集中的一个训练用于分类文本数据的监督学习算法。

    使用ngrams加载数据

    一个ngrams特征包(A bag of ngrams feature)被用来捕获一些关于本地词序的部分信息。 在实际应用中,双字元(bi-gram)或三字元(tri-gram)作为词组比只使用一个单词(word)更有益处。例如:

    "load data with ngrams"
    Bi-grams results: "load data", "data with", "with ngrams"
    Tri-grams results: "load data with", "data with ngrams"
    

    TextClassification Dataset支持 ngrams 方法。通过将 ngrams 设置为2, 数据集中的示例文本将是一个单字加上bi-grams字符串的列表。

    import torch
    import torchtext
    from torchtext.datasets import text_classification
    NGRAMS = 2
    import os
    if not os.path.isdir(''./.data''):
        os.mkdir(''./.data'')
    train_dataset, test_dataset = text_classification.DATASETS[''AG_NEWS''](
        root=''./.data'', ngrams=NGRAMS, vocab=None)
    BATCH_SIZE = 16
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    

    定义模型

    模型由 EmbeddingBag 层和线性层组成(见下图)。 nn.EmbeddingBag 计算 embeddings 的 “bag” 的平均值。这里的文本条目有不同的长度。 nn.EmbeddingBag 此处不需要填充(padding),因为文本长度以偏移量形式保存。

    此外,由于 nn.EmbeddingBag 在线动态地累积了embeddings的平均值,因此 nn.EmbeddingBag 可以提高处理张量序列的性能和内存效率。 ../_images/text_sentiment_ngrams_model.png

    import torch.nn as nn
    import torch.nn.functional as F
    class TextSentiment(nn.Module):
        def __init__(self, vocab_size, embed_dim, num_class):
            super().__init__()
            self.embedding = nn.EmbeddingBag(vocab_size, embed_dim, sparse=True)
            self.fc = nn.Linear(embed_dim, num_class)
            self.init_weights()
    
        def init_weights(self):
            initrange = 0.5
            self.embedding.weight.data.uniform_(-initrange, initrange)
            self.fc.weight.data.uniform_(-initrange, initrange)
            self.fc.bias.data.zero_()
    
        def forward(self, text, offsets):
            embedded = self.embedding(text, offsets)
            return self.fc(embedded)
    

    初始化模型

    AG_NEWS 数据集有四个标签,因此类的数量是四个。

    1 : World 2 : Sports 3 : Business 4 : Sci/Tec

    The vocab size is equal to the length of vocab (including single word and ngrams). The number of classes is equal to the number of labels, which is four in AG_NEWS case.

    VOCAB_SIZE = len(train_dataset.get_vocab())
    EMBED_DIM = 32
    NUN_CLASS = len(train_dataset.get_labels())
    model = TextSentiment(VOCAB_SIZE, EMBED_DIM, NUN_CLASS).to(device)
    

    用于产生批量数据的函数

    由于文本条目的长度不同,因此使用自定义函数 generate_batch() 生成数据batch和偏移量。 此函数传递给 torch.utils.data.DataLoader.中的 collate_fn 。 collate_fn 的输入是一个具有batch_size大小的张量列表, collate_fn 函数将它们打包成一个 mini-batch 。注意这里必须确保 collate_fn 被声明为顶级定义的函数, 这样可以确保每个线程(worker)都可以使用该功能。

    原始数据batch输入中的文本条目被打包成一个列表,并作为 nn.EmbeddingBag 的输入连接为单个张量。 偏移量(offsets)是分隔符的张量,表示文本张量中单个序列的起始索引。Label 是保存单个文本条目标签的张量。

    def generate_batch(batch):
        label = torch.tensor([entry[0] for entry in batch])
        text = [entry[1] for entry in batch]
        offsets = [0] + [len(entry) for entry in text]
        # torch.Tensor.cumsum returns the cumulative sum
        # of elements in the dimension dim.
        # torch.Tensor([1.0, 2.0, 3.0]).cumsum(dim=0)
    
        offsets = torch.tensor(offsets[:-1]).cumsum(dim=0)
        text = torch.cat(text)
        return text, offsets, label
    

    定义训练和评估模型的函数

    建议PyTorch用户使用 torch.utils.data.DataLoader , 它可以轻松地并行加载数据(这里有一个教程: 数据加载 )。 我们在这里使用 DataLoader 加载AG_NEWS数据集并将其发送到模型进行训练/验证。

    from torch.utils.data import DataLoader
    
    def train_func(sub_train_):
    
        # 训练模型
        train_loss = 0
        train_acc = 0
        data = DataLoader(sub_train_, batch_size=BATCH_SIZE, shuffle=True,
                          collate_fn=generate_batch)
        for i, (text, offsets, cls) in enumerate(data):
            optimizer.zero_grad()
            text, offsets, cls = text.to(device), offsets.to(device), cls.to(device)
            output = model(text, offsets)
            loss = criterion(output, cls)
            train_loss += loss.item()
            loss.backward()
            optimizer.step()
            train_acc += (output.argmax(1) == cls).sum().item()
    
        # 调整学习率
        scheduler.step()
    
        return train_loss / len(sub_train_), train_acc / len(sub_train_)
    
    def test(data_):
        loss = 0
        acc = 0
        data = DataLoader(data_, batch_size=BATCH_SIZE, collate_fn=generate_batch)
        for text, offsets, cls in data:
            text, offsets, cls = text.to(device), offsets.to(device), cls.to(device)
            with torch.no_grad():
                output = model(text, offsets)
                loss = criterion(output, cls)
                loss += loss.item()
                acc += (output.argmax(1) == cls).sum().item()
    
        return loss / len(data_), acc / len(data_)
    

    划分数据集并运行模型

    由于原始的 AG_NEWS 没有有效的数据集,我们将训练数据集分割为具有0.95(train)和0.05(valid)分割比的train/valid集。 这里我们使用PyTorch核心库中的 torch.utils.data.dataset.random_split 函数。

    CrossEntropyLoss 准则把 nn.LogSoftmax() 和 nn.NLLLoss() 组合进了一个类中。 它在训练C类分类问题时非常有用。 SGD 作为优化器实现了随机梯度下降法。初始学习率设置为4.0。这里使用 StepLR 来调整各个回合(epoch)的学习率。

    import time
    from torch.utils.data.dataset import random_split
    N_EPOCHS = 5
    min_valid_loss = float(''inf'')
    
    criterion = torch.nn.CrossEntropyLoss().to(device)
    optimizer = torch.optim.SGD(model.parameters(), lr=4.0)
    scheduler = torch.optim.lr_scheduler.StepLR(optimizer, 1, gamma=0.9)
    
    train_len = int(len(train_dataset) * 0.95)
    sub_train_, sub_valid_ = \
        random_split(train_dataset, [train_len, len(train_dataset) - train_len])
    
    for epoch in range(N_EPOCHS):
    
        start_time = time.time()
        train_loss, train_acc = train_func(sub_train_)
        valid_loss, valid_acc = test(sub_valid_)
    
        secs = int(time.time() - start_time)
        mins = secs / 60
        secs = secs % 60
    
        print(''Epoch: %d'' %(epoch + 1), " | time in %d minutes, %d seconds" %(mins, secs))
        print(f''\tLoss: {train_loss:.4f}(train)\t|\tAcc: {train_acc * 100:.1f}%(train)'')
        print(f''\tLoss: {valid_loss:.4f}(valid)\t|\tAcc: {valid_acc * 100:.1f}%(valid)'')
    

    在GPU上运行模型并得到以下信息:

    Epoch: 1 | time in 0 minutes, 11 seconds
    
    Loss: 0.0263(train)     |       Acc: 84.5%(train)
    Loss: 0.0001(valid)     |       Acc: 89.0%(valid)
    
    Epoch: 2 | time in 0 minutes, 10 seconds
    
    Loss: 0.0119(train)     |       Acc: 93.6%(train)
    Loss: 0.0000(valid)     |       Acc: 89.6%(valid)
    
    Epoch: 3 | time in 0 minutes, 9 seconds
    
    Loss: 0.0069(train)     |       Acc: 96.4%(train)
    Loss: 0.0000(valid)     |       Acc: 90.5%(valid)
    
    Epoch: 4 | time in 0 minutes, 11 seconds
    
    Loss: 0.0038(train)     |       Acc: 98.2%(train)
    Loss: 0.0000(valid)     |       Acc: 90.4%(valid)
    
    Epoch: 5 | time in 0 minutes, 11 seconds
    
    Loss: 0.0022(train)     |       Acc: 99.0%(train)
    Loss: 0.0000(valid)     |       Acc: 91.0%(valid)
    

    使用测试数据集评估模型

    print(''Checking the results of test dataset...'')
    test_loss, test_acc = test(test_dataset)
    print(f''\tLoss: {test_loss:.4f}(test)\t|\tAcc: {test_acc * 100:.1f}%(test)'')
    

    检查测试数据集的结果

    Loss: 0.0237(test)      |       Acc: 90.5%(test)
    

    在一条随机新闻上测试

    使用目前为止最好的模型,测试一个高尔夫(golf)新闻。 标签信息在 此处 提供。

    import re
    from torchtext.data.utils import ngrams_iterator
    from torchtext.data.utils import get_tokenizer
    
    ag_news_label = {1 : "World",
                     2 : "Sports",
                     3 : "Business",
                     4 : "Sci/Tec"}
    
    def predict(text, model, vocab, ngrams):
        tokenizer = get_tokenizer("basic_english")
        with torch.no_grad():
            text = torch.tensor([vocab[token]
                                for token in ngrams_iterator(tokenizer(text), ngrams)])
            output = model(text, torch.tensor([0]))
            return output.argmax(1).item() + 1
    
    ex_text_str = "MEMPHIS, Tenn. – Four days ago, Jon Rahm was \
        enduring the season’s worst weather conditions on Sunday at The \
        Open on his way to a closing 75 at Royal Portrush, which \
        considering the wind and the rain was a respectable showing. \
        Thursday’s first round at the WGC-FedEx St. Jude Invitational \
        was another story. With temperatures in the mid-80s and hardly any \
        wind, the Spaniard was 13 strokes better in a flawless round. \
        Thanks to his best putting performance on the PGA Tour, Rahm \
        finished with an 8-under 62 for a three-stroke lead, which \
        was even more impressive considering he’d never played the \
        front nine at TPC Southwind."
    
    vocab = train_dataset.get_vocab()
    model = model.to("cpu")
    
    print("This is a %s news" %ag_news_label[predict(ex_text_str, model, vocab, 2)])
    

    This is a Sports news

    今天关于使用Pytorch和BERT进行多标签文本分类pytorch 多标签分类的介绍到此结束,谢谢您的阅读,有关5个简单的步骤使用Pytorch进行文本摘要总结、BERT 实现多标签文本分类:强大模型的应用与展望、Multi label 多标签分类问题(Pytorch,TensorFlow,Caffe)、PyTorch 使用TorchText进行文本分类等更多相关知识的信息可以在本站进行查询。

    本文标签:

    上一篇SpringBoot+Vue实现用户头像修改功能(vue头像上传和修改)

    下一篇WebSphere远程代码执行漏洞分析(weblogic远程代码执行漏洞cve-2019-2729)