PyGでModelNetを利用する方法
PyGとは
PyTorchで使えるライブラリ
Graph Neural Networksの記述と訓練(学習)を簡単に行うことができる
構造化データに関連するアプリケーション用途で用いる
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などと表記されることがある
参考
ModelNetに関するpeperは以下
ModelNetを手動でダウンロードして使う
zipを展開するとoffという拡張子のデータが得られる(例:bathtub_0001.off)
MeshLabなどで閲覧することができる
カテゴリ名、training用、test用でフォルダが分けられている
├─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()などを利用することで、この問題を解決できそうなので時間が許すときに試したい
この記事が気に入ったらサポートをしてみませんか?