# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ This script includes code to calculating MAP score for results form sentiment analysis, textual similarity, and mrc task """ import argparse import json import math import os def get_args(): parser = argparse.ArgumentParser("map eval") parser.add_argument("--pred_path", required=True) parser.add_argument("--golden_path", required=True) parser.add_argument("--language", type=str, required=True, help="language that the model is built for") args = parser.parse_args() return args def evids_load(args, path): golden_f = open(args.golden_path, "r") golden = {} ins_num = 0 for golden_line in golden_f.readlines(): line = json.loads(golden_line) if line["sample_type"] == "disturb": ins_num += 1 golden[line["sent_id"]] = line evids = {} with open(path, "r") as f: for line in f.readlines(): dic = json.loads(line) dic["sample_type"] = golden[dic["id"]]["sample_type"] if "rel_ids" in golden[dic["id"]]: dic["rel_ids"] = golden[dic["id"]]["rel_ids"] evids[dic["id"]] = dic return evids, ins_num def _calc_MAP_by_bin(top_p, length_adv, adv_attriRank_list, ori_attriRank_list): """ This is our old way to calculate MAP, which follows equation two in consistency section of README """ hits = 0 sum_precs = 0.0 length_t = math.ceil(length_adv * top_p) adv_t = adv_attriRank_list[:length_t] for char_idx, char in enumerate(adv_t): if char in ori_attriRank_list[: char_idx + 1]: hits += 1 sum_precs += hits / (char_idx + 1) if length_t > 0: sum_precs /= length_t return sum_precs def _calc_MAP_by_bin_paper(top_p, length_adv, adv_attriRank_list, ori_attriRank_list): """ This function calculates MAP using the equation in our paper, which follows equation one in consistency section of README """ total_precs = 0.0 for i in range(length_adv): hits = 0.0 i += 1 adv_t = adv_attriRank_list[:i] for char_idx, char in enumerate(adv_t): if char in ori_attriRank_list[:i]: hits += 1 hits = hits / i total_precs += hits if length_adv == 0: return 0 return total_precs / length_adv def _calc_map(evids, key, ins_num): t_map = 0.0 adv_num = 0 ori_num = 0 for ori_idx in evids: if evids[ori_idx]["sample_type"] == "ori": ori = evids[ori_idx] ori_num += 1 # One original instance can be related to several disturbed instance for adv_idx in evids[ori_idx]["rel_ids"]: if adv_idx in evids: adv_num += 1 adv = evids[adv_idx] ori_attriRank_list = list(ori["rationale_token"][key]) adv_attriRank_list = list(adv["rationale_token"][key]) length_adv = len(adv_attriRank_list) sum_precs = _calc_MAP_by_bin_paper(1, length_adv, adv_attriRank_list, ori_attriRank_list) t_map += sum_precs return t_map / ins_num, ori_num + adv_num def cal_MAP(args, pred_path, la): evids, ins_num = evids_load(args, pred_path) if not evids: print(pred_path + " file empty!") return 0 first_key = list(evids.keys())[0] t_map = 0 num = 0 for i in range(len(evids[first_key]["rationale"])): t_map_tmp, num_tmp = _calc_map(evids, i, ins_num) t_map += t_map_tmp num += num_tmp t_map /= len(evids[first_key]["rationale"]) num /= len(evids[first_key]["rationale"]) print("total\t%d\t%.1f" % (num, 100 * t_map)) return 0 if __name__ == "__main__": args = get_args() la = args.language pred_path = args.pred_path if os.path.exists(pred_path): cal_MAP(args, pred_path, la) else: print("Prediction file does not exists!")