如何使用Longformer将向量化文本转为嵌入矩阵适配LightGBM输入
问题描述
众所周知,将文本转换为向量、再将向量整合为矩阵,是为LightGBM等机器学习模型构造输入特征的常规流程。
以下为文本分词的参考代码:
import pandas as pd import transformers from transformers import LongformerTokenizer,LongformerForSequenceClassification, LongformerConfig,LongformerTokenizerFast import tensorflow as tf from torch.utils.data import Dataset, DataLoader config=LongformerConfig() test=pd.read_csv('../input/feedback-prize-effectiveness/test.csv') train=pd.read_csv('../input/feedback-prize-effectiveness/train.csv') # 加载模型、分词器,定义文本序列长度 model = LongformerForSequenceClassification.from_pretrained('allenai/longformer-base-4096',gradient_checkpointing=False,attention_window = 512) tokenizer = LongformerTokenizerFast.from_pretrained('allenai/longformer-base-4096', max_length = 1024) k=[] for i in train['discourse_text']: inputs=tokenizer(i) m=inputs.input_ids k.append(m) train['long_tokens']=k
上述代码调用Longformer提供的分词方法对数据集内的文本进行编码,处理完成后的数据集结构如下图所示:
核心疑问:生成的long_tokens字段存储的是变长张量/分词ID列表,需要如何转换才能满足LightGBM的输入格式要求。
解决方法
LightGBM的原生输入要求为二维固定长度数值矩阵:每一行对应1个样本,每一列对应1项数值特征,不支持直接输入变长列表、张量格式的序列数据。针对当前的long_tokens字段,按以下步骤处理即可:
- 统一序列长度,完成填充截断
当前生成的分词ID序列长度随原文本长度动态变化,首先需要将所有样本的序列对齐到固定长度。你在加载分词器时已经指定了max_length=1024,直接在分词阶段开启截断、填充参数即可一步完成对齐,无需事后手动处理:# 替换原有循环分词逻辑 def extract_token_ids(text): tokenized_res = tokenizer( text, max_length=1024, truncation=True, padding="max_length", return_tensors="np" ) return tokenized_res["input_ids"][0] train["long_tokens"] = train["discourse_text"].apply(extract_token_ids) - 拼接为二维特征矩阵
对齐后的每个long_tokens对应长度为1024的一维数组,将所有样本的数组堆叠为二维矩阵,即可直接输入LightGBM:import numpy as np # 最终特征矩阵形状为(样本总数, 1024),符合LightGBM输入要求 X_feature = np.stack(train["long_tokens"].values) - 效果优化提示
直接将分词ID作为特征输入的效果较差:模型会将ID值作为离散数值处理,很难学习到语义关联。更合理的做法是用Longformer提取稠密语义嵌入,例如取最后一层隐藏状态的平均池化结果、CLS位输出,得到固定维度的语义向量后再输入LightGBM,示例代码如下:import torch model.eval() embedding_list = [] with torch.no_grad(): for text in train["discourse_text"]: inputs = tokenizer( text, max_length=1024, truncation=True, padding="max_length", return_tensors="pt" ) model_out = model(**inputs, output_hidden_states=True) # 对最后一层隐藏态做平均池化,得到768维语义向量 text_emb = model_out.hidden_states[-1].mean(dim=1).squeeze().numpy() embedding_list.append(text_emb) X_feature = np.stack(embedding_list)
注意:如果坚持直接使用分词ID作为输入,需要在LightGBM训练时将这1024列特征全部指定为类别特征(
categorical_feature参数),否则模型会错误地将ID值作为连续数值计算,完全损失序列的语义信息。
内容的提问来源于stack exchange,提问作者Satwik
相关产品推荐
相关产品推荐

