Graph Embedding：NetworkX での実践と機械学習への応用

## はじめに
![graph-embedding-concept.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/26f91e90-0161-a3c6-3b1e-4110c41da40f.png)

グラフ埋め込み（Graph Embedding）は、グラフ構造を持つデータを低次元のベクトル空間にマッピングする手法です。本記事では、NetworkX を用いたグラフ埋め込みの実践方法と、機械学習タスク（分類・回帰）への応用について解説します。


## 1. グラフの構築
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/99ac9750-1a57-0c1c-02f9-618475500933.png)

まずは、NetworkX を用いてサンプルグラフを作成します。


```python
import networkx as nx
import matplotlib.pyplot as plt

# サンプルグラフの作成
g = nx.karate_club_graph()

# グラフの描画
plt.figure(figsize=(8, 6))
nx.draw(g, with_labels=True, node_color='lightblue', edge_color='gray')
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/9410b81f-691f-cb5a-df27-ea6c1d7a5187.png)

## 2. グラフ埋め込みの実施

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/40d6dfc6-f0c4-45b2-e57c-912cc68b10c3.png)

ここでは、Node2Vec を使用してノード埋め込みを取得します。

```python
from node2vec import Node2Vec

# Node2Vec のモデルを作成
node2vec = Node2Vec(g, dimensions=64, walk_length=30, num_walks=200, workers=4)

# 学習
model = node2vec.fit(window=10, min_count=1, batch_words=4)

# ノード 0 の埋め込みベクトルを取得
embedding = model.wv[str(0)]
print(f'ノード 0 の埋め込みベクトル: {embedding}')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/21a8e6c0-8f20-60fd-95e8-234b610282d8.png)


## 3. 埋め込みの可視化

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/9039f0f3-4fca-1e4d-f0e2-b45e0ac99157.png)

得られた埋め込みベクトルを t-SNE を用いて次元圧縮し、可視化します。

```python
import numpy as np
from sklearn.manifold import TSNE
import seaborn as sns

# すべてのノードの埋め込みを取得
node_ids = list(g.nodes)
embeddings = np.array([model.wv[str(node)] for node in node_ids])

# t-SNE による次元圧縮
tsne = TSNE(n_components=2, random_state=42)
embeddings_2d = tsne.fit_transform(embeddings)

# 可視化
plt.figure(figsize=(8, 6))
sns.scatterplot(x=embeddings_2d[:, 0], y=embeddings_2d[:, 1])
for i, node in enumerate(node_ids):
    plt.annotate(node, (embeddings_2d[i, 0], embeddings_2d[i, 1]))
plt.title('Graph Embedding Visualization using t-SNE')
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/deffd89e-4a5a-fa7f-1498-d669e8d661e6.png)


## 4. 機械学習タスクへの応用

![node-classification-concept.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/afeaa9e4-ac72-9f45-3ac5-3be1492b4b05.png)

グラフ埋め込みを用いて、ノード分類を実施します。

```python
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score

# ノードのクラブ所属ラベルを取得
labels = np.array([g.nodes[node]['club'] == 'Mr. Hi' for node in node_ids], dtype=int)

# 訓練データとテストデータの分割
X_train, X_test, y_train, y_test = train_test_split(embeddings, labels, test_size=0.3, random_state=42)

# ランダムフォレストを使用した分類
clf = RandomForestClassifier(n_estimators=100, random_state=42)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)

# 精度の評価
accuracy = accuracy_score(y_test, y_pred)
print(f'分類精度: {accuracy:.2f}')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/daaaa1e8-2dde-ea74-a7c3-c3eac7d46f03.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/b5b19270-7831-6ce2-6adb-d02e5f63e508.png)


## まとめ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/e24200e9-e170-24d3-6184-2fa79e67bf5c.png)

- NetworkX を用いてグラフを作成し、Node2Vec によりノード埋め込みを取得しました。
- t-SNE による可視化を行い、ノード間の関係を視覚的に確認しました。
- 埋め込みを用いたノード分類を実施し、機械学習タスクへの応用を示しました。

今後は、異なる埋め込み手法（DeepWalk, GraphSAGE など）との比較や、エッジ予測などのタスクにも応用してみると良いでしょう。

## 実装例

```python
# Graph Embedding with Node2Vec Tutorial
# 必要なライブラリのインストール
!pip install node2vec networkx matplotlib scikit-learn seaborn

# 必要なライブラリのインポート
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
from node2vec import Node2Vec
from sklearn.manifold import TSNE
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score

# 1. グラフの構築
# サンプルグラフの作成（空手クラブのデータ）
g = nx.karate_club_graph()

# グラフの描画
plt.figure(figsize=(10, 8))
nx.draw(g, with_labels=True, node_color='lightblue',
        edge_color='gray', node_size=500, font_size=10)
plt.title("Karate Club Graph")
plt.show()

# 2. Node2Vecによるグラフ埋め込み
# Node2Vecモデルの作成と学習
node2vec = Node2Vec(g,
                   dimensions=64,  # 埋め込みの次元数
                   walk_length=30,  # ランダムウォークの長さ
                   num_walks=200,   # ウォークの回数
                   workers=4)       # 並列処理のワーカー数

# モデルの学習
model = node2vec.fit(window=10, min_count=1, batch_words=4)

# ノード0の埋め込みベクトルを確認
embedding = model.wv[str(0)]
print(f'ノード0の埋め込みベクトル（最初の10次元）:\n{embedding[:10]}')

# 3. 埋め込みの可視化
# すべてのノードの埋め込みを取得
node_ids = list(g.nodes)
embeddings = np.array([model.wv[str(node)] for node in node_ids])

# t-SNEによる次元圧縮
tsne = TSNE(n_components=2, random_state=42)
embeddings_2d = tsne.fit_transform(embeddings)

# 可視化
plt.figure(figsize=(10, 8))
sns.scatterplot(x=embeddings_2d[:, 0], y=embeddings_2d[:, 1])
for i, node in enumerate(node_ids):
    plt.annotate(node, (embeddings_2d[i, 0], embeddings_2d[i, 1]))
plt.title('Node Embeddings Visualization (t-SNE)')
plt.show()

# 4. 機械学習タスクへの応用（ノード分類）
# ノードのクラブ所属ラベルを取得
labels = np.array([g.nodes[node]['club'] == 'Mr. Hi' for node in node_ids], dtype=int)

# データの分割（訓練データとテストデータ）
X_train, X_test, y_train, y_test = train_test_split(
    embeddings, labels, test_size=0.3, random_state=42
)

# ランダムフォレストによる分類
clf = RandomForestClassifier(n_estimators=100, random_state=42)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)

# 精度の評価
accuracy = accuracy_score(y_test, y_pred)
print(f'\n分類精度: {accuracy:.2f}')

# 結果の可視化（クラス別）
plt.figure(figsize=(10, 8))
scatter = plt.scatter(embeddings_2d[:, 0], embeddings_2d[:, 1], c=labels, cmap='coolwarm')
plt.title('Node Embeddings by Club Membership')
plt.colorbar(scatter)
plt.show()
```