使用多种文本输入功能训练 NLP

数据挖掘 机器学习 nlp nltk
2021-09-16 23:29:49

问题:

如何训练具有基于多个文本输入特征的离散标签的 NLP 模型?

背景:

我正在尝试根据问题的文本及其可能的回答来预测 4 选项多项选择考试题的难度(考生选择正确答案的概率) 。我希望能够考虑到一些不正确但令人信服的回答(其确切主题与问题的内容相关)如何扭曲考试问题的难度,以及问题的措辞如何会使问题产生误导。

直觉、尝试和选择:

我的直觉是,问题的内容及其回答对于预测其难度都具有重要意义。然而,当使用像 Spacy、NLTK 或 Textacy 这样的库时,训练似乎一次只在一个文本列上进行。我一次可能会查看五个文本列,如果我将问题响应连接在一起,则可能是两个。

我在这个话题上找不到太多东西,但这我发现的一个尝试。我认为这种尝试是有缺陷的,因为他们只是多次进行单列训练,例如根据薪水值训练城市并将其连接到您的职位描述训练与薪水值不会对您的第一个模型,因为培训时职位描述不依赖于城市。

我发现的选择是:

  • 毕竟遵循上述尝试(我认为这是有缺陷的)
  • 连接我的文本特征(我不明白为什么在这种情况下这是有道理的,但似乎是常态)
  • 完全消除我的一些功能,例如按主题缩小问题范围并处理问题内容,​​并仅对连接在一起的响应选项进行培训(这也删除了问题内容中可能导致预测值的一些非常重要的信息)

想法和建议?有没有图书馆可以让这更容易?谢谢!

3个回答

将整个问题及其答案连接到 RNN 中可能是一种尝试,但始终使用保留的特殊标记(或各种标记)来标记问题的开始位置。例如,您可以像这样连接:

问题文本 <1> 答案 1 <2> 答案 2 <3> 答案 3 <4> 答案 4

其中 <1>, <2>... 是特殊标记,因此模型通过足够的示例可能能够理解其含义。

“足够多的例子”值得强调,特别是因为这种类型的模型可能需要很大的复杂性(就参数数量而言)才能使其工作,因此您将需要足够大的数据集,可能是10k 或更大。您还可以通过混合输入中答案的顺序来测试一些数据增强,当然还可以更改正确的标签。

NLP 是相当超维的。我会采用数据驱动的方式并使用一些预训练的嵌入器。现在有一些可供选择,比如Facebook 的LASER有非官方的pypi lib,虽然它工作得很好。如果你想达到开创性的分数,那么手工进行 NLP 是没有意义的。嵌入器通常涵盖数十种语言,因此您可以使用任何您想要的语言提供训练数据。您的模型也适用于那些开箱即用的语言,即使您对它们进行了其他语言的培训。如果你需要一些定制的东西,你可以从谷歌选择BERT,但你必须自己进一步推动它。它并没有真正预训练那么多。

您可以尝试分别对问题和每个答案进行编码,然后将其组合在一起。您也可以尝试将其全部加入编码器。它应该做得很好。

对于您的培训,您似乎想要组合不一定引用相同上下文的特征。本质上,您有一个问题功能和一个响应功能。这两个恰好是文本数据,但它可以是数字的年龄或分类的性别。出于这个原因,您可能需要一个能够组合不同输入的模型,例如多层网络。

方法

您将拥有一个 LSTM 来处理您的问题功能和一个 LSTM 来处理您的回复功能。您将在第二阶段将两个 LSTM 的输出组合成一个完全连接的层,然后再将其传递给您的最终激活。

具体来说,步骤包括:

  1. 拆分批处理:准备您的数据集以允许questions单独responses分发。这是确保批次大小相同所必需的。
  2. LSTM 模型:实例化两个 LSTM 模型,它们将分别接收问题和响应批次。
  3. FC 层:连接两个 LSTM 中每一个的最终 LSTM 单元的输出或隐藏状态,并馈入全连接层。在这里,您可以添加更多 FC 层,本质上使其成为要优化的超参数。
  4. 激活:激活函数适用于您的问题类型,例如二进制、多类等。
Questions_INPUTS ------> LSTM-1 -------->
                                         |---> MERGE ---> SIGMOID
Responses_INPUTS ------> LSTM-2 -------->

我在 pytorch 中开发了类似的组合模型系统。在这方面,以下附录可能会有所帮助。


拆分批处理示例

实用程序:sklearn.model_selection.train_test_split(*arrays, **options)torch.utils.data

# train/test data split 
questions_train, questions_test, responses_train, responses_test, train_y, test_y =  train_test_split(
    questions, responses, y, train_size=0.666, random_state=666)

# create tensor dataset
train_data = TensorDataset(
           torch.from_numpy(questions_train), torch.from_numpy(responses_train), 
           torch.from_numpy(np.array(train_y))
)

# create dataloaders
train_loader = DataLoader(
             train_data, shuffle=True, 
             batch_size=666, drop_last=True)

Forward() 方法示例

def forward(questions_batch,  responses_batch):
    # Handle questions features
    X1 = self.embedding(questions_batch) # I assume you deal with text
    # X1.shape = (batch_size, questions_len, embed_dim)
    output, (h, c) = self.lstm(X1, self.hidden)
    X1 = h[-1] # I assume unidirectional LSTM to keep example simple
    # X1. shape = (batch_size, embed_dim1)

    # Handle responses features
    X2 = self.embedding(responses_batch) # I assume you deal with text
    # X2.shape = (batch_size, responses_len, embed_dim)
    output, (h, c) = self.lstm(X2, self.hidden)
    X2 = h[-1] # I assume unidirectional LSTM to keep example simple
    # X2. shape = (batch_size, embed_dim2)

    # Merge features
    X = torch.concat([X1, X2], dim=1)
    # X.shape = (batch_size, (embed_dim1+embed_dim2) )
    X = self.fc(X) # 1 or more fully connected linear layers
    # X.shape = (batch_size, output_dim_fc1)
    # Last layers to get the right output for your loss
    ...

在此处查看相关答案和讨论: