def METHOD_NAME(exe,
                       train_exe,
                       valid_exe,
                       train_ds,
                       valid_ds,
                       test_ds,
                       train_prog,
                       valid_prog,
                       model,
                       metric,
                       epoch=20,
                       dev_count=1,
                       train_log_step=5,
                       eval_step=10000,
                       evaluator=None,
                       output_path=None):
    """train and evaluate"""
    global_step = 0
    log_path = os.path.join(output_path, "log")
    _create_if_not_exist(log_path)
    writer = LogWriter(log_path)
    best_model = 0
    for e in range(epoch):
        for feed_dict in tqdm.tqdm(
                multi_device(train_ds.generator(), dev_count),
                desc='Epoch %s' % e):
            if dev_count > 1:
                ret = train_exe.run(feed=feed_dict, fetch_list=metric.vars)
                ret = [[np.mean(v)] for v in ret]
            else:
                ret = train_exe.run(train_prog,
                                    feed=feed_dict,
                                    fetch_list=metric.vars)
            ret = metric.parse(ret)
            if global_step % train_log_step == 0:
                for key, value in ret.items():
                    writer.add_scalar(
                        'train_' + key, value, global_step)
            global_step += 1
            if global_step % eval_step == 0:
                eval_ret = evaluate(model, exe, valid_ds, valid_prog, 1,
                                    evaluator, "valid")
                test_eval_ret = evaluate(model, exe, test_ds, valid_prog, 1,
                                         evaluator, "test")
                eval_ret.update(test_eval_ret)
                sys.stderr.write(json.dumps(eval_ret, indent=4) + "\n")
                for key, value in eval_ret.items():
                    writer.add_scalar(key, value, global_step)
                if eval_ret["valid_hits@100"] > best_model:
                    F.io.save_persistables(
                        exe,
                        os.path.join(output_path, "checkpoint"), train_prog)
                    eval_ret["step"] = global_step
                    with open(os.path.join(output_path, "best.txt"), "w") as f:
                        f.write(json.dumps(eval_ret, indent=2) + '\n')
                    best_model = eval_ret["valid_hits@100"]
        # Epoch End
        eval_ret = evaluate(model, exe, valid_ds, valid_prog, 1, evaluator,
                            "valid")
        test_eval_ret = evaluate(model, exe, test_ds, valid_prog, 1, evaluator,
                                 "test")
        eval_ret.update(test_eval_ret)
        sys.stderr.write(json.dumps(eval_ret, indent=4) + "\n")
        for key, value in eval_ret.items():
            writer.add_scalar(key, value, global_step)
        if eval_ret["valid_hits@100"] > best_model:
            F.io.save_persistables(exe,
                                   os.path.join(output_path, "checkpoint"),
                                   train_prog)
            eval_ret["step"] = global_step
            with open(os.path.join(output_path, "best.txt"), "w") as f:
                f.write(json.dumps(eval_ret, indent=2) + '\n')
            best_model = eval_ret["valid_hits@100"]
    writer.close()