1. Node2Vecを試してみる
node2vecを試しに使ってみたので、よければ参考にしてください。
今回は空手データセットを使用しました。
node2vec.py
import sys
import torch
import matplotlib.pyplot as plt
from torch_geometric.datasets import KarateClub
from torch_geometric.nn import Node2Vec
# 埋め込みデータ保存先
MODEL_PATH = f'./embedded_data/node2vec_karate.pickle'
# パラメータ設定
torch.manual_seed(1)
batch_size = 2
num_workers = 4 if sys.platform == 'linux' else 0
# データセットの取得
data = KarateClub()[0]
# テストマスクの作成
test_mask_list = []
for i in data.train_mask:
if i.item():
test_mask_list.append(False)
else:
test_mask_list.append(True)
data.test_mask = torch.tensor(test_mask_list)
# モデルの定義
model = Node2Vec(
data.edge_index,
embedding_dim=2,
walk_length=5,
walks_per_node=5,
context_size=5,
)
# データを分ける
loader = model.loader(batch_size=batch_size, shuffle=True, num_workers=num_workers)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
# 学習
for epoch in range(1, 51):
model.train()
for pos_rw, neg_rw in loader:
optimizer.zero_grad()
loss = model.loss(pos_rw, neg_rw)
loss.backward()
optimizer.step()
print(f'Epoch: {epoch:03d}, Loss: {loss:.4f}')
# 評価
model.eval()
z = model()
acc = model.test(
train_z=z[data.train_mask],
train_y=data.y[data.train_mask],
test_z=z[data.test_mask],
test_y=data.y[data.test_mask]
)
print(f'Acc: {acc:.4f}')
# 埋め込みデータの作成
torch.save(z, MODEL_PATH)
emb2d = z.detach().numpy()
# 埋め込みデータをグラフで表示
plt.title("node embedding in 2D")
plt.scatter(emb2d[:,0],emb2d[:,1])
plt.show()
1-1. 実行してみる
コンソール結果
$ python node2vec.py
Epoch: 001, Loss: 1.8213
Epoch: 002, Loss: 1.2606
Epoch: 003, Loss: 1.6105
Epoch: 004, Loss: 1.5242
Epoch: 005, Loss: 1.3069
Epoch: 006, Loss: 1.3369
Epoch: 007, Loss: 1.8641
Epoch: 008, Loss: 1.3242
Epoch: 009, Loss: 1.4022
Epoch: 010, Loss: 1.3207
Epoch: 011, Loss: 1.4372
Epoch: 012, Loss: 1.4107
Epoch: 013, Loss: 1.2831
Epoch: 014, Loss: 1.4015
Epoch: 015, Loss: 1.3993
Epoch: 016, Loss: 1.3672
Epoch: 017, Loss: 1.2989
Epoch: 018, Loss: 1.4034
Epoch: 019, Loss: 1.4309
Epoch: 020, Loss: 1.3101
Epoch: 021, Loss: 1.3130
Epoch: 022, Loss: 1.3254
Epoch: 023, Loss: 1.3217
Epoch: 024, Loss: 1.2541
Epoch: 025, Loss: 1.2302
Epoch: 026, Loss: 1.2345
Epoch: 027, Loss: 1.5912
Epoch: 028, Loss: 1.2548
Epoch: 029, Loss: 1.2034
Epoch: 030, Loss: 1.4017
Epoch: 031, Loss: 1.4904
Epoch: 032, Loss: 1.3489
Epoch: 033, Loss: 1.0623
Epoch: 034, Loss: 1.2798
Epoch: 035, Loss: 1.2487
Epoch: 036, Loss: 1.0272
Epoch: 037, Loss: 1.3455
Epoch: 038, Loss: 1.2721
Epoch: 039, Loss: 1.2123
Epoch: 040, Loss: 1.1893
Epoch: 041, Loss: 1.1719
Epoch: 042, Loss: 1.0916
Epoch: 043, Loss: 1.1160
Epoch: 044, Loss: 1.2925
Epoch: 045, Loss: 1.2810
Epoch: 046, Loss: 1.2935
Epoch: 047, Loss: 1.3090
Epoch: 048, Loss: 0.8576
Epoch: 049, Loss: 1.3404
Epoch: 050, Loss: 1.3522
Acc: 0.8000
1-2. 埋め込みグラフの表示
埋め込み画像
2. SVMでノード分類
svm.py
import torch
import numpy as np
from sklearn.svm import SVC
from sklearn.model_selection import LeaveOneOut
from torch_geometric.datasets import KarateClub
from sklearn.metrics import accuracy_score
MODEL_PATH = f'./embedded_data/node2vec_karate.pickle'
# 保存した埋め込みデータを読み込む
x = torch.load(MODEL_PATH).detach().numpy()
# 空手データセットのノードラベルを取得
y = KarateClub()[0].y.detach().numpy()
# SVMモデル呼び出し
model = SVC(kernel='linear')
# 予測ラベル保存用
pred_list = np.array([])
# Leave One Outを用いる
loo = LeaveOneOut()
i = 1
# ノードごとに予測する
for train_index, test_index in loo.split(x):
x_train, x_test = x[train_index], x[test_index]
y_train, y_test = y[train_index], y[test_index]
# 埋め込みデータを学習
model.fit(x_train, y_train)
# 予測
pred = model.predict(x_test)
print(i, "番目")
print("予測:", pred)
print("正解:", y_test)
print()
pred_list = np.append(pred_list, pred.item())
i = i + 1
accuracy = accuracy_score(y, pred_list)
print(f"正解率: {round(accuracy, 3)}")
2-1. 実行してみる
コンソール結果
$ python svm.py
1 番目
予測: [1]
正解: [1]
2 番目
予測: [1]
正解: [1]
3 番目
予測: [1]
正解: [1]
4 番目
予測: [1]
正解: [1]
5 番目
予測: [3]
正解: [3]
6 番目
予測: [3]
正解: [3]
7 番目
予測: [3]
正解: [3]
8 番目
予測: [1]
正解: [1]
9 番目
予測: [0]
正解: [0]
10 番目
予測: [0]
正解: [1]
11 番目
予測: [3]
正解: [3]
12 番目
予測: [1]
正解: [1]
13 番目
予測: [1]
正解: [1]
14 番目
予測: [1]
正解: [1]
15 番目
予測: [0]
正解: [0]
16 番目
予測: [0]
正解: [0]
17 番目
予測: [3]
正解: [3]
18 番目
予測: [1]
正解: [1]
19 番目
予測: [0]
正解: [0]
20 番目
予測: [1]
正解: [1]
21 番目
予測: [0]
正解: [0]
22 番目
予測: [1]
正解: [1]
23 番目
予測: [0]
正解: [0]
24 番目
予測: [0]
正解: [0]
25 番目
予測: [0]
正解: [2]
26 番目
予測: [0]
正解: [2]
27 番目
予測: [0]
正解: [0]
28 番目
予測: [2]
正解: [0]
29 番目
予測: [0]
正解: [2]
30 番目
予測: [0]
正解: [0]
31 番目
予測: [0]
正解: [0]
32 番目
予測: [0]
正解: [2]
33 番目
予測: [0]
正解: [0]
34 番目
予測: [0]
正解: [0]
正解率: 0.824
3. KNN(K近傍法)でノード分類
knn.py
import torch
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import LeaveOneOut
from torch_geometric.datasets import KarateClub
from sklearn.metrics import accuracy_score
MODEL_PATH = f'./embedded_data/node2vec_karate.pickle'
# 保存した埋め込みデータを読み込む
x = torch.load(MODEL_PATH).detach().numpy()
# 空手データセットのノードラベルを取得
y = KarateClub()[0].y.detach().numpy()
# knnモデル呼び出し
knn = KNeighborsClassifier(n_neighbors=3)
# 予測ラベル保存用
pred_list = np.array([])
# Leave One Outを用いる
loo = LeaveOneOut()
i = 1
# ノードごとに予測する
for train_index, test_index in loo.split(x):
x_train, x_test = x[train_index], x[test_index]
y_train, y_test = y[train_index], y[test_index]
# 埋め込みデータを学習
knn.fit(x_train, y_train)
# 予測
pred = knn.predict(x_test)
print(i, "番目")
print("予測:", pred)
print("正解:", y_test)
print()
pred_list = np.append(pred_list, pred.item())
i = i + 1
accuracy = accuracy_score(y, pred_list)
print(f"正解率: {round(accuracy, 3)}")
3-1. 実行してみる
コンソール結果
$ python svm.py
1 番目
予測: [1]
正解: [1]
2 番目
予測: [1]
正解: [1]
3 番目
予測: [1]
正解: [1]
4 番目
予測: [1]
正解: [1]
5 番目
予測: [3]
正解: [3]
6 番目
予測: [3]
正解: [3]
7 番目
予測: [3]
正解: [3]
8 番目
予測: [1]
正解: [1]
9 番目
予測: [0]
正解: [0]
10 番目
予測: [0]
正解: [1]
11 番目
予測: [3]
正解: [3]
12 番目
予測: [1]
正解: [1]
13 番目
予測: [1]
正解: [1]
14 番目
予測: [1]
正解: [1]
15 番目
予測: [0]
正解: [0]
16 番目
予測: [0]
正解: [0]
17 番目
予測: [3]
正解: [3]
18 番目
予測: [1]
正解: [1]
19 番目
予測: [0]
正解: [0]
20 番目
予測: [1]
正解: [1]
21 番目
予測: [0]
正解: [0]
22 番目
予測: [1]
正解: [1]
23 番目
予測: [0]
正解: [0]
24 番目
予測: [0]
正解: [0]
25 番目
予測: [2]
正解: [2]
26 番目
予測: [2]
正解: [2]
27 番目
予測: [0]
正解: [0]
28 番目
予測: [2]
正解: [0]
29 番目
予測: [0]
正解: [2]
30 番目
予測: [0]
正解: [0]
31 番目
予測: [0]
正解: [0]
32 番目
予測: [2]
正解: [2]
33 番目
予測: [0]
正解: [0]
34 番目
予測: [0]
正解: [0]
正解率: 0.912
4. PyTorchを用いてノード分類
nn.py
import torch
import numpy as np
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from sklearn.model_selection import LeaveOneOut
from torch_geometric.datasets import KarateClub
from sklearn.metrics import accuracy_score
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(2, 32)
self.fc2 = nn.Linear(32, 4)
def forward(self, x):
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
x = F.softmax(x, dim=1)
return x
MODEL_PATH = f'./embedded_data/node2vec_karate.pickle'
# 保存した埋め込みデータを読み込む
x = torch.load(MODEL_PATH)
# 空手データセットのノードラベルを取得
y = KarateClub()[0].y
# 予測ラベル保存用
pred_list = np.array([])
# Leave One Outを用いる
loo = LeaveOneOut()
# モデル呼び出し
model = Net()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)
i = 1
# ノードごとに予測する
for train_index, test_index in loo.split(x):
x_train, x_test = x[train_index], x[test_index]
y_train, y_test = y[train_index], y[test_index]
# 学習
model.train()
for epoch in range(40):
optimizer.zero_grad()
out = model(x_train)
loss = criterion(out, y_train)
loss.backward(retain_graph=True)
optimizer.step()
# 予測
model.eval()
_, pred = model(x_test).max(dim=1)
print(i, "番目")
print("予測:", pred.item())
print("正解:", y_test.item())
print("loss:", round(loss.item(), 3))
print()
pred_list = np.append(pred_list, pred.item())
i = i + 1
accuracy = accuracy_score(y, pred_list)
print(f"正解率: {round(accuracy, 3)}")
4-1. 実行してみる
コンソール結果
$ python nn.py
1 番目
予測: 1
正解: 1
loss: 0.922
2 番目
予測: 1
正解: 1
loss: 0.896
3 番目
予測: 1
正解: 1
loss: 0.836
4 番目
予測: 1
正解: 1
loss: 0.83
5 番目
予測: 3
正解: 3
loss: 0.825
6 番目
予測: 3
正解: 3
loss: 0.819
7 番目
予測: 3
正解: 3
loss: 0.814
8 番目
予測: 1
正解: 1
loss: 0.811
9 番目
予測: 0
正解: 0
loss: 0.808
10 番目
予測: 0
正解: 1
loss: 0.777
11 番目
予測: 3
正解: 3
loss: 0.807
12 番目
予測: 1
正解: 1
loss: 0.806
13 番目
予測: 1
正解: 1
loss: 0.806
14 番目
予測: 1
正解: 1
loss: 0.806
15 番目
予測: 0
正解: 0
loss: 0.805
16 番目
予測: 0
正解: 0
loss: 0.805
17 番目
予測: 3
正解: 3
loss: 0.805
18 番目
予測: 1
正解: 1
loss: 0.805
19 番目
予測: 0
正解: 0
loss: 0.805
20 番目
予測: 1
正解: 1
loss: 0.805
21 番目
予測: 0
正解: 0
loss: 0.805
22 番目
予測: 1
正解: 1
loss: 0.805
23 番目
予測: 0
正解: 0
loss: 0.805
24 番目
予測: 0
正解: 0
loss: 0.805
25 番目
予測: 2
正解: 2
loss: 0.805
26 番目
予測: 2
正解: 2
loss: 0.805
27 番目
予測: 0
正解: 0
loss: 0.805
28 番目
予測: 2
正解: 0
loss: 0.774
29 番目
予測: 2
正解: 2
loss: 0.804
30 番目
予測: 0
正解: 0
loss: 0.805
31 番目
予測: 0
正解: 0
loss: 0.804
32 番目
予測: 2
正解: 2
loss: 0.804
33 番目
予測: 0
正解: 0
loss: 0.804
34 番目
予測: 0
正解: 0
loss: 0.804
正解率: 0.941