DGL 0.5ユーザガイド : 1 章 グラフ : 1.5 異質グラフ (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 09/16/2020 (0.5.1)
* 本ページは、DGL の以下のドキュメントを翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
ユーザガイド : 1 章 グラフ : 1.5 異質グラフ
異質 (= heterogeneous) グラフは異なるタイプのノードとエッジを持つことができます。異なるタイプのノード/エッジは独立な ID 空間と特徴ストレージを持ちます。例えば下の図で、ユーザとゲームノード ID の両者はゼロから始まりそしてそれらは異なる特徴を持ちます。
異質グラフを作成する
DGL では、異質グラフ (短く、ヘテログラフ) は関係毎に一つ、下のように一連のグラフで指定されます。各関係は文字列トリプレット (ソース・ノード型, エッジ型, destination ノード型) です。関係はエッジ型の曖昧さをなくしますので、DGL はそれらを canonical エッジ型と呼称します。
{relation1 : node_tensor_tuple1, relation2 : node_tensor_tuple2, ...}
次のコードスニペットは DGL で異質グラフを作成するためのサンプルです。
>>> import dgl >>> import torch as th >>> # Create a heterograph with 3 node types and 3 edges types. >>> graph_data = { ... ('drug', 'interacts', 'drug'): (th.tensor([0, 1]), th.tensor([1, 2])), ... ('drug', 'interacts', 'gene'): (th.tensor([0, 1]), th.tensor([2, 3])), ... ('drug', 'treats', 'disease'): (th.tensor([1]), th.tensor([2])) ... } >>> g = dgl.heterograph(graph_data) >>> g.ntypes ['disease', 'drug', 'gene'] >>> g.etypes ['interacts', 'interacts', 'treats'] >>> g.canonical_etypes [('drug', 'interacts', 'drug'), ('drug', 'interacts', 'gene'), ('drug', 'treats', 'disease')]
均質と 2 部グラフは一つの関係を持つ単に一つの関係を持つ異質なグラフであることに注意してください。
>>> # A homogeneous graph >>> dgl.heterograph({('node_type', 'edge_type', 'node_type'): (u, v)}) >>> # A bipartite graph >>> dgl.heterograph({('source_type', 'edge_type', 'destination_type'): (u, v)})
異質グラフに関連する メタグラフ はグラフのスキーマです。それはノードとノード間のエッジのセット上の型制約を指定します。メタグラフのノード $u$ は関連するヘテログラフのノード型に対応します。メタグラフのエッジ \((u, v)\) は関連するヘテログラフで型 $u$ のノードから型 $v$ のノードへのエッジがあることを示します。
>>> g Graph(num_nodes={'disease': 3, 'drug': 3, 'gene': 4}, num_edges={('drug', 'interacts', 'drug'): 2, ('drug', 'interacts', 'gene'): 2, ('drug', 'treats', 'disease'): 1}, metagraph=[('drug', 'drug', 'interacts'), ('drug', 'gene', 'interacts'), ('drug', 'disease', 'treats')]) >>> g.metagraph().edges() OutMultiEdgeDataView([('drug', 'drug'), ('drug', 'gene'), ('drug', 'disease')])
API 参照 : dgl.heterograph(), ntypes, etypes, canonical_etypes, metagraph。
マルチ型で作業する
複数のノード/エッジ型が導入されるとき、型固有の情報のための DGLGraph API を呼び出すときユーザは特定のノード/エッジ型を指定する必要があります。そして、異なる型のノード/エッジは分離した ID を持ちます。
>>> # Get the number of all nodes in the graph >>> g.num_nodes() 10 >>> # Get the number of drug nodes >>> g.num_nodes('drug') 3 >>> # Nodes of different types have separate IDs, >>> # hence not well-defined without a type specified >>> g.nodes() DGLError: Node type name must be specified if there are more than one node types. >>> g.nodes('drug') tensor([0, 1, 2])
特定のノード/エッジ型のために特徴を設定/取得するために、DGL は 2 つの新しいタイプにシンタックスを提供します – g.nodes[‘node_type’].data[‘feat_name’] と g.edges[‘edge_type’].data[‘feat_name’] です。
>>> # Set/get feature 'hv' for nodes of type 'drug' >>> g.nodes['drug'].data['hv'] = th.ones(3, 1) >>> g.nodes['drug'].data['hv'] tensor([[1.], [1.], [1.]]) >>> # Set/get feature 'he' for edge of type 'treats' >>> g.edges['treats'].data['he'] = th.zeros(1, 1) >>> g.edges['treats'].data['he'] tensor([[0.]])
グラフが一つのノード/エッジ型だけを持つ場合には、ノード/エッジ型を指定する必要はありません。
>>> g = dgl.heterograph({ ... ('drug', 'interacts', 'drug'): (th.tensor([0, 1]), th.tensor([1, 2])), ... ('drug', 'is similar', 'drug'): (th.tensor([0, 1]), th.tensor([2, 3])) ... }) >>> g.nodes() tensor([0, 1, 2, 3]) >>> # To set/get feature with a single type, no need to use the new syntax >>> g.ndata['hv'] = th.ones(4, 1)
Note: エッジ型がソースと destination ノードのタイプを一意に決定するとき、エッジ型を指定するために文字列トリプレットの代わりに単に一つの文字列を使用できます。例えば、2 つの関係 (‘user’, ‘plays’, ‘game’) と (‘user’, ‘likes’, ‘game’) を持つヘテログラフに対して、2 つの関係を参照するために ‘plays’ や ‘likes’ を単に使用することは安全です。
ディスクから経トログラフをロードする
カンマ区切り値 (CSV)
ヘテログラフをストアする一般的な方法は異なるタイプのノードとエッジを異なる CSV ファイルにストアすることです。サンプルは次のようなものです。
# data folder data/ |-- drug.csv # drug nodes |-- gene.csv # gene nodes |-- disease.csv # disease nodes |-- drug-interact-drug.csv # drug-drug interaction edges |-- drug-interact-gene.csv # drug-gene interaction edges |-- drug-treat-disease.csv # drug-treat-disease edges
均質なグラフの場合と同様に、CSV ファイルを numpy 配列やフレームワーク tensor にパースし、関係辞書をビルドしてそれからヘテログラフを構築するために Pandas のようなパッケージを利用することができます。このアプローチはまた GML/JSON のような他のポピュラーな形式にも適用されます。
DGL バイナリ形式
DGL は異質グラフをバイナリ形式でセーブしてそれらをバイナリ形式からロードするためにそれぞれ dgl.save_graphs() と dgl.load_graphs() を提供します。
エッジ型サブグラフ
もしあれば特徴をコピーしながら、保持する関係を指定することにより異質グラフのサブグラフを作成することができます。
>>> g = dgl.heterograph({ ... ('drug', 'interacts', 'drug'): (th.tensor([0, 1]), th.tensor([1, 2])), ... ('drug', 'interacts', 'gene'): (th.tensor([0, 1]), th.tensor([2, 3])), ... ('drug', 'treats', 'disease'): (th.tensor([1]), th.tensor([2])) ... }) >>> g.nodes['drug'].data['hv'] = th.ones(3, 1) >>> # Retain relations ('drug', 'interacts', 'drug') and ('drug', 'treats', 'disease') >>> # All nodes for 'drug' and 'disease' will be retained >>> eg = dgl.edge_type_subgraph(g, [('drug', 'interacts', 'drug'), ... ('drug', 'treats', 'disease')]) >>> eg Graph(num_nodes={'disease': 3, 'drug': 3}, num_edges={('drug', 'interacts', 'drug'): 2, ('drug', 'treats', 'disease'): 1}, metagraph=[('drug', 'drug', 'interacts'), ('drug', 'disease', 'treats')]) >>> # The associated features will be copied as well >>> eg.nodes['drug'].data['hv'] tensor([[1.], [1.], [1.]])
異質グラフを等質グラフに変換する
ヘテログラフは異なる型のノード/エッジとそれらの関連する特徴を管理するための明瞭なインターフェイスを提供します。これは以下のときに特に有用です :
- 異なる型のノード/エッジのための特徴が異なるデータ型かサイズを持つ。
- 異なる演算を異なる型のノード/エッジに適用することを望む。
上の条件が当てはまらずそしてモデリングでノード/エッジ型を識別することを望まない場合、DGL は dgl.DGLGraph.to_homogeneous() API で異質グラフを等質グラフに変換することを可能にします。
それは次のように進みます :
- 0 から始まる連続する整数を使用して総ての方のノード/エッジを再ラベル付けする
- ユーザに指定されたノード/エッジ型に渡り特徴をマージする。
>>> g = dgl.heterograph({ ... ('drug', 'interacts', 'drug'): (th.tensor([0, 1]), th.tensor([1, 2])), ... ('drug', 'treats', 'disease'): (th.tensor([1]), th.tensor([2]))}) >>> g.nodes['drug'].data['hv'] = th.zeros(3, 1) >>> g.nodes['disease'].data['hv'] = th.ones(3, 1) >>> g.edges['interacts'].data['he'] = th.zeros(2, 1) >>> g.edges['treats'].data['he'] = th.zeros(1, 2) >>> # By default, it does not merge any features >>> hg = dgl.to_homogeneous(g) >>> 'hv' in hg.ndata False >>> # Copy edge features >>> # For feature copy, it expects features to have >>> # the same size and dtype across node/edge types >>> hg = dgl.to_homogeneous(g, edata=['he']) DGLError: Cannot concatenate column ‘he’ with shape Scheme(shape=(2,), dtype=torch.float32) and shape Scheme(shape=(1,), dtype=torch.float32) >>> # Copy node features >>> hg = dgl.to_homogeneous(g, ndata=['hv']) >>> hg.ndata['hv'] tensor([[1.], [1.], [1.], [0.], [0.], [0.]]) The original node/edge types and type-specific IDs are stored in :py:attr:`~dgl.DGLGraph.ndata` and :py:attr:`~dgl.DGLGraph.edata`.
>>> # Order of node types in the heterograph >>> g.ntypes ['disease', 'drug'] >>> # Original node types >>> hg.ndata[dgl.NTYPE] tensor([0, 0, 0, 1, 1, 1]) >>> # Original type-specific node IDs >>> hg.ndata[dgl.NID] >>> tensor([0, 1, 2, 0, 1, 2]) >>> # Order of edge types in the heterograph >>> g.etypes ['interacts', 'treats'] >>> # Original edge types >>> hg.edata[dgl.ETYPE] tensor([0, 0, 1]) >>> # Original type-specific edge IDs >>> hg.edata[dgl.EID] tensor([0, 1, 0])
モデリング目的のためには、幾つかの関係を一緒にグループ分けしてそれらに同じ演算を適用することを望むかもしれません。この必要性にこたえるため、最初にヘテログラフのエッジ型サブグラフを取りそれからサブグラフを均質グラフに変換することができます。
>>> g = dgl.heterograph({ ... ('drug', 'interacts', 'drug'): (th.tensor([0, 1]), th.tensor([1, 2])), ... ('drug', 'interacts', 'gene'): (th.tensor([0, 1]), th.tensor([2, 3])), ... ('drug', 'treats', 'disease'): (th.tensor([1]), th.tensor([2])) ... }) >>> sub_g = dgl.edge_type_subgraph(g, [('drug', 'interacts', 'drug'), ... ('drug', 'interacts', 'gene')]) >>> h_sub_g = dgl.to_homogeneous(sub_g) >>> h_sub_g Graph(num_nodes=7, num_edges=4, ...)
以上