def main(args): if args.X_path[:-3] == 'csv': X = np.loadtxt(args.X_path, delimiter=',') # elif args.X_path[:-3] == 'xls': else: X = pandas.read_excel(args.X_path) X = np.array(X) W_est = notears.notears_linear_l1(X, lambda1=args.lambda1, loss_type=args.loss_type) assert utils.is_dag(W_est) np.savetxt(args.W_path, W_est, delimiter=',') plot_matrix.plot_matrix(args)
if h_new > 0.25 * h: rho *= 10 else: break w_est, h = w_new, h_new alpha += rho * h if h <= h_tol or rho >= rho_max: break W_est = _adj(w_est) W_est[np.abs(W_est) < w_threshold] = 0 return W_est if __name__ == '__main__': import utils as ut ut.set_random_seed(1) n, d, s0, graph_type, sem_type = 100, 20, 20, 'ER', 'gauss' B_true = ut.simulate_dag(d, s0, graph_type) W_true = ut.simulate_parameter(B_true) np.savetxt('W_true.csv', W_true, delimiter=',') X = ut.simulate_linear_sem(W_true, n, sem_type) np.savetxt('X.csv', X, delimiter=',') W_est = notears_linear_l1(X, lambda1=0.1, loss_type='l2') assert ut.is_dag(W_est) np.savetxt('W_est.csv', W_est, delimiter=',') acc = ut.count_accuracy(B_true, W_est != 0) print(acc)
def main(args): starttime = datetime.datetime.now() torch.set_default_dtype(torch.double) np.set_printoptions(precision=3) # load data X_envs, X_test = utils.load_data(dataset_name=args.dataset_name, preprocess=args.preprocess, num_envs=args.num_envs) # set target variable Y y_index = args.y_index vertex_label = utils.load_vertex_label(dataset_name=args.dataset_name) # adjust dataset X according to y_index X_swap = [] for X_env in X_envs: swap(X_env, 0, y_index) X_swap.append(X_env) X = np.stack(X_swap) # swap X_test swap(X_test, 0, y_index) # we do not need swap label because when drawing, we keep the original order. # Build model n_envs, n, d = X.shape Y_pred_hidden = [args.Y_hidden] * (args.Y_hidden_layer - 1) notears_hidden = [args.hidden] * args.notears_hidden_layer model = isl.isl_module(n_envs=n_envs, Y_dims=[d, args.hidden] + Y_pred_hidden + [1], dims=[d] + notears_hidden + [1], bias=True) if args.continue_path: pretrained_path = args.continue_path model.load_state_dict(torch.load(pretrained_path)) model.to(device) # conduct Invariant Structure Learning (ISL) y_pred_loss, w_est_origin_envs = isl.notears_nonlinear( model, X, y_index=0, lambda1=args.lambda1, lambda2=args.lambda2, lambda1_Y=args.lambda1_Y, lambda2_Y_fc1=args.lambda2_Y_fc1, lambda2_Y_fc2=args.lambda2_Y_fc2, w_threshold=0, beta=args.beta) # y_index always be 0, w-thresh always be zero # tune W for the best shd score g, w_true = utils.load_w_true(args.dataset_name) # save results os.makedirs(args.Output_path, exist_ok=True) for i in range(len(w_est_origin_envs)): # for each environment swap_row_column(w_est_origin_envs[i], 0, y_index) # recover to original order if args.Only_Y_DAG: # for boston housing, we only care Y related DAG w_est_origin_envs[i][:, 1:] = np.zeros(w_true[:, 1:].shape) # rank W wthresh_w_shd_dict = utils.rank_W(w_est_origin_envs[i], 10, 90, w_true, w_threshold_low=0, w_threshold_high=10, step_size=0.02) # select W w_est = None w_threshold = None for threshold, w_element in wthresh_w_shd_dict.items(): if utils.is_dag(w_element[0]): w_est = w_element[0] w_threshold = threshold break exp = (args.Output_path + ( 'pretrain-IRM_BH_norm={}_wthreshold={}_beta={}_y_index={}_lam1' + '={}_lam2fc1={}_lam2fc2_laterY{}NO{}_Yhid={}').format( args.normalization, w_threshold, args.beta, args.y_index, args.lambda1_Y, args.lambda2_Y_fc1, args.lambda2_Y_fc2, args.Y_hidden_layer, args.notears_hidden_layer, args.Y_hidden)) utils.save_dag(w_est, f'{exp}_{i}', vertex_label=vertex_label) np.savetxt(f'{exp}_West_env_{i}.csv', w_est, fmt='%.2f', delimiter=',') np.savetxt(f'{exp}_West_origin_env_{i}.csv', w_est_origin_envs[i], fmt='%.2f', delimiter=',') # save accuracy acc = metrics.count_accuracy(w_true, w_est != 0) # acc = {} y = model.test(X_test) mse = squared_loss(y[:, 0], X_test[:, 0]) acc['mse'] = mse print(acc) with open(f'{exp}_metrics.json', 'w+') as f: json.dump(acc, f) # save and load model weights model_save_path = exp + '_save_model.pt' torch.save(model.state_dict(), model_save_path) endtime = datetime.datetime.now() print('total time is ', (endtime - starttime).seconds)
def main(args): torch.set_default_dtype(torch.double) np.set_printoptions(precision=3) # load data X = utils.load_data(dataset_name=args.dataset_name, num_envs=args.num_envs) # load vertex labels vertex_label = utils.load_vertex_label(dataset_name=args.dataset_name) # the initial potential causal parents for each variable if args.dataset_name == 'Insurance': Init_Relation = { 'PropCost': ['ThisCarCost', 'OtherCarCost'], # 0 'GoodStudent': ['Age', 'SocioEcon'], # 1 'Age': ['GoodStudent', 'RiskAversion'], # 2 'SocioEcon': ['RiskAversion', 'MakeModel', 'HomeBase'], # 3 'RiskAversion': ['SocioEcon', 'DrivQuality', 'DrivingSkill', 'HomeBase'], # 4 'VehicleYear': ['SocioEcon', 'MakeModel', 'Antilock', 'CarValue', 'Airbag'], # 5 'ThisCarDam': ['RuggedAuto', 'Accident', 'ThisCarCost'], # 6 'RuggedAuto': ['VehicleYear', 'MakeModel', 'Antilock', 'Cushioning'], # 7 'Accident': ['ThisCarDam', 'RuggedAuto'], # 8 'MakeModel': ['SocioEcon', 'VehicleYear', 'RuggedAuto', 'CarValue'], # 9 'DrivQuality': ['RiskAversion', 'DrivingSkill'], # 10 'Mileage': ['MakeModel', 'CarValue'], # 11 'Antilock': ['VehicleYear', 'MakeModel'], # 12 'DrivingSkill': ['Age', 'DrivQuality'], # 13 'SeniorTrain': ['Age', 'RiskAversion'], # 14 'ThisCarCost': ['RuggedAuto', 'CarValue'], # 15 'Theft': ['ThisCarDam', 'MakeModel', 'CarValue', 'HomeBase', 'AntiTheft'], # 16 'CarValue': ['VehicleYear', 'MakeModel'], # 17 'HomeBase': ['SocioEcon', 'RiskAversion'], # 18 'AntiTheft': ['SocioEcon', 'RiskAversion'], # 19 'OtherCarCost': ['RuggedAuto', 'Accident'], # 20 'OtherCar': ['SocioEcon'], # 21 'MedCost': ['Age', 'Accident', 'Cushioning'], # 22 'Cushioning': ['RuggedAuto', 'Airbag'], # 23 'Airbag': ['VehicleYear'], # 24 'ILiCost': ['Accident'], # 25 'DrivHist': ['RiskAversion', 'DrivingSkill'], # 26 } elif args.dataset_name == 'Sachs': Init_Relation = { 'Raf': ['Mek'], 'Mek': ['Raf'], 'Plcg': ['PIP2'], 'PIP2': ['Plcg', 'PIP3'], 'PIP3': ['Plcg', 'PIP2'], 'Erk': ['Akt', 'Mek', 'PKA'], 'Akt': ['PKA', 'Erk'], 'PKA': ['Akt'], 'PKC': ['P38'], 'P38': ['PKA', 'PKC'], 'Jnk': ['PKC'], } Init_DAG = np.zeros((len(vertex_label), len(vertex_label))) for x in vertex_label: x_index = vertex_label.index(x) for y in Init_Relation[x]: # for each causal parent of x y_index = vertex_label.index(y) Init_DAG[y_index, x_index] = 1 n, d = X.shape model = isl.notearsmlp_self_supervised(dims=[d, args.hidden, 1], bias=True, Init_DAG=Init_DAG) model.to(device) # To use a different feature as label, change y_index to column index of the # feature. w_est_origin = isl.notears_nonlinear(model, X, lambda1=args.lambda1, lambda2=args.lambda2, w_threshold=0) # keep origin w_est # tune W for the best shd score g, w_true = utils.load_w_true(args.dataset_name) wthresh_w_shd_dict = utils.rank_W(w_est_origin, 10, 90, w_true, w_threshold_low=0, w_threshold_high=10, step_size=0.01) w_est = None w_threshold = None for threshold, w_element in wthresh_w_shd_dict.items(): if utils.is_dag(w_element[0]): w_est = w_element[0] w_threshold = threshold break exp = (args.Output_path + 'notear_mlp_sachs_norm={}wthreshold={}lambda1={}lambda2={}'.format( args.normalization, w_threshold, args.lambda1, args.lambda2)) os.makedirs(args.Output_path, exist_ok=True) np.savetxt(exp + '_w_est_origin.csv', w_est_origin, delimiter=',') np.savetxt(exp + '_w_est.csv', w_est, delimiter=',') utils.save_dag(w_est, exp + '_w_est', vertex_label=vertex_label) acc = metrics.count_accuracy(w_true, w_est != 0) print(acc) y = model(torch.from_numpy(X)) y = y.cpu().detach().numpy() mse = squared_loss(y[:, 0], X[:, 0]) print('mse:', mse) acc['mse'] = mse with open(f'{exp}_metrics.json', 'w+') as f: json.dump(acc, f)
def train(X, b_true, vertex_label, model_type, args): """Trains the model.""" n_envs, n, d = X.shape os.makedirs(args.Output_path, exist_ok=True) if model_type == 'notear-mlp': X = np.vstack(X) model = nonlinear.NotearsMLP(dims=[d, args.hidden, 1], bias=True) w_est_origin = nonlinear.notears_nonlinear(model, X, lambda1=args.lambda1, lambda2=args.lambda2, w_threshold=0) wthresh_w_shd_dict = utils.rank_W(w_est_origin, 2, 10, b_true, w_threshold_low=0, w_threshold_high=5, step_size=0.02) w_est = None w_threshold = None for threshold, w_element in wthresh_w_shd_dict.items(): if utils.is_dag(w_element[0]): w_est = w_element[0] w_threshold = threshold break exp = f'notear_mlp_syn_{args.synthetic_source}_W-thresh{round(w_threshold, 3)}' # save the learned W matrix np.savetxt(args.Output_path + f'{exp}_West.csv', w_est, fmt='%.3f', delimiter=',') np.savetxt(args.Output_path + f'{exp}_WOrigin.csv', w_est_origin, fmt='%.3f', delimiter=',') # save the learned DAG utils.save_dag(w_est, args.Output_path + f'{exp}_DAG', vertex_label=vertex_label) # count the accuracy of Y b_true_Y = np.zeros_like(b_true) b_true_Y[:, 0] = b_true[:, 0] w_est_Y = np.zeros_like(w_est) w_est_Y[:, 0] = w_est[:, 0] acc = metrices.count_accuracy(b_true_Y, w_est_Y != 0) metrics[f'{exp}_train_acc'] = acc y = model(torch.from_numpy(X)) y = y.cpu().detach().numpy() mse = mean_squared_loss(y.shape[0], y[:, 0], X[:, 0]) print('mse:', mse) metrics[f'{model_type}_train_MSE'] = mse elif model_type == 'notear-castle': X = np.vstack(X) model = nonlinear_castle.NotearsMLP(dims=[d, args.hidden, 1], bias=True) # To use a different feature as label, change y_index to column # index of the feature. w_est_origin = nonlinear_castle.notears_nonlinear( model, X, lambda1=args.lambda1, lambda2=args.lambda2, w_threshold=args.w_threshold) wthresh_w_shd_dict = utils.rank_W(w_est_origin, 2, 10, b_true, w_threshold_low=0, w_threshold_high=5, step_size=0.02) w_est = None w_threshold = None for threshold, w_element in wthresh_w_shd_dict.items(): if utils.is_dag(w_element[0]): w_est = w_element[0] w_threshold = threshold break exp = f'notear_castle_syn_{args.synthetic_source}_W-thresh-{round(w_threshold, 3)}' # save the learned W matrix np.savetxt(args.Output_path + f'{exp}_West.csv', w_est, fmt='%.2f', delimiter=',') np.savetxt(args.Output_path + f'{exp}_WOrigin.csv', w_est_origin, fmt='%.3f', delimiter=',') # save the learned DAG utils.save_dag(w_est, args.Output_path + f'{exp}_DAG', vertex_label=vertex_label) # estimate the accuracy of Y b_true_Y = np.zeros_like(b_true) b_true_Y[:, 0] = b_true[:, 0] w_est_Y = np.zeros_like(w_est) w_est_Y[:, 0] = w_est[:, 0] acc = metrices.count_accuracy(b_true_Y, w_est_Y != 0) metrics[f'{exp}_train_acc'] = acc y = model(torch.from_numpy(X)) y = y.cpu().detach().numpy() mse = mean_squared_loss(y.shape[0], y[:, 0], X[:, 0]) print('mse:', mse) metrics[f'{model_type}_train_MSE'] = mse elif model_type == 'ISL': model = isl.isl_module(n_envs=n_envs, Y_dims=[d, args.hidden, 1], dims=[d, args.hidden, 1], bias=True) model.to(device) # To use a different feature as label, change y_index to column index of # the feature. _, w_est_origin_envs = isl.notears_nonlinear( model, X, y_index=0, lambda1=args.lambda1, lambda2=args.lambda2, lambda1_Y=args.lambda1_Y, lambda2_Y_fc1=args.lambda2_Y_fc1, lambda2_Y_fc2=args.lambda2_Y_fc2, beta=args.beta, w_threshold=0) for i in range(len(w_est_origin_envs)): wthresh_w_shd_dict = utils.rank_W(w_est_origin_envs[i], 2, 30, b_true, w_threshold_low=0, w_threshold_high=5, step_size=0.02) w_est = None w_threshold = None for threshold, w_element in wthresh_w_shd_dict.items(): if utils.is_dag(w_element[0]): w_est = w_element[0] w_threshold = threshold break exp = f'notear_ISL_syn_{args.synthetic_source}_W-thresh{round(w_threshold, 3)}' # save the leared W matrix np.savetxt(args.Output_path + f'{exp}_West_{i}.csv', w_est, fmt='%.3f', delimiter=',') np.savetxt(args.Output_path + f'{exp}_WOrigin_env_{i}.csv', w_est_origin_envs[i], fmt='%.3f', delimiter=',') # save the learned DAG utils.save_dag(w_est, args.Output_path + f'{exp}_DAG_{i}', vertex_label=vertex_label) # count the accuracy of Y b_true_Y = np.zeros_like(b_true) b_true_Y[:, 0] = b_true[:, 0] w_est_Y = np.zeros_like(w_est) w_est_Y[:, 0] = w_est[:, 0] acc = metrices.count_accuracy(b_true_Y, w_est_Y != 0) print(acc) metrics[f'{exp}_train_env_{i}_acc'] = acc y = model.test(X) mse = mean_squared_loss(y.shape[0] * y.shape[1], y, X[:, :, 0][:, :, np.newaxis]) print('mse:', mse) metrics[f'{exp}_train_env_{i}_mse'] = mse else: ValueError('Invalid model type') return model, w_est
utils.set_random_seed(5) n = 1000 # samples n_i = 8 # data points (X) per sample d = 8 # DAG vertices s0 = 8 # DAG edges e = 20 # epigenetic markers k = 4 # archetypes graph_type, sem_type = 'ER', 'gauss' # Create network and epigenetic archetypes W_k = np.ones((k, d, d)) e_k = np.ones((k, e)) # Ensure any combination of networks is DAG while not utils.is_dag(np.sum(W_k, axis=0)): for j in range(k): B_true = utils.simulate_dag(d, s0, graph_type) W_true = utils.simulate_parameter(B_true) W_k[j] = W_true e_k[j] = utils.simulate_epigenome(e) np.savez('outputs/archetypes.npz', W_k=W_k, e_k=e_k) # Generate n samples from k archetypes subtypes = np.zeros((n, k)) W_n = np.zeros((n, d, d)) e_n = np.zeros((n, e)) X_n = np.zeros((n, n_i, d)) for i in range(n): weights = np.random.uniform(0, 1, k) weights /= np.sum(weights)
lag_range = range(1, 12 + 1) all_variable_names = list(df.columns) plotname = "TemporalModel__" + file plotname_REDUCED = "TemporalModel*REDUCED*__" + file #lambda1 used in example provided by authors is 0.1 #default w_threshold=0.3 for lambda1 in [0.1]: for w_threshold in [0.3]: W_est = notears.notears_linear_l1(X.copy(), lambda1=lambda1, loss_type='l2', w_threshold=w_threshold) if not utils.is_dag(W_est): plotname_add = '!!!NODAG!!!' else: plotname_add = '' np.savetxt('AdjMatrix_' + plotname + '_lambda1=' + str(lambda1) + '_Wthresh=' + str(w_threshold) + plotname_add + '.csv', W_est, delimiter=',') plot_graph.visualize_temporal( np.around(W_est, 2), feature_names=feature_names, full_feature_names=full_feature_names, lag_range=lag_range, all_variable_names=all_variable_names,
def count_accuracy(B_true, B_est): """Computes various accuracy metrics for B_est. true positive = predicted association exists in condition in correct direction reverse = predicted association exists in condition in opposite direction false positive = predicted association does not exist in condition Args: B_true (np.ndarray): [d, d] ground truth graph, {0, 1} B_est (np.ndarray): [d, d] estimate, {0, 1, -1}, -1 is undirected edge in CPDAG. Returns: fdr: (reverse + false positive) / prediction positive tpr: (true positive) / condition positive fpr: (reverse + false positive) / condition negative shd: undirected extra + undirected missing + reverse nnz: prediction positive """ if (B_est == -1).any(): # cpdag if not ((B_est == 0) | (B_est == 1) | (B_est == -1)).all(): raise ValueError('B_est should take value in {0,1,-1}') if ((B_est == -1) & (B_est.T == -1)).any(): raise ValueError('undirected edge should only appear once') else: # dag if not ((B_est == 0) | (B_est == 1)).all(): raise ValueError('B_est should take value in {0,1}') if not utils.is_dag(B_est): raise ValueError('B_est should be a DAG') d = B_true.shape[0] # linear index of nonzeros pred_und = np.flatnonzero(B_est == -1) pred = np.flatnonzero(B_est == 1) cond = np.flatnonzero(B_true) cond_reversed = np.flatnonzero(B_true.T) cond_skeleton = np.concatenate([cond, cond_reversed]) # true pos true_pos = np.intersect1d(pred, cond, assume_unique=True) # treat undirected edge favorably true_pos_und = np.intersect1d(pred_und, cond_skeleton, assume_unique=True) true_pos = np.concatenate([true_pos, true_pos_und]) # false pos false_pos = np.setdiff1d(pred, cond_skeleton, assume_unique=True) false_pos_und = np.setdiff1d(pred_und, cond_skeleton, assume_unique=True) false_pos = np.concatenate([false_pos, false_pos_und]) # reverse extra = np.setdiff1d(pred, cond, assume_unique=True) reverse = np.intersect1d(extra, cond_reversed, assume_unique=True) # compute ratio pred_size = len(pred) + len(pred_und) cond_neg_size = 0.5 * d * (d - 1) - len(cond) fdr = float(len(reverse) + len(false_pos)) / max(pred_size, 1) tpr = float(len(true_pos)) / max(len(cond), 1) fpr = float(len(reverse) + len(false_pos)) / max(cond_neg_size, 1) # structural hamming distance pred_lower = np.flatnonzero(np.tril(B_est + B_est.T)) cond_lower = np.flatnonzero(np.tril(B_true + B_true.T)) extra_lower = np.setdiff1d(pred_lower, cond_lower, assume_unique=True) missing_lower = np.setdiff1d(cond_lower, pred_lower, assume_unique=True) shd = len(extra_lower) + len(missing_lower) + len(reverse) return {'fdr': fdr, 'tpr': tpr, 'fpr': fpr, 'shd': shd, 'nnz': pred_size}
rho *= 10 else: break w_est, h = w_new, h_new alpha += rho * h if h <= h_tol or rho >= rho_max: break W_est = _adj(w_est) W_est[np.abs(W_est) < w_threshold] = 0 return W_est if __name__ == '__main__': import utils utils.set_random_seed(1) n, d, s0, graph_type, sem_type = 100, 20, 20, 'ER', 'gauss' B_true = utils.simulate_dag(d, s0, graph_type) W_true = utils.simulate_parameter(B_true) np.savetxt('W_true.csv', W_true, delimiter=',') X = utils.simulate_linear_sem(W_true, n, sem_type) np.savetxt('X.csv', X, delimiter=',') W_est = notears_linear(X, lambda1=0.1, loss_type='l2') assert utils.is_dag(W_est) np.savetxt('W_est.csv', W_est, delimiter=',') acc = utils.count_accuracy(B_true, W_est != 0) print(acc)