PyG (PyTorch Geometric) で MetaPath2Vec して Node2Vec と比較

グラフ構造を深層学習する PyG (PyTorch Geometric) を Google Colaboratory 上で使ってみました。今回は、MetaPath2Vec を使うことがテーマです。[Node2Vec](https://qiita.com/maskot1977/items/2854fdd19f4ba4bbe715)との比較も行ないました。

# PyG (PyTorch Geometric) インストール

PyG (PyTorch Geometric) のレポジトリは https://github.com/pyg-team/pytorch_geometric にあります。また、コードはチュートリアルドキュメント https://pytorch-geometric.readthedocs.io/en/latest/index.html を参考にしています。


```python
import os
import torch

torch.manual_seed(0)
os.environ['TORCH'] = torch.__version__
print(torch.__version__)

!pip install -q torch-scatter -f https://data.pyg.org/whl/torch-${TORCH}.html
!pip install -q torch-sparse -f https://data.pyg.org/whl/torch-${TORCH}.html
!pip install -q git+https://github.com/pyg-team/pytorch_geometric.git

import torch_geometric
```

    1.12.0+cu113
    [K     |████████████████████████████████| 7.9 MB 56.3 MB/s 
    [K     |████████████████████████████████| 3.5 MB 27.1 MB/s 
    [?25h  Building wheel for torch-geometric (setup.py) ... [?25l[?25hdone



```python
device = 'cuda' if torch.cuda.is_available() else 'cpu'
```

# データセットの自作

今回もデータセットを自作します。自作する理由は、結果の良し悪しを自分の目で判断しやすくするためです。


## 小さなデータで説明

A, B, C の３タイプのノード（頂点）があるものとします。


```python
import numpy as np

num = 3

A = np.random.rand(num * 2)
A.sort()
B = np.random.rand(num * 4)
B.sort()
C = np.random.rand(num)
C.sort()
```

次のような基準を満たすときに、異なるタイプのノード間にのみエッジ（辺）が作られるものとします。


```python
def get_edges(top, X, Y):
    edges = []

    for i1, n1 in enumerate(X):
        for i2 in np.argsort([np.abs(n1 - n2) for n2 in Y])[:top]:
            edge = [i1, i2]
            if edge not in edges:
                edges.append(edge)

    for i1, n1 in enumerate(Y):
        for i2 in np.argsort([np.abs(n1 - n2) for n2 in X])[:top]:
            edge = [i2, i1]
            if edge not in edges:
                edges.append(edge)

    edges.sort()
    return edges
```

このようにして、タイプAのノードとタイプBのノードの間、タイプBのノードとタイプCのノードの間にのみエッジが作られるものとします。タイプAのノードとタイプCのノードの間にはエッジは作りません。また、同じタイプのノード（タイプA同士、タイプB同士、タイプC同士）間にはエッジは作りません。


```python
edges_AB = get_edges(2, A, B)
edges_BC = get_edges(2, B, C)
edges_BA = get_edges(2, B, A)
edges_CB = get_edges(2, C, B)
```

形成されるネットワーク（グラフ構造）は、たとえば次のような感じです。


```python
import matplotlib.pyplot as plt

plt.scatter([0 for _ in range(len(A))], A)
plt.scatter([1 for _ in range(len(B))], B)
plt.scatter([2 for _ in range(len(C))], C)
for edge in edges_AB:
    plt.plot([0, 1], [A[edge[0]], B[edge[1]]], color="b", alpha=0.5)
for edge in edges_BC:
    plt.plot([1, 2], [B[edge[0]], C[edge[1]]], color="b", alpha=0.5)
plt.xticks([0, 1, 2], ["A", "B", "C"], color="k", fontsize=12, rotation=0)
plt.show()
```

![MetaPath2Vec試す2_10_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/16eaf8e4-6b8c-9bc1-8b0b-67a10f198b6a.png)

        

話をややこしくしてしまうかもしれませんが、AC間のエッジの引き方も、説明しておきましょう。ここでは、AとCが１個のBを介して間接的につながっている場合に、AC間にエッジを引くことにします。


```python
edges_AC = []
for x in edges_AB:
    for y in edges_BC:
        if x[1] == y[0]:
            if [x[0], y[1]] not in edges_AC:
                edges_AC.append([x[0], y[1]])
```

そうすると、AC間のエッジは次のようになります。ただし、話をややこしくしてしまうかもしれませんが、このエッジは学習には用いません。学習結果を確認するときにのみ使う予定です。


```python
import matplotlib.pyplot as plt

plt.scatter([0 for _ in range(len(A))], A)
plt.scatter([2 for _ in range(len(C))], C)
for edge in edges_AC:
    plt.plot([0, 2], [A[edge[0]], C[edge[1]]], color="b", alpha=0.5)
plt.xticks([0, 2], ["A", "C"], color="k", fontsize=12, rotation=0)
plt.show()
```

![MetaPath2Vec試す2_14_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/bb00b9ac-5e82-078f-6f10-fcca8875a6e8.png)

    
    

## 実際に使うデータ

以上、データ構造を説明するため、小さなサイズのデータを用いました。ここから先は、実際に使うデータを作成します。


```python
import numpy as np

num = 100

A = np.random.rand(num * 2)
A.sort()
B = np.random.rand(num * 4)
B.sort()
C = np.random.rand(num)
C.sort()

edges_AB = get_edges(5, A, B)
edges_BC = get_edges(5, B, C)
edges_BA = get_edges(5, B, A)
edges_CB = get_edges(5, C, B)
```

このようなネットワーク（グラフ構造）になります。


```python
import matplotlib.pyplot as plt

plt.scatter([0 for _ in range(len(A))], A, alpha=0.5)
plt.scatter([1 for _ in range(len(B))], B, alpha=0.5)
plt.scatter([2 for _ in range(len(C))], C, alpha=0.5)
for edge in edges_AB:
    plt.plot([0, 1], [A[edge[0]], B[edge[1]]], color="b", alpha=0.01)
for edge in edges_BC:
    plt.plot([1, 2], [B[edge[0]], C[edge[1]]], color="b", alpha=0.01)
plt.xticks([0, 1, 2], ["A", "B", "C"], color="k", fontsize=12, rotation=0)
plt.show()
```

![MetaPath2Vec試す2_18_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/c0f3df4b-8057-78ca-cb7f-a94775d48ba2.png)

    
    

学習に用いない、AC間のエッジも計算しておきましょう。


```python
edges_AC = []
for x in edges_AB:
    for y in edges_BC:
        if x[1] == y[0]:
            if [x[0], y[1]] not in edges_AC:
                edges_AC.append([x[0], y[1]])
```

こんな感じです。


```python
import matplotlib.pyplot as plt

plt.scatter([0 for _ in range(len(A))], A, alpha=0.5)
plt.scatter([2 for _ in range(len(C))], C, alpha=0.5)
for edge in edges_AC:
    plt.plot([0, 2], [A[edge[0]], C[edge[1]]], color="b", alpha=0.01)
plt.xticks([0, 2], ["A", "C"], color="k", fontsize=12, rotation=0)
plt.show()
```

![MetaPath2Vec試す2_22_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/ffaf212c-cf2a-7278-7e3e-f3967b408020.png)

    
    

## HeteroData

作成したネットワークを、HeteroData クラスで実装しましょう。


```python
data = torch_geometric.data.HeteroData()
data['A'].y = torch.tensor(np.where(A > 0.5, 1, 0))
data['A'].y_index = torch.argsort(torch.tensor(A))
data['A'].num_nodes = len(A)
data['B'].num_nodes = len(B)
data['B'].y_index = torch.argsort(torch.tensor(B)) ##
data['C'].y = torch.tensor(C)
data['C'].y_index = torch.argsort(torch.tensor(C))
data['C'].num_nodes = len(C)
data[('A', 'A2B', 'B')].edge_index = torch.tensor(edges_AB).T
data[('B', 'B2C', 'C')].edge_index = torch.tensor(edges_BC).T
data[('B', 'B2A', 'A')].edge_index = torch.tensor(edges_BA).T
data[('C', 'C2B', 'B')].edge_index = torch.tensor(edges_CB).T
data
```




    HeteroData(
      [1mA[0m={
        y=[200],
        y_index=[200],
        num_nodes=200
      },
      [1mB[0m={
        num_nodes=400,
        y_index=[400]
      },
      [1mC[0m={
        y=[100],
        y_index=[100],
        num_nodes=100
      },
      [1m(A, A2B, B)[0m={ edge_index=[2, 2046] },
      [1m(B, B2C, C)[0m={ edge_index=[2, 2003] },
      [1m(B, B2A, A)[0m={ edge_index=[2, 2046] },
      [1m(C, C2B, B)[0m={ edge_index=[2, 2003] }
    )



# MetaPath2Vec (embedding_dim = 2)

次のようにして MetaPath2Vec のモデルを実装しましょう。まずは embedding_dim = 2 とします。


```python
from torch_geometric.nn import MetaPath2Vec

embedding_dim = 2

metapath = [
    ('A', 'A2B', 'B'),
    ('B', 'B2C', 'C'),
    ('B', 'B2A', 'A'),
    ('C', 'C2B', 'B'),
    ]

model = MetaPath2Vec(data.edge_index_dict, 
                     embedding_dim=embedding_dim,
                     metapath=metapath,
                     walk_length=4, 
                     context_size=3,
                     walks_per_node=3,
                     num_negative_samples=1,
                     sparse=True
                    ).to(device)
```

トレーニングとテストのコードです。


```python
def train(epoch, log_steps=500, eval_steps=1000):
    model.train()

    total_loss = 0
    for i, (pos_rw, neg_rw) in enumerate(loader):
        optimizer.zero_grad()
        loss = model.loss(pos_rw.to(device), neg_rw.to(device))
        loss.backward()
        optimizer.step()

        total_loss += loss.item()
        if (i + 1) % log_steps == 0:
            print((f'Epoch: {epoch}, Step: {i + 1:05d}/{len(loader)}, '
                   f'Loss: {total_loss / log_steps:.4f}'))
            total_loss = 0

        if (i + 1) % eval_steps == 0:
            acc = test()
            print((f'Epoch: {epoch}, Step: {i + 1:05d}/{len(loader)}, '
                   f'Acc: {acc:.4f}'))
    
    return total_loss

@torch.no_grad()
def test(train_ratio=0.1):
    model.eval()

    z = model('A', batch=data.y_index_dict['A'].to(device))
    y = data.y_dict['A']

    perm = torch.randperm(z.size(0))
    train_perm = perm[:int(z.size(0) * train_ratio)]
    test_perm = perm[int(z.size(0) * train_ratio):]

    return model.test(z[train_perm], y[train_perm], z[test_perm],
                      y[test_perm], max_iter=150)
```

モデルのローダーとオプティマイザを初期化します。


```python
loader = model.loader(batch_size=128, shuffle=True, num_workers=2)
optimizer = torch.optim.SparseAdam(list(model.parameters()), lr=0.01)
```

学習開始。ベストモデルを best_model として保存します。


```python
import copy

loss_hist = []
acc_hist = []
best_score = None
for epoch in range(600):
    loss = train(epoch)
    acc = test()
    loss_hist.append(loss)
    acc_hist.append(acc)
    if best_score is None or best_score < acc:
        best_model = copy.deepcopy(model)
        best_score = acc
        print(f'Epoch: {epoch}, Accuracy: {acc:.4f}, Loss: {loss: .6f}')
```

    Epoch: 0, Accuracy: 0.5278, Loss:  3.505347
    Epoch: 4, Accuracy: 0.5333, Loss:  3.430402
    Epoch: 6, Accuracy: 0.5556, Loss:  3.333481
    Epoch: 50, Accuracy: 0.5667, Loss:  2.891399
    Epoch: 77, Accuracy: 0.6056, Loss:  2.772557
    Epoch: 81, Accuracy: 0.6500, Loss:  2.747386
    Epoch: 95, Accuracy: 0.6778, Loss:  2.678413
    Epoch: 99, Accuracy: 0.6833, Loss:  2.662748
    Epoch: 120, Accuracy: 0.7278, Loss:  2.539922
    Epoch: 140, Accuracy: 0.7444, Loss:  2.418053
    Epoch: 145, Accuracy: 0.7556, Loss:  2.350473
    Epoch: 148, Accuracy: 0.7722, Loss:  2.356267
    Epoch: 151, Accuracy: 0.7889, Loss:  2.363546
    Epoch: 155, Accuracy: 0.7944, Loss:  2.343060
    Epoch: 163, Accuracy: 0.8000, Loss:  2.264058
    Epoch: 167, Accuracy: 0.8056, Loss:  2.252009
    Epoch: 173, Accuracy: 0.8278, Loss:  2.263199
    Epoch: 180, Accuracy: 0.8444, Loss:  2.254319
    Epoch: 185, Accuracy: 0.8500, Loss:  2.201037
    Epoch: 195, Accuracy: 0.8722, Loss:  2.203842
    Epoch: 207, Accuracy: 0.8778, Loss:  2.179795
    Epoch: 220, Accuracy: 0.8944, Loss:  2.183385
    Epoch: 273, Accuracy: 0.9056, Loss:  2.128915
    Epoch: 315, Accuracy: 0.9111, Loss:  2.125498
    Epoch: 335, Accuracy: 0.9278, Loss:  2.124234
    Epoch: 375, Accuracy: 0.9500, Loss:  2.057485
    Epoch: 398, Accuracy: 0.9556, Loss:  2.043905
    Epoch: 400, Accuracy: 0.9611, Loss:  2.095988
    Epoch: 410, Accuracy: 0.9667, Loss:  2.038597
    Epoch: 419, Accuracy: 0.9833, Loss:  2.061875
    Epoch: 433, Accuracy: 0.9889, Loss:  2.090283
    Epoch: 457, Accuracy: 0.9944, Loss:  2.028480
    Epoch: 484, Accuracy: 1.0000, Loss:  2.061711


学習履歴は次のようになりました。


```python
plt.plot(loss_hist, label="Loss")
plt.legend()
plt.show()
plt.plot(acc_hist, label="ACC")
plt.legend()
plt.show()
```

![MetaPath2Vec試す2_34_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/2381b362-cdaf-2c4a-0af9-450456ee3e30.png)

![MetaPath2Vec試す2_34_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/a56301bb-f4fd-79fa-c9e7-de65c40ad472.png)

    
    

各々のノードを潜在空間上にマッピングします。


```python
z_A = best_model('A', batch=data.y_index_dict['A'].to(device)).detach().cpu().numpy()
z_B = best_model('B', batch=data.y_index_dict['B'].to(device)).detach().cpu().numpy()
z_C = best_model('C', batch=data.y_index_dict['C'].to(device)).detach().cpu().numpy()
```

潜在空間の次元が２以上のときは、可視化のため Isomap などで次元削減します。


```python
from sklearn.manifold import Isomap

embedding_model = Isomap()

z_A_2d = embedding_model.fit_transform(z_A)
z_B_2d = embedding_model.transform(z_B)
z_C_2d = embedding_model.transform(z_C)
```


潜在空間が２次元のときはそのまま使います。


```python
z_A_2d = z_A
z_B_2d = z_B
z_C_2d = z_C
```

さて、可視化しましょう。Aタイプのノードは赤丸、Bタイプのノードは緑バツ、Cタイプのノードは青四角とします。AB間のエッジは赤線、BC間のエッジは青線にしました。


```python
import matplotlib.pyplot as plt

plt.figure(figsize=(12, 12))
plt.scatter(z_A_2d[:,0],z_A_2d[:,1],color="red",alpha=0.5,label="A")
plt.scatter(z_B_2d[:,0],z_B_2d[:,1],color="green",alpha=0.5,label="B", marker="x")
plt.scatter(z_C_2d[:,0],z_C_2d[:,1],color="blue",alpha=0.5,label="C", marker="s")

for edge in edges_AB:
    plt.plot(
        [z_A_2d[edge[0],0], z_B_2d[edge[1],0]], 
        [z_A_2d[edge[0],1], z_B_2d[edge[1],1]],
        color='red', alpha=0.05)

for edge in edges_BC:
    plt.plot(
        [z_B_2d[edge[0],0], z_C_2d[edge[1],0]], 
        [z_B_2d[edge[0],1], z_C_2d[edge[1],1]],
        color='blue', alpha=0.05)

plt.legend()
plt.title("2D embedding")
plt.show()
```

![MetaPath2Vec試す2_42_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/c91ead96-4899-7131-d83d-d3697d1fc33c.png)

    
    

うーん、解釈が難しそうですね。「似たようなノードと繋がっている同じタイプのノードは、近くに配置されている」と解釈できるでしょうか。「互いに接続しているノード同士が、近くに配置されている」わけではなさそうです。

解釈の助けになるかどうかは分かりませんが、AB間、BC間のエッジは消して、AC間のエッジを緑線で表示してみるとこのようになります。


```python
import matplotlib.pyplot as plt

plt.figure(figsize=(12, 12))
plt.scatter(z_A_2d[:,0],z_A_2d[:,1],color="red",alpha=0.5,label="A")
plt.scatter(z_B_2d[:,0],z_B_2d[:,1],color="green",alpha=0.5,label="B", marker="x")
plt.scatter(z_C_2d[:,0],z_C_2d[:,1],color="blue",alpha=0.5,label="C", marker="s")

for edge in edges_AC:
    plt.plot(
        [z_A_2d[edge[0],0], z_C_2d[edge[1],0]], 
        [z_A_2d[edge[0],1], z_C_2d[edge[1],1]],
        color='green', alpha=0.05)

plt.legend()
plt.title("2D embedding")
plt.show()
```

![MetaPath2Vec試す2_44_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/39290f52-9f70-22fc-b6b4-edaf921d4f0b.png)

    
# MetaPath2Vec (embedding_dim = 4)

[過去記事](https://qiita.com/maskot1977/items/2854fdd19f4ba4bbe715)と同様に、embedding_dim を大きくするとどうなるか実験したいと思います。まずは embedding_dim = 4

![MetaPath2Vec試す4_18_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/a5d57592-f417-0578-1fcc-1e1dceb506c0.png)

![MetaPath2Vec試す4_18_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/26fab39d-b82c-f18e-5cea-c89c4ffa6999.png)

![MetaPath2Vec試す4_21_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/764319d1-8922-5cdf-cb08-4d78f9e30b42.png)

![MetaPath2Vec試す4_22_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/d4bb0e37-b03b-618e-f72d-fbc37c8e6270.png)

むむっ、embedding_dim = 2 のときよりも結果がつぶれて悪化したように見えますね...



# MetaPath2Vec (embedding_dim = 8)

![MetaPath2Vec試す8_18_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/370c7408-8469-c5ab-072a-29a40c533cc0.png)

![MetaPath2Vec試す8_18_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/9c893244-e9a2-69c6-c44a-905ec3226f9a.png)

![MetaPath2Vec試す8_21_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/c0f38497-dcb7-4150-cef5-0db8b5851696.png)

![MetaPath2Vec試す8_22_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/480f98cc-f89e-cfe0-d7fc-33b7fce2fc7d.png)

embedding_dim = 4 のときよりは良いかもしれませんが、embedding_dim = 2 のときのほうがもっと良いな...

# MetaPath2Vec (embedding_dim = 16)

![MetaPath2Vec試す16_18_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/26fbb965-3617-86a8-8693-304c4ee663c8.png)

![MetaPath2Vec試す16_18_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/b659163f-e70c-66aa-53e6-97a22080d791.png)

![MetaPath2Vec試す16_21_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/bad80317-ce56-5ee2-7092-2111a368e2c5.png)

![MetaPath2Vec試す16_22_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/b8c620a9-8fa5-d762-77ac-4a74b1f0b0a1.png)

なんだろこれ...理解不能

# MetaPath2Vec (embedding_dim = 32)

![MetaPath2Vec試す32_18_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/05860479-228d-52b8-4967-85e73673736e.png)


![MetaPath2Vec試す32_18_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/6d38f108-0de0-eb91-ca93-9dde013cef90.png)

![MetaPath2Vec試す32_21_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/f5d0b5a2-81eb-13a1-a89a-31db7482eeaf.png)

![MetaPath2Vec試す32_22_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/578e908e-85f8-baac-f2bb-95d073b9cb10.png)


おっ？なんとなく理解「可」能っぽい構造になってきた？


# MetaPath2Vec (embedding_dim = 64)

![MetaPath2Vec試す64_18_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/2bee39ea-3474-ae73-b790-532835aa1b64.png)

![MetaPath2Vec試す64_18_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/4f681568-e88b-a3ab-024b-2067c0b49302.png)

![MetaPath2Vec試す64_21_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/9bb938b1-820d-5a77-dbee-4ec8619fccc9.png)

![MetaPath2Vec試す64_22_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/74d778d4-5632-cef7-ba6b-691b3be64697.png)

タイプが異なっていても、関係の深いノード間が近くに配置されるようになってきた、かも？

# MetaPath2Vec (embedding_dim = 128)

![MetaPath2Vec試す128_18_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/756c6767-3b44-a823-588b-ddea17099ec4.png)

![MetaPath2Vec試す128_18_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/475b4aa0-0fd6-7c77-edb4-8b800dbab1d4.png)

![MetaPath2Vec試す128_21_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/5865c092-b815-d08d-6913-50747316eead.png)

![MetaPath2Vec試す128_22_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/cf033463-f51c-6f52-e60b-21a52349eee8.png)

理解不能理解不能理解不能理解不能ッ...！


# Node2Vec (embedding_dim = 2)

それでは、次に Node2Vec で計算してみて、 MetaPath2Vec の結果と比較しますよ！ 基本的には[過去記事](https://qiita.com/maskot1977/items/2854fdd19f4ba4bbe715)と同じなので参照していただくとして、主な変更部分だけお示ししますね。


```python
import numpy as np
from torch_geometric.data import Data, InMemoryDataset

class HeteroDataset(InMemoryDataset):
    def __init__(self, transform = None):
        super().__init__('.', transform)

        embeddings = np.concatenate([A, B, C])
        embeddings = torch.tensor([[x] for x in embeddings])

        edges = []
        for edge in edges_AB:
            edges.append([edge[0], edge[1] + len(A)])
        for edge in edges_BC:
            edges.append([edge[0] + len(A), edge[1] + len(A) + len(B)])
        edges = torch.tensor(edges).T

        ys = np.concatenate([A, B, C])
        ys = np.where(ys > 0.5, 1, 0)
        ys = torch.tensor(ys)

        data = Data(x=embeddings, edge_index=edges, y=ys)
        self.data, self.slices = self.collate([data])
```

```python
dataset = HeteroDataset()
data = dataset.data
data.x = torch.zeros_like(data.x)
```

```python
embedding_dim = 2
model = torch_geometric.nn.Node2Vec(
    data.edge_index, embedding_dim=embedding_dim, walk_length=20,
    context_size=10, walks_per_node=20,
    num_negative_samples=10, p=0.5, q=1, sparse=True).to(device)

loader = model.loader(batch_size=64, shuffle=True, num_workers=2)
optimizer = torch.optim.SparseAdam(list(model.parameters()), lr=0.01)
```

![MetaPath2Vec試すNode2Vec比較2_20_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/8426386a-78d0-bc3e-bea7-b4e692640f2a.png)

![MetaPath2Vec試すNode2Vec比較2_20_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/150f25fb-6b1c-9ae2-1856-539638c7dc05.png)

```python
z = best_model(torch.arange(data.num_nodes, device=device)).detach().cpu().numpy()
z.shape
```

```python
z_A = z[:len(A), :]
z_B = z[len(A):len(A)+len(B), :]
z_C = z[len(A)+len(B):, :]
```

![MetaPath2Vec試すNode2Vec比較2_25_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/102da618-62a7-447b-4981-72c70da45b79.png)

![MetaPath2Vec試すNode2Vec比較2_26_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/6f801045-eaf4-9cc0-7028-a4cd63887fd0.png)

なんだこれはw

よく分からないが、これはこれで面白いww


# Node2Vec (embedding_dim = 4)

![MetaPath2Vec試すNode2Vec比較4_20_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/68b3b0b2-a334-9a18-541f-8b6335e48ce4.png)

![MetaPath2Vec試すNode2Vec比較4_20_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/7cec5d82-e3fa-b6ab-fc0a-fa0e05f058f1.png)

![MetaPath2Vec試すNode2Vec比較4_24_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/19602985-de1f-663f-e325-03b8220cbca8.png)

![MetaPath2Vec試すNode2Vec比較4_25_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/b9dcbf2b-e357-139e-cf66-a2e3082ba4fd.png)

うーん、何と言ったら良いやら...w


# Node2Vec (embedding_dim = 8)

![MetaPath2Vec試すNode2Vec比較8_20_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/9b2b9895-6c18-61ba-0e7d-e0999e35d2be.png)

![MetaPath2Vec試すNode2Vec比較8_20_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/b76ae375-98c3-fc62-e6f7-91f64eb48302.png)

![MetaPath2Vec試すNode2Vec比較8_24_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/54e95ed7-8300-4b77-e343-79774d1d1282.png)

![MetaPath2Vec試すNode2Vec比較8_25_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/d06b1ce1-e88c-39d4-55af-9360589fd69e.png)

おわw 面白いw これ、今回自作したデータ構造を非常によく表しているかもしれない！

# Node2Vec (embedding_dim = 16)

![MetaPath2Vec試すNode2Vec比較16_20_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/25d294ac-118c-8383-7160-1813a4191205.png)

![MetaPath2Vec試すNode2Vec比較16_20_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/5b334edb-5aa8-628d-2c85-138117561594.png)

![MetaPath2Vec試すNode2Vec比較16_24_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/79981212-050d-191f-1cc4-6dfa59a95352.png)

![MetaPath2Vec試すNode2Vec比較16_25_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/f9bc8ccb-f3d4-9bcd-4dd7-e9c1b18f9ba6.png)

結果はあまり変わらないけど学習曲線は安定している


# Node2Vec (embedding_dim = 32)

![MetaPath2Vec試すNode2Vec比較32_20_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/a3691531-a1ca-fff8-dae8-d7fdae7f5b18.png)

![MetaPath2Vec試すNode2Vec比較32_20_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/4c99f7e7-0233-c787-5d0c-99216994f729.png)

![MetaPath2Vec試すNode2Vec比較32_24_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/8f8eaf02-eb5d-dfb4-3163-8b377b8e3485.png)


![MetaPath2Vec試すNode2Vec比較32_25_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/947178be-a929-efc1-4a8b-3b71c277bcc1.png)

うーん、安定しているなぁ。ひょっとして MetaPath2Vec より Node2Vec のほうが良いんじゃない？

# Node2Vec (embedding_dim = 64)

![MetaPath2Vec試すNode2Vec比較64_20_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/3c0a932e-df81-3219-ba3d-8b89195c7f51.png)

![MetaPath2Vec試すNode2Vec比較64_20_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/cf5e5436-d5f7-3cf6-a43f-e61a56d45561.png)

![MetaPath2Vec試すNode2Vec比較64_24_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/6e7cf124-53ae-d69f-b2df-9e9af2afb23f.png)

![MetaPath2Vec試すNode2Vec比較64_25_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/db1ad968-40da-6987-0d18-131792eb08bf.png)

お？ ぐにゃっと曲がった。でもまだデータ構造をうまく表しているかも。


# Node2Vec (embedding_dim = 128)

![MetaPath2Vec試すNode2Vec比較128_20_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/23105067-51e9-0ff2-f6b6-7f7f7943cad5.png)

![MetaPath2Vec試すNode2Vec比較128_20_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/fb6edd32-4c28-8624-13e5-268591961a08.png)

![MetaPath2Vec試すNode2Vec比較128_24_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/bcf90147-b5f4-5210-c539-5fe1511ea827.png)


![MetaPath2Vec試すNode2Vec比較128_25_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/13ce2a2a-2d27-66f3-f2cd-7856ffe4bc81.png)

学習曲線も、潜在空間も、乱れ始めたかな...？






# Node2Vec (embedding_dim = 256)

![MetaPath2Vec試すNode2Vec比較256_20_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/ca466ade-ae7c-4f1e-239f-5f8f1e0cb2db.png)

![MetaPath2Vec試すNode2Vec比較256_20_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/2f775995-3471-09d8-79f5-f963a8a02090.png)

![MetaPath2Vec試すNode2Vec比較256_24_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/d0dc54af-fecd-42fe-55ef-c203f8c0f0fa.png)

![MetaPath2Vec試すNode2Vec比較256_25_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/c6c6fcda-e1f2-6499-4400-2eb614c438c0.png)

なるほど、もうダメっぽいですね。

# まとめ

自作ネットワークを作成して、MetaPath2Vec でノードを潜在空間にプロットしたり、 Node2Vec と比較したりしました。結果としては、Node2Vec のほうが良いかも、となりましたが、これは、今回用いたデータや、ハイパーパラメーターに依存するんだろうとは思います。まだ分からないことも多いので、またいろいろ勉強してみます。