实验二 因果推理算法及工具应用

1、实验目的

(1) 学习“NOTEARS/ PC/GraN-DAG”等因果发现算法在python中的使用与实现;

(2) 学习AMOS软件,计算因果效应

(3) 学会用Grid Search/BOHB等方法或工具进行超参数调优

(4) 通过多项指标比较比较算法效果

(5) 结果可视化与展示。

2、实验准备

1、准备好python环境和编译器软件(PyCharm或Visual Studio Code);

2、自行在网络资源中找到“NOTEARS/ PC/GraN-DAG”对应的python代码(推荐渠道:

1.NOTEARS官方项目https://github.com/xunzheng/notears;

2.GraN-DAG官方项目https://github.com/lihebi/GraN-DAG-nodata;

3.华为诺亚实验室集成工具库

https://github.com/huawei-noah/trustworthyAI/tree/master/gcastle),并完成算法所需要python库的安装;

注:上课演示以NOTEARS为例,先保证该算法配置完成

3、根据教程安装好AMOS软件https://blog.csdn.net/2303_81888702/article/details/135410867;

4、提前熟悉实验材料(及其中涉及的概念)和所对应的算法代码,可借助大模型平台(如:ChatGPT/通义千问等)。

NOTEARS的官方资源和AMOS安装文件已分别打包为“notears-master.zip”、“Amos28.zip”。

3、实验原理

一、因果发现:

因果发现,即从纯观测数据中发现并获取因果关系

因果模型本质是反应变量之间的因果关系,比如我们有两个变量“吃饭”与“饱足”,我们知道二者是相关的,那么我们如何描述两个变量间的因果关系呢?其实通过一种有向的描述即可,我们会发现:吃饭会导致饱足,但是饱足不会导致吃饭。因此吃饭是饱足的因。我们可以表述为“吃饭-->饱足”,所以我们可以发现,这本质上是一个有向无环的图模型(DAG)。所以如果我们可以通过一组变量写出其对应的DAG(专业术语是马尔可夫相容),那么我们就可以表达变量间的因果关系。

image-20240526143229535

因果发现是一种统计学和机器学习的分支,旨在识别哪些变量是其他变量的原因,哪些是结果。由于直接观察因果关系通常是困难的,尤其是在自然和社会科学中,因果发现方法利用统计依赖性、干预数据(如果有的话)、背景知识以及特定的算法和技术来估计这些关系。

当前主要有四种因果结构发现的方法:基于约束的方法、基于分数的方法、利用结构不对称性的方法和利用各种形式的干预的方法。每种方法可进一步被分为通过基于组合/搜索的算法和通过基于连续优化的算法。这些方法也可以被分为局部(local)即每次测试一条边,和全局(global)即测试整张图。

**二、**PC

PC (Hoyer等人(2008))使用加性噪声模型进行因果发现,并提供了线性非高斯因果发现框架的推广,以处理变量具有加性噪声的非线性函数依赖。它提到非线性因果关系通常有助于打破观察变量之间的对称性,并有助于确定因果方向。PC假设观测变量的数据生成过程如下面的公式所示,其中变量xi是其父变量的函数,而噪声项ei是独立的加性噪声。

image-20240526143303372

**三、**NOTEARS

NOTEARS (Zheng et al .(2018))是第一个将组合图搜索问题重建为连续优化问题的,并且许多方法改造了该方法的主要贡献即无环性惩罚。强制无环性的函数/惩罚 h 为:

image-20240526143322203

将传统的组合优化问题(左)转化为连续程序(右)

image-20240526143336620

使用增广拉格朗日的标准机制,转换为无约束问题

image-20240526143350425

**四、**GraN-DAG

基于梯度的神经DAG学习(GraN-DAG)是一种基于分数的结构学习方法,它使用神经网络(nn)来处理非线性因果关系(Lachapelle et al .(2019))。它使用随机梯度方法来训练神经网络,以提高可扩展性并允许隐式正则化。它基于NOTEARS为神经网络制定了一种新的非周期性表征(Zheng et al .(2018))。为了确保非线性模型中的非周期性,它使用了一个类似于NOTEARS的参数,并首先在神经网络路径级别应用它,然后在图路径级别应用它。对于正则化,GraN-DAG使用一个称为初步邻居选择(PNS)的过程来为每个变量选择一组潜在的父变量。它使用最后的修剪步骤来去除假边。该算法主要适用于非线性高斯加性噪声模型。

五、结构因果模型:

结构因果模型由 Pearl 提出,其将所有考虑的变量组织成一个有向无环图,也称作因果图 (causal graph),记作 G = (V,E) ,每个节点代表一个变量,一条由X 指向Y 的边代表X对Y有直接的因果作用。

image-20240526143404196

节点的值由外生变量 (exogenous variable) 和所有直接父节点变量通过结构方程 (structural equations) 唯一确定。

image-20240526143419218

六、超参数调优

https://zhuanlan.zhihu.com/p/590823641

模型参数:这些是由模型从给定的数据中估计出来的参数。例如,一个深度神经网络的权重。

模型超参数:这些是不能由模型从给定数据中估计的参数,超参数被用来估计模型的参数,例如,深度神经网络的学习率。

超参数调优的主要方法:

手动超参数调优:包括通过手动方式来实验不同的超参数集。

随机搜索(Random Search):在随机搜索方法中,我们为超参数创建了拥有很多可能值的网格。每次迭代都从这个网格中尝试随机的超参数组合,记录性能,最后得到最佳性能的超参数组合。

**网格搜索(Grid Search):**在网格搜索法中,我们为超参数创建了一个可能值的网格。每次迭代都以特定的顺序尝试超参数的组合。它在每一个可能的超参数组合上拟合模型并记录模型的性能。最后,它返回具有最佳超参数的最佳模型。

贝叶斯优化:为模型调整和寻找合适的超参数是一种优化问题。我们希望通过改变模型参数来最小化我们模型的损失函数。贝叶斯优化帮助我们通过最少的步骤中找到最小的点。贝叶斯优化还使用了采集函数(Acquisition Funtion),将采样引向有可能比当前最佳观察结果更好的区域。

4、实验内容

1、在python上实现NOTEARS,并通过生成数据对算法进行调参,之后将调优后的算法应用到给定数据,最后得到学习到的DAG。

exp_linear/graph00000_data00000_X _.csv

其中给定数据的已知信息如下:

真实DAG的边缘数量为14-18

graph_type = 'ER';sem_type = 'gauss';w_ranges = ((-2.0, -0.5), (0.5, 2.0)); noise_scale = np.ones(d)

2、将学习到的DAG应用到AMOS中进行计算因果效应,观察拟合指标是否达标,若不达标说明算法从数据中学习到的DAG与实际不符,需要重新进行步骤1。要求最终的AMOS指标处于合理范围。

3、基于分组给定三组不同分布的数据(包含三个文件),此时已知真实DAG。要求分别在三组数据上应用三个算法,并通过评价指标比较算法性能。

数据文件的具体分配参照“数据集分配.xlsx”

要用的数据文件格式如下:

exp_nonlinear1/ graph0000x_W_true .csv

exp_nonlinear1/graph0000x_data0000y_X .csv

exp_nonlinear2/ graph0000x_W_true .csv

exp_nonlinear2/graph0000x_data0000y_X .csv

exp_nonlinear3/ graph0000x_W_true .csv

exp_nonlinear3/graph0000x_data0000y_X .csv

其中x,y的值为0或1

5、实验要求

1.通过python实现PC/NOTEARS/GraN-DAG算法的运行。

2.通过使用超参数调优策略(网格搜索或贝叶斯策略或其它工具,如BOHB)参数调整理解优化算法中各个参数的意义。

3.通过NOTEARS学习未知真实DAG数据集中的DAG,并应用AMOS软件计算因果效应.

4.提交NOTEARS最后的超参数、因果图以及各路径的因果效应。

6、实验过程

展示主要的实验过程(整体流程+具体进行设计的地方+实验测试过程)

文字+图片(如:代码截图)

A 未知真实DAG数据集:

主要体现两部分内容:

(1)超参数调优代码设计

数据生成:

from notears import linear, utils
import numpy as np
from tqdm import tqdm
from collections import defaultdict
import os


def main():
    utils.set_random_seed(123)
    # 生成图的数量
    num_graph = 2
    # 每个图对应生成数据集的份数
    num_data_per_graph = 2
    # 生成数据的保存地址
    save_data_path = r"data"
    # n-生成的数据量,多少条数据;d-生成图/生成数据的维度,几个变量;s0-生成图的边数;
    # graph_type-生成图的类型(ER, SF, BP);sem_type-生成数据的分布类型(线性:gauss, exp, gumbel, uniform, logistic, poisson/非线性:mlp, mim, gp, gp-add)
    n, d, s0, graph_type, sem_type = 200, 7, 14, 'ER', 'gauss'
    n1, d1, s1, graph_type1, sem_type1 = 30, 3, 2, 'SF', 'mlp'

    # 线性数据例子
    # w_ranges-参数的数据范围
    w_ranges = ((-2.0, -0.5), (0.5, 2.0))
    # noise_scale-加性噪声的范围
    noise_scale = np.ones(d)
    # 生成数据存放文件夹的名称
    expt_name = 'equal_var'
    run_expt(save_data_path, num_graph, num_data_per_graph, n, d, s0, graph_type, sem_type, w_ranges, noise_scale, expt_name)

    # 非线性数据例子
    w_ranges = ((-2.0, -1.1), (1.1, 2.0))
    noise_scale1 = [1., 0.15, 0.2]
    expt_name = 'large_a'
    #run_expt_(save_data_path, num_graph, num_data_per_graph, n1, d1, s1, graph_type1, sem_type1, w_ranges, noise_scale1, expt_name)

# 线性
def run_expt(save_data_path, num_graph, num_data_per_graph, n, d, s0, graph_type, sem_type, w_ranges, noise_scale, expt_name):
    # os.mkdir(expt_name)
    # os.chmod(expt_name, 0o777)
    # perf = defaultdict(list)
    noise_scale  =np.array(noise_scale)
    for ii in tqdm(range(num_graph)):
        # B_true-生成因果图的0-1邻接矩阵
        B_true = utils.simulate_dag(d, s0, graph_type)
        W_true = utils.simulate_parameter(B_true, w_ranges=w_ranges)
        B_true_fn = os.path.join(save_data_path, expt_name, f'graph{ii:05}_W_true.csv')
        np.savetxt(B_true_fn, B_true, delimiter=',')
        for jj in range(num_data_per_graph):
            # X = utils.simulate_linear_sem(W_true, n, sem_type, noise_scale=noise_scale)
            X = utils.simulate_linear_sem(W_true, n, sem_type, noise_scale=noise_scale)
            X_fn = os.path.join(save_data_path, expt_name, f'graph{ii:05}_data{jj:05}_X.csv')
            np.savetxt(X_fn, X, delimiter=',')

# 非线性
def run_expt_(save_data_path, num_graph, num_data_per_graph, n, d, s0, graph_type, sem_type, noise_scale, expt_name):
    noise_scale = np.array(noise_scale)
    for ii in tqdm(range(num_graph)):
        B_true = utils.simulate_dag(d, s0, graph_type)
        B_true_fn = os.path.join(save_data_path, expt_name, f'graph{ii:05}_W_true.csv')
        np.savetxt(B_true_fn, B_true, delimiter=',')
        for jj in range(num_data_per_graph):
            X = utils.simulate_nonlinear_sem(B_true, n, sem_type, noise_scale=noise_scale)
            X_fn = os.path.join(save_data_path, expt_name, f'graph{ii:05}_data{jj:05}_X.csv')
            np.savetxt(X_fn, X, delimiter=',')


if __name__ == '__main__':
    main()

网格搜索代码:

from notears import linear, nonlinear, utils
import numpy as np

with open(r'D:\Desktop\TBFILE\学习\群体智能\群体智能第二次实验\notears-master\data\equal_var\graph00001_W_true.csv') as f:
    line = f.readline()
    data_array = []
    while line:
        num = list(map(float, line.split(',')))
        data_array.append(num)
        line = f.readline()
    B_true = np.array(data_array)

with open(r'D:\Desktop\TBFILE\学习\群体智能\群体智能第二次实验\notears-master\data\equal_var\graph00001_data00001_X.csv') as f:
    line = f.readline()
    data_array = []
    while line:
        num = list(map(float, line.split(',')))
        data_array.append(num)
        line = f.readline()
    X = np.array(data_array)

param_dist = {
              # "max_iter":[100, 200, 300, 400, 500],
              "w_threshold":[0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95],
              # "lambda0":[0.001,  0.003, 0.004, 0.005,  0.007, 0.009, 0.011, 0.013, 0.015, 0.017, 0.019, 0.021, 0.023, 0.025],
              "lambda1":[0.01,  0.02, 0.03, 0.04,  0.05, 0.06, 0.07, 0.08, 0.09, 0.1],
              }

# MI = param_dist.get("max_iter")
WT = param_dist.get("w_threshold")
L1 = param_dist.get("lambda1")

result = []
for i in range(len(L1)):
    for j in range(len(WT)):
        #notears
        lambda1 = L1[i]
        w_threshold = WT[j]
        W_notears = linear.notears_linear(X, lambda1=lambda1, loss_type='l2',max_iter=300,
                                          w_threshold=w_threshold)

        assert utils.is_dag(W_notears)
        #eval
        B_notears = (W_notears !=0)
        acc = utils.count_accuracy(B_true, B_notears)
        print("----------------------------------")
        print("lambda1:",L1[i],"w_threshold:",WT[j])
        ret = [L1[i],WT[j]]

        v_met = list(acc.values())
        print("fdr|tpr|shd|nnz")
        print(v_met)
        ret += v_met
        result.append(ret)

import csv

# 创建csv writer对象
with open(r'data\equal_var\res_save\r_l11.csv', 'w', newline='') as file1:
    writer1 = csv.writer(file1)

    # 写入数据
    for row in result:
        writer1.writerow(row)

# 数据
data = [["lambda1:","w_threshold:"] + list(acc.keys())]+result

# 创建csv writer对象
with open(r'data\equal_var\res_save\d_l11.csv', 'w', newline='') as file2:
    writer2 = csv.writer(file2)

    # 写入数据
    for row in data:
        writer2.writerow(row)

(2)测试过程

体现测试的过程,至少将两次测试不成功(即:应用AMOS计算因果效应时,拟合指标不达标,学习到的因果图不准确)的例子进行呈现——此时预设的边的数量,超参数,结果图(因果图+AMOS计算结果)

试验过程:

Lambda1:0.01 w_threshold:0.2
**边的数量:**16
image-20240525202150037
Lambda1:0.01 w_threshold:0.2
**边的数量:****15
image-20240525202250520

最优解:

**最优:**Lambda1:0.05 w_threshold:0.1
边的数量:16
img

B 已知真实DAG数据集:

主要体现不同算法在不同数据集上的超参数调优过程及对比过程

要求对最后的比较结果作分析和评述

NOTEARS

网格搜索超参数优化:

import torch
import torch.nn as nn
import numpy as np
import csv
from notears.locally_connected import LocallyConnected
from notears.lbfgsb_scipy import LBFGSBScipy
from notears.trace_expm import trace_expm
import notears.utils as ut
from notears import linear, nonlinear, utils


# Define NotearsMLP and NotearsSobolev classes here (as provided)

def squared_loss(output, target):
    n = target.shape[0]
    loss = 0.5 / n * torch.sum((output - target) ** 2)
    return loss


def dual_ascent_step(model, X, lambda1, lambda2, rho, alpha, h, rho_max):
    h_new = None
    optimizer = LBFGSBScipy(model.parameters())
    X_torch = torch.from_numpy(X)
    while rho < rho_max:
        def closure():
            optimizer.zero_grad()
            X_hat = model(X_torch)
            loss = squared_loss(X_hat, X_torch)
            h_val = model.h_func()
            penalty = 0.5 * rho * h_val * h_val + alpha * h_val
            l2_reg = 0.5 * lambda2 * model.l2_reg()
            l1_reg = lambda1 * model.fc1_l1_reg()
            primal_obj = loss + penalty + l2_reg + l1_reg
            primal_obj.backward()
            return primal_obj

        optimizer.step(closure)
        with torch.no_grad():
            h_new = model.h_func().item()
        if h_new > 0.25 * h:
            rho *= 10
        else:
            break
    alpha += rho * h_new
    return rho, alpha, h_new


def notears_nonlinear(model: nn.Module,
                      X: np.ndarray,
                      lambda1: float = 0.,
                      lambda2: float = 0.,
                      max_iter: int = 100,
                      h_tol: float = 1e-8,
                      rho_max: float = 1e+16,
                      w_threshold: float = 0.3):
    rho, alpha, h = 1.0, 0.0, np.inf
    for _ in range(max_iter):
        rho, alpha, h = dual_ascent_step(model, X, lambda1, lambda2,
                                         rho, alpha, h, rho_max)
        if h <= h_tol or rho >= rho_max:
            break
    W_est = model.fc1_to_adj()
    W_est[np.abs(W_est) < w_threshold] = 0
    return W_est


def main():
    torch.set_default_dtype(torch.double)
    np.set_printoptions(precision=3)
    ut.set_random_seed(123)

    # Load observational data from CSV file
    X = np.genfromtxt(
        r'D:\Desktop\TBFILE\学习\群体智能\群体智能第二次实验\exp_nonlinear_3\graph00001_data00000_X.csv',
        delimiter=',', skip_header=1)
    # Load ground truth DAG from CSV file
    B_true = np.genfromtxt(
        r'D:\Desktop\TBFILE\学习\群体智能\群体智能第二次实验\exp_nonlinear_3\graph00001_W_true.csv',
        delimiter=',')

    d = X.shape[1]

    # Hyperparameters for grid search
    param_dist = {
        "w_threshold": [0.05,0.10,0.15,0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5],
        "lambda1": [0.01, 0.02, 0.03,  0.04, 0.05],
        "lambda2": [0.01, 0.02, 0.03, 0.04, 0.05]
    }

    WT = param_dist["w_threshold"]
    L1 = param_dist["lambda1"]
    L2 = param_dist["lambda2"]

    result = []
    for lambda1 in L1:
        for lambda2 in L2:
            for w_threshold in WT:
                model = nonlinear.NotearsMLP(dims=[d, 10, 1], bias=True)
                W_est = notears_nonlinear(model, X, lambda1=lambda1, lambda2=lambda2, w_threshold=w_threshold)

                # Ensure W_est is a DAG
                if not ut.is_dag(W_est):
                    continue  # Skip this combination if it's not a DAG

                # Save results for each combination
                ret = [lambda1, lambda2, w_threshold]
                ret += list(ut.count_accuracy(B_true, W_est != 0).values())
                result.append(ret)

                # Print results for debugging
                print(f"lambda1: {lambda1}, lambda2: {lambda2}, w_threshold: {w_threshold}")
                print("fdr|tpr|shd|nnz")
                print(list(ut.count_accuracy(B_true, W_est != 0).values()))

    # Save results to CSV files
    headers = ["lambda1", "lambda2", "w_threshold"] + list(ut.count_accuracy(B_true, W_est != 0).keys())
    data = [headers] + result

    with open(
            r'D:\Desktop\TBFILE\学习\群体智能\群体智能第二次实验\exp_nonlinear_3\grid_search_results1.csv',
            'w', newline='') as file:
        writer = csv.writer(file)
        for row in data:
            writer.writerow(row)


if __name__ == '__main__':
    main()
    print('done')

优化结果:

lambda1 lambda2 w_threshold fdr tpr fpr shd nnz
exp_nonlinear_1 0.02 0.01 0.35 0.04 0.888889 0.019608 4 25
exp_nonlinear_2 0.01 0.02 0.4 0.058824 0.592593 0.019608 11 17
exp_nonlinear_3 0.01 0.02 0.25 0.2 0.888889 0.117647 9 30

GraN-DAG

网格搜索超参数优化:

from castle.common import GraphDAG
from castle.metrics import MetricsDAG
from castle.algorithms import GraNDAG
import numpy as np
import pandas as pd
import itertools

# Set random seed for reproducibility
np.random.seed(42)

# Load observational data from CSV file
X = np.genfromtxt(
    r'D:\Desktop\TBFILE\学习\群体智能\群体智能第二次实验\exp_nonlinear_1\graph00001_data00000_X.csv',
    delimiter=',', skip_header=1)

# Load ground truth DAG from CSV file
B_true = np.genfromtxt(
    r'D:\Desktop\TBFILE\学习\群体智能\群体智能第二次实验\exp_nonlinear_1\graph00001_W_true.csv',
    delimiter=',')

# Normalize data
normalize = True
if normalize:
    X_mean = np.mean(X, axis=0, keepdims=True)
    X_std = np.std(X, axis=0, keepdims=True)
    X = (X - X_mean) / X_std

# Check for NaN values in the data
if np.isnan(X).any():
    raise ValueError("Input data contains NaN values. Please check the data preprocessing steps.")

# Define hyperparameter grid
param_grid = {
    'model_name': ['NonLinGauss'],
    'nonlinear': ['leaky-relu'],
    'optimizer': ['sgd'],
    'norm_prod': ['paths'],
    'device_type': ['cpu'],
    'random_seed': [42],
    'normalize': [normalize],
    'input_dim': [X.shape[1]],
    'hidden_num': [2, 3,4],
    'hidden_dim': [10, 20, 30],
    'batch_size': [64,128],
    'lr': [0.0001],
    'iterations': [1000]
}

# Create a list to store results
results = []

# Perform grid search
keys, values = zip(*param_grid.items())
for v in itertools.product(*values):
    params = dict(zip(keys, v))

    try:
        # Create and train GraNDAG instance
        gnd = GraNDAG(**params)
        gnd.learn(data=X)

        # Evaluate the learned DAG
        mm = MetricsDAG(gnd.causal_matrix, B_true)

        # Store results
        result = params.copy()
        result.update(mm.metrics)
        results.append(result)

        # Print current result
        print(f"Evaluated params: {params}")
        print(f"Metrics: {mm.metrics}\n")

    except ValueError as e:
        print(f"Error with params: {params}")
        print(e)
        continue

# Save results to CSV
results_df = pd.DataFrame(results)
results_df.to_csv(r'D:\Desktop\TBFILE\学习\群体智能\群体智能第二次实验\exp_nonlinear_1\pc_results.csv',
                  index=False)

print("Grid search completed. Results saved to CSV.")

优化结果

hidden_num hidden_dim batch_size fdr tpr fpr shd nnz
exp_nonlinear_1 2 10 128 0.5556 0.1481 0.098 28 9
exp_nonlinear_2 4 10 64 0.6667 0.1481 0.1569 28 12
exp_nonlinear_3 3 20 64 0.8 0.0741 0.1569 30 10

PC

贝叶斯搜索超参数优化:

import pandas as pd
import numpy as np
from castle.algorithms import PC
from castle.metrics import MetricsDAG
from bayes_opt import BayesianOptimization
import logging

# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# 读取CSV数据文件
data_path = r'D:\Desktop\TBFILE\学习\群体智能\群体智能第二次实验\exp_nonlinear_3\graph00001_data00000_X.csv'
data = pd.read_csv(data_path)

# 读取真实的因果图
true_graph_path = r'D:\Desktop\TBFILE\学习\群体智能\群体智能第二次实验\exp_nonlinear_3\graph00001_W_true.csv'
true_dag = pd.read_csv(true_graph_path, header=None).values

# 将数据转换为numpy数组
X = data.values

# 定义目标函数
def objective(alpha):
    try:
        # 设置新的alpha值并初始化PC算法
        pc = PC(variant='stable', alpha=alpha)

        # 学习因果结构
        pc.learn(X)

        # 评估学习到的因果矩阵
        met = MetricsDAG(pc.causal_matrix, true_dag)

        # 记录SHD值
        shd = met.metrics['shd']
        
        logger.info(f'Alpha: {alpha}, SHD: {shd}')

        # 返回SHD的负值,因为贝叶斯优化是一个最大化器,我们希望最小化SHD
        return -shd
    except Exception as e:
        logger.error(f"Error with alpha: {alpha}")
        logger.error(e)
        return -float('inf')

# 设置贝叶斯优化的参数范围
pbounds = {'alpha': (0.01, 0.1)}

# 初始化贝叶斯优化器
optimizer = BayesianOptimization(
    f=objective,
    pbounds=pbounds,
    random_state=42
)

# 执行优化过程
optimizer.maximize(n_iter=20)

# 获取最佳参数
best_alpha = optimizer.max['params']['alpha']
best_shd = -optimizer.max['target']

print(f'\nBest alpha: {best_alpha}, with SHD: {best_shd}')

优化结果:

pc alpha fdr tpr fpr shd nnz
exp_nonlinear_1 0.04370861069626263 0.2857 0.3704 0.0784 19 14
exp_nonlinear_2 0.09556428757689246 0.4118 0.3704 0.1373 20 17
exp_nonlinear_3 0.04370861069626263 0.3889 0.4074 0.1373 18 18

8、结果展示

A.未知真实DAG数据集:

(0)超参数优化图/或表格

体现超参数优化过程

image-20240525184830992

img

(1)因果图:

坐标点图、连线图或其他形式,可自行设计

image-20240525184853909 img

(2)AMOS计算结果图:

截图呈现

**最优:**lambda1:0.05 w_threshold:0.1
img
路径效应计算结果截图,包含总效应/直接效应/间接效应
image-20240525190241153
img
img

B.已知真实DAG数据集:

(1)因果图:

给出各个算法在不同数据集上学习到的最优DAG

image.png

(2)算法性能比较结果呈现:

通过表格呈现比较结果
exp_nonlinear_1:

fdr tpr fpr shd nnz
NOTEARS 0.04 0.888889 0.019608 4 2
GraN-DAG 0.5556 0.1481 0.098 28 9
PC 0.4118 0.3704 0.1373 20 17

exp_nonlinear_2:

fdr tpr fpr shd nnz
NOTEARS 0.058824 0.592593 0.019608 11 17
GraN-DAG 0.6667 0.1481 0.1569 28 1
PC 0.4118 0.3704 0.1373 20 17

exp_nonlinear_3:

fdr tpr fpr shd nnz
NOTEARS 0.2 0.888889 0.117647 9 30
GraN-DAG 0.8 0.0741 0.1569 30 10
PC 0.3889 0.4074 0.1373 18 18

通过上面表格比较可知:

NOTEARS 算法在所有三个数据集上都表现最佳,SHD 最低,能够更准确地学习到真实的因果关系结构

而GraN-DAG、PC算法表现不佳的原因可能是因为该数据集不适合这两种算法,从而导致这两种算法不能很好的学习因果关系结构

代码下载地址:https://www.123pan.com/s/WlFzVv-gdRph.html

转载请注明出处