ホーム » DGL » DGL 0.5 ユーザガイド : 4 章 グラフ・データパイプライン

DGL 0.5 ユーザガイド : 4 章 グラフ・データパイプライン

DGL 0.5ユーザガイド : 4 章 グラフ・データパイプライン (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 09/20/2020 (0.5.1)

* 本ページは、DGL の以下のドキュメントを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ユーザガイド : 4 章 グラフ・データパイプライン

DGL は dgl.data で多くの一般的に利用されるグラフ・データセットを実装しています。それらはクラス dgl.data.DGLDataset で定義された標準パイプラインに従っています。グラフデータを dgl.data.DGLDataset サブクラス内に処理することを強く勧めます、何故ならばパイプラインはグラフデータをロード、処理してセーブするための単純でクリーンな解を提供するからです。

この章は私達自身のグラフデータのために DGL-データセットをどのように作成するかを紹介します。以下の内容はパイプラインがどのように動作するかを説明し、そしてその各コンポーネントをどのように実装するかを示します。

 

DGLDataset クラス

dgl.data.DGLDataset は dgl.data で定義されたグラフデータセットを処理し、ロードしてセーブするための基底クラスです。それはグラフデータを処理するために基本的なパイプラインを実装します。下のフローチャートはパイプラインがどのように動作するかを示します。

遠隔サーバかローカルディスクにあるグラフ・データセットを処理するため、dgl.data.DGLDataset から継承したクラス、例えば MyDataset を定義します。MyDataset のテンプレートは次のようなものです。

クラス DGLDataset で定義されたグラフデータ入力パイプラインのためのフローチャート。

from dgl.data import DGLDataset

class MyDataset(DGLDataset):
    """ Template for customizing graph datasets in DGL.

    Parameters
    ----------
    url : str
        URL to download the raw dataset
    raw_dir : str
        Specifying the directory that will store the
        downloaded data or the directory that
        already stores the input data.
        Default: ~/.dgl/
    save_dir : str
        Directory to save the processed dataset.
        Default: the value of `raw_dir`
    force_reload : bool
        Whether to reload the dataset. Default: False
    verbose : bool
        Whether to print out progress information
    """
    def __init__(self,
                 url=None,
                 raw_dir=None,
                 save_dir=None,
                 force_reload=False,
                 verbose=False):
        super(MyDataset, self).__init__(name='dataset_name',
                                        url=url,
                                        raw_dir=raw_dir,
                                        save_dir=save_dir,
                                        force_reload=force_reload,
                                        verbose=verbose)

    def download(self):
        # download raw data to local disk
        pass

    def process(self):
        # process raw data to graphs, labels, splitting masks
        pass

    def __getitem__(self, idx):
        # get one example by index
        pass

    def __len__(self):
        # number of data examples
        pass

    def save(self):
        # save processed data to directory `self.save_path`
        pass

    def load(self):
        # load processed data from directory `self.save_path`
        pass

    def has_cache(self):
        # check whether there are processed data in `self.save_path`
        pass

dgl.data.DGLDataset クラスは抽象関数 process(), __getitem__(idx) と __len__() を持ちます、これらはサブクラスで実装されなければなりません。しかしセーブとロードを実装することも勧めます、何故ならばそれらは巨大なデータセットを処理するための多大な時間をセーブできるからです、そしてそれを容易にする幾つかの API があります (Save and load data 参照)。

dgl.data.DGLDataset の目的はグラフデータをロードするための標準的で便利な方法を提供することです。グラフ、特徴、ラベル、マスクとクラス数、ラベル数等のようなデータセットについての基本的な情報をストアできます。サンプリング、分割や特徴正規化のような演算は dgl.data.DGLDataset の外側で成されます。

この章の残りはパイプラインの関数を実装するためのベストプラクティスを示します。

 

raw データをダウンロードする (オプション)

データセットが既にローカルディスクにあるならば、それがディレクトリ raw_dir にあることを確実にしてください。データをダウンロードして正しいディレクトリに移動する手間なしにコードをどこでも実行することを望むのであれば、関数 download() を実装することにより自動的にそれを成すことができます。

データセットが zip ファイルであれば、MyDataset を dgl.data.DGLBuiltinDataset クラスから継承させます、これは zip file 展開を処理します。さもなければ、dgl.data.QM7bDataset でのように download() を実装します :

import os
from dgl.data.utils import download

def download(self):
    # path to store the file
    file_path = os.path.join(self.raw_dir, self.name + '.mat')
    # download file
    download(self.url, path=file_path)

上のコードは .mat ファイルをディレクトリ self.raw_dir にダウンロードします。ファイルが .gz, .tar, .tar.gz か .tgz file であれば、展開するために dgl.data.utils.extract_archive() 関数を使用します。次のコードは dgl.data.BitcoinOTCDataset で .gz ファイルをどのようにダウンロードするかを示します :

from dgl.data.utils import download, extract_archive

def download(self):
    # path to store the file
    # make sure to use the same suffix as the original file name's
    gz_file_path = os.path.join(self.raw_dir, self.name + '.csv.gz')
    # download file
    download(self.url, path=gz_file_path)
    # check SHA-1
    if not check_sha1(gz_file_path, self._sha1_str):
        raise UserWarning('File {} is downloaded but the content hash does not match.'
                          'The repo may be outdated or download may be incomplete. '
                          'Otherwise you can create an issue for it.'.format(self.name + '.csv.gz'))
    # extract file to directory `self.name` under `self.raw_dir`
    self._extract_gz(gz_file_path, self.raw_path)

上のコードはファイルを self.raw_dir 下のディレクトリ self.name に展開します。クラスが zip ファイルを扱うために dgl.data.DGLBuiltinDataset から継承しているのであれば、それはファイルをディレクトリ self.name にまた展開します。

オプションで、上のサンプルが行なっているようにダウンロードされたファイルの SHA-1 文字列を確認できます、作者が遠隔サーバのファイルをいつの日にか変更した場合に。

 

データを処理する

関数 process() でデータ処理コードを実装します、そして raw データが既に self.raw_dir にあることを仮定します。グラフ上の機械学習では典型的には 3 つのタイプのタスクがあります : グラフ分類ノード分類、そして リンク予測 です。これらのタスクに関連するデータセットをどのように処理するかを示します。

ここではグラフ、特徴とマスクを処理する標準的な方法にフォーカスします。サンプルとして組込みデータセットを使用してファイルからグラフを構築するための実装はスキップしますが、詳細な実装へのリンクを追加します。外部ソースからどのようにグラフを構築するかの完全なガイドを見るには 1.4 外部ソースからグラフを作成する を参照してください。

 

グラフ分類データセットを処理する

グラフ分類データセットは典型的な機械学習タスクの大半のデータセットとと殆ど同じで、そこではミニバッチ訓練が利用されます。そして raw データを dgl.DGLGraph オブジェクトのリストとラベル tensor のリストに処理します。加えて、raw データが幾つかのファイルに分割されている場合、データの特定のパートをロードするためにパラメータ split を追加できます。

サンプルとして dgl.data.QM7bDataset を取ります :

class QM7bDataset(DGLDataset):
    _url = 'http://deepchem.io.s3-website-us-west-1.amazonaws.com/' \
           'datasets/qm7b.mat'
    _sha1_str = '4102c744bb9d6fd7b40ac67a300e49cd87e28392'

    def __init__(self, raw_dir=None, force_reload=False, verbose=False):
        super(QM7bDataset, self).__init__(name='qm7b',
                                          url=self._url,
                                          raw_dir=raw_dir,
                                          force_reload=force_reload,
                                          verbose=verbose)

    def process(self):
        mat_path = self.raw_path + '.mat'
        # process data to a list of graphs and a list of labels
        self.graphs, self.label = self._load_graph(mat_path)

    def __getitem__(self, idx):
        """ Get graph and label by index

        Parameters
        ----------
        idx : int
            Item index

        Returns
        -------
        (dgl.DGLGraph, Tensor)
        """
        return self.graphs[idx], self.label[idx]

    def __len__(self):
        """Number of graphs in the dataset"""
        return len(self.graphs)

process() では、raw データはグラフのリストとラベルのリストに処理されます。反復のために __getitem__(idx) と __len__() を実装しなければなりません。__getitem__(idx) は上のようにタプル (graph, label) を返すようにすることを勧めます。self._load_graph() and __getitem__ の詳細については QM7bDataset ソースコード を確認してください。

データセットの幾つかの有用な情報を示すためにクラスにプロパティを追加することもできます。dgl.data.QM7bDataset では、このマルチタスク・データセットで予測タスクの総数示すためにプロパティ num_labels を追加できます :

@property
def num_labels(self):
    """Number of labels for each graph, i.e. number of prediction tasks."""
    return 14

これら総てのコーディングの後、最後に次のように dgl.data.QM7bDataset を使用できます :

from torch.utils.data import DataLoader

# load data
dataset = QM7bDataset()
num_labels = dataset.num_labels

# create collate_fn
def _collate_fn(batch):
    graphs, labels = batch
    g = dgl.batch(graphs)
    labels = torch.tensor(labels, dtype=torch.long)
    return g, labels

# create dataloaders
dataloader = DataLoader(dataset, batch_size=1, shuffle=True, collate_fn=_collate_fn)

# training
for epoch in range(100):
    for g, labels in dataloader:
        # your training code here
        pass

グラフ分類モデルを訓練するための完全なガイドは 5.4 Graph Classification で見つけられます。

グラフ分類データセットのより多くのサンプルについては、組込みグラフ分類データセットを参照してください :

 

ノード分類データセットを処理する

グラフ分類とは異なり、ノード分類は典型的には単一グラフ上です。そのようなものとして、データセットの分割はグラフのノード上です。分割を指定するためにノードマスクを使用することを勧めます。サンプルとして組込みデータセット CitationGraphDataset を使用します :

import dgl
from dgl.data import DGLBuiltinDataset

class CitationGraphDataset(DGLBuiltinDataset):
    _urls = {
        'cora_v2' : 'dataset/cora_v2.zip',
        'citeseer' : 'dataset/citeseer.zip',
        'pubmed' : 'dataset/pubmed.zip',
    }

    def __init__(self, name, raw_dir=None, force_reload=False, verbose=True):
        assert name.lower() in ['cora', 'citeseer', 'pubmed']
        if name.lower() == 'cora':
            name = 'cora_v2'
        url = _get_dgl_url(self._urls[name])
        super(CitationGraphDataset, self).__init__(name,
                                                   url=url,
                                                   raw_dir=raw_dir,
                                                   force_reload=force_reload,
                                                   verbose=verbose)

    def process(self):
        # Skip some processing code
        # === data processing skipped ===

        # build graph
        g = dgl.graph(graph)
        # splitting masks
        g.ndata['train_mask'] = generate_mask_tensor(train_mask)
        g.ndata['val_mask'] = generate_mask_tensor(val_mask)
        g.ndata['test_mask'] = generate_mask_tensor(test_mask)
        # node labels
        g.ndata['label'] = F.tensor(labels)
        # node features
        g.ndata['feat'] = F.tensor(_preprocess_features(features),
                                   dtype=F.data_type_dict['float32'])
        self._num_labels = onehot_labels.shape[1]
        self._labels = labels
        self._g = g

    def __getitem__(self, idx):
        assert idx == 0, "This dataset has only one graph"
        return self._g

    def __len__(self):
        return 1

簡潔さのため、ノード分類データセットを処理するための主要パートをハイライトするために process() のあるコードはスキップします : マスク、ノード特徴とノードラベルの分割は g.ndata にストアされます。詳細な実装については、CitationGraphDataset ソースコード を参照してください。

__getitem__(idx) と __len__() の実装もまた変えられたことに気付いてください、ノード分類タスクのためにはしばしば一つのグラフだけがあるためです。マスクは PyTorch と TensorFlow では bool tensor で、MXNet では float tensor です。

その使用方法を示すため、CitationGraphDataset, dgl.data.CiteseerGraphDataset のサブクラスを使用します。

# load data
dataset = CiteseerGraphDataset(raw_dir='')
graph = dataset[0]

# get split masks
train_mask = graph.ndata['train_mask']
val_mask = graph.ndata['val_mask']
test_mask = graph.ndata['test_mask']

# get node features
feats = graph.ndata['feat']

# get labels
labels = graph.ndata['label']

ノード分類モデルを訓練するための完全なガイドは 5.1 Node Classification/Regression で見つけられます。

ノード分類データセットのより多くのサンプルについては、組込みデータセットを参照してください :

 

リンク予測データセットのためのデータセットを処理する

リンク予測データセットの処理はノード分類のそれに類似していて、データセットにしばしば一つのグラフがあります。

サンプルとして組込みデータセット KnowledgeGraphDataset を使用して、そしてまたリンク予測データセットを処理するために主要パートをハイライトするために詳細なデータ処理コードはスキップします :

# Example for creating Link Prediction datasets
class KnowledgeGraphDataset(DGLBuiltinDataset):
    def __init__(self, name, reverse=True, raw_dir=None, force_reload=False, verbose=True):
        self._name = name
        self.reverse = reverse
        url = _get_dgl_url('dataset/') + '{}.tgz'.format(name)
        super(KnowledgeGraphDataset, self).__init__(name,
                                                    url=url,
                                                    raw_dir=raw_dir,
                                                    force_reload=force_reload,
                                                    verbose=verbose)

    def process(self):
        # Skip some processing code
        # === data processing skipped ===

        # splitting mask
        g.edata['train_mask'] = train_mask
        g.edata['val_mask'] = val_mask
        g.edata['test_mask'] = test_mask
        # edge type
        g.edata['etype'] = etype
        # node type
        g.ndata['ntype'] = ntype
        self._g = g

    def __getitem__(self, idx):
        assert idx == 0, "This dataset has only one graph"
        return self._g

    def __len__(self):
        return 1

コードで示されるように、splitting マスクをグラフの edata フィールドに追加します。完全なコードを見るためには KnowledgeGraphDataset ソースコード を確認してください。その使用方法を示すために KnowledgeGraphDataset, dgl.data.FB15k237Dataset のサブクラスを使用します :

import torch

# load data
dataset = FB15k237Dataset()
graph = dataset[0]

# get training mask
train_mask = graph.edata['train_mask']
train_idx = torch.nonzero(train_mask).squeeze()
src, dst = graph.edges(train_idx)
# get edge types in training set
rel = graph.edata['etype'][train_idx]

リンク予測モデルを訓練するための完全なガイドは 5.3 リンク予測 で見つけられます。

リンク予測データセットのより多くのサンプルについては、組込みデータセットを参照してください :

 

データをセーブしてロードする

処理されたデータをローカルディスクにキャッシュするためにセーブとロード関数を実装することを勧めます。これは殆どの場合多くのデータ処理時間を節約します。物事を単純にするため 4 つの関数を提供します :

次のサンプルはグラフのリストとデータセット情報をどのようにセーブしてロードするかを示します。

import os
from dgl import save_graphs, load_graphs
from dgl.data.utils import makedirs, save_info, load_info

def save(self):
    # save graphs and labels
    graph_path = os.path.join(self.save_path, self.mode + '_dgl_graph.bin')
    save_graphs(graph_path, self.graphs, {'labels': self.labels})
    # save other information in python dict
    info_path = os.path.join(self.save_path, self.mode + '_info.pkl')
    save_info(info_path, {'num_classes': self.num_classes})

def load(self):
    # load processed data from directory `self.save_path`
    graph_path = os.path.join(self.save_path, self.mode + '_dgl_graph.bin')
    self.graphs, label_dict = load_graphs(graph_path)
    self.labels = label_dict['labels']
    info_path = os.path.join(self.save_path, self.mode + '_info.pkl')
    self.num_classes = load_info(info_path)['num_classes']

def has_cache(self):
    # check whether there are processed data in `self.save_path`
    graph_path = os.path.join(self.save_path, self.mode + '_dgl_graph.bin')
    info_path = os.path.join(self.save_path, self.mode + '_info.pkl')
    return os.path.exists(graph_path) and os.path.exists(info_path)

処理されたデータをセーブするに適さないケースがあることに注意してください。例えば、組込みデータセット dgl.data.GDELTDataset では、処理されたデータは非常に巨大ですので、__getitem__(idx) で各データサンプルを処理することがより効果的です。

 

ogb パッケージを使用して OGB データセットをロードする

Open グラフ・ベンチマーク (OGB) はベンチマーク・データセットのコレクションです。公式 OGB パッケージ ogb は OGB データセットを dgl.data.DGLGraph オブジェクトにダウンロードして処理するための API を提供します。ここではそれらの基本的な使用方法を紹介します。

最初に pip を使用して ogb パッケージをインストールします :

pip install ogb

次のコードはグラフ特性 (= property) 予測タスクのためのデータセットをどのようにロードするかを示します。

# Load Graph Property Prediction datasets in OGB
import dgl
import torch
from ogb.graphproppred import DglGraphPropPredDataset
from torch.utils.data import DataLoader


def _collate_fn(batch):
    # batch is a list of tuple (graph, label)
    graphs = [e[0] for e in batch]
    g = dgl.batch(graphs)
    labels = [e[1] for e in batch]
    labels = torch.stack(labels, 0)
    return g, labels

# load dataset
dataset = DglGraphPropPredDataset(name='ogbg-molhiv')
split_idx = dataset.get_idx_split()
# dataloader
train_loader = DataLoader(dataset[split_idx["train"]], batch_size=32, shuffle=True, collate_fn=_collate_fn)
valid_loader = DataLoader(dataset[split_idx["valid"]], batch_size=32, shuffle=False, collate_fn=_collate_fn)
test_loader = DataLoader(dataset[split_idx["test"]], batch_size=32, shuffle=False, collate_fn=_collate_fn)

グラフ特性予測データセットをロードすることも同様ですが、この種類のデータセットには一つのグラフオブジェクトだけがあることに注意してください。

# Load Node Property Prediction datasets in OGB
from ogb.nodeproppred import DglNodePropPredDataset

dataset = DglNodePropPredDataset(name='ogbn-proteins')
split_idx = dataset.get_idx_split()

# there is only one graph in Node Property Prediction datasets
g, labels = dataset[0]
# get split labels
train_label = dataset.labels[split_idx['train']]
valid_label = dataset.labels[split_idx['valid']]
test_label = dataset.labels[split_idx['test']]

リンク特性予測データセットもまたデータセット毎に一つのグラフを含みます :

# Load Link Property Prediction datasets in OGB
from ogb.linkproppred import DglLinkPropPredDataset

dataset = DglLinkPropPredDataset(name='ogbl-ppa')
split_edge = dataset.get_edge_split()

graph = dataset[0]
print(split_edge['train'].keys())
print(split_edge['valid'].keys())
print(split_edge['test'].keys())
 

以上






AI導入支援 #2 ウェビナー

スモールスタートを可能としたAI導入支援   Vol.2
[無料 WEB セミナー] [詳細]
「画像認識 AI PoC スターターパック」の紹介
既に AI 技術を実ビジネスで活用し、成果を上げている日本企業も多く存在しており、競争優位なビジネスを展開しております。
しかしながら AI を導入したくとも PoC (概念実証) だけでも高額な費用がかかり取組めていない企業も少なくないようです。A I導入時には欠かせない PoC を手軽にしかも短期間で認知度を確認可能とするサービの紹介と共に、AI 技術の特性と具体的な導入プロセスに加え運用時のポイントについても解説いたします。
日時:2021年10月13日(水)
会場:WEBセミナー
共催:クラスキャット、日本FLOW(株)
後援:働き方改革推進コンソーシアム
参加費: 無料 (事前登録制)
人工知能開発支援
◆ クラスキャットは 人工知能研究開発支援 サービスを提供しています :
  • テクニカルコンサルティングサービス
  • 実証実験 (プロトタイプ構築)
  • アプリケーションへの実装
  • 人工知能研修サービス
◆ お問合せ先 ◆
(株)クラスキャット
セールス・インフォメーション
E-Mail:sales-info@classcat.com