Exemplo n.º 1
0
def calculate_class_weights(classes=None):
    """
    Counter - generates counts for keys, in this case star ratings are the keys.
    Series - Creates a series object from dict in this case. Keys of the dict i.e star/rating value
             are the index of the series and value of the series are the freq of corresponding ratings.
             This is calculated over the entire trainset. Then, we calculate probability distribution
             for each possible rating stars and inverse it. This will help us to analyse class imbalance becase classes
             in our case are have different possible ratings.
    :return:
    """
    # TODO: handle weights for label paddings
    label_values = []
    if classes is None:
        for _, _, labels in train_loader():
            for label in labels:
                label_values.append(label)
    else:
        for labels in classes:
            for label in labels:
                label_values.append(label)

    class_weights = pd.Series(Counter(label_values))
    # Inverse of probability distribution of weights
    class_weights = 1 / (class_weights / class_weights.mean())
    class_weights = class_weights.to_dict()
    return class_weights
def train(config=None, reporter=None):
    """
    Main method to start training

    :param hyperparam_tune: flag for controlling hyperparameter tuning
    :param config: contains grid searched values for hyperparameters to be tuned
    :param reporter: can contain reporting values like accuracy, f1-score etc
    :return:
    """
    # set values according to hyperparamter tuner
    if args.hyperparam_tune:
        print('Data dir : ' + DATA_DIR)
        args.lr = config['learning_rate']
        args.batch_size = config['batch_size']
        args.dropout_keep_prob = config['dropout_keep_prob']

    print(args)
    write_experiment_parameters(args)

    # https://stackoverflow.com/questions/44873273/what-do-the-options-in-configproto-like-allow-soft-placement-and-log-device-plac
    config = tf.ConfigProto(allow_soft_placement=True)

    # Clears the default graph stack and resets the global default graph.
    tf.reset_default_graph()

    with tf.Session(config=config) as session:
        # attach keras and tf session so that we can use keras layers together with tf
        K.set_session(session)

        # load previous trained model
        previous_ops = model.get_previous_model(session=session, args=args)

        # get model and saver instances
        _, saver, ops = model.get_model(session=session,
                                        args=args,
                                        restore_only=False)

        # get label weights for handling class imbalance
        class_weights = calculate_class_weights()

        # create a training summary writer
        train_writer = tf.summary.FileWriter(TFLOG_DIR, graph=session.graph)

        # initializations
        val_accuracies = []
        val_per_class_accuracies = []
        val_per_class_f1_scores = []
        val_macro_f1_scores = []
        train_accuracies = []
        train_per_class_f1_scores = []
        train_per_class_accuracies = []
        train_macro_f1_scores = []
        train_confusion_matrix = np.zeros(shape=(args.num_classes,
                                                 args.num_classes),
                                          dtype=np.int32)
        best_macro_f1_score = 0
        best_step_number = 0

        # start training
        for i, (x1, x2, y) in enumerate(
                batch_iterator(train_loader(args.epochs), args.batch_size)):

            t0 = time.clock()

            # calculate dynamic class weights
            if args.dynamic_class_weights:
                class_weights = calculate_class_weights(classes=y)

            # get feed_dicts
            fd = get_feed_data(x1,
                               x2,
                               y,
                               class_weights=class_weights,
                               is_training=True,
                               args=args)

            # get previous feed_dicts
            previous_fd = fd.copy()
            previous_fd[IS_TRAINING_TENSOR_NAME] = False

            # execute previous model
            word_level_inputs, aspect_embedded_encoder_output, aspect_embedded_sentence_inputs, birnn_output = session.run(
                [
                    previous_ops['word_level_inputs'],
                    previous_ops['aspect_embedded_encoder_output'],
                    previous_ops['aspect_embedded_sentence_inputs'],
                    previous_ops['birnn_output']
                ], previous_fd)

            fd[PREFIX +
               PREVIOUS_WORD_LEVEL_INPUTS_TENSOR_NAME] = word_level_inputs
            fd[PREFIX +
               PREVIOUS_ASPECT_EMBEDDED_ENCODER_OUTPUT_TENSOR_NAME] = aspect_embedded_encoder_output
            fd[PREFIX +
               PREVIOUS_ASPECT_EMBEDDED_SENTENCE_INPUTS_TENSOR_NAME] = aspect_embedded_sentence_inputs
            fd[PREFIX +
               PREVIOUS_SENTENCE_ENCODER_OUTPUT_TENSOR_NAME] = birnn_output
            fd[PREFIX + ASPECTS_TENSOR_NAME] = fd[ASPECTS_TENSOR_NAME]
            fd[PREFIX +
               PADDED_REVIEWS_TENSOR_NAME] = fd[PADDED_REVIEWS_TENSOR_NAME]
            fd[PREFIX + ACTUAL_SENTENCE_COUNT_TENSOR_NAME] = fd[
                ACTUAL_SENTENCE_COUNT_TENSOR_NAME]
            fd[PREFIX + ACTUAL_WORD_COUNT_TENSOR_NAME] = fd[
                ACTUAL_WORD_COUNT_TENSOR_NAME]
            fd[PREFIX +
               SENTENCE_MASK_TENSOR_NAME] = fd[SENTENCE_MASK_TENSOR_NAME]
            fd[PREFIX + WORD_MASK_TENSOR_NAME] = fd[WORD_MASK_TENSOR_NAME]
            fd[PREFIX +
               PADDED_LABELS_TENSOR_NAME] = fd[PADDED_LABELS_TENSOR_NAME]
            fd[PREFIX +
               LABLE_WEIGHTS_TENSOR_NAME] = fd[LABLE_WEIGHTS_TENSOR_NAME]
            fd[PREFIX + IS_TRAINING_TENSOR_NAME] = fd[IS_TRAINING_TENSOR_NAME]

            # run session
            step, summaries, loss, accuracy, f1_score, f1_score_0, f1_score_1, f1_score_2, f1_score3, \
            confusion_matrix, labels, predictions, label_weights, _ = session.run(
                [
                    ops['global_step'],
                    ops['summary_op'],
                    ops['loss'],
                    ops['accuracy'],
                    ops['f1_score'],
                    ops['f1_score_0'],
                    ops['f1_score_1'],
                    ops['f1_score_2'],
                    ops['f1_score_3'],
                    ops['confusion_matrix'],
                    ops['padded_labels'],
                    ops['predictions'],
                    ops['label_weights'],
                    ops['train_op']
                ], fd)

            train_writer.add_summary(summaries, global_step=step)
            td = time.clock() - t0

            if args.hyperparam_tune:
                reporter(f1_score=f1_score)

            if step % args.print_frequency == 0:
                train_confusion_matrix += confusion_matrix
                print(
                    'step %s, loss=%s, accuracy=%s, f1_score=%s, t=%s, inputs=%s'
                    % (step, loss, accuracy, f1_score, round(
                        td, 2), fd[PREFIX + PADDED_REVIEWS_TENSOR_NAME].shape))
            if step != 0 and step % args.eval_frequency == 0:
                # run validation
                val_results = evaluate(session=session,
                                       ops=ops,
                                       previous_ops=previous_ops,
                                       dataset=val_loader(epochs=1))
                print_results(val_results, args, 'VALIDATION RESULTS',
                              val_accuracies, val_per_class_accuracies,
                              val_macro_f1_scores, val_per_class_f1_scores)
                # save a checkpoint if best f1 score
                if val_macro_f1_scores[-1] >= best_macro_f1_score:
                    best_macro_f1_score = val_macro_f1_scores[-1]
                    best_step_number = step
                    print('Best Macro F1 Score : %.2f' % best_macro_f1_score)
                    print('Best step at : ' + str(best_step_number))
                    saver.save(session, CHECKPOINT_PATH, global_step=step)
                    print('checkpoint saved')
                train_results = {
                    'loss': loss,
                    'accuracy': accuracy,
                    'f1_score': f1_score,
                    'confusion_matrix': train_confusion_matrix
                }
                print_results(train_results, args, 'TRAINING RESULTS',
                              train_accuracies, train_per_class_accuracies,
                              train_macro_f1_scores, train_per_class_f1_scores)
                # reset train confusion matrix
                train_confusion_matrix = np.zeros(shape=(args.num_classes,
                                                         args.num_classes),
                                                  dtype=np.int32)

        val_per_class_accuracies = np.asarray(val_per_class_accuracies)
        train_per_class_accuracies = np.asarray(train_per_class_accuracies)
        val_per_class_f1_scores = np.asarray(val_per_class_f1_scores)
        train_per_class_f1_scores = np.asarray(train_per_class_f1_scores)

        plot_accuracy(val_accuracies, train_accuracies, title='Accuracy')
        plot_accuracy(val_per_class_accuracies[:, 0],
                      train_per_class_accuracies[:, 0],
                      title='Accuracy Class 0 Positive Sentiment')
        plot_accuracy(val_per_class_accuracies[:, 1],
                      train_per_class_accuracies[:, 1],
                      title='Accuracy Class 1 Negative Sentiment')
        plot_accuracy(val_per_class_accuracies[:, 2],
                      train_per_class_accuracies[:, 2],
                      title='Accuracy Class 2 Neutral Sentiment')
        plot_accuracy(val_per_class_accuracies[:, 3],
                      train_per_class_accuracies[:, 3],
                      title='Accuracy Class 3 Not Applicable Sentiment')

        plot_f1_score(val_macro_f1_scores,
                      train_macro_f1_scores,
                      title='Macro F1 Score')
        plot_f1_score(val_per_class_f1_scores[:, 0],
                      train_per_class_f1_scores[:, 0],
                      title='F1 Score Class 0 Positive Sentiment')
        plot_f1_score(val_per_class_f1_scores[:, 1],
                      train_per_class_f1_scores[:, 1],
                      title='F1 Score Class 1 Negative Sentiment')
        plot_f1_score(val_per_class_f1_scores[:, 2],
                      train_per_class_f1_scores[:, 2],
                      title='F1 Score Class 2 Neutral Sentiment')
        plot_f1_score(val_per_class_f1_scores[:, 3],
                      train_per_class_f1_scores[:, 3],
                      title='F1 Score Class 3 Not Applicable Sentiment')

        return best_step_number