utils.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. import os
  2. import pickle
  3. import math
  4. from PIL import Image
  5. import warnings
  6. import datetime
  7. import configparser
  8. import numpy as np
  9. from scipy.stats.stats import kendalltau
  10. # np.random.seed(20200501)
  11. warnings.filterwarnings("ignore")
  12. """Set seed and Init cuda"""
  13. os.environ["TF_CPP_MIN_LOG_LEVEL"] = '2' # 只显示 warning 和 Error
  14. os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
  15. os.environ["CUDA_VISIBLE_DEVICES"] = ""
  16. class ModelUtils:
  17. def __init__(self):
  18. pass
  19. @staticmethod
  20. def model_copy(model, mode=''):
  21. from scripts.mutation.mutation_utils import LayerUtils
  22. import keras
  23. suffix = '_copy_' + mode
  24. if model.__class__.__name__ == 'Sequential':
  25. new_layers = []
  26. for layer in model.layers:
  27. new_layer = LayerUtils.clone(layer)
  28. new_layer.name += suffix
  29. new_layers.append(new_layer)
  30. new_model = keras.Sequential(layers=new_layers, name=model.name + suffix)
  31. else:
  32. new_model = ModelUtils.functional_model_operation(model, suffix=suffix)
  33. s = datetime.datetime.now()
  34. new_model.set_weights(model.get_weights())
  35. e1 = datetime.datetime.now()
  36. td1 = e1 - s
  37. h, m, s = ToolUtils.get_HH_mm_ss(td1)
  38. print("Set model weights! {} hour,{} min,{} sec".format(h, m, s))
  39. del model
  40. return new_model
  41. @staticmethod
  42. def functional_model_operation(model, operation=None, suffix=None):
  43. from scripts.mutation.mutation_utils import LayerUtils
  44. input_layers = {}
  45. output_tensors = {}
  46. model_output = None
  47. for layer in model.layers:
  48. for node in layer._outbound_nodes:
  49. layer_name = node.outbound_layer.name
  50. if layer_name not in input_layers.keys():
  51. input_layers[layer_name] = [layer.name]
  52. else:
  53. input_layers[layer_name].append(layer.name)
  54. output_tensors[model.layers[0].name] = model.input
  55. for layer in model.layers[1:]:
  56. layer_input_tensors = [output_tensors[l] for l in input_layers[layer.name]]
  57. if len(layer_input_tensors) == 1:
  58. layer_input_tensors = layer_input_tensors[0]
  59. if operation is not None and layer.name in operation.keys():
  60. x = layer_input_tensors
  61. cloned_layer = LayerUtils.clone(layer)
  62. if suffix is not None:
  63. cloned_layer.name += suffix
  64. x = operation[layer.name](x, cloned_layer)
  65. else:
  66. cloned_layer = LayerUtils.clone(layer)
  67. if suffix is not None:
  68. cloned_layer.name += suffix
  69. x = cloned_layer(layer_input_tensors)
  70. output_tensors[layer.name] = x
  71. model_output = x
  72. import keras
  73. return keras.Model(inputs=model.inputs, outputs=model_output)
  74. @staticmethod
  75. def save_initial_weights(model):
  76. weights = model.get_weights()
  77. np.save('initial_weights.npy', weights)
  78. @staticmethod
  79. def load_initial_weights(model):
  80. weights = np.load('initial_weights.npy')
  81. model.set_weights(weights)
  82. return model
  83. @staticmethod
  84. def save_layers_output(path, layers_output):
  85. dirname = os.path.dirname(path)
  86. if len(dirname) > 0 and (not os.path.exists(dirname)):
  87. os.makedirs(dirname)
  88. with open(path, 'wb') as f:
  89. pickle.dump(layers_output, f)
  90. @staticmethod
  91. def load_layers_output(path):
  92. if not os.path.exists(path):
  93. return None
  94. with open(path, 'rb') as f:
  95. layers_output = pickle.load(f)
  96. return layers_output
  97. @staticmethod
  98. def layer_divation(model, model_nodes, layer_index, layers_output_1, layers_output_2, epsilon=1e-7):
  99. layer = model.layers[layer_index]
  100. # get all of its input layers
  101. input_layers_index = []
  102. for node in layer._inbound_nodes:
  103. if node not in model_nodes:
  104. continue
  105. for l in node.inbound_layers:
  106. from keras.engine.input_layer import InputLayer
  107. if isinstance(l, InputLayer):
  108. continue
  109. # find the index of l in model
  110. for i, model_layer in enumerate(model.layers):
  111. if l == model_layer:
  112. input_layers_index.append(i)
  113. break
  114. else:
  115. raise Exception('can not find the layer in model')
  116. # calculate the divation of current layer
  117. cur_output_1 = layers_output_1[layer_index]
  118. cur_output_2 = layers_output_2[layer_index]
  119. delta_cur = MetricsUtils.delta(cur_output_1, cur_output_2)[0] # the second value of delta is sum()
  120. if len(input_layers_index) == 0:
  121. delta_pre = 0
  122. else:
  123. delta_pre_list = []
  124. for i in input_layers_index:
  125. pre_output_1 = layers_output_1[i]
  126. pre_output_2 = layers_output_2[i]
  127. delta_pre_list.append(MetricsUtils.delta(pre_output_1, pre_output_2)[0])
  128. delta_pre = np.max(delta_pre_list, axis=0)
  129. return delta_cur, (delta_cur - delta_pre) / (delta_pre + epsilon), [model.layers[i].name for i in
  130. input_layers_index]
  131. @staticmethod
  132. def layers_divation(model, layers_output_1, layers_output_2):
  133. relevant_nodes = []
  134. for v in model._nodes_by_depth.values():
  135. relevant_nodes += v
  136. layers_divation = []
  137. for i in range(len(model.layers)):
  138. layers_divation.append(
  139. ModelUtils.layer_divation(model, relevant_nodes, i, layers_output_1, layers_output_2))
  140. return layers_divation
  141. @staticmethod
  142. def layers_output(model, input):
  143. from keras import backend as K
  144. # print(K.backend()+" in loadmodel")
  145. from keras.engine.input_layer import InputLayer
  146. get_layer_output = K.function([model.layers[0].input, K.learning_phase()],
  147. [l.output for l in
  148. (model.layers[1:]
  149. if isinstance(model.layers[0], InputLayer)
  150. else model.layers)])
  151. if isinstance(model.layers[0], InputLayer):
  152. layers_output = [input]
  153. layers_output.extend(get_layer_output([input, 0]))
  154. else:
  155. layers_output = get_layer_output([input, 0])
  156. return layers_output
  157. @staticmethod
  158. def layers_input(model, input):
  159. inputs = [[input]]
  160. from keras import backend as K
  161. from keras.engine.input_layer import InputLayer
  162. for i, layer in enumerate(model.layers):
  163. if i == 0:
  164. continue
  165. if i == 1 and isinstance(model.layers[0], InputLayer):
  166. continue
  167. get_layer_input = K.function([model.layers[0].input, K.learning_phase()],
  168. layer.input if isinstance(layer.input, list) else [layer.input])
  169. inputs.append(get_layer_input([input, 0]))
  170. return inputs
  171. @staticmethod
  172. def generate_permutation(size_of_permutation, extract_portion):
  173. assert extract_portion <= 1
  174. num_of_extraction = math.floor(size_of_permutation * extract_portion)
  175. permutation = np.random.permutation(size_of_permutation)
  176. permutation = permutation[:num_of_extraction]
  177. return permutation
  178. @staticmethod
  179. def shuffle(a):
  180. shuffled_a = np.empty(a.shape, dtype=a.dtype)
  181. length = len(a)
  182. permutation = np.random.permutation(length)
  183. index_permutation = np.arange(length)
  184. shuffled_a[permutation] = a[index_permutation]
  185. return shuffled_a
  186. @staticmethod
  187. def compile_model(model, optimer, loss, metric: list):
  188. model.compile(optimizer=optimer,
  189. loss=loss,
  190. metrics=metric)
  191. return model
  192. @staticmethod
  193. def custom_objects():
  194. from scripts.mutation.mutation_utils import ActivationUtils
  195. objects = {}
  196. objects['no_activation'] = ActivationUtils.no_activation
  197. objects['leakyrelu'] = ActivationUtils.leakyrelu
  198. return objects
  199. @staticmethod
  200. def weighted_layer_indices(model):
  201. indices = []
  202. for i, layer in enumerate(model.layers):
  203. weight_count = layer.count_params()
  204. if weight_count > 0:
  205. indices.append(i)
  206. return indices
  207. @staticmethod
  208. def is_valid_model(inputs_backends, backends_nums, threshold=0.95):
  209. invalid_status_num = 0
  210. inputs_values = list(inputs_backends.values())
  211. # results like (1500,1) is valid
  212. if inputs_values[0].shape[1] == 1:
  213. return True
  214. else:
  215. for inputs in inputs_backends.values():
  216. indice_map = {}
  217. for input in inputs:
  218. #input相当于一条数据10个输出
  219. max_indice = np.argmax(input)
  220. if max_indice not in indice_map.keys():
  221. indice_map[max_indice] = 1
  222. else:
  223. indice_map[max_indice] += 1
  224. #print(indice_map)
  225. for indice in indice_map.keys():
  226. #print(indice)
  227. if indice_map[indice] > len(inputs) * threshold:
  228. invalid_status_num += 1
  229. # print('-------indice_map------\n',indice_map)
  230. # print('-------inputs_backends-------\n',inputs_backends)
  231. # print('backends_nums:',backends_nums)
  232. print("indice_map:",indice_map)
  233. return False if invalid_status_num == backends_nums else True
  234. class DataUtils:
  235. @staticmethod
  236. def image_resize(x, shape):
  237. x_return = []
  238. for x_test in x:
  239. tmp = np.copy(x_test)
  240. img = Image.fromarray(tmp.astype('uint8')).convert('RGB')
  241. img = img.resize(shape, Image.ANTIALIAS)
  242. x_return.append(np.array(img))
  243. return np.array(x_return)
  244. @staticmethod
  245. def get_data_by_exp(exp):
  246. import keras
  247. import keras.backend as K
  248. K.set_image_data_format("channels_last")
  249. lemon_cfg = configparser.ConfigParser()
  250. lemon_cfg.read("../config/demo.conf")
  251. dataset_dir = lemon_cfg['parameters']['dataset_dir']
  252. grandparent_directory = os.path.dirname(os.path.dirname(os.getcwd()))
  253. x_test = y_test = []
  254. if 'fashion-mnist' in exp:
  255. _, (x_test, y_test) = keras.datasets.fashion_mnist.load_data()
  256. x_test = DataUtils.get_fashion_mnist_data(x_test) # 矩阵转换
  257. y_test = keras.utils.to_categorical(y_test, num_classes=10) # 把类别标签转换为onehot编码
  258. elif 'fashion2' in exp:
  259. basedir = os.path.abspath(os.getcwd())
  260. labels_path = os.path.join(basedir, 'data', 't10k-labels-idx1-ubyte.gz')
  261. images_path = os.path.join(basedir, 'data', 't10k-images-idx3-ubyte.gz')
  262. import gzip
  263. with gzip.open(labels_path, 'rb') as lbpath:
  264. labels = np.frombuffer(lbpath.read(), dtype=np.uint8,
  265. offset=8)
  266. with gzip.open(images_path, 'rb') as imgpath:
  267. images = np.frombuffer(imgpath.read(), dtype=np.uint8,
  268. offset=16).reshape(len(labels), 28, 28, 1)
  269. X_test = images.astype('float32') / 255.0
  270. Y_test = keras.utils.to_categorical(labels, 10)
  271. x_test,y_test=X_test, Y_test
  272. elif 'mnist' in exp:
  273. _, (x_test, y_test) = keras.datasets.mnist.load_data()
  274. x_test = DataUtils.get_mnist_data(x_test)
  275. y_test = keras.utils.to_categorical(y_test, num_classes=10)
  276. elif 'cifar100' in exp:
  277. from keras.datasets import cifar100
  278. subtract_pixel_mean = True
  279. # Load the CIFAR10 data.
  280. (X_train, Y_train), (X_test, Y_test) = cifar100.load_data()
  281. # Normalize data.
  282. X_train = X_train.astype('float32') / 255
  283. X_test = X_test.astype('float32') / 255
  284. # If subtract pixel mean is enabled
  285. if subtract_pixel_mean:
  286. X_train_mean = np.mean(X_train, axis=0)
  287. X_train -= X_train_mean
  288. X_test -= X_train_mean
  289. Y_test = keras.utils.to_categorical(Y_test, 100)
  290. x_test,y_test=X_test,Y_test
  291. elif 'cifar10' in exp:
  292. _, (x_test, y_test) = keras.datasets.cifar10.load_data()
  293. x_test = DataUtils.get_cifar10_data(x_test)
  294. y_test = keras.utils.to_categorical(y_test, num_classes=10)
  295. elif 'imagenet' in exp:
  296. input_precessor = DataUtils.imagenet_preprocess_dict()
  297. input_shapes_dict = DataUtils.imagenet_shape_dict()
  298. model_name = exp.split("-")[0]
  299. shape = input_shapes_dict[model_name]
  300. #data_path = os.path.join(dataset_dir, "sampled_imagenet-1500.npz")
  301. data_path = grandparent_directory + "/dataset/sampled_imagenet-1500.npz"
  302. data = np.load(data_path)
  303. x, y = data['x_test'], data['y_test']
  304. x_resize = DataUtils.image_resize(np.copy(x), shape)
  305. x_test = input_precessor[model_name](x_resize)
  306. y_test = keras.utils.to_categorical(y, num_classes=1000)
  307. elif 'svhn' in exp:
  308. import run.SVNH_DatasetUtil
  309. (_, _), (X_test, Y_test) = run.SVNH_DatasetUtil.load_data()
  310. x_test,y_test=X_test, Y_test
  311. elif 'sinewave' in exp:
  312. """
  313. see more details in
  314. https://github.com/StevenZxy/CIS400/tree/f69489c0624157ae86b5d8ddb1fa99c89a927256/code/LSTM-Neural-Network-for-Time-Series-Prediction-master
  315. """
  316. import pandas as pd
  317. cvs_path = grandparent_directory + "/dataset/sinewave.csv"
  318. dataframe = pd.read_csv(cvs_path)
  319. test_size, seq_len = 1500, 50
  320. data_test = dataframe.get("sinewave").values[-(test_size + 50):]
  321. data_windows = []
  322. for i in range(test_size):
  323. data_windows.append(data_test[i:i + seq_len])
  324. data_windows = np.array(data_windows).astype(float).reshape((test_size, seq_len, 1))
  325. data_windows = np.array(data_windows).astype(float)
  326. x_test = data_windows[:, :-1]
  327. y_test = data_windows[:, -1, [0]]
  328. elif 'price' in exp:
  329. """see more details in https://github.com/omerbsezer/LSTM_RNN_Tutorials_with_Demo/tree/master/StockPricesPredictionProject"""
  330. x_test, y_test = DataUtils.get_price_data(dataset_dir)
  331. # TODO: Add your own data preprocessing here
  332. # Note: The returned inputs should be preprocessed and labels should decoded as one-hot vector which could be directly feed in model.
  333. # Both of them should be returned in batch, e.g. shape like (1500,28,28,1) and (1500,10)
  334. # elif 'xxx' in exp:
  335. # x_test, y_test = get_your_data(dataset_dir)
  336. return x_test, y_test
  337. @staticmethod
  338. def save_img_from_array(path, array, index, exp):
  339. im = Image.fromarray(array)
  340. # path = path.rstrip("/")
  341. # save_path = "{}/{}_{}.png".format(path,exp,index)
  342. save_path = os.path.join(path, "{}_{}.png".format(exp, index))
  343. im.save(save_path)
  344. return save_path
  345. @staticmethod
  346. def shuffled_data(x, y, bs=None):
  347. ds = x.shape[0]
  348. all_idx = np.arange(ds)
  349. np.random.shuffle(all_idx)
  350. shuffle_idx = all_idx
  351. # shuffle_idx = all_idx[:bs]
  352. return x[shuffle_idx], y[shuffle_idx]
  353. @staticmethod
  354. def get_mnist_data(x_test):
  355. x_test = x_test.astype('float32') / 255.0
  356. x_test = x_test.reshape(x_test.shape[0], 28, 28, 1)
  357. return x_test
  358. @staticmethod
  359. def get_fashion_mnist_data(x_test):
  360. x_test = x_test.astype('float32') / 255.0
  361. w, h = 28, 28
  362. x_test = x_test.reshape(x_test.shape[0], w, h, 1)
  363. return x_test
  364. @staticmethod
  365. def get_cifar10_data(x_test):
  366. x_test = x_test.astype('float32') / 255.0
  367. w, h = 32, 32
  368. x_test = x_test.reshape(x_test.shape[0], w, h, 3)
  369. return x_test
  370. @staticmethod
  371. def get_price_data(data_dir):
  372. import pandas as pd
  373. from sklearn.preprocessing import MinMaxScaler
  374. def create_dataset(dataset, look_back=1):
  375. dataX, dataY = [], []
  376. for i in range(len(dataset) - look_back - 1):
  377. a = dataset[i:(i + look_back), 0]
  378. dataX.append(a)
  379. dataY.append(dataset[i + look_back, 0])
  380. return np.array(dataX), np.array(dataY)
  381. grandparent_directory = os.path.dirname(os.path.dirname(os.getcwd()))
  382. csv_path = grandparent_directory + "/dataset/DIS.csv"
  383. df = pd.read_csv(csv_path, header=None, index_col=None, delimiter=',')
  384. all_y = df[5].values
  385. dataset = all_y.reshape(-1, 1)
  386. scaler = MinMaxScaler(feature_range=(0, 1))
  387. dataset = scaler.fit_transform(dataset)
  388. train_size = int(len(dataset) * 0.5)
  389. test_size = len(dataset) - train_size
  390. train, test = dataset[0:train_size, :], dataset[train_size:len(dataset), :]
  391. # reshape into X=t and Y=t+1, timestep 240
  392. look_back = 240
  393. trainX, trainY = create_dataset(train, look_back)
  394. # reshape input to be [samples, time steps, features]
  395. trainX = np.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1]))
  396. return trainX, trainY
  397. @staticmethod
  398. def imagenet_preprocess_dict():
  399. import keras
  400. keras_preprocess_dict = dict()
  401. keras_preprocess_dict['resnet50'] = keras.applications.resnet50.preprocess_input
  402. keras_preprocess_dict['densenet121'] = keras.applications.densenet.preprocess_input
  403. keras_preprocess_dict['mobilenet.1.00.224'] = keras.applications.mobilenet.preprocess_input
  404. keras_preprocess_dict['vgg16'] = keras.applications.vgg16.preprocess_input
  405. keras_preprocess_dict['vgg19'] = keras.applications.vgg19.preprocess_input
  406. keras_preprocess_dict['inception.v3'] = keras.applications.inception_v3.preprocess_input
  407. keras_preprocess_dict['inception.v2'] = keras.applications.inception_resnet_v2.preprocess_input
  408. keras_preprocess_dict['xception'] = keras.applications.xception.preprocess_input
  409. return keras_preprocess_dict
  410. @staticmethod
  411. def imagenet_shape_dict():
  412. image_shapes = dict()
  413. image_shapes['resnet50'] = (224, 224)
  414. image_shapes['densenet121'] = (224, 224)
  415. image_shapes['mobilenet.1.00.224'] = (224, 224)
  416. image_shapes['vgg16'] = (224, 224)
  417. image_shapes['vgg19'] = (224, 224)
  418. image_shapes['inception.v3'] = (299, 299)
  419. image_shapes['inception.v2'] = (299, 299)
  420. image_shapes['xception'] = (299, 299)
  421. return image_shapes
  422. class ToolUtils:
  423. @staticmethod
  424. def select_mutant(roulette, **kwargs):
  425. return roulette.choose_mutant()
  426. @staticmethod
  427. def select_mutator(logic, **kwargs):
  428. # import numpy as np
  429. # return np.random.permutation(mutate_ops)[0]
  430. last_used_mutator = kwargs['last_used_mutator']
  431. return logic.choose_mutator(last_used_mutator)
  432. @staticmethod
  433. def get_HH_mm_ss(td):
  434. days, seconds = td.days, td.seconds
  435. hours = days * 24 + seconds // 3600
  436. minutes = (seconds % 3600) // 60
  437. secs = seconds % 60
  438. return hours, minutes, secs
  439. class MetricsUtils:
  440. @staticmethod
  441. def kendall(y1_pred,y2_pred):
  442. return kendalltau(y1_pred,y2_pred)
  443. @staticmethod
  444. def delta(y1_pred, y2_pred, y_true=None):
  445. y1_pred = np.reshape(y1_pred, [np.shape(y1_pred)[0], -1])
  446. y2_pred = np.reshape(y2_pred, [np.shape(y2_pred)[0], -1])
  447. return np.mean(np.abs(y1_pred - y2_pred), axis=1), np.sum(np.abs(y1_pred - y2_pred), axis=1)
  448. @staticmethod
  449. def D_MAD_metrics(y1_pred, y2_pred, y_true, epsilon=1e-7):
  450. # sum could be remove and use mean in branch.
  451. theta_y1, sum_y1 = MetricsUtils.delta(y1_pred, y_true)
  452. theta_y2, sum_y2 = MetricsUtils.delta(y2_pred, y_true)
  453. return [
  454. 0
  455. if (sum_y1[i] == 0 and sum_y2[i] == 0)
  456. else
  457. np.abs(theta_y1[i] - theta_y2[i]) / (theta_y1[i] + theta_y2[i])
  458. for i in range(len(y_true))
  459. ]
  460. @staticmethod
  461. def get_theta_mean(y_pred,y_true):
  462. th,s=MetricsUtils.delta(y_pred,y_true)
  463. return np.mean(th)
  464. @staticmethod
  465. def get_all_metrics(): # 计算D_MAD(公式2)
  466. metrics_dict = {}
  467. metrics_dict['D_MAD'] = MetricsUtils.D_MAD_metrics
  468. return metrics_dict
  469. @staticmethod
  470. def get_gini_mid(y_pred):
  471. y_pred =MetricsUtils.softmax(np.reshape(y_pred, [np.shape(y_pred)[0], -1]))
  472. res=[]
  473. for i in range(y_pred.shape[0]):
  474. gini = 0
  475. for j in range(y_pred.shape[1]):
  476. gini += y_pred[i][j] ** 2
  477. res.append(1 - gini)
  478. return np.median(res)
  479. @staticmethod
  480. def get_gini_mean(y_pred): # 计算gini
  481. res = []
  482. for i in range(y_pred.shape[0]):
  483. gini = 0
  484. for j in range(y_pred.shape[1]):
  485. gini += y_pred[i][j] ** 2
  486. res.append(1 - gini)
  487. return sum(res)/len(res)
  488. @staticmethod
  489. def softmax(y_pred):
  490. for i in range(y_pred.shape[0]):
  491. y_pred[i] = np.exp(y_pred[i]) / np.sum(np.exp(y_pred[i]))
  492. return y_pred
  493. @staticmethod
  494. def get_metrics_by_name(name):
  495. metrics = MetricsUtils.get_all_metrics()
  496. return metrics[name]
  497. @staticmethod
  498. def generate_result_by_metrics(metrics_list, lemon_results, save_dir, exp):
  499. for metrics_name in metrics_list:
  500. file_name = "{}/{}_{}_result.csv".format(save_dir, exp, metrics_name)
  501. metrics_result_dict = lemon_results[metrics_name]
  502. with open(file_name, "w") as writer:
  503. writer.write("Mutation-Backend-Pair,Inconsistency Score\n")
  504. for dm_k, dm_v in metrics_result_dict.items():
  505. writer.write("{},{}\n".format(dm_k, dm_v))
  506. if __name__ == '__main__':
  507. pass