BERT特征提取作为CNN/LSTM嵌入层的Keras实现及报错解决
问题解决与实现方案
一、当前报错原因修复
报错根因
你当前的报错核心原因是混用了PyTorch生态和TensorFlow生态的代码:
你使用的BertModel.from_pretrained默认加载的是PyTorch版本的BERT模型,它要求输入为PyTorch张量,内部会调用张量的size()方法获取维度。但你替换torch.cat为TensorFlow的张量拼接方法,又开启了TensorFlow的numpy行为,导致传入模型的input_ids2是TensorFlow张量/ numpy数组,这类对象的size是存储元素总数的整数属性,不是可调用的方法,因此触发了'numpy.int64' object is not callable的错误。
临时修复方案(PyTorch提取特征喂给Keras)
如果你想继续用PyTorch版BERT提取特征,再输入到Keras的CNN/LSTM模型中,修正后的特征提取代码如下:
import torch import numpy as np import pandas as pd from transformers import BertTokenizer, BertConfig, BertModel # 初始化分词器 tokenizer = BertTokenizer.from_pretrained("bert-base-uncased") input_ids=[] attention_masks=[] for row in df['posts'].tolist(): bert_inp=tokenizer.encode_plus( str(row), add_special_tokens = True, max_length =64, padding='max_length', truncation=True, return_attention_mask = True ) # 先转成张量再添加,避免拼接错误 input_ids.append(torch.tensor(bert_inp['input_ids'], dtype=torch.long).unsqueeze(0)) attention_masks.append(torch.tensor(bert_inp['attention_mask'], dtype=torch.long).unsqueeze(0)) # 张量拼接,原torch.cat是正确的,不需要替换 input_ids = torch.cat(input_ids, dim=0) attention_masks = torch.cat(attention_masks, dim=0) # 加载BERT模型提取特征 config = BertConfig.from_pretrained("bert-base-uncased", output_hidden_states=True) model = BertModel.from_pretrained("bert-base-uncased", config=config) model.eval() # 切换到推理模式,关闭 dropout 等训练逻辑 with torch.no_grad(): # 关闭梯度计算,节省内存 outputs = model(input_ids, attention_mask=attention_masks) # 提取最后一层隐藏态,转numpy数组后可直接喂给Keras模型 # 形状为 [样本数, 序列长度, 768],如果只需要<[BOS_never_used_51bce0c785ca2f68081bfa7d91973934]>特征,取[:,0,:]即可 bert_feature = outputs.last_hidden_state.numpy()
二、Keras生态下BERT+CNN/LSTM融合实现方案
如果要避免跨框架兼容问题,建议直接使用TensorFlow版本的BERT(TFBertModel),可以直接嵌入Keras模型实现端到端训练,实现步骤如下:
1. 依赖导入
from transformers import BertTokenizer, TFBertModel import tensorflow as tf from tensorflow.keras import layers, Model import numpy as np import pandas as pd
2. 文本编码处理
def bert_encode(texts, max_len=64): tokenizer = BertTokenizer.from_pretrained("bert-base-uncased") input_ids = [] attention_masks = [] for text in texts: encoded = tokenizer.encode_plus( str(text), add_special_tokens=True, max_length=max_len, padding='max_length', truncation=True, return_attention_mask=True ) input_ids.append(encoded['input_ids']) attention_masks.append(encoded['attention_mask']) return np.array(input_ids), np.array(attention_masks) # 替换为你的数据集路径与字段 df = pd.read_csv("你的数据集.csv") input_ids, attention_masks = bert_encode(df['posts'].values) labels = df['label'].values # 替换为你的标签字段
3. BERT+CNN模型构建示例
def build_bert_cnn(max_len=64, num_classes=1): # BERT输入层 input_ids = layers.Input(shape=(max_len,), dtype=tf.int32, name="input_ids") attention_mask = layers.Input(shape=(max_len,), dtype=tf.int32, name="attention_mask") # 加载TF版BERT bert = TFBertModel.from_pretrained("bert-base-uncased") # 可设置bert.trainable = False冻结BERT参数,只训练下游CNN层,减少计算量 bert.trainable = True # 取BERT最后一层的序列输出 bert_output = bert(input_ids, attention_mask=attention_mask)[0] # CNN特征提取层 cnn_layer = layers.Conv1D(filters=128, kernel_size=3, activation="relu")(bert_output) pool_layer = layers.GlobalMaxPooling1D()(cnn_layer) # 分类输出层,二分类用sigmoid,多分类用softmax并调整num_classes output = layers.Dense(num_classes, activation="sigmoid")(pool_layer) model = Model(inputs=[input_ids, attention_mask], outputs=output) model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["accuracy"]) return model # 模型训练 model = build_bert_cnn() model.fit([input_ids, attention_masks], labels, epochs=5, batch_size=16, validation_split=0.2)
4. BERT+LSTM模型替换方案
只需要将模型构建中的CNN部分替换为LSTM层即可:
# 替换上述模型的CNN+池化部分 lstm_layer = layers.Bidirectional(layers.LSTM(64, return_sequences=False))(bert_output) output = layers.Dense(1, activation="sigmoid")(lstm_layer)
内容的提问来源于stack exchange,提问作者Shorouk Adel
相关产品推荐
相关产品推荐

