预训练Bert模型输出类型为str问题解决
input_ids=keras.layers.Input(shape=(MAXLEN,),dtype='int32') attention_mask=keras.layers.Input(shape=(MAXLEN,),dtype='int32') token_type_ids=keras.layers.Input(shape=(MAXLEN,),dtype='int32') _,x=bert_model([input_ids,attention_mask,token_type_ids]) outputs=keras.layers.Dense(1,activation='sigmoid')(x) #给模型加一个全连接层,将bert的输出x调整后用sigmoid函数做一个二分类 model=keras.models.Model(inputs=[input_ids,attention_mask,token_type_ids],outputs=outputs) model.compile(loss='binary_crossentropy',optimizer=keras.optimizers.Adam(lr=LEARNING_RATE),metrics=['accuracy'])
这是一段用预训练的bert模型来构造一个文本分类器的代码。在代码中,先用设定好的输入,输入模型得到最后一层隐藏层的输出,作为变量x再输入全连接层,再经过sigmiod函数来实现二分类的效果。最后将全连接层加入到模型中,设置好损失函数等参数进行编译。
_,x=bert_model([input_ids,attention_mask,token_type_ids]) 这句的作用是获取bert模型的最后一池化层的输出。
在运行代码时,每次运行到Dense层就会报错提示格式错误,type(x)查看x的格式,发现它的输出是一个名为pooler_output的字符串,这显然不合要求:模型的输出应该是一个张量而不是字符串,而全连接层需要的也是一个二维的张量。但如果使用x=tf.convert_to_tensor(x)强制将x转为张量,那么得到的是一个丢失了形状信息的空张量。
在网上查找资料后发现, 这和tranformers库的版本有关系。在transfromer库3.X版本后,模型不再返回张量的元组,而是返回特定对象。pip show transformer指令查看版本。如果版本高于4.0,那么输出的确实会是字符串,解决办法是在一开始的模型定义语句里增加一个参数return_dict=flase,让模型正确返回一个元组。
如果不想考虑这么麻烦,或者加上了return_dict后,解释器报错,那就直接用
x=bert_model([input_ids,attention_mask,token_type_ids])[1]即可。因为模型的输出是包含两个张量的元组。第一部分是所有时刻的输出,第二部分就是最后一层隐藏层的输出。用[1]就能直接得到最后一层张量,避免了格式问题。不得不说这不是什么大问题,但遇到以后真的很糟心。