見出し画像

PyGでModelNetを利用する方法

PyGとは

  • PyTorchで使えるライブラリ

  • Graph Neural Networksの記述と訓練(学習)を簡単に行うことができる

  • 構造化データに関連するアプリケーション用途で用いる

PyG (PyTorch Geometric) is a library built upon PyTorch to easily write and train Graph Neural Networks (GNNs) for a wide range of applications related to structured data.

https://pytorch-geometric.readthedocs.io/en/latest/

PyGで簡単なグラフデータを作成

  • 以下のようなグラフを作成(公式サイトのサンプルより)

    • 重みなし、方向なし

    • 3つのノード、4つのエッジを持つ

    • 各ノードは1つの特徴を持つ

import torch
from torch_geometric.data import Data

edge_index = torch.tensor([[0, 1, 1, 2],
                           [1, 0, 2, 1]], dtype=torch.long)
x = torch.tensor([[-1], [0], [1]], dtype=torch.float)

data = Data(x=x, edge_index=edge_index)
>>> Data(edge_index=[2, 4], x=[3, 1])

PyGで点群を表現する

  • ノードだけを持つことで、点群データを表現することができる

  • 点群なので、方向やエッジは持たない(不要)

  • 色や法線は特徴として持つことができそうだ

PyGのデータセット機能

  • PyGには、公開されているデータセットを利用できる機能がある

  • 様々なデータセットが簡単に扱えるようになっている

    • 利用できるデータセットはこちらにある

  • 自動でダウンロードしてくれ、その後、データを加工することもできる

  • 今回は、ModelNetと呼ばれるデータセットを試してみる

ModelNet

  • ModelNetは、バスタブ、ベッド、椅子などを含む3Dデータ(点群に変換することができる)

  • ModelNetの目的は、研究者に3D CAD modelsを提供すること

  • 現状、カテゴリ数は10と40のものがある

  • それぞれ、ModelNet10、ModelNet40などと表記されることがある

椅子の例

参考

The goal of the Princeton ModelNet project is to provide researchers in computer vision, computer graphics, robotics and cognitive science, with a comprehensive clean collection of 3D CAD models for objects.

https://modelnet.cs.princeton.edu/

The ModelNet10/40 datasets from the “3D ShapeNets: A Deep Representation for Volumetric Shapes” paper, containing CAD models of 10 and 40 categories, respectively.

https://modelnet.cs.princeton.edu/

ModelNetを手動でダウンロードして使う

├─bathtub
│  ├─test
│  └─train
├─bed
│  ├─test
│  └─train
├─chair
│  ├─test
│  └─train
├─desk
│  ├─test
│  └─train
├─dresser
│  ├─test
│  └─train
├─monitor
│  ├─test
│  └─train
├─night_stand
│  ├─test
│  └─train
├─sofa
│  ├─test
│  └─train
├─table
│  ├─test
│  └─train
└─toilet
    ├─test
    └─train

PyGでModelNetを扱う

  • ModelNetクラス を利用する

  • 継承関係は、ModelNet > InMemoryDataset > Dataset

    • ModelNetクラスのオブジェクトを操作するとき、InMemoryDatasetやDatasetのAPIなども使うことがありそうだ

  • クラスの完全な名前は以下

    • torch_geometric.datasets.ModelNet

    • torch_geometric.data.InMemoryDataset

    • torch_geometric.data.Dataset

ModelNetクラスの資料

InMemoryDatasetクラスの資料

Datasetクラスの資料

PyG経由でModelNet10をダウンロードする

  • 以下のように実装すると、mydata1/rawフォルダにデータがダウンロードされる

    • zipファイルが451MBytesあって、おそらくUSのサーバーのためダウンロードに時間がかかる(ことがある)

  • name=10でModelNet10、name=40でModelNet40になる

  • train=Trueとすると、訓練用のデータになる(Flaseにするとテスト用データになる)

    • どちらを指定しても、train/testの両方が処理(ダウンロード)される(キャッシュとして保存される)

from torch_geometric.datasets import ModelNet
from torch_geometric import transforms

my_dataset = ModelNet("mydata1", name="10", train=True)
(train_dataset.urls)
  • 毎回、ダウンロード、展開、加工処理するのは時間がかかるので、2回目以降は保存済みのデータが使われる仕組みがある

    • ModelNetクラスのコンストラクタの第1引数の名前のフォルダ名にデータが保存される

  • 以下は、キャッシュされた状態で、pre_transformを新たに指定して、ノード数(頂点数)を256に加工する処理を追加したが反映されない例

  • ノード数の平均が、256になることを期待するが、9897と表示されている

    • 引数の指定と処理済みのデータセットが異なるという旨の警告が出る

    • force_reloadを使ってください、と表示される

my_dataset = ModelNet("mydata1", name="10", train=True, pre_transform=transforms.SamplePoints(256, True, True))
my_dataset.print_summary()
UserWarning: The `pre_transform` argument differs from the one used in the pre-processed version of this dataset.
If you want to make use of another pre-processing technique, pass `force_reload=True` explicitly to reload the dataset.

+------------+----------+----------+
|            |   #nodes |   #edges |
|------------+----------+----------|
| mean       |   9897.5 |        0 |
| std        |  23756.8 |        0 |
| min        |    100   |        0 |
| quantile25 |    977.5 |        0 |
| median     |   2900   |        0 |
| quantile75 |   8801.5 |        0 |
| max        | 502603   |        0 |
+------------+----------+----------+
  • force_reload=Trueとすると、再処理をしてくれる

    • おそらく、rawフォルダがある場合はダウンロードは行わず、processedデータを再度、作り直す(加工処理のみ行う)

  • ノード数の平均が256になったので、transformが処理されたと考えてよさそう

my_dataset = ModelNet("mydata1", name="10", train=True, pre_transform=pre_transform=transforms.SamplePoints(256, True, True), force_reload=True)
my_dataset.print_summary()
Processing...
Done!
ModelNet (#graphs=3991):
+------------+----------+----------+
|            |   #nodes |   #edges |
|------------+----------+----------|
| mean       |      256 |        0 |
| std        |        0 |        0 |
| min        |      256 |        0 |
| quantile25 |      256 |        0 |
| median     |      256 |        0 |
| quantile75 |      256 |        0 |
| max        |      256 |        0 |
+------------+----------+----------+
  • 頂点数512と256にしたデータを両方を保存しておきたいと思うことがある

  • root(第1引数)をmydata1からmydata2に変更するという手法が考えられる

  • ただし、このままだと再びダウンロードすることになる

  • ダウンロードを回避するために、mydata1フォルダをコピーして、mydata2を作り

  • mydata2フォルダ内の、processedフォルダを削除して、rawフォルダのみ残せば、ダウンロードを回避できる

my_dataset = ModelNet("mydata2", name="10", train=True, pre_transform=pre_transform=transforms.SamplePoints(256, True, True))

PyGで各種情報を取得する

  • ModelNet10をPyGで処理して各情報を取得する参考コード

  • カテゴリ数は、num_classes

  • カテゴリの名称は、raw_file_namesを利用すればよさそう

my_dataset = ModelNet("mydata", name="10", train=True)

print(f'my_dataset.root:{my_dataset.root}')
print(f'my_dataset.name:{my_dataset.name}')
print(f'my_dataset.get_summary():{my_dataset.get_summary()}')
print(f'my_dataset.num_classes:{my_dataset.num_classes}')
print(f'my_dataset.raw_file_names:{my_dataset.raw_file_names}')
print(f'my_dataset.processed_file_names:{my_dataset.processed_file_names}')
print(f'my_dataset.processed_dir:{my_dataset.processed_dir}')
print(f'has_download:{my_dataset.has_download}')
print(f'has_process:{my_dataset.has_process}')
print(f'num_edge_features:{my_dataset.num_edge_features}')
print(f'num_features:{my_dataset.num_features}')
print(f'num_node_features:{my_dataset.num_node_features}')
print(f'processed_paths:{my_dataset.processed_paths}')
print(f'raw_paths:{my_dataset.raw_paths}')
print(f'dataset.len():{my_dataset.len()}')
my_dataset.root:mydata
my_dataset.name:10
my_dataset.get_summary():ModelNet (#graphs=):
+------------+----------+----------+
|            |   #nodes |   #edges |
|------------+----------+----------|
| mean       |      256 |        0 |
| std        |        0 |        0 |
| min        |      256 |        0 |
| quantile25 |      256 |        0 |
| median     |      256 |        0 |
| quantile75 |      256 |        0 |
| max        |      256 |        0 |
+------------+----------+----------+
my_dataset.num_classes:10
my_dataset.raw_file_names:['bathtub', 'bed', 'chair', 'desk', 'dresser', 'monitor', 'night_stand', 'sofa', 'table', 'toilet']
my_dataset.processed_file_names:['training.pt', 'test.pt']
my_dataset.processed_dir:mydata\processed
has_download:True
has_process:True
num_edge_features:0
num_features:0
num_node_features:0
processed_paths:['mydata\\processed\\training.pt', 'mydata\\processed\\test.pt']
raw_paths:['mydata\\raw\\bathtub', 'mydata\\raw\\bed', 'mydata\\raw\\chair', 'mydata\\raw\\desk', 'mydata\\raw\\dresser', 'mydata\\raw\\monitor', 'mydata\\raw\\night_stand', 'mydata\\raw\\sofa', 'mydata\\raw\\table', 'mydata\\raw\\toilet']
dataset.len():3991

今回の試みの感想

  • Datasets機能は、便利だが、使い方を理解しないと混乱する

    • 状況によっては、独自に管理した方が都合がよい場合もありそうと感じた

  • pre_transformの種類ごとにフォルダ分けすることはできるが、rawデータを複数所持することになる

    • rawデータは2GB以上あるので、あまり複数持ちたくない

  • save()、load()などを利用することで、この問題を解決できそうなので時間が許すときに試したい

この記事が気に入ったらサポートをしてみませんか?