keras – 使用相同训练数据的evaluate_generator产生不同准确率

简而言之 我的模型训练了1个周期——用于测试目的。然而,当多次评估时,每次运行evaluate_generator方法时,即使使用相同训练数据,它也会产生不同的准确率。这是为什么呢?有没有什么方法可以在同一个模型上多次评估相同训练数据时获得相同的准确率?


我正在研究对话行为分类的语言学问题,我的模型基于这篇论文。使用keraskeras_contrib库提供的工具,我正在复制相同的模型,但我有一个关于为什么评估会产生不同准确率的问题。

为了参考,我训练了模型一个周期,然后使用keras_contrib模块提供的save_load_utils工具将训练好的模型保存到文件中。然而,每当我用这些权重运行模型时,这些权重是为一个周期训练的,我得到的准确率每次都不一样。我尝试了5到10次,结果在68%到74%之间波动,这相当大。由于我加载的是预训练的(即1个周期)模型权重,我期望得到相同的准确率。(即不考虑浮点数的精度差异)然而,结果的这种波动表明我可能做了一些错误的事情。

有谁知道为什么model.evaluate_generator方法每次运行时,即使使用相同的一周期训练模型的权重来评估,结果也会如此不同吗?有没有什么方法可以修复我的评估代码,以便每次评估相同的训练模型时获得相同的准确率?(即考虑到浮点运算导致的微小差异)

以下是所有相关的代码。代码样本确实比标准的StackOverflow问题要长一些,但我希望包含代码的所有相关部分。抱歉代码对Python程序员来说有点长。我是一个新手Python程序员,可能本可以用更简洁、更符合Python习惯的方式编写整个代码。

模型准备代码:

def prepare_kadjk_model(max_mini_batch_size,                        max_conversation_length, timesteps, num_word_dimensions,                        word_to_index, word_vec_dict,                        num_tags):    #超参数    m = timesteps    h = timesteps    model = Sequential()    dictionary_size = len(word_to_index) + 1    embedding_weights = numpy.zeros((dictionary_size, num_word_dimensions))    for word, index in word_to_index.items():        embedding_weights[index, :] = word_vec_dict[word]    # 定义输入    embedding_layer = Embedding(dictionary_size, num_word_dimensions,                                weights=[embedding_weights],                                embeddings_regularizer=regularizers.l2(0.0001))    model.add(TimeDistributed(embedding_layer,                              input_shape=(max_conversation_length, timesteps)))    model.add(TimeDistributed(Bidirectional(LSTM(m // 2, return_sequences=True,                                            kernel_regularizer=regularizers.l2(0.0001)))))    model.add(TimeDistributed(Dropout(0.2)))    model.add(TimeDistributed(GlobalMaxPooling1D()))    model.add(Bidirectional(LSTM(h // 2, return_sequences = True,                                 kernel_regularizer=regularizers.l2(0.0001)), merge_mode='concat'))    model.add(Dropout(0.2))    crf = CRF(num_tags, sparse_target=False, kernel_regularizer=regularizers.l2(0.0001))    model.add(crf)    model.compile(optimizer, loss = crf_loss,                  metrics=[crf_accuracy])    return model

批次准备函数:

def form_mini_batches(dataset_x, max_mini_batch_size):    num_conversations = len(dataset_x)    # 形成等长度对话的迷你批次    mini_batches = {}    for i in range(num_conversations):        num_utterances = len(dataset_x[i])        if num_utterances in mini_batches:            mini_batches[num_utterances].append( i )        else:            mini_batches[num_utterances] = [ i ]    # 强制对之前形成的迷你批次实施最大批次大小    mini_batch_list = []    for conversations in mini_batches.values():        mini_batch_list += [conversations[x: x + max_mini_batch_size] for x in range(0, len(conversations), max_mini_batch_size)]    return mini_batch_listdef kadjk_batch_generator(dataset_x, dataset_y, tag_indices,                          mini_batch_list, max_conversation_length,                          timesteps, num_word_dimensions, num_tags,                          word_index_to_append, tag_index_to_append):    num_mini_batches = len(mini_batch_list)    # 打乱批次顺序    index_list = [x for x in range(num_mini_batches)]    random.shuffle(index_list)    k = -1    while True:        k = (k + 1) % len(index_list)        index = index_list[k]        conversation_indices = mini_batch_list[index]        num_conversations = len(conversation_indices)        batch_features = numpy.empty(shape = (num_conversations, max_conversation_length, timesteps),                                     dtype = int)        label_list = []        for i in range(num_conversations):            utterances = dataset_x[conversation_indices[i]]            labels = copy.deepcopy(dataset_y[conversation_indices[i]])            num_utterances = len(utterances)            num_labels_to_append = max(0, max_conversation_length - len(labels))            labels += [tag_index_to_append] * num_labels_to_append            tags = to_categorical(labels, num_tags)            del labels            for j in range(num_utterances):                utterance = copy.deepcopy(utterances[j])                num_to_append = max(0, timesteps - len(utterance))                if num_to_append > 0:                    appendage = [word_index_to_append] * num_to_append                    utterance += appendage                batch_features[i][j] = utterance                del utterance            remaining_space = (max_conversation_length - num_utterances, timesteps)            batch_features[i][num_utterances:] = numpy.ones(remaining_space) * word_index_to_append            label_list.append(tags)        batch_labels = numpy.array(label_list)        del label_list        yield batch_features, batch_labels

训练函数:

def train_kadjk(model, training, validation, num_epochs_to_train, tag_indices, max_mini_batch_size,                max_conversation_length, timesteps, num_word_dimensions, num_tags,                end_of_line_word_index, uninterpretable_label_index):    training_mini_batch_list = form_mini_batches(training[0], max_mini_batch_size)    validation_mini_batch_list = form_mini_batches(validation[0], max_mini_batch_size)    num_training_steps = len(training_mini_batch_list)    num_validation_steps = len(validation_mini_batch_list)    early_stop = EarlyStopping(patience = 5)    change_learning_rate = LearningRateScheduler(learning_rate_scheduler)    model.fit_generator(kadjk_batch_generator(training[0], training[1], tag_indices,                                              training_mini_batch_list, max_conversation_length,                                              timesteps, num_word_dimensions, num_tags,                                              end_of_line_word_index, uninterpretable_label_index),                        steps_per_epoch = num_training_steps,                        epochs = num_epochs_to_train,                        validation_data = kadjk_batch_generator(validation[0], validation[1],                                                                tag_indices,                                                                validation_mini_batch_list,                                                                 max_conversation_length, timesteps,                                                                num_word_dimensions, num_tags,                                                                end_of_line_word_index,                                                                uninterpretable_label_index),                        validation_steps = num_validation_steps,                        callbacks = [early_stop, change_learning_rate])

评估函数:

def evaluate_kadjk(model, testing, tag_indices, max_mini_batch_size, max_conversation_length,                   timesteps, num_word_dimensions, num_tags,                   end_of_line_word_index, uninterpretable_label_index):    testing_mini_batch_list = form_mini_batches(testing[0], max_mini_batch_size)    num_testing_steps = len(testing_mini_batch_list)    score = model.evaluate_generator(kadjk_batch_generator(testing[0], testing[1],                                                           tag_indices,                                                           testing_mini_batch_list,                                                            max_conversation_length, timesteps,                                                           num_word_dimensions, num_tags,                                                           end_of_line_word_index,                                                           uninterpretable_label_index),                                     steps = num_testing_steps)    print("len(score):" + str(len(score)))    print("score:" + str(score))

您可以访问这里以了解我正在进行的研究生论文项目的更完整视角,但我尽量提供了任何能够帮助的人所需的所有相关功能部分。


回答:

我深入研究了keras的Github问题,并在这个评论中找到了可能的错误原因。

显然,类似于批量归一化层,使用Dropout层会导致我在问题中描述的变化。Dropout层会在训练过程中使神经元失效。因此,当模型训练完成后,最初编译的模型中并非所有神经元都存在。

如果使用函数keras_contrib.save_load_utils.save_all_weights保存模型权重,那么模型的权重会被保存。然而,一旦你终止该过程而不保存最终的神经元配置(不仅仅是权重),模型的最终配置就会丢失。正如这里所述,我用来保存模型的save_all_weights函数并不保存模型本身的配置。

因此,如果你在不同的进程中编译模型,并使用keras_contrib.save_load_utils.load_all_weights加载你保存的权重,即使你用之前运行时测试过的相同数据测试模型,新编译的模型也会有一些在原始模型训练过程中被丢弃的神经元。这种配置的差异,再加上它们可能是(而且在这种情况下确实是)以随机权重初始化的,导致每次运行评估时都会产生不同的准确率。

解决方案似乎在于不仅记录权重,还记录所有配置。这可以通过使用模型实例的save方法来代替keras_contrib.save_load_utils.save_all_weights来简单实现。显然,要在不同的进程中加载整个模型,应该使用keras.models.load_model而不是keras_contrib.save_load_utils.load_all_weights

Related Posts

在使用k近邻算法时,有没有办法获取被使用的“邻居”?

我想找到一种方法来确定在我的knn算法中实际使用了哪些…

Theano在Google Colab上无法启用GPU支持

我在尝试使用Theano库训练一个模型。由于我的电脑内…

准确性评分似乎有误

这里是代码: from sklearn.metrics…

Keras Functional API: “错误检查输入时:期望input_1具有4个维度,但得到形状为(X, Y)的数组”

我在尝试使用Keras的fit_generator来训…

如何使用sklearn.datasets.make_classification在指定范围内生成合成数据?

我想为分类问题创建合成数据。我使用了sklearn.d…

如何处理预测时不在训练集中的标签

已关闭。 此问题与编程或软件开发无关。目前不接受回答。…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注