質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.34%

GCNN実装にあたってのグラフとラベル

受付中

回答 0

投稿

  • 評価
  • クリップ 0
  • VIEW 534

score 27

実装したい内容

dglライブラリを用いて、
グラフ構造の畳み込みニューラルネットワークを実装したいです。
2種類のグラフ(circle,star)
をランダムに生成して、2クラス分類をするものを作成する予定です。

モデルの定義と訓練は以下のようになります。
(pytorchを用いて実装しています。)

import dgl
import torch

def collate(samples):
    # The input `samples` is a list of pairs
    #  (graph, label).
    graphs, labels = map(list, zip(*samples))
    batched_graph = dgl.batch(graphs)
    return batched_graph, torch.tensor(labels)

from dgl.nn.pytorch import GraphConv

import torch.nn as nn
import torch.nn.functional as F

#モデルの定義
class Classifier(nn.Module):
    def __init__(self, in_dim, hidden_dim, n_classes):
        super(Classifier, self).__init__()
        self.conv1 = GraphConv(in_dim, hidden_dim)
        self.conv2 = GraphConv(hidden_dim, hidden_dim)
        self.classify = nn.Linear(hidden_dim, n_classes)
        #self.conv1,self.conv2をconvolution層として表現
        #nn.Linearで全結合層として表現


    #グラフの畳み込みと活性化関数
    #順伝播
    def forward(self, g):
        # Use node degree as the initial node feature. For undirected graphs, the in-degree
        # is the same as the out_degree.
        h = g.in_degrees().view(-1, 1).float()
        # Perform graph convolution and activation function.
        h = F.relu(self.conv1(g, h))
        h = F.relu(self.conv2(g, h))
        g.ndata['h'] = h
        # Calculate graph representation by averaging all the node representations.
        hg = dgl.mean_nodes(g, 'h')
        return self.classify(hg)

import torch.optim as optim
from torch.utils.data import DataLoader

#320個のトレーニングセットと、80個の訓練セットを作成する。
trainset = testclusternotest.MiniGCDatasetnotest(320, 10, 20)
testset = testclusternotest.MiniGCDatasetnotest(80, 10, 20)
# Use PyTorch's DataLoader and the collate function
# defined before.

data_loader = DataLoader(trainset, batch_size=32, shuffle=True,
                         collate_fn=collate)


# modelを作成
model = Classifier(1, 256, trainset.num_classes)
loss_func = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

model.train()

epoch_losses = []

#80回同じ操作を繰り返す。
for epoch in range(80):
    epoch_loss = 0
    for iter, (bg, label) in enumerate(data_loader):
        prediction = model(bg)
        loss = loss_func(prediction, label)
        #勾配のリセット
        optimizer.zero_grad()
        #逆伝播の更新
        loss.backward()
        #重みの更新
        optimizer.step()
        epoch_loss += loss.detach().item()
    # epoch_loss = epoch_loss/(iter+1)
    epoch_loss /= (iter + 1)
    print('Epoch {}, loss {:.4f}'.format(epoch, epoch_loss))
    epoch_losses.append(epoch_loss)

使用したデータセット

以下のようなMiniGCDatasetと呼ばれるDGLライブラリ付属のデータセットを改変いたしました。元は8クラス分類でしたが、2クラスに書き換えました。

元のデータセットは、以下のURLからご覧ください。
https://docs.dgl.ai/en/0.4.x/_modules/dgl/data/minigc.html#MiniGCDataset

"""作成したデータセット"""
import math, os
import networkx as nx
import numpy as np

from .dgl_dataset import DGLDataset
from .utils import save_graphs, load_graphs, makedirs
from .. import backend as F
from ..convert import from_networkx
from ..transform import add_self_loop

__all__ = ['MiniGCDatasetnotest']

class MiniGCDatasetnotest(DGLDataset):

    def __init__(self, num_graphs, min_num_v, max_num_v, seed=0,
                 save_graph=True, force_reload=False, verbose=False):
        self.num_graphs = num_graphs
        self.min_num_v = min_num_v
        self.max_num_v = max_num_v
        self.seed = seed
        self.save_graph = save_graph
        super(MiniGCDatasetnotest, self).__init__(name="testclusternotest", hash_key=(num_graphs, min_num_v, max_num_v, seed),
                                            force_reload=force_reload,
                                            verbose=verbose)

    def process(self):
        self.graphs = []
        self.labels = []
        self._generate(self.seed)

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

    def __getitem__(self, idx):
        """Get the idx-th sample.

        Parameters
        ---------
        idx : int
            The sample index.

        Returns
        -------
        (:class:`dgl.Graph`, Tensor)
            The graph and its label.
        """
        return self.graphs[idx], self.labels[idx]

    def has_cache(self):
        graph_path = os.path.join(self.save_path, 'dgl_graph_{}.bin'.format(self.hash))
        if os.path.exists(graph_path):
            return True

        return False

    def save(self):
        """save the graph list and the labels"""
        if self.save_graph:
            graph_path = os.path.join(self.save_path, 'dgl_graph_{}.bin'.format(self.hash))
            save_graphs(str(graph_path), self.graphs, {'labels': self.labels})

    def load(self):
        graphs, label_dict = load_graphs(os.path.join(self.save_path, 'dgl_graph_{}.bin'.format(self.hash)))
        self.graphs = graphs
        self.labels = label_dict['labels']

    @property
    def num_classes(self):
        """Number of classes."""
        return 2

    def _generate(self, seed):
        if seed is not None:
            np.random.seed(seed)
        self._gen_cycle(self.num_graphs // 2)
        self._gen_star(self.num_graphs // 2)

        # preprocess
        for i in range(self.num_graphs):
            # convert to DGLGraph, and add self loops
            self.graphs[i] = add_self_loop(from_networkx(self.graphs[i]))
        self.labels = F.tensor(np.array(self.labels).astype(np.int))

    def _gen_cycle(self, n):
        for _ in range(n):
            num_v = np.random.randint(self.min_num_v, self.max_num_v)
            g = nx.cycle_graph(num_v)
            self.graphs.append(g)
            self.labels.append(0)

    def _gen_star(self, n):
        for _ in range(n):
            num_v = np.random.randint(self.min_num_v, self.max_num_v)
            # nx.star_graph(N) gives a star graph with N+1 nodes
            g = nx.star_graph(num_v - 1)
            self.graphs.append(g)
            self.labels.append(1)

発生したエラー

Index Error: Target 5 is out of bounds.が出力されました。

IndexError                                Traceback (most recent call last)
<ipython-input-15-dfdf585f0b2f> in <module>
     36     for iter, (bg, label) in enumerate(data_loader):
     37         prediction = model(bg)
---> 38         loss = loss_func(prediction, label)
     39         #勾配のリセット
     40         optimizer.zero_grad()

~/anaconda/envs/pytorch/lib/python3.6/site-packages/torch/nn/functional.py in nll_loss(input, target, weight, size_average, ignore_index, reduce, reduction)
   2216                          .format(input.size(0), target.size(0)))
   2217     if dim == 2:
-> 2218         ret = torch._C._nn.nll_loss(input, target, weight, _Reduction.get_enum(reduction), ignore_index)
   2219     elif dim == 4:
   2220         ret = torch._C._nn.nll_loss2d(input, target, weight, _Reduction.get_enum(reduction), ignore_index)

IndexError: Target 5 is out of bounds.

自分で試したこと

print関数にて、labelがきちんと出力されているのか以下で確認しました。

for epoch in range(80):
    epoch_loss = 0
    for iter, (bg, label) in enumerate(data_loader):
        prediction = model(bg)
        print(label)
        loss = loss_func(prediction, label)

出力結果

tensor([5, 1, 4, 3, 7, 4, 0, 6, 0, 0, 4, 0, 2, 3, 0, 3, 4, 5, 3, 1, 3, 4, 7, 7, 4, 6, 5, 7, 3, 0, 1, 1])


これより、tensorの最初の要素5が、0か1でないために、エラーが出ていると推測されます。元は8クラス分類だったのでこの出力は納得がいくのですが、どのようにデータセットを書き換えれば良いかがわかりません。
どなたかご教授願えませんでしょうか。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

まだ回答がついていません

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る