首页 > 解决方案 > 使用 Spacy 进行文本分类:超越基础以提高性能

问题描述

我正在尝试在文本训练数据集(Reddit 帖子)上训练一个文本分类器,该数据集具有两个专有类(1 和 0),涉及帖子作者的特征,而不是帖子本身
类不平衡:大约 75:25,这意味着 75% 的作者是“0”,而 25% 是“1”。
整个数据集由 3 列组成:第一列代表帖子的作者,第二列代表帖子所属的 subreddit,第三列代表实际帖子。

数据

数据集如下所示:

In [1]: train_data_full.head(5)
Out[1]: 
          author          subreddit            body
0        author1         subreddit1         post1_1 
1        author2         subreddit2         post2_1
2        author3         subreddit2         post3_1
3        author2         subreddit3         post2_2 
4        author5         subreddit4         post5_1

其中 postI_J 是第 I 个作者的第 J 个帖子。请注意,在此数据集中,同一作者可能会出现多次,如果她/他发布了多次。

在一个单独的数据集中,我有每个作者所属的类。

我做的第一件事是按作者分组:


def proc_subs(l):
    s = set(l)
    return " ".join([st.lower() for st in s])

train_data_full_agg = train_data_full.groupby(["author"], as_index = False).agg({'subreddit':  proc_subs, "body": " ".join}) 

train_data_full_agg.head(5)

Out[2]:
 author                          subreddit                      body     
author1              subreddit1 subreddit2           post1_1 post1_2
author2   subreddit3 subreddit2 subreddit3   post2_1 post2_2 post2_3   
author3              subreddit1 subreddit5           post3_1 post3_2 
author4                         subreddit7                   post4_1
author5              subreddit1 subreddit2           post5_1 post5_2


总共有 5000 个作者,4000 个用于训练,1000 个用于验证(roc_auc 分数)。这是我正在使用的 spaCy 代码(train_textstrain_data_full_agg.body.tolist()我用于训练的子集,而test_texts我用于验证的代码)。

# Before this line of code there are others (imports and data loading mostly) which i think are irrelevant

train_data  = list(zip(train_texts, train_labels))

nlp = spacy.blank("en")
if 'textcat' not in nlp.pipe_names:
    textcat = nlp.create_pipe("textcat", config={"exclusive_classes": True, "architecture": "ensemble"})
        nlp.add_pipe(textcat, last = True)
else:
    textcat = nlp.get_pipe('textcat')

textcat.add_label("1")
textcat.add_label("0")

def evaluate_roc(nlp,textcat):
    docs = [nlp.tokenizer(tex) for tex in test_texts]
    scores , a = textcat.predict(docs) 
    y_pred = [b[0] for b in scores]
    roc = roc_auc_score(test_labels, y_pred)
    return roc


dec = decaying(0.6 , 0.2, 1e-4)
pipe_exceptions = ['textcat']
    other_pipes = [pipe for pipe in nlp.pipe_names if pipe not in pipe_exceptions]
    with nlp.disable_pipes(*other_pipes): 
        optimizer = nlp.begin_training()
        for epoch in range(10):
        random.shuffle(train_data)
            batches = minibatch(train_data, size = compounding(4., 32., 1.001) )                                                             
            for batch in batches:
                texts1, labels = zip(*batch)
                nlp.update(texts1, labels, sgd=optimizer, losses=losses, drop = next(dec))
            with textcat.model.use_params(optimizer.averages):
                rocs.append(evaluate_roc(nlp, textcat))

问题

如果与可以使用 scikit learn 编写的更简单算法(如 tfidf、bow、词嵌入等)相比,我的性能也很差(在我没有标签的测试数据集上使用 ROC 测量)

尝试

我试图通过以下过程获得更好的性能:

  1. 文本的各种预处理/词形还原:最好的方法似乎是删除所有标点符号、数字、停用词、词汇表之外的单词,然后对所有剩余的词进行词形还原
  2. 尝试过的 textcat 架构:集成、弓(也带有ngram_sizeattr参数):最好的似乎是集成,如spaCy 文档
  3. 试图包含 subreddit 信息:我通过在相同的 4000 个作者的subreddit列上训练一个单独的 textcat 来做到这一点(请参阅第 5 点,以了解如何使用来自此步骤的信息)。
  4. 试图包含词嵌入信息:使用来自en_core_web_lg-2.2.5spaCy 模型的文档向量,我在相同的 4000 个作者的聚合帖子上训练了一个 scikit 多层感知器。(请参阅第 5 点,了解如何使用来自此步骤的信息)。
  5. 然后,为了混合来自 subreddits、帖子和文档向量的信息,我对三个模型的 1000 个预测进行了逻辑回归训练(我还尝试在最后一步使用adasyn来平衡类

使用这个逻辑回归,我在测试数据集上得到 ROC = 0.89。如果我删除这些步骤中的任何一个并使用中间模型,ROC 就会降低。

我还尝试了以下步骤,这再次降低了 ROC:

  1. 使用预训练的模型,例如 bert。我使用的代码类似于这个
  2. 从一开始就尝试平衡类,因此使用较小的训练集。
  3. 试图留下标点符号并将 a放在管道sentencizer的开头nlp

附加信息(主要来自评论)

问题

  1. 你对我正在使用的 spaCy 代码有什么特别的建议吗(例如,使用 subreddits 和/或文档向量信息的任何其他方式)?
  2. 如何改进整体模型?

任何建议都受到高度赞赏。
由于我是 NLP 新手,请在代码/解释/参考方面尽可能明确。

标签: nlpspacytext-classificationword-embeddingspacy-transformers

解决方案


推荐阅读