我知道这之前已经讨论过了,但是我没有找到一个具体的答案,一些答案在尝试后没有工作,情况很简单,我有一个模型,如果我使用批规范,model.fit(training_data)
报告的训练精度在0.9以上(它不断增加,损失减少),但是在训练后如果我运行model.evaluate(training_data)
(注意是相同的数据)它返回0.09,而且预测非常糟糕(如果使用model.predict(training_data)
的结果手动计算,精度也很低)。我知道批量规范中训练和测试时间之间的差异,我知道差异应该是预料之中的,但是从0.9到0.09的下降似乎是错误的(并且模型完全不可用)。我从其他线程尝试了一些解决方案:
- 在
.evaluate
中使用batch_size与.fit
相同:没有影响 - set
tf.keras.backend.set_learning_phase(0)
:收到一个消息,说它现在已被弃用,没有任何区别。 - 设置所有批规范层在
.predict
和.evaluate
之前有layer.trainable=False
:它没有区别。
如果我删除批规范层,model.fit(training_data)
的报告与model.evaluate(training_data)
一致,但训练没有任何进展(结果一致但不好),所以我需要添加它。
这是TF 2.6中的一个主要bug吗?
更新:也测试了TF 2.5,结果是一样的。
示例代码(省略不相关的代码,如数据读取和预处理):### model definition
class CLS_BERT_Embedding(tf.keras.Model):
"""Will only use the CLS token"""
def __init__(self, bert_trainable=False, number_filters=50,FNN_units=512,
number_clases=2,dropout_rate=0.1,name="dcnn"):
super(CLS_BERT_Embedding,self).__init__(name)
self.checkpoint_id ="CLS_BERT_Embedding_bn_3fc_{}filters_{}fc_units_berttrainable{}".format(number_filters,
FNN_units,bert_trainable)
# trainable= False so we don't fine-tune bert, just use as embedding layer
self.bert_layer = hub.KerasLayer("https://tfhub.dev/tensorflow/bert_en_uncased_L-12_H-768_A-12/1",
trainable=bert_trainable,
input_shape=(3,376))
self.dense_1 = layers.Dense(units = FNN_units,activation="relu")
self.bn1 = layers.BatchNormalization()
self.dense_2 = layers.Dense(units = FNN_units, activation="relu")
self.bn2 = layers.BatchNormalization()
self.dense_3 = layers.Dense(units = FNN_units, activation="relu")
self.bn3 = layers.BatchNormalization()
self.dropout = layers.Dropout(rate=dropout_rate)
if number_clases == 2:
self.last_dense = layers.Dense(units=1,activation="sigmoid")
else:
self.last_dense = layers.Dense(units=number_clases,activation="softmax")
def get_bert_embeddings(self,all_tokens):
CLS_embedding ,embeddings = self.bert_layer([all_tokens[:,0,:],
all_tokens[:,1,:],
all_tokens[:,2,:]])
return CLS_embedding,embeddings
def call(self,inputs,training):
CLS_embedding, x_seq = self.get_bert_embeddings(inputs)
x = self.dense_1(CLS_embedding)
x = self.bn1(x,training)
x = self.dense_2(x)
x = self.bn2(x,training)
x = self.dense_3(x)
x = self.bn3(x,training)
output = self.last_dense(x)
return output
#### config and hyper-params
NUMBER_FILTERS = 1024
FNN_UNITS = 2048
BERT_TRAINABLE = False
NUMBER_CLASSES = len(tokenizer.vocab)
DROPOUT_RATE = 0.2
NUMBER_EPOCHS = 3
LR = 0.001
DEVICE = '/GPU:0'
#### optimization definition
with tf.device(DEVICE):
model = CLS_BERT_Embedding(
bert_trainable = BERT_TRAINABLE,
number_filters=NUMBER_FILTERS,
FNN_units=FNN_UNITS,
number_clases=NUMBER_CLASSES,
dropout_rate = DROPOUT_RATE)
if NUMBER_CLASSES == 2:
loss = "binary_crossentropy"
metrics = ["accuracy"]
else:
loss="sparse_categorical_crossentropy"
metrics = ["sparse_categorical_accuracy"]
optimizer = tf.keras.optimizers.Adam(learning_rate = LR)
loss="sparse_categorical_crossentropy"
model.compile(loss=loss,optimizer=optimizer,metrics=metrics)
### training
with tf.device(DEVICE):
model.fit(train_dataset,
batch_size = BATCH_SIZE ,
epochs=NUMBER_EPOCHS,
shuffle=True,
callbacks=[MyCustomCallback(),
tf.keras.callbacks.ReduceLROnPlateau(monitor="loss",patience=5),
tensorboard,lr_tensorboard])
### testing
train_results = model.evaluate(train_dataset,batch_size = BATCH_SIZE)
print(train_results)
尝试在不调整可训练标志的情况下运行推理,然后验证self.bn1.trainable=True。然后通过在每一批训练数据上调用模型作为可调用对象来运行forward prop,但要设置training=True,并每次进行评估。比如
for idx d in enumerate(train_dataset_:
_ = model(d[0], training=True)
model.evaluate(d)
if idx > 100:
break
如果你的损失开始下降,那么这是你的批规范移动统计更新不够快的一个例子,这是可能的,因为bn没有训练,但你的bert模式/层是。如果没有,请忽略其余部分,因为您可能遇到了不同的问题。
如果是这种情况,您有两个选择。一种是不断调用模型,使BN移动统计稳定。
另一种是统计分析你的bert层的输出(得到均值和var)并直接更新BN的移动统计权值。也许第一个BN是足够的,因为你的后密集层Xavier gloot初始化,但考虑到您正在使用Relu,您也可以尝试对它们进行Kaiming He初始化。
我同意@Yaoshiang的观点,很可能内部统计(移动平均,移动var)与每批的均值和方差不一致,因此在训练和测试时,BN层的归一化是不同的。想想看,如果我们在训练和测试中使用相同的批大小,那么我们可以在测试中保持training=True,而不会出现真正的问题(当使用predict或evaluate时)。否则,我们可以在训练中强制使用移动平均和移动方差进行归一化,而不是批处理的均值和方差,并且仍然估计beta和gamma。(这意味着对BatchNormalization类做一个小修改。)