機械学習 PR

PyTorch GeometricでGraph Neural Network(GNN)入門

グラフ
記事内に商品プロモーションを含む場合があります

こんにちは。

現役エンジニアの”はやぶさ”@Cpp_Learningです。仕事でもプライベートでも機械学習で色々やってます。

今回はGraph Neural Network(以下 GNN)について勉強したので、備忘録も兼ねて本記事を書きます。

データ構造とグラフ

ここでいうグラフとは折れ線グラフや棒グラフではなく、下図のようなデータ構造のことをグラフと呼びます。

Mini-Graph-Classification-Dataset

引用元:Deep Graph Library Blog(2019/01/25)

例えば、以下に示すネットワークや分子構造、3Dデータなどもグラフといえます。

  • インターネット
  • Web
  • 交通網
  • SNS(FacebookやTwitterなど)
  • 分子構造(化合物など)
  • 神経回路
  • 3Dデータのポリゴン

データとデータが繋がったものという意味で、画像(隣り合うピクセル)やテキスト(文字列)もグラフとして表現できます。

画像とテキストのグラフ表現

引用元:機は熟した!グラフ構造に対するDeep Learning、Graph Convolutionのご紹介

グラフとは -ノードとエッジなどの用語について-

SNSや分子構造などの様々なデータが下図のようなグラフで表現できます。

グラフ

グラフの用語について簡単に整理したものが以下です。

グラフ関連の用語
  • ノード(node):データ
  • エッジ(edge):ノード間の関係、相互作用
  • 有向エッジ:向きがあるエッジ
  • 無向エッジ:向きがないエッジ
  • 有向グラフ:有向エッジがあるグラフ
  • 無向グラフ:有向エッジがないグラフ
  • ループ(loop):エッジが1つのノードにしか繋がっていないもの
  • 孤立ノード(isolation):ノードが孤立しているもの
  • 属性:各ノードや各エッジがもつパラメータ(≒特徴量)
  • ノードの位置情報:あり/なし
  • ラベル:あり/なし ※
  • 重み:あり/なし

※上図だと色でラベルを表現

例えば、SNSの場合は以下の通りです。

  • ノード(node):人(アカウント名)
  • エッジ(edge):フォロー関係(相互フォローなど)
  • ノード属性:アカウント情報(生年月日など)
スポンサーリンク

問題設定 -グラフと深層学習(GNN)-

グラフと深層学習(GNN)の活用により、解決できる問題(タスク)の例を紹介します。

グラフ分類

化合物の場合、グラフから毒性の有無などを分類することができます。

グラフ分類

深層学習によるグラフ分類ができれば、未知の化合物に対し、毒性の有無などを予測できるようになります。

ノード分類

赤チーム青チームで対戦するゲームの各キャラクタ相関図(グラフ)が下図だとします(所属が不明なノードはグレーで表現)。

交友関係のグラフ

同じチーム内でも交友の有無があり、敵同士でもライバル関係などのエッジが存在します。

各ノードとエッジの情報から、グレーの所属(ラベル)を分類できれば、分類結果を考慮した戦略を検討できます(情報戦)。

このような各ノードのラベルを予測するノード分類にも深層学習が活用できます。

リンク予測

Amazonなどの購入履歴をグラフで表現したものが下図だとします。

Amazonの購入履歴とグラフ
  • カバン👜を購入する人は、革靴👞も一緒に購入する
  • 革靴👞のページをn日間でm回以上閲覧している人は購入する

など

もし上記の関係性を予測できるなら、潜在的な顧客の発見に繋がります。このような関係性の予測(リンク予測)にも深層学習を活用できます。

本記事では分類タスクのみを紹介しましたが、回帰タスクもあります。

Graph Neural Network(GNN)とは

グラフの概要や深層学習(GNN)で解決できる問題(タスク)について説明したので、今度はGNN(主にGCN)のアルゴリズムについて説明します。

GNN・GCNについて

よくわかる Gated Graph Neural Network (GGNN)の記事で紹介している以下の動画がとても分かりやすいです。

この動画でイメージを掴んだら、次はGraph Convolutional Network(GCN)について学びます。以下の2つスライドがオススメです。

 

 

最後に数式の意味を深く学びたい人のために、以下の記事をオススメしておきます。

はやぶさ
はやぶさ
どの資料も大変勉強になりました!ありがとうございます。

Graph Neural Network(GNN)実践

各資料でGNNのアルゴリズムを勉強したので、次は実践します!

Graph Neural Network(GNN)ライブラリ

どのGNNライブラリを使うか迷ったので、Twitterのアンケート機能を使ってみました。

アンケートにご回答いただき、ありがとうございました。ユーザーの多い PyTorch Geometric を使ってGNNを実践します。

インストール

メインで使用するライブラリは以下の通りです。

Requirements
  • PyTorch Geometric  1.4.3
  • PyTorch 1.4.0
  • CUDA  10.1

※Google Colaboratoryで動作確認しました(2020/04/18)

まずはGoogle Colaboratoryにプリインストールしてあるライブラリのバージョンを確認します。

PyTorch == 1.4.0
CUDA available True
CUDA == 10.1

CUDAについては以下のコマンドでも確認できます。

nvcc: NVIDIA (R) Cuda compiler driver Copyright (c) 2005-2018 NVIDIA Corporation Built on Sat_Aug_25_21:08:01_CDT_2018 Cuda compilation tools, release 10.0, V10.0.130

※CUDAのバージョンに注意!

PyTorch Geometric|公式のインストール手順だと失敗するので、解決策を参考に以下のコマンドでインストールします。

以上でPyTorch Geometricを使う環境が準備できました。以降からソースコードを書いてきます。

PyTorch Geometricの基本的な使い方 -グラフ作成-

まずは下図の有向グラフ(ノードに属性とラベルあり)を作成してみます。

グラフ

PyTorch Geometricを使えば、以下のコードで簡単にグラフを作成できます。

edge_indexでエッジを定義します。今回の場合「0⇒1, 1⇒2, 2⇒1」とメッセージを伝播させたいので、コードの4~9行目で設定しています。

各ノードの属性(特徴量)とラベルはtorch.tensorで定義します(コード 15行目、21行目)。

最後にDataモジュールでグラフを作成します(コード 23行目)。

慣れてくれば、以下のような短いコードでグラフを作成できます。

今回は各ノードの特徴量とラベルを定義してますが、edge_indexだけでもグラフを生成できます

グラフ情報の確認

作成したグラフを以下の自作関数で確認します。

グラフ構造: Data(edge_index=[2, 3], x=[3, 2], y=[3, 1])
グラフのキー: [‘x’, ‘edge_index’, ‘y’]
ノード数: 3
エッジ数: 3
ノードの特徴量数: 2
孤立したノードの有無: False
自己ループの有無: False
====== ノードの特徴量:x ======
tensor([[1., 2.], [3., 4.], [5., 6.]])
====== ノードのクラス:y ======
tensor([[1.], [0.], [1.]])
======== エッジ形状 ========
tensor([[0, 1, 2], [1, 2, 1]])

ちゃんと下図のグラフを作成できてますね。

グラフ

Graph Convolutional Network(GCN)実践

PyTorch Geometricによるグラフの扱い方が分かったので、今度はGCNによるノード分類を実践します。

Import

最初はimportから

データセットのダウンロード

自作グラフのデータセットを作成しても良いのですが、今回はGNN界隈のMNISTに相当する空手クラブのデータセットを使います。

グラフ数: 1
クラス数: 2
グラフ構造: Data(edge_index=[2, 156], x=[34, 34], y=[34])
グラフのキー: [‘x’, ‘edge_index’, ‘y’]
ノード数: 34
エッジ数: 156
ノードの特徴量数: 34
孤立したノードの有無: False
自己ループの有無: False
====== ノードの特徴量:x ======
tensor([[1., 0., 0., …, 0., 0., 0.],
[0., 1., 0., …, 0., 0., 0.],
[0., 0., 1., …, 0., 0., 0.],
…,
[0., 0., 0., …, 1., 0., 0.],
[0., 0., 0., …, 0., 1., 0.],
[0., 0., 0., …, 0., 0., 1.]])
==== ノードのクラス:y ====
tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
========= エッジ形状 =========
tensor([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 10, 10, 10, 11, 12, 12, 13, 13, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 23, 23, 23, 24, 24, 24, 25, 25, 25, 26, 26, 27, 27, 27, 27, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33],
[ 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 17, 19, 21, 31, 0, 2, 3, 7, 13, 17, 19, 21, 30, 0, 1, 3, 7, 8, 9, 13, 27, 28, 32, 0, 1, 2, 7, 12, 13, 0, 6, 10, 0, 6, 10, 16, 0, 4, 5, 16, 0, 1, 2, 3, 0, 2, 30, 32, 33, 2, 33, 0, 4, 5, 0, 0, 3, 0, 1, 2, 3, 33, 32, 33, 32, 33, 5, 6, 0, 1, 32, 33, 0, 1, 33, 32, 33, 0, 1, 32, 33, 25, 27, 29, 32, 33, 25, 27, 31, 23, 24, 31, 29, 33, 2, 23, 24, 33, 2, 31, 33, 23, 26, 32, 33, 1, 8, 32, 33, 0, 24, 25, 28, 32, 33, 2, 8, 14, 15, 18, 20, 22, 23, 29, 30, 31, 33, 8, 9, 13, 14, 15, 18, 19, 20, 22, 23, 26, 27, 28, 29, 30, 31, 32]])

空手クラブの情報を簡単に整理すると以下の通りです。

  • 会員数(ノード数):34
  • 特徴量x:ノード番号(会員ナンバー)をone-hotベクトルで定義
  • ラベルy:”0”または”1″の2クラス(2グループ)
  • 交友関係(エッジ数):156
  • グラフの種類:無向グラフ※

※各ノード間を有効エッジで相互に接続(0⇒1, 1⇒0など)することで無向グラフとして表現することがあります

グラフ構造の可視化

PyTorch Geometricにはグラフ構造を可視化する関数がないため、NetworkXを使って可視化します(ここのコードここのコードを参考にしました)。

空手クラブ

可視化によりリーダー格のノード”0”とノード”33”を中心とした2グループの存在が明確になりました。

またノード”8”やノード”9”は両グループと交友関係があることも分かります(伏線)。

この空手クラブのデータセットを使ってノード分類を実践します。

モデル設計

PyTorch Geometricは GCNConv などの メジャーなLayers をサポートしているので、ピュアPyTorchと同じ感覚でモデルを設計できます。

ピュアPyTorchと同じコードが使えます

各種設定

以下のコードで各種の設定を行います。

学習

入力データ(入力するグラフ)、オプティマイザ設定、学習ループのコードは以下の通りです。

Epoch 0 | Loss: 0.6980
Epoch 1 | Loss: 0.6863
Epoch 2 | Loss: 0.6748

Epoch 97 | Loss: 0.0563
Epoch 98 | Loss: 0.0559
Epoch 99 | Loss: 0.0555

推論

以下のコードで推論します。

結果: tensor([0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
真値: tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])

ノード”8”を誤分類していることが分かります。

ノード”8”は両グループのリーダーと交流があるため、ラベル(所属)の判断が難しかったと考えられます

テストデータ作成

学習に未使用のテストデータでも推論してみます。空手クラブのデータセットを活用し、交友関係(リンク)を一部変更したグラフを作成します。

変更前後のグラフは以下の通りです。

変更前
空手クラブ
変更後
空手クラブ
変更点

 

  • ノード”2”とノード”33”のリンクを接続
  • ノード”8”とノード”0”のリンクを切る
  • ノード”9”とノード”33”のリンクを切る
  • ノード”9”とノード”0”のリンクを接続

など

要するにリーダとの交友関係の有無(リンク変更)により、モデルのラベル予測(ノード分類結果)がどう変わるかを検証するために、グラフに色々と手を加えました。

  • モデルがリーダとの交友関係を考慮して、ノード分類をしているか検証
  • GNNとモデルの説明性に興味津々

リンク変更によるノード分類結果について

リンク変更後のグラフに対し、学習済みモデルによるノード分類(ラベル予測)を行います。

======== リンク変更前のラベル ========
tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
==== リンク変更後のラベル予測結果 ====
tensor([0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])

ノード分類結果を反映したグラフが下図です。

リンク変更前
空手クラブ
リンク変更後
空手クラブ

つまり今回のモデルは、以下の予測傾向があると考えられます。

モデルの予測傾向
  • 交流のあるリーダと同じグループだと予測
  • 両リーダーと交流がある場合、ノード”0”と同じグループと予測

以上 簡単な検証ですが、リンク変更による検証からGNNのモデル説明性を深堀りできそうです。

  • GNN(GCN)により、ノード分類(ラベル予測)ができる
  • リンク変更によりモデルのノード分類結果が変化する
  • 結果の変化からモデルの予測傾向を把握できそう(モデル説明性)
  • 分類結果に大きく影響を与える重要なノードを把握できそう(潜在的なリーダの予測に活用できそう)
スポンサーリンク

まとめ

Graph Neural Network(GNN)の概要からPyTorch Geometricを使ったノード分類の実践などについて説明しました。

GNN入門したい!

と考えている人の参考になれば嬉しいです。

また私自身もGNN入門したばかりなので、本記事の内容について間違っている箇所があれば、教えて頂けると嬉しいです。

はやぶさ
はやぶさ
GNNに関する情報共有をしながら、一緒に成長できると嬉しいです。よろしくお願いします。

PICK UP BOOKS

  • 数理モデル入門
    数理モデル
  • Jetoson Nano 超入門
    Jetoson Nano
  • 図解速習DEEP LEARNING
    DEEP LEARNING
  • Pythonによる因果分析
    Python