使用SentencePieceTokenizer与HuggingFace数据集时目标输出不匹配
我尝试基于HuggingFace数据集,使用keras_nlp.tokenizers.SentencePieceTokenizer层构建简单模型,但无法让数据集的目标形状与模型输出形状匹配。
数据加载代码
将HF数据集转换为TensorFlow版本数据集,适配keras.Model.fit():
trainDS = LH_dataset_HF['train'].to_tf_dataset( columns=["text"], label_cols=["answer"], batch_size=batch_size, shuffle=False, )
数据加载及Tokenizer状态
数据加载和SentencePieceTokenizer工作正常,输出如下:
trainTF shape=(6, 3) answer shape=(6,) all answers=[b'Yes' b'Yes' b'Yes' b'No' b'No' b'No'] echo1: b'My roommate and I were feeling unwell in our basement apartment for a long ...
模型结构
Model: "functional_1" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ input_layer (InputLayer) │ (None) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ sentence_piece_tokenizer │ (None, 32) │ 0 │ │ (SentencePieceTokenizer) │ │ │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ embed (Embedding) │ (None, 32, 100) │ 1,000,000 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ predictions (Dense) │ (None, 32, 1) │ 101 │ └─────────────────────────────────┴────────────────────────┴───────────────┘ Total params: 1,000,101 (3.82 MB) Trainable params: 1,000,101 (3.82 MB) Non-trainable params: 0 (0.00 B)
训练时的错误
调用model.fit(trainDS)时触发错误:
ValueError: Arguments
targetandoutputmust have the same rank (ndim). Received: target.shape=(None,), output.shape=(None, 32, 1)
尝试的标签转换
我用map将字符串标签转为整数:
def binaryLbl(txt,tlbl): return(txt,1 if tlbl=='Yes' else 0) trainDS2 = trainDS.map(binaryLbl)
但trainDS2.element_spec()显示标签无形状:
(TensorSpec(shape=(None,), dtype=tf.string, name=None), TensorSpec(shape=(), dtype=tf.int32, name=None))
疑问
- 为什么
target.shape=(None,)? - 如何让目标形状匹配模型的单个预测输出节点?
包版本信息
torch=2.1.0.post100 torchtext=0.16.1 tensorflow=2.15.0 tensorflow_text=2.15.0 keras=3.0.5 keras_nlp=0.7.0
问题根源分析
- 目标形状
(None,)的原因:每个样本对应一个"Yes/No"单标签,所以批次标签形状为(batch_size,),即(None,)(None代表批次维度)。而模型最后一层输出(None, 32, 1),是因为Embedding层输出保留了序列长度维度32,直接接Dense层时仅作用于最后一维,导致输出为3维,和标签的2维不匹配。 - 模型输出与目标不匹配的核心:任务是单标签二分类,但模型却输出了序列中每个token的预测,不符合任务需求。
具体修复步骤
1. 修改模型结构,压缩序列维度输出单值预测
在Embedding层后添加全局池化层,把序列特征压缩为全局特征,再接Dense层输出单值:
from keras.layers import GlobalAveragePooling1D model = keras.Sequential([ keras.Input(shape=(), dtype=tf.string), SentencePieceTokenizer(...), # 你的Tokenizer层 keras.layers.Embedding(vocab_size, 100), GlobalAveragePooling1D(), # 压缩序列维度,输出形状(None,100) keras.layers.Dense(1, activation='sigmoid') # 二分类输出单值,形状(None,1) ])
修改后模型输出形状为(None,1),可与标签的(None,)兼容(Keras会自动广播匹配)。
2. 修正标签形状,对齐模型输出
将标量标签转为(1,)形状的张量,确保批次后标签形状为(batch_size,1):
def binaryLbl(txt, tlbl): label = tf.constant(1, dtype=tf.int32) if tlbl == b'Yes' else tf.constant(0, dtype=tf.int32) return txt, tf.expand_dims(label, axis=-1) trainDS2 = trainDS.map(binaryLbl)
此时trainDS2.element_spec()的标签部分会变为TensorSpec(shape=(1,), dtype=tf.int32, name=None),批次后为(None,1),与模型输出形状完全一致。
3. 验证匹配性
修改后检查:
- 模型输出形状:
(None,1) - 数据集标签形状:
(None,1)
此时调用model.fit(trainDS2)即可解决形状不匹配的问题。
内容的提问来源于stack exchange,提问作者rikb

