[バイオインフォマティクス] Noteears 線形構造方程式モデルにおける因果関係推定のための線形アルゴリズム

目次

1. 実験の紹介

2. 実験環境

1. 仮想環境を構成する

2. ライブラリバージョンの紹介

3.IDE

3. 実験内容

0. 必要なツールをインポートする 

1. set_random_seed

2.公証人_リニア

a. 入力パラメータ

b. 内部関数_adj

c. 内部機能の損失

d.内部関数_h

e. 内部関数_func 

f. 関数本体

3. メインプログラム

データ部表示

グラフを描く

n. コードの統合


1. 実験の紹介

        この実験により、線形構造方程式モデルにおける Noteears Linear アルゴリズムの因果関係推定が完了しました。

チャットGPT:

        Noteears Linear アルゴリズムは、線形構造方程式モデルの因果関係を推定するための効率的な方法です。行列が変数間の因果関係を説明できるように、損失関数を最小化することで最適な重み行列を見つけます。このアルゴリズムには次の利点があります。

  1. 効率: Notears Linear アルゴリズムは、因果関係を効率的に推定できる最適化ベースのアプローチを使用します。アルゴリズムの複雑さは変数の数と観測サンプルのサイズによって異なりますが、一般に計算の複雑さは低くなります。

  2. 線形モデルの適用性: Noteears 線形アルゴリズムは線形構造方程式モデルに適しており、線形因果関係を効果的に処理できます。非線形関係の場合、このアルゴリズムは機能しない可能性があります。

  3. 制約項の導入: Noteears 線形アルゴリズムは、推定されたグラフが非循環であることを保証するために制約項を導入し、因果関係の因果関係を確立します。これにより、推定結果の解釈可能性と信頼性が向上します。

2. 実験環境

    この一連の実験では PyTorch 深層学習フレームワークを使用し、関連する操作は次のとおりです (深層学習シリーズの記事の環境に基づく)。

1. 仮想環境を構成する

ディープラーニング連載記事の環境

conda create -n DL python=3.7 
conda activate DL
pip install torch==1.8.1+cu102 torchvision==0.9.1+cu102 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html
conda install matplotlib
conda install scikit-learn

新規追加

conda install pandas
conda install seaborn
conda install networkx
conda install statsmodels
pip install pyHSICLasso

注:私の実験環境では上記の順番で各種ライブラリをインストールしていますので、まとめてインストールしてみたい方は試してみてください(問題が起こるかどうかは神のみぞ知る)

2. ライブラリバージョンの紹介

ソフトウェアパッケージ 今回の実験版は 現在の最新バージョン
マットプロットライブラリ 3.5.3 3.8.0
しこり 1.21.6 1.26.0
パイソン 3.7.16
scikit-learn 0.22.1 1.3.0
松明 1.8.1+cu102 2.0.1
トーショーディオ 0.8.1 2.0.2
トーチビジョン 0.9.1+cu102 0.15.2

新しい

ネットワークx 2.6.3 3.1
パンダ 1.2.3 2.1.1
pyHSICLase 1.4.2 1.4.2
シーボーン 0.12.2 0.13.0
状態モデル 0.13.5 0.14.0

3.IDE

        Pycharmの使用を推奨します(中でもpyHSICLassoライブラリはVScodeでエラーが発生しており、解決策はまだ見つかっていません...)

内部関数

3. 実験内容

0. 必要なツールをインポートする 

import numpy as np
import scipy.linalg as slin
import scipy.optimize as sopt
import random

import networkx as nx
import matplotlib.pyplot as plt

1. set_random_seed

def set_random_seed(seed=1):
    random.seed(seed)
    np.random.seed(seed)

        再現可能な結果を​​保証するためにランダム シードを設定するために使用されます。

2.公証人_リニア

def notears_linear(X, lambda1=0.08, max_iter=100, h_tol=1e-8, rho_max=1e+16, w_threshold=0.3):

a. 入力パラメータ

  • X: 入力データ行列の形状は です (n, d)。ここで、 n はサンプル数、 はd 特徴の次元です。
  • lambda1: L1 正則化項の重み。デフォルトは 0.08 です。
  • max_iter: 最大反復数、デフォルトは 100 です。
  • h_tol: 反復を停止するためのターゲット エラー許容値。デフォルトは 1e-8 です。
  • rho_max: 最大ペナルティパラメータ。デフォルトは 1e+16 です。
  • w_threshold: 重みのしきい値。スパース推定結果に使用されます。デフォルトは 0.3 です。

関数内には、次のようないくつかの補助関数が定義されています。

b. 内部機能_adj

   def _adj(w):
        return w.reshape([d, d])

        平坦化された重みパラメータをw正方行列形式の重み行列に変換します。W。

c. 内部関数_loss

    def _loss(W):
        X_ = X @ W
        R = X - X_
        loss = 0.5 / X.shape[0] * (R ** 2).sum() + lambda1 * W.sum()  # Form 2
        G_loss = - 1.0 / X.shape[0] * X.T @ R + lambda1

        return loss, G_loss
  • 損失関数とその勾配を計算します。
    • 損失関数は、データ フィッティング項と正則化項の 2 つの部分で構成されます。
    • 勾配は、重み行列に関する損失関数の導関数を表します。

d.内部関数_h

    def _h(W):
        E = slin.expm(W * W)
        h = np.trace(E) - d
        G_h = E.T * W * 2  # Form 6
        return h, G_h
  • 別の制約とその勾配を計算します。
    • 制約は、推定されたグラフが非循環であることを保証するために使用されます。
    • 勾配は、重み行列に関する制約項の導関数を表します。

e.内部関数_func 

    def _func(w):
        W = _adj(w)
        loss, G_loss = _loss(W)
        h, G_h = _h(W)
        obj = loss + 0.5 * rho * h * h + alpha * h  # Form 11
        G_smooth = G_loss + (rho * h + alpha) * G_h  # G of Form 11
        g_obj = G_smooth.reshape(-1, )
        return obj, g_obj
  • 完全な目的関数とその勾配を計算します。
    • 目的関数には、損失関数、制約項、正則化項が含まれます。
    • 勾配は、重みパラメータに関する目的関数の導関数を表します。

f. 関数本体

    n, d = X.shape
    w_est, rho, alpha, h = np.zeros(d * d), 1.0, 0.0, np.inf
    bnds = [(0, 0) if i == j else (0, None) for i in range(d) for j in range(d)]
    X = X - np.mean(X, axis=0)
    for _ in range(max_iter):
        w_new, h_new = None, None
        while rho < rho_max:
            sol = sopt.minimize(_func, w_est, jac=True, bounds=bnds)
            w_new = sol.x
            h_new, _ = _h(_adj(w_new))
            if h_new > 0.25 * h:  # h下降不够快时 提高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
  • 変数の初期化
    • 入力データ行列の次元を取得し、いくつかの変数を初期化します。
    • 入力データ行列を中央に配置します。

  • ループの反復
    • 各反復では、 scipy.optimize.minimize 関数を呼び出すことによって最適なモデル パラメーター推定値が見つかります。

    • 最適解を見つけるプロセスでは、 rho ペナルティ パラメーターの値を調整することでモデル構造のスパース性が制御されます。

    • 反復プロセス中に、 rho 値は目的関数の値の変化と制約に基づいて動的に調整されます。

    • 停止条件に達すると (ターゲット誤差が許容値未満であるか、ペナルティ パラメーターが rho 最大値に達すると)、反復は停止します。

  • しきい値: 絶対値が指定されたしきい値未満である重み行列内の要素をゼロに設定します。
  • 推定されたモデル パラメーター行列を返しますW_est

3. メインプログラム

if __name__ == '__main__':
    set_random_seed()

    X = np.loadtxt('Notears_X.csv', delimiter=',')
    W_est = notears_linear(X)

    print("W_est")
    print(W_est)

    G_nx = nx.DiGraph(W_est)
    print(nx.is_directed_acyclic_graph(G_nx))

    nx.draw_planar(G_nx, with_labels=True)
    plt.show()
  • ランダムシードを設定します。
  • ファイル「Notears_X.csv」からデータ行列をロードします X
  • 関数を呼び出して notears_linear 線形構造方程式モデルのパラメーターを推定し、推定されたモデル パラメーター行列 を取得します W_est
  • 推定されたモデル パラメーター行列を出力します W_est
  • 推定されたモデル パラメーター行列に基づいて W_est 有向グラフを作成します G_nx
  • G_nx グラフが有向非巡回グラフ (DAG) であるかどうかを判断します 。
  • G_nx 図の平面レイアウトを描画し 、グラフを表示します。

データ部表示

6.24E-01 9.07E-01 7.77E-01 1.58E+00 ####### ####### 5.62E+00 ####### ####### 7.16E+00
7.50E-01 7.33E-01 ####### 7.01E-03 ####### ####### 3.93E-01 ####### 2.40E+00 #######
3.77E-01 7.12E-01 1.71E-01 1.58E-01 1.08E+00 1.73E+00 ####### 3.05E+00 4.09E+00 #######
1.39E-01 1.10E+00 7.96E-01 1.67E+00 2.94E-01 ####### 4.86E+00 ####### ####### 7.24E+00
####### ####### ####### ####### ####### 9.83E-01 ####### 3.42E+00 4.28E+00 #######
####### ####### 8.44E-01 5.92E-01 9.75E-02 ####### ####### 8.99E-01 ####### 1.18E+00
####### 1.68E-01 ####### ####### 1.50E+00 3.22E+00 ####### 3.14E+00 4.26E+00 #######
####### ####### 2.18E-01 ####### 1.18E+00 2.19E+00 ####### 1.41E+00 9.86E-01 #######
1.85E-01 3.48E-02 3.65E-01 ####### 3.91E-01 1.97E+00 ####### 4.16E+00 4.85E+00 #######
####### ####### ####### ####### 3.01E-01 7.11E-01 2.77E-02 ####### ####### #######

グラフを描く

n. コードの統合

import numpy as np
import scipy.linalg as slin
import scipy.optimize as sopt
import random

import networkx as nx
import matplotlib.pyplot as plt


def set_random_seed(seed=1):
    random.seed(seed)
    np.random.seed(seed)


def notears_linear(X, lambda1=0.08, max_iter=100, h_tol=1e-8, rho_max=1e+16, w_threshold=0.3):

    def _adj(w):
        return w.reshape([d, d])

    def _loss(W):
        X_ = X @ W
        R = X - X_
        loss = 0.5 / X.shape[0] * (R ** 2).sum() + lambda1 * W.sum()  # Form 2
        G_loss = - 1.0 / X.shape[0] * X.T @ R + lambda1

        return loss, G_loss

    def _h(W):
        E = slin.expm(W * W)
        h = np.trace(E) - d
        G_h = E.T * W * 2  # Form 6
        return h, G_h


    def _func(w):
        W = _adj(w)
        loss, G_loss = _loss(W)
        h, G_h = _h(W)
        obj = loss + 0.5 * rho * h * h + alpha * h  # Form 11
        G_smooth = G_loss + (rho * h + alpha) * G_h  # G of Form 11
        g_obj = G_smooth.reshape(-1, )
        return obj, g_obj

    n, d = X.shape
    w_est, rho, alpha, h = np.zeros(d * d), 1.0, 0.0, np.inf
    bnds = [(0, 0) if i == j else (0, None) for i in range(d) for j in range(d)]
    X = X - np.mean(X, axis=0)
    for _ in range(max_iter):
        w_new, h_new = None, None
        while rho < rho_max:
            sol = sopt.minimize(_func, w_est, jac=True, bounds=bnds)
            w_new = sol.x
            h_new, _ = _h(_adj(w_new))
            if h_new > 0.25 * h:  # h下降不够快时 提高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__':
    set_random_seed()

    X = np.loadtxt('Notears_X.csv', delimiter=',')
    W_est = notears_linear(X)

    print("W_est")
    print(W_est)

    G_nx = nx.DiGraph(W_est)
    print(nx.is_directed_acyclic_graph(G_nx))

    nx.draw_planar(G_nx, with_labels=True)
    plt.show()

    # edges, weights = zip(*nx.get_edge_attributes(G_nx, 'weight').items())
    # pos = nx.spring_layout(G_nx)
    # nx.draw(G_nx, pos, node_color='b', edgelist=edges, edge_color=weights, width=5, with_labels=True, edge_cmap=plt.cm.Blues)
    # plt.show()

おすすめ

転載: blog.csdn.net/m0_63834988/article/details/133499102