TensorFlow中使用VocabularyProcessor处理可变长度文本的问题
解决VocabularyProcessor输出补零序列的问题
嘿,我之前做RNN文本预处理时也踩过VocabularyProcessor自动补零的坑!这玩意儿默认会把所有序列补到训练数据的最大长度,刚好有几个实用的解决思路,帮你得到无补零的数字序列:
思路1:手动去除序列中的补零
VocabularyProcessor返回的numpy数组里,有效token的id在前,补零在后。我们可以写个简单的函数,遍历每个序列并剔除末尾的零:
import numpy as np def strip_padding(transformed_sequences): # 遍历每个序列,提取非零元素并转成列表 return [seq[seq != 0].tolist() for seq in transformed_sequences] # 你的示例用法 b = ['tomorrow is a good friday','you'] # 先获取带补零的结果 padded_seq = np.array(list(vocab_processor.transform(b))) # 去除补零 no_pad_seq = strip_padding(padded_seq) # 输出就是你想要的无补零序列:[[对应tomorrow等的id], [you对应的id]]
这个方法简单直接,不需要改动原有的VocabularyProcessor拟合流程。
思路2:绕过transform,手动映射token到id
既然VocabularyProcessor的补零是自动行为,我们可以直接用它拟合好的词汇表,自己实现文本转id的逻辑,完全避免补零:
# 获取VocabularyProcessor的词汇映射字典 vocab_map = vocab_processor.vocabulary_._mapping def text_to_id_sequence(text): # 注意:这里的分词逻辑要和VocabularyProcessor保持一致(默认按空格分词) tokens = text.split() # 只保留词汇表中存在的token对应的id return [vocab_map[token] for token in tokens if token in vocab_map] # 处理你的新文本 no_pad_seq = [text_to_id_sequence(sent) for sent in b]
这个方法更灵活,你可以自定义过滤规则(比如忽略OOV词),完全控制序列长度。
思路3:替换为更现代的Tokenizer工具
VocabularyProcessor其实是TensorFlow旧版本tf.contrib里的组件,现在已经被官方弃用了。推荐换成tf.keras的Tokenizer,它默认就会返回无补零的序列,补零操作可以按需手动调用:
from tensorflow.keras.preprocessing.text import Tokenizer # 初始化Tokenizer并拟合训练文本 tokenizer = Tokenizer() tokenizer.fit_on_texts(your_train_texts) # 直接得到无补零的数字序列 no_pad_seq = tokenizer.texts_to_sequences(b) # 如果之后需要统一序列长度,再调用pad_sequences: # padded_seq = pad_sequences(no_pad_seq, maxlen=your_desired_length)
这是长期来看更靠谱的方案,Tokenizer功能更完善,社区支持也更好。
补充说明
VocabularyProcessor自动补零的原因是:它在fit阶段会记录训练数据中最长序列的长度,transform时为了输出形状统一的数组(方便模型批量输入),会把所有序列补到这个长度。如果你的RNN支持变长输入(比如用dynamic_rnn),完全不需要统一长度,用上面的方法得到无补零序列就可以直接输入模型啦。
内容的提问来源于stack exchange,提问作者HAO CHEN
相关产品推荐
相关产品推荐

