Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file addedrecognition/s4571084/.DS_Store
Binary file not shown.
64 changes: 64 additions & 0 deletions recognition/s4571084/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
# Graph Convolutional Networks
This is a PyTorch implementation of GCN model to carry out a semi supervised multi-class
node classification using Facebook Large Page-Page Network dataset.

References:
Thomas N. Kipf, Max Welling, [Semi-Supervised Classification with Graph Convolutional Networks (ICLR 2017)](https://arxiv.org/abs/1609.02907)

## Description of algorithm
GCN is the first-order local approximation of spectral convolution. It is a multi-layer graph convolution neural network. Each convolution layer only processes the first-order neighborhood information. The information transmission of multi-order neighborhood can be realized by stacking several convolution layers. The magic of GCN is that it can aggregate the node features near a node and learn the node features through weighted aggregation, so as to do a series of prediction tasks.
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot.png)
Reference: [https://paperswithcode.com/method/gcn](https://paperswithcode.com/method/gcn)<br />
Suppose we have a batch of graph data, in which there are n nodes, and each node has its own feature dimensions, we set the features of these nodes to form an n * d dimensional characteristic matrix X, and then the relationship between each node will also form an n × n dimension adjacency matrix A. X and A are the inputs to our model.
For all nodes, H<sup>(l)</sup> represents the eigenvector matrix of all stages in layer l, H<sup>(l+1)</sup> represents all eigenvector matrices after one convolution operation. The formula of one-time convolution operation is as follows:
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot2.png)<br />
Reference: [https://medium.com/skylab-air/text-classification-using-graph-convolutional-networks-9a3b0479ada1](https://medium.com/skylab-air/text-classification-using-graph-convolutional-networks-9a3b0479ada1)<br />
In fact, it realizes the weighted summation of the neighbor nodes of each node in the graph, and uses the multiplication with the parameter matrix to obtain the characteristics of the node of the new layer. The features of each node are iteratively calculated in the form of matrix, then convolution is carried out through layer propagation, and finally the features of node are updated.


## Data
Using Facebook Large Page-Page Network dataset:
- 22470 nodes
- 171002 edges
- 128 dim vectors
- [Partially processed dataset](https://graphmining.ai/datasets/ptg/facebook.npz) will be used, which contains three npy files.

## Implement
- Load data
- N * N adjacency matrix (N: number of nodes)
- N * D features matrix (E: number of features)
- labels to one hot
- GCN layers
- similar to the CNN layers
- reset_parameters function to ensure the inital parameters are the same for every test
- forward function uses A * X * W
- GCN model
- two layers of GCN model
- first layers input dimension: 128; output dimension: 16
- first layers input dimension: 16; output dimension: 4
- forward function to getpPropagation mode: Relu -> Fropout -> gc2 -> softmax
- Train
- loss function only calculate the nodes in train set
- back propagation to optimize loss function

## Requirements
- PyTorch 0.4 or 0.5
- python 2.7 or 3.6

## Run the model
```
cd s4571084

python gcn.py
```
## Visualization
Rnning the training script:<br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/shotScreen5.jpeg)<br />
Visualize the loss and accuracy <br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot4.png)<br />
TSNE Result:<br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot3.png)<br />




82 changes: 82 additions & 0 deletions recognition/s4571084/gcn.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
"""gcn.ipynb

Automatically generated by Colaboratory.

Original file is located at
https://colab.research.google.com/drive/19CHiNNkww39vm_KI_Ov0GMJekRuEg3BF
"""

import numpy as np
import scipy.sparse as sp
import torch
from sklearn.preprocessing import LabelBinarizer
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import model as M

#train the model
learning_rate = 0.1
weight_decay = 5e-4
epochs = 200

#define some arguments
device = "cuda" if torch.cuda.is_available() else "cpu"
model = M.GcnNet().to(device)
criterion = nn.CrossEntropyLoss().to(device)
optimizer = optim.Adam(model.parameters(), lr=learning_rate, weight_decay=weight_decay)

adjacency, features, labels, train_mask, val_mask, test_mask= M.load_data()
tensor_x = features.to(device)
tensor_y = labels.to(device)
tensor_train_mask = torch.from_numpy(train_mask).to(device)
tensor_val_mask = torch.from_numpy(val_mask).to(device)
tensor_test_mask = torch.from_numpy(test_mask).to(device)
indices = torch.from_numpy(np.asarray([adjacency.row, adjacency.col]).astype('int64')).long()
values = torch.from_numpy(adjacency.data.astype(np.float32))
tensor_adjacency = torch.sparse.FloatTensor(indices, values, (22470, 22470)).to(device)

#train function
def train():
loss_history = []
val_acc_history = []
model.train()
train_y = tensor_y[tensor_train_mask]
for epoch in range(epochs):
logits = model(tensor_adjacency, tensor_x)
#only choose node to train
train_mask_logits = logits[tensor_train_mask]
loss = criterion(train_mask_logits, train_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_acc, _, _ = test(tensor_train_mask)
val_acc, _, _ = test(tensor_val_mask)
loss_history.append(loss.item())
val_acc_history.append(val_acc.item())
print("Epoch {:03d}: Loss {:.4f}, TestAcc {:.4}, TrainAcc {:.4f}".format(
epoch, loss.item(), train_acc.item(), val_acc.item()))

def test(mask):
model.eval()
with torch.no_grad():
logits = model(tensor_adjacency, tensor_x)
test_mask_logits = logits[mask]
predict_y = test_mask_logits.max(1)[1]
accuarcy = torch.eq(predict_y, tensor_y[mask]).float().mean()
return accuarcy, test_mask_logits.cpu().numpy(), tensor_y[mask].cpu().numpy()

train()

"""
TSNE part for embeddings plot.
"""
from sklearn.manifold import TSNE
test_accuracy, test_data, test_labels = test(tensor_test_mask)
tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000)
low_dim_embs = tsne.fit_transform(test_data)
plt.title('tsne result')
plt.scatter(low_dim_embs[:,0], low_dim_embs[:,1], marker='o', c=test_labels)
plt.savefig("tsne.png")
130 changes: 130 additions & 0 deletions recognition/s4571084/model.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
"""gcn.ipynb

Automatically generated by Colaboratory.

Original file is located at
https://colab.research.google.com/drive/19CHiNNkww39vm_KI_Ov0GMJekRuEg3BF
"""

import numpy as np
import scipy.sparse as sp
import torch
from sklearn.preprocessing import LabelBinarizer
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import matplotlib.pyplot as plt

"""
normalize the data to 1.
"""
def normalize_adj(adjacency):
adjacency += sp.eye(adjacency.shape[0])
degree = np.array(adjacency.sum(1))
d_hat = sp.diags(np.power(degree, -0.5).flatten())
return d_hat.dot(adjacency).dot(d_hat).tocoo()


def normalize_features(features):
return features / features.sum(1)

"""
load data from facebook.npz and set adjacency/features/labels
"""
def load_data():
dataset = np.load('./facebook.npz')
edges = dataset['edges']
features = dataset['features']
target = dataset['target']

# set adjacency
n = len(target)
x = np.zeros((n, n), dtype=np.float32)
for i in edges:
x[i[0]][i[1]] = 1

edges = sp.csr_matrix(x)
adjacency = normalize_adj(edges)

# set features
features = sp.csr_matrix(features, dtype=np.float32)
features = normalize_features(features)
features = torch.FloatTensor(np.array(features))

# set labels
encode_onehot = LabelBinarizer()
labels = encode_onehot.fit_transform(target)
labels = torch.LongTensor(np.where(labels)[1])

num_nodes = features.shape[0]
train_mask = np.zeros(num_nodes, dtype=np.bool)
val_mask = np.zeros(num_nodes, dtype=np.bool)
test_mask = np.zeros(num_nodes, dtype=np.bool)

train_mask[0:140] = True
val_mask[200:500] = True
test_mask[500:1500] = True

return adjacency, features, labels, train_mask, val_mask, test_mask

"""
Graph Convalution
"""
class GraphConvolution(nn.Module):
def __init__(self, input_dim, output_dim, use_bias=True):
"""
input_dim: int. Dimension of input nodes
output_dim: int. Dimension of output nodes
use_bias : bool, optional.
"""
super(GraphConvolution, self).__init__()
self.input_dim = input_dim
self.output_dim = output_dim
self.use_bias = use_bias
self.weight = nn.Parameter(torch.Tensor(input_dim, output_dim))
if self.use_bias:
self.bias = nn.Parameter(torch.Tensor(output_dim))
else:
self.register_parameter('bias', None)
self.reset_parameters()

def reset_parameters(self):
init.kaiming_uniform_(self.weight)
if self.use_bias:
init.zeros_(self.bias)

def forward(self, adjacency, input_feature):
"""
adjacency matrix is sparse matrix, thus this will use sparse when
calculate.
adjacency: torch.sparse.FloatTensor
input_feature: torch.Tensor
"""
device = "cuda" if torch.cuda.is_available() else "cpu"
support = torch.mm(input_feature, self.weight.to(device))
output = torch.sparse.mm(adjacency, support)
if self.use_bias:
output += self.bias.to(device)
return output

def __repr__(self):
return self.__class__.__name__ + ' (' + str(self.in_features) + ' -> ' + str(self.out_features) + ')'

"""
define model
"""
class GcnNet(nn.Module):

def __init__(self, input_dim=128):
super(GcnNet, self).__init__()
self.gcn1 = GraphConvolution(input_dim, 16)
self.gcn2 = GraphConvolution(16, 4)

def forward(self, adjacency, feature):
h = F.relu(self.gcn1(adjacency, feature))
logits = self.gcn2(adjacency, h)
return logits


, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file addedrecognition/s4571084/.DS_Store
Binary file not shown.
64 changes: 64 additions & 0 deletions recognition/s4571084/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
# Graph Convolutional Networks
This is a PyTorch implementation of GCN model to carry out a semi supervised multi-class
node classification using Facebook Large Page-Page Network dataset.

References:
Thomas N. Kipf, Max Welling, [Semi-Supervised Classification with Graph Convolutional Networks (ICLR 2017)](https://arxiv.org/abs/1609.02907)

## Description of algorithm
GCN is the first-order local approximation of spectral convolution. It is a multi-layer graph convolution neural network. Each convolution layer only processes the first-order neighborhood information. The information transmission of multi-order neighborhood can be realized by stacking several convolution layers. The magic of GCN is that it can aggregate the node features near a node and learn the node features through weighted aggregation, so as to do a series of prediction tasks.
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot.png)
Reference: [https://paperswithcode.com/method/gcn](https://paperswithcode.com/method/gcn)<br />
Suppose we have a batch of graph data, in which there are n nodes, and each node has its own feature dimensions, we set the features of these nodes to form an n * d dimensional characteristic matrix X, and then the relationship between each node will also form an n × n dimension adjacency matrix A. X and A are the inputs to our model.
For all nodes, H<sup>(l)</sup> represents the eigenvector matrix of all stages in layer l, H<sup>(l+1)</sup> represents all eigenvector matrices after one convolution operation. The formula of one-time convolution operation is as follows:
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot2.png)<br />
Reference: [https://medium.com/skylab-air/text-classification-using-graph-convolutional-networks-9a3b0479ada1](https://medium.com/skylab-air/text-classification-using-graph-convolutional-networks-9a3b0479ada1)<br />
In fact, it realizes the weighted summation of the neighbor nodes of each node in the graph, and uses the multiplication with the parameter matrix to obtain the characteristics of the node of the new layer. The features of each node are iteratively calculated in the form of matrix, then convolution is carried out through layer propagation, and finally the features of node are updated.


## Data
Using Facebook Large Page-Page Network dataset:
- 22470 nodes
- 171002 edges
- 128 dim vectors
- [Partially processed dataset](https://graphmining.ai/datasets/ptg/facebook.npz) will be used, which contains three npy files.

## Implement
- Load data
- N * N adjacency matrix (N: number of nodes)
- N * D features matrix (E: number of features)
- labels to one hot
- GCN layers
- similar to the CNN layers
- reset_parameters function to ensure the inital parameters are the same for every test
- forward function uses A * X * W
- GCN model
- two layers of GCN model
- first layers input dimension: 128; output dimension: 16
- first layers input dimension: 16; output dimension: 4
- forward function to getpPropagation mode: Relu -> Fropout -> gc2 -> softmax
- Train
- loss function only calculate the nodes in train set
- back propagation to optimize loss function

## Requirements
- PyTorch 0.4 or 0.5
- python 2.7 or 3.6

## Run the model
```
cd s4571084

python gcn.py
```
## Visualization
Rnning the training script:<br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/shotScreen5.jpeg)<br />
Visualize the loss and accuracy <br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot4.png)<br />
TSNE Result:<br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot3.png)<br />




82 changes: 82 additions & 0 deletions recognition/s4571084/gcn.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
"""gcn.ipynb

Automatically generated by Colaboratory.

Original file is located at
https://colab.research.google.com/drive/19CHiNNkww39vm_KI_Ov0GMJekRuEg3BF
"""

import numpy as np
import scipy.sparse as sp
import torch
from sklearn.preprocessing import LabelBinarizer
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import model as M

#train the model
learning_rate = 0.1
weight_decay = 5e-4
epochs = 200

#define some arguments
device = "cuda" if torch.cuda.is_available() else "cpu"
model = M.GcnNet().to(device)
criterion = nn.CrossEntropyLoss().to(device)
optimizer = optim.Adam(model.parameters(), lr=learning_rate, weight_decay=weight_decay)

adjacency, features, labels, train_mask, val_mask, test_mask= M.load_data()
tensor_x = features.to(device)
tensor_y = labels.to(device)
tensor_train_mask = torch.from_numpy(train_mask).to(device)
tensor_val_mask = torch.from_numpy(val_mask).to(device)
tensor_test_mask = torch.from_numpy(test_mask).to(device)
indices = torch.from_numpy(np.asarray([adjacency.row, adjacency.col]).astype('int64')).long()
values = torch.from_numpy(adjacency.data.astype(np.float32))
tensor_adjacency = torch.sparse.FloatTensor(indices, values, (22470, 22470)).to(device)

#train function
def train():
loss_history = []
val_acc_history = []
model.train()
train_y = tensor_y[tensor_train_mask]
for epoch in range(epochs):
logits = model(tensor_adjacency, tensor_x)
#only choose node to train
train_mask_logits = logits[tensor_train_mask]
loss = criterion(train_mask_logits, train_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_acc, _, _ = test(tensor_train_mask)
val_acc, _, _ = test(tensor_val_mask)
loss_history.append(loss.item())
val_acc_history.append(val_acc.item())
print("Epoch {:03d}: Loss {:.4f}, TestAcc {:.4}, TrainAcc {:.4f}".format(
epoch, loss.item(), train_acc.item(), val_acc.item()))

def test(mask):
model.eval()
with torch.no_grad():
logits = model(tensor_adjacency, tensor_x)
test_mask_logits = logits[mask]
predict_y = test_mask_logits.max(1)[1]
accuarcy = torch.eq(predict_y, tensor_y[mask]).float().mean()
return accuarcy, test_mask_logits.cpu().numpy(), tensor_y[mask].cpu().numpy()

train()

"""
TSNE part for embeddings plot.
"""
from sklearn.manifold import TSNE
test_accuracy, test_data, test_labels = test(tensor_test_mask)
tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000)
low_dim_embs = tsne.fit_transform(test_data)
plt.title('tsne result')
plt.scatter(low_dim_embs[:,0], low_dim_embs[:,1], marker='o', c=test_labels)
plt.savefig("tsne.png")
130 changes: 130 additions & 0 deletions recognition/s4571084/model.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
"""gcn.ipynb

Automatically generated by Colaboratory.

Original file is located at
https://colab.research.google.com/drive/19CHiNNkww39vm_KI_Ov0GMJekRuEg3BF
"""

import numpy as np
import scipy.sparse as sp
import torch
from sklearn.preprocessing import LabelBinarizer
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import matplotlib.pyplot as plt

"""
normalize the data to 1.
"""
def normalize_adj(adjacency):
adjacency += sp.eye(adjacency.shape[0])
degree = np.array(adjacency.sum(1))
d_hat = sp.diags(np.power(degree, -0.5).flatten())
return d_hat.dot(adjacency).dot(d_hat).tocoo()


def normalize_features(features):
return features / features.sum(1)

"""
load data from facebook.npz and set adjacency/features/labels
"""
def load_data():
dataset = np.load('./facebook.npz')
edges = dataset['edges']
features = dataset['features']
target = dataset['target']

# set adjacency
n = len(target)
x = np.zeros((n, n), dtype=np.float32)
for i in edges:
x[i[0]][i[1]] = 1

edges = sp.csr_matrix(x)
adjacency = normalize_adj(edges)

# set features
features = sp.csr_matrix(features, dtype=np.float32)
features = normalize_features(features)
features = torch.FloatTensor(np.array(features))

# set labels
encode_onehot = LabelBinarizer()
labels = encode_onehot.fit_transform(target)
labels = torch.LongTensor(np.where(labels)[1])

num_nodes = features.shape[0]
train_mask = np.zeros(num_nodes, dtype=np.bool)
val_mask = np.zeros(num_nodes, dtype=np.bool)
test_mask = np.zeros(num_nodes, dtype=np.bool)

train_mask[0:140] = True
val_mask[200:500] = True
test_mask[500:1500] = True

return adjacency, features, labels, train_mask, val_mask, test_mask

"""
Graph Convalution
"""
class GraphConvolution(nn.Module):
def __init__(self, input_dim, output_dim, use_bias=True):
"""
input_dim: int. Dimension of input nodes
output_dim: int. Dimension of output nodes
use_bias : bool, optional.
"""
super(GraphConvolution, self).__init__()
self.input_dim = input_dim
self.output_dim = output_dim
self.use_bias = use_bias
self.weight = nn.Parameter(torch.Tensor(input_dim, output_dim))
if self.use_bias:
self.bias = nn.Parameter(torch.Tensor(output_dim))
else:
self.register_parameter('bias', None)
self.reset_parameters()

def reset_parameters(self):
init.kaiming_uniform_(self.weight)
if self.use_bias:
init.zeros_(self.bias)

def forward(self, adjacency, input_feature):
"""
adjacency matrix is sparse matrix, thus this will use sparse when
calculate.
adjacency: torch.sparse.FloatTensor
input_feature: torch.Tensor
"""
device = "cuda" if torch.cuda.is_available() else "cpu"
support = torch.mm(input_feature, self.weight.to(device))
output = torch.sparse.mm(adjacency, support)
if self.use_bias:
output += self.bias.to(device)
return output

def __repr__(self):
return self.__class__.__name__ + ' (' + str(self.in_features) + ' -> ' + str(self.out_features) + ')'

"""
define model
"""
class GcnNet(nn.Module):

def __init__(self, input_dim=128):
super(GcnNet, self).__init__()
self.gcn1 = GraphConvolution(input_dim, 16)
self.gcn2 = GraphConvolution(16, 4)

def forward(self, adjacency, feature):
h = F.relu(self.gcn1(adjacency, feature))
logits = self.gcn2(adjacency, h)
return logits


, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file addedrecognition/s4571084/.DS_Store
Binary file not shown.
64 changes: 64 additions & 0 deletions recognition/s4571084/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
# Graph Convolutional Networks
This is a PyTorch implementation of GCN model to carry out a semi supervised multi-class
node classification using Facebook Large Page-Page Network dataset.

References:
Thomas N. Kipf, Max Welling, [Semi-Supervised Classification with Graph Convolutional Networks (ICLR 2017)](https://arxiv.org/abs/1609.02907)

## Description of algorithm
GCN is the first-order local approximation of spectral convolution. It is a multi-layer graph convolution neural network. Each convolution layer only processes the first-order neighborhood information. The information transmission of multi-order neighborhood can be realized by stacking several convolution layers. The magic of GCN is that it can aggregate the node features near a node and learn the node features through weighted aggregation, so as to do a series of prediction tasks.
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot.png)
Reference: [https://paperswithcode.com/method/gcn](https://paperswithcode.com/method/gcn)<br />
Suppose we have a batch of graph data, in which there are n nodes, and each node has its own feature dimensions, we set the features of these nodes to form an n * d dimensional characteristic matrix X, and then the relationship between each node will also form an n × n dimension adjacency matrix A. X and A are the inputs to our model.
For all nodes, H<sup>(l)</sup> represents the eigenvector matrix of all stages in layer l, H<sup>(l+1)</sup> represents all eigenvector matrices after one convolution operation. The formula of one-time convolution operation is as follows:
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot2.png)<br />
Reference: [https://medium.com/skylab-air/text-classification-using-graph-convolutional-networks-9a3b0479ada1](https://medium.com/skylab-air/text-classification-using-graph-convolutional-networks-9a3b0479ada1)<br />
In fact, it realizes the weighted summation of the neighbor nodes of each node in the graph, and uses the multiplication with the parameter matrix to obtain the characteristics of the node of the new layer. The features of each node are iteratively calculated in the form of matrix, then convolution is carried out through layer propagation, and finally the features of node are updated.


## Data
Using Facebook Large Page-Page Network dataset:
- 22470 nodes
- 171002 edges
- 128 dim vectors
- [Partially processed dataset](https://graphmining.ai/datasets/ptg/facebook.npz) will be used, which contains three npy files.

## Implement
- Load data
- N * N adjacency matrix (N: number of nodes)
- N * D features matrix (E: number of features)
- labels to one hot
- GCN layers
- similar to the CNN layers
- reset_parameters function to ensure the inital parameters are the same for every test
- forward function uses A * X * W
- GCN model
- two layers of GCN model
- first layers input dimension: 128; output dimension: 16
- first layers input dimension: 16; output dimension: 4
- forward function to getpPropagation mode: Relu -> Fropout -> gc2 -> softmax
- Train
- loss function only calculate the nodes in train set
- back propagation to optimize loss function

## Requirements
- PyTorch 0.4 or 0.5
- python 2.7 or 3.6

## Run the model
```
cd s4571084

python gcn.py
```
## Visualization
Rnning the training script:<br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/shotScreen5.jpeg)<br />
Visualize the loss and accuracy <br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot4.png)<br />
TSNE Result:<br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot3.png)<br />




82 changes: 82 additions & 0 deletions recognition/s4571084/gcn.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
"""gcn.ipynb

Automatically generated by Colaboratory.

Original file is located at
https://colab.research.google.com/drive/19CHiNNkww39vm_KI_Ov0GMJekRuEg3BF
"""

import numpy as np
import scipy.sparse as sp
import torch
from sklearn.preprocessing import LabelBinarizer
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import model as M

#train the model
learning_rate = 0.1
weight_decay = 5e-4
epochs = 200

#define some arguments
device = "cuda" if torch.cuda.is_available() else "cpu"
model = M.GcnNet().to(device)
criterion = nn.CrossEntropyLoss().to(device)
optimizer = optim.Adam(model.parameters(), lr=learning_rate, weight_decay=weight_decay)

adjacency, features, labels, train_mask, val_mask, test_mask= M.load_data()
tensor_x = features.to(device)
tensor_y = labels.to(device)
tensor_train_mask = torch.from_numpy(train_mask).to(device)
tensor_val_mask = torch.from_numpy(val_mask).to(device)
tensor_test_mask = torch.from_numpy(test_mask).to(device)
indices = torch.from_numpy(np.asarray([adjacency.row, adjacency.col]).astype('int64')).long()
values = torch.from_numpy(adjacency.data.astype(np.float32))
tensor_adjacency = torch.sparse.FloatTensor(indices, values, (22470, 22470)).to(device)

#train function
def train():
loss_history = []
val_acc_history = []
model.train()
train_y = tensor_y[tensor_train_mask]
for epoch in range(epochs):
logits = model(tensor_adjacency, tensor_x)
#only choose node to train
train_mask_logits = logits[tensor_train_mask]
loss = criterion(train_mask_logits, train_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_acc, _, _ = test(tensor_train_mask)
val_acc, _, _ = test(tensor_val_mask)
loss_history.append(loss.item())
val_acc_history.append(val_acc.item())
print("Epoch {:03d}: Loss {:.4f}, TestAcc {:.4}, TrainAcc {:.4f}".format(
epoch, loss.item(), train_acc.item(), val_acc.item()))

def test(mask):
model.eval()
with torch.no_grad():
logits = model(tensor_adjacency, tensor_x)
test_mask_logits = logits[mask]
predict_y = test_mask_logits.max(1)[1]
accuarcy = torch.eq(predict_y, tensor_y[mask]).float().mean()
return accuarcy, test_mask_logits.cpu().numpy(), tensor_y[mask].cpu().numpy()

train()

"""
TSNE part for embeddings plot.
"""
from sklearn.manifold import TSNE
test_accuracy, test_data, test_labels = test(tensor_test_mask)
tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000)
low_dim_embs = tsne.fit_transform(test_data)
plt.title('tsne result')
plt.scatter(low_dim_embs[:,0], low_dim_embs[:,1], marker='o', c=test_labels)
plt.savefig("tsne.png")
130 changes: 130 additions & 0 deletions recognition/s4571084/model.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
"""gcn.ipynb

Automatically generated by Colaboratory.

Original file is located at
https://colab.research.google.com/drive/19CHiNNkww39vm_KI_Ov0GMJekRuEg3BF
"""

import numpy as np
import scipy.sparse as sp
import torch
from sklearn.preprocessing import LabelBinarizer
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import matplotlib.pyplot as plt

"""
normalize the data to 1.
"""
def normalize_adj(adjacency):
adjacency += sp.eye(adjacency.shape[0])
degree = np.array(adjacency.sum(1))
d_hat = sp.diags(np.power(degree, -0.5).flatten())
return d_hat.dot(adjacency).dot(d_hat).tocoo()


def normalize_features(features):
return features / features.sum(1)

"""
load data from facebook.npz and set adjacency/features/labels
"""
def load_data():
dataset = np.load('./facebook.npz')
edges = dataset['edges']
features = dataset['features']
target = dataset['target']

# set adjacency
n = len(target)
x = np.zeros((n, n), dtype=np.float32)
for i in edges:
x[i[0]][i[1]] = 1

edges = sp.csr_matrix(x)
adjacency = normalize_adj(edges)

# set features
features = sp.csr_matrix(features, dtype=np.float32)
features = normalize_features(features)
features = torch.FloatTensor(np.array(features))

# set labels
encode_onehot = LabelBinarizer()
labels = encode_onehot.fit_transform(target)
labels = torch.LongTensor(np.where(labels)[1])

num_nodes = features.shape[0]
train_mask = np.zeros(num_nodes, dtype=np.bool)
val_mask = np.zeros(num_nodes, dtype=np.bool)
test_mask = np.zeros(num_nodes, dtype=np.bool)

train_mask[0:140] = True
val_mask[200:500] = True
test_mask[500:1500] = True

return adjacency, features, labels, train_mask, val_mask, test_mask

"""
Graph Convalution
"""
class GraphConvolution(nn.Module):
def __init__(self, input_dim, output_dim, use_bias=True):
"""
input_dim: int. Dimension of input nodes
output_dim: int. Dimension of output nodes
use_bias : bool, optional.
"""
super(GraphConvolution, self).__init__()
self.input_dim = input_dim
self.output_dim = output_dim
self.use_bias = use_bias
self.weight = nn.Parameter(torch.Tensor(input_dim, output_dim))
if self.use_bias:
self.bias = nn.Parameter(torch.Tensor(output_dim))
else:
self.register_parameter('bias', None)
self.reset_parameters()

def reset_parameters(self):
init.kaiming_uniform_(self.weight)
if self.use_bias:
init.zeros_(self.bias)

def forward(self, adjacency, input_feature):
"""
adjacency matrix is sparse matrix, thus this will use sparse when
calculate.
adjacency: torch.sparse.FloatTensor
input_feature: torch.Tensor
"""
device = "cuda" if torch.cuda.is_available() else "cpu"
support = torch.mm(input_feature, self.weight.to(device))
output = torch.sparse.mm(adjacency, support)
if self.use_bias:
output += self.bias.to(device)
return output

def __repr__(self):
return self.__class__.__name__ + ' (' + str(self.in_features) + ' -> ' + str(self.out_features) + ')'

"""
define model
"""
class GcnNet(nn.Module):

def __init__(self, input_dim=128):
super(GcnNet, self).__init__()
self.gcn1 = GraphConvolution(input_dim, 16)
self.gcn2 = GraphConvolution(16, 4)

def forward(self, adjacency, feature):
h = F.relu(self.gcn1(adjacency, feature))
logits = self.gcn2(adjacency, h)
return logits


, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file addedrecognition/s4571084/.DS_Store
Binary file not shown.
64 changes: 64 additions & 0 deletions recognition/s4571084/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
# Graph Convolutional Networks
This is a PyTorch implementation of GCN model to carry out a semi supervised multi-class
node classification using Facebook Large Page-Page Network dataset.

References:
Thomas N. Kipf, Max Welling, [Semi-Supervised Classification with Graph Convolutional Networks (ICLR 2017)](https://arxiv.org/abs/1609.02907)

## Description of algorithm
GCN is the first-order local approximation of spectral convolution. It is a multi-layer graph convolution neural network. Each convolution layer only processes the first-order neighborhood information. The information transmission of multi-order neighborhood can be realized by stacking several convolution layers. The magic of GCN is that it can aggregate the node features near a node and learn the node features through weighted aggregation, so as to do a series of prediction tasks.
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot.png)
Reference: [https://paperswithcode.com/method/gcn](https://paperswithcode.com/method/gcn)<br />
Suppose we have a batch of graph data, in which there are n nodes, and each node has its own feature dimensions, we set the features of these nodes to form an n * d dimensional characteristic matrix X, and then the relationship between each node will also form an n × n dimension adjacency matrix A. X and A are the inputs to our model.
For all nodes, H<sup>(l)</sup> represents the eigenvector matrix of all stages in layer l, H<sup>(l+1)</sup> represents all eigenvector matrices after one convolution operation. The formula of one-time convolution operation is as follows:
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot2.png)<br />
Reference: [https://medium.com/skylab-air/text-classification-using-graph-convolutional-networks-9a3b0479ada1](https://medium.com/skylab-air/text-classification-using-graph-convolutional-networks-9a3b0479ada1)<br />
In fact, it realizes the weighted summation of the neighbor nodes of each node in the graph, and uses the multiplication with the parameter matrix to obtain the characteristics of the node of the new layer. The features of each node are iteratively calculated in the form of matrix, then convolution is carried out through layer propagation, and finally the features of node are updated.


## Data
Using Facebook Large Page-Page Network dataset:
- 22470 nodes
- 171002 edges
- 128 dim vectors
- [Partially processed dataset](https://graphmining.ai/datasets/ptg/facebook.npz) will be used, which contains three npy files.

## Implement
- Load data
- N * N adjacency matrix (N: number of nodes)
- N * D features matrix (E: number of features)
- labels to one hot
- GCN layers
- similar to the CNN layers
- reset_parameters function to ensure the inital parameters are the same for every test
- forward function uses A * X * W
- GCN model
- two layers of GCN model
- first layers input dimension: 128; output dimension: 16
- first layers input dimension: 16; output dimension: 4
- forward function to getpPropagation mode: Relu -> Fropout -> gc2 -> softmax
- Train
- loss function only calculate the nodes in train set
- back propagation to optimize loss function

## Requirements
- PyTorch 0.4 or 0.5
- python 2.7 or 3.6

## Run the model
```
cd s4571084

python gcn.py
```
## Visualization
Rnning the training script:<br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/shotScreen5.jpeg)<br />
Visualize the loss and accuracy <br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot4.png)<br />
TSNE Result:<br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot3.png)<br />




82 changes: 82 additions & 0 deletions recognition/s4571084/gcn.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
"""gcn.ipynb

Automatically generated by Colaboratory.

Original file is located at
https://colab.research.google.com/drive/19CHiNNkww39vm_KI_Ov0GMJekRuEg3BF
"""

import numpy as np
import scipy.sparse as sp
import torch
from sklearn.preprocessing import LabelBinarizer
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import model as M

#train the model
learning_rate = 0.1
weight_decay = 5e-4
epochs = 200

#define some arguments
device = "cuda" if torch.cuda.is_available() else "cpu"
model = M.GcnNet().to(device)
criterion = nn.CrossEntropyLoss().to(device)
optimizer = optim.Adam(model.parameters(), lr=learning_rate, weight_decay=weight_decay)

adjacency, features, labels, train_mask, val_mask, test_mask= M.load_data()
tensor_x = features.to(device)
tensor_y = labels.to(device)
tensor_train_mask = torch.from_numpy(train_mask).to(device)
tensor_val_mask = torch.from_numpy(val_mask).to(device)
tensor_test_mask = torch.from_numpy(test_mask).to(device)
indices = torch.from_numpy(np.asarray([adjacency.row, adjacency.col]).astype('int64')).long()
values = torch.from_numpy(adjacency.data.astype(np.float32))
tensor_adjacency = torch.sparse.FloatTensor(indices, values, (22470, 22470)).to(device)

#train function
def train():
loss_history = []
val_acc_history = []
model.train()
train_y = tensor_y[tensor_train_mask]
for epoch in range(epochs):
logits = model(tensor_adjacency, tensor_x)
#only choose node to train
train_mask_logits = logits[tensor_train_mask]
loss = criterion(train_mask_logits, train_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_acc, _, _ = test(tensor_train_mask)
val_acc, _, _ = test(tensor_val_mask)
loss_history.append(loss.item())
val_acc_history.append(val_acc.item())
print("Epoch {:03d}: Loss {:.4f}, TestAcc {:.4}, TrainAcc {:.4f}".format(
epoch, loss.item(), train_acc.item(), val_acc.item()))

def test(mask):
model.eval()
with torch.no_grad():
logits = model(tensor_adjacency, tensor_x)
test_mask_logits = logits[mask]
predict_y = test_mask_logits.max(1)[1]
accuarcy = torch.eq(predict_y, tensor_y[mask]).float().mean()
return accuarcy, test_mask_logits.cpu().numpy(), tensor_y[mask].cpu().numpy()

train()

"""
TSNE part for embeddings plot.
"""
from sklearn.manifold import TSNE
test_accuracy, test_data, test_labels = test(tensor_test_mask)
tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000)
low_dim_embs = tsne.fit_transform(test_data)
plt.title('tsne result')
plt.scatter(low_dim_embs[:,0], low_dim_embs[:,1], marker='o', c=test_labels)
plt.savefig("tsne.png")
130 changes: 130 additions & 0 deletions recognition/s4571084/model.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
"""gcn.ipynb

Automatically generated by Colaboratory.

Original file is located at
https://colab.research.google.com/drive/19CHiNNkww39vm_KI_Ov0GMJekRuEg3BF
"""

import numpy as np
import scipy.sparse as sp
import torch
from sklearn.preprocessing import LabelBinarizer
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import matplotlib.pyplot as plt

"""
normalize the data to 1.
"""
def normalize_adj(adjacency):
adjacency += sp.eye(adjacency.shape[0])
degree = np.array(adjacency.sum(1))
d_hat = sp.diags(np.power(degree, -0.5).flatten())
return d_hat.dot(adjacency).dot(d_hat).tocoo()


def normalize_features(features):
return features / features.sum(1)

"""
load data from facebook.npz and set adjacency/features/labels
"""
def load_data():
dataset = np.load('./facebook.npz')
edges = dataset['edges']
features = dataset['features']
target = dataset['target']

# set adjacency
n = len(target)
x = np.zeros((n, n), dtype=np.float32)
for i in edges:
x[i[0]][i[1]] = 1

edges = sp.csr_matrix(x)
adjacency = normalize_adj(edges)

# set features
features = sp.csr_matrix(features, dtype=np.float32)
features = normalize_features(features)
features = torch.FloatTensor(np.array(features))

# set labels
encode_onehot = LabelBinarizer()
labels = encode_onehot.fit_transform(target)
labels = torch.LongTensor(np.where(labels)[1])

num_nodes = features.shape[0]
train_mask = np.zeros(num_nodes, dtype=np.bool)
val_mask = np.zeros(num_nodes, dtype=np.bool)
test_mask = np.zeros(num_nodes, dtype=np.bool)

train_mask[0:140] = True
val_mask[200:500] = True
test_mask[500:1500] = True

return adjacency, features, labels, train_mask, val_mask, test_mask

"""
Graph Convalution
"""
class GraphConvolution(nn.Module):
def __init__(self, input_dim, output_dim, use_bias=True):
"""
input_dim: int. Dimension of input nodes
output_dim: int. Dimension of output nodes
use_bias : bool, optional.
"""
super(GraphConvolution, self).__init__()
self.input_dim = input_dim
self.output_dim = output_dim
self.use_bias = use_bias
self.weight = nn.Parameter(torch.Tensor(input_dim, output_dim))
if self.use_bias:
self.bias = nn.Parameter(torch.Tensor(output_dim))
else:
self.register_parameter('bias', None)
self.reset_parameters()

def reset_parameters(self):
init.kaiming_uniform_(self.weight)
if self.use_bias:
init.zeros_(self.bias)

def forward(self, adjacency, input_feature):
"""
adjacency matrix is sparse matrix, thus this will use sparse when
calculate.
adjacency: torch.sparse.FloatTensor
input_feature: torch.Tensor
"""
device = "cuda" if torch.cuda.is_available() else "cpu"
support = torch.mm(input_feature, self.weight.to(device))
output = torch.sparse.mm(adjacency, support)
if self.use_bias:
output += self.bias.to(device)
return output

def __repr__(self):
return self.__class__.__name__ + ' (' + str(self.in_features) + ' -> ' + str(self.out_features) + ')'

"""
define model
"""
class GcnNet(nn.Module):

def __init__(self, input_dim=128):
super(GcnNet, self).__init__()
self.gcn1 = GraphConvolution(input_dim, 16)
self.gcn2 = GraphConvolution(16, 4)

def forward(self, adjacency, feature):
h = F.relu(self.gcn1(adjacency, feature))
logits = self.gcn2(adjacency, h)
return logits


, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file addedrecognition/s4571084/.DS_Store
Binary file not shown.
64 changes: 64 additions & 0 deletions recognition/s4571084/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
# Graph Convolutional Networks
This is a PyTorch implementation of GCN model to carry out a semi supervised multi-class
node classification using Facebook Large Page-Page Network dataset.

References:
Thomas N. Kipf, Max Welling, [Semi-Supervised Classification with Graph Convolutional Networks (ICLR 2017)](https://arxiv.org/abs/1609.02907)

## Description of algorithm
GCN is the first-order local approximation of spectral convolution. It is a multi-layer graph convolution neural network. Each convolution layer only processes the first-order neighborhood information. The information transmission of multi-order neighborhood can be realized by stacking several convolution layers. The magic of GCN is that it can aggregate the node features near a node and learn the node features through weighted aggregation, so as to do a series of prediction tasks.
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot.png)
Reference: [https://paperswithcode.com/method/gcn](https://paperswithcode.com/method/gcn)<br />
Suppose we have a batch of graph data, in which there are n nodes, and each node has its own feature dimensions, we set the features of these nodes to form an n * d dimensional characteristic matrix X, and then the relationship between each node will also form an n × n dimension adjacency matrix A. X and A are the inputs to our model.
For all nodes, H<sup>(l)</sup> represents the eigenvector matrix of all stages in layer l, H<sup>(l+1)</sup> represents all eigenvector matrices after one convolution operation. The formula of one-time convolution operation is as follows:
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot2.png)<br />
Reference: [https://medium.com/skylab-air/text-classification-using-graph-convolutional-networks-9a3b0479ada1](https://medium.com/skylab-air/text-classification-using-graph-convolutional-networks-9a3b0479ada1)<br />
In fact, it realizes the weighted summation of the neighbor nodes of each node in the graph, and uses the multiplication with the parameter matrix to obtain the characteristics of the node of the new layer. The features of each node are iteratively calculated in the form of matrix, then convolution is carried out through layer propagation, and finally the features of node are updated.


## Data
Using Facebook Large Page-Page Network dataset:
- 22470 nodes
- 171002 edges
- 128 dim vectors
- [Partially processed dataset](https://graphmining.ai/datasets/ptg/facebook.npz) will be used, which contains three npy files.

## Implement
- Load data
- N * N adjacency matrix (N: number of nodes)
- N * D features matrix (E: number of features)
- labels to one hot
- GCN layers
- similar to the CNN layers
- reset_parameters function to ensure the inital parameters are the same for every test
- forward function uses A * X * W
- GCN model
- two layers of GCN model
- first layers input dimension: 128; output dimension: 16
- first layers input dimension: 16; output dimension: 4
- forward function to getpPropagation mode: Relu -> Fropout -> gc2 -> softmax
- Train
- loss function only calculate the nodes in train set
- back propagation to optimize loss function

## Requirements
- PyTorch 0.4 or 0.5
- python 2.7 or 3.6

## Run the model
```
cd s4571084

python gcn.py
```
## Visualization
Rnning the training script:<br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/shotScreen5.jpeg)<br />
Visualize the loss and accuracy <br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot4.png)<br />
TSNE Result:<br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot3.png)<br />




82 changes: 82 additions & 0 deletions recognition/s4571084/gcn.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
"""gcn.ipynb

Automatically generated by Colaboratory.

Original file is located at
https://colab.research.google.com/drive/19CHiNNkww39vm_KI_Ov0GMJekRuEg3BF
"""

import numpy as np
import scipy.sparse as sp
import torch
from sklearn.preprocessing import LabelBinarizer
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import model as M

#train the model
learning_rate = 0.1
weight_decay = 5e-4
epochs = 200

#define some arguments
device = "cuda" if torch.cuda.is_available() else "cpu"
model = M.GcnNet().to(device)
criterion = nn.CrossEntropyLoss().to(device)
optimizer = optim.Adam(model.parameters(), lr=learning_rate, weight_decay=weight_decay)

adjacency, features, labels, train_mask, val_mask, test_mask= M.load_data()
tensor_x = features.to(device)
tensor_y = labels.to(device)
tensor_train_mask = torch.from_numpy(train_mask).to(device)
tensor_val_mask = torch.from_numpy(val_mask).to(device)
tensor_test_mask = torch.from_numpy(test_mask).to(device)
indices = torch.from_numpy(np.asarray([adjacency.row, adjacency.col]).astype('int64')).long()
values = torch.from_numpy(adjacency.data.astype(np.float32))
tensor_adjacency = torch.sparse.FloatTensor(indices, values, (22470, 22470)).to(device)

#train function
def train():
loss_history = []
val_acc_history = []
model.train()
train_y = tensor_y[tensor_train_mask]
for epoch in range(epochs):
logits = model(tensor_adjacency, tensor_x)
#only choose node to train
train_mask_logits = logits[tensor_train_mask]
loss = criterion(train_mask_logits, train_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_acc, _, _ = test(tensor_train_mask)
val_acc, _, _ = test(tensor_val_mask)
loss_history.append(loss.item())
val_acc_history.append(val_acc.item())
print("Epoch {:03d}: Loss {:.4f}, TestAcc {:.4}, TrainAcc {:.4f}".format(
epoch, loss.item(), train_acc.item(), val_acc.item()))

def test(mask):
model.eval()
with torch.no_grad():
logits = model(tensor_adjacency, tensor_x)
test_mask_logits = logits[mask]
predict_y = test_mask_logits.max(1)[1]
accuarcy = torch.eq(predict_y, tensor_y[mask]).float().mean()
return accuarcy, test_mask_logits.cpu().numpy(), tensor_y[mask].cpu().numpy()

train()

"""
TSNE part for embeddings plot.
"""
from sklearn.manifold import TSNE
test_accuracy, test_data, test_labels = test(tensor_test_mask)
tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000)
low_dim_embs = tsne.fit_transform(test_data)
plt.title('tsne result')
plt.scatter(low_dim_embs[:,0], low_dim_embs[:,1], marker='o', c=test_labels)
plt.savefig("tsne.png")
130 changes: 130 additions & 0 deletions recognition/s4571084/model.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
"""gcn.ipynb

Automatically generated by Colaboratory.

Original file is located at
https://colab.research.google.com/drive/19CHiNNkww39vm_KI_Ov0GMJekRuEg3BF
"""

import numpy as np
import scipy.sparse as sp
import torch
from sklearn.preprocessing import LabelBinarizer
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import matplotlib.pyplot as plt

"""
normalize the data to 1.
"""
def normalize_adj(adjacency):
adjacency += sp.eye(adjacency.shape[0])
degree = np.array(adjacency.sum(1))
d_hat = sp.diags(np.power(degree, -0.5).flatten())
return d_hat.dot(adjacency).dot(d_hat).tocoo()


def normalize_features(features):
return features / features.sum(1)

"""
load data from facebook.npz and set adjacency/features/labels
"""
def load_data():
dataset = np.load('./facebook.npz')
edges = dataset['edges']
features = dataset['features']
target = dataset['target']

# set adjacency
n = len(target)
x = np.zeros((n, n), dtype=np.float32)
for i in edges:
x[i[0]][i[1]] = 1

edges = sp.csr_matrix(x)
adjacency = normalize_adj(edges)

# set features
features = sp.csr_matrix(features, dtype=np.float32)
features = normalize_features(features)
features = torch.FloatTensor(np.array(features))

# set labels
encode_onehot = LabelBinarizer()
labels = encode_onehot.fit_transform(target)
labels = torch.LongTensor(np.where(labels)[1])

num_nodes = features.shape[0]
train_mask = np.zeros(num_nodes, dtype=np.bool)
val_mask = np.zeros(num_nodes, dtype=np.bool)
test_mask = np.zeros(num_nodes, dtype=np.bool)

train_mask[0:140] = True
val_mask[200:500] = True
test_mask[500:1500] = True

return adjacency, features, labels, train_mask, val_mask, test_mask

"""
Graph Convalution
"""
class GraphConvolution(nn.Module):
def __init__(self, input_dim, output_dim, use_bias=True):
"""
input_dim: int. Dimension of input nodes
output_dim: int. Dimension of output nodes
use_bias : bool, optional.
"""
super(GraphConvolution, self).__init__()
self.input_dim = input_dim
self.output_dim = output_dim
self.use_bias = use_bias
self.weight = nn.Parameter(torch.Tensor(input_dim, output_dim))
if self.use_bias:
self.bias = nn.Parameter(torch.Tensor(output_dim))
else:
self.register_parameter('bias', None)
self.reset_parameters()

def reset_parameters(self):
init.kaiming_uniform_(self.weight)
if self.use_bias:
init.zeros_(self.bias)

def forward(self, adjacency, input_feature):
"""
adjacency matrix is sparse matrix, thus this will use sparse when
calculate.
adjacency: torch.sparse.FloatTensor
input_feature: torch.Tensor
"""
device = "cuda" if torch.cuda.is_available() else "cpu"
support = torch.mm(input_feature, self.weight.to(device))
output = torch.sparse.mm(adjacency, support)
if self.use_bias:
output += self.bias.to(device)
return output

def __repr__(self):
return self.__class__.__name__ + ' (' + str(self.in_features) + ' -> ' + str(self.out_features) + ')'

"""
define model
"""
class GcnNet(nn.Module):

def __init__(self, input_dim=128):
super(GcnNet, self).__init__()
self.gcn1 = GraphConvolution(input_dim, 16)
self.gcn2 = GraphConvolution(16, 4)

def forward(self, adjacency, feature):
h = F.relu(self.gcn1(adjacency, feature))
logits = self.gcn2(adjacency, h)
return logits


, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file addedrecognition/s4571084/.DS_Store
Binary file not shown.
64 changes: 64 additions & 0 deletions recognition/s4571084/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
# Graph Convolutional Networks
This is a PyTorch implementation of GCN model to carry out a semi supervised multi-class
node classification using Facebook Large Page-Page Network dataset.

References:
Thomas N. Kipf, Max Welling, [Semi-Supervised Classification with Graph Convolutional Networks (ICLR 2017)](https://arxiv.org/abs/1609.02907)

## Description of algorithm
GCN is the first-order local approximation of spectral convolution. It is a multi-layer graph convolution neural network. Each convolution layer only processes the first-order neighborhood information. The information transmission of multi-order neighborhood can be realized by stacking several convolution layers. The magic of GCN is that it can aggregate the node features near a node and learn the node features through weighted aggregation, so as to do a series of prediction tasks.
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot.png)
Reference: [https://paperswithcode.com/method/gcn](https://paperswithcode.com/method/gcn)<br />
Suppose we have a batch of graph data, in which there are n nodes, and each node has its own feature dimensions, we set the features of these nodes to form an n * d dimensional characteristic matrix X, and then the relationship between each node will also form an n × n dimension adjacency matrix A. X and A are the inputs to our model.
For all nodes, H<sup>(l)</sup> represents the eigenvector matrix of all stages in layer l, H<sup>(l+1)</sup> represents all eigenvector matrices after one convolution operation. The formula of one-time convolution operation is as follows:
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot2.png)<br />
Reference: [https://medium.com/skylab-air/text-classification-using-graph-convolutional-networks-9a3b0479ada1](https://medium.com/skylab-air/text-classification-using-graph-convolutional-networks-9a3b0479ada1)<br />
In fact, it realizes the weighted summation of the neighbor nodes of each node in the graph, and uses the multiplication with the parameter matrix to obtain the characteristics of the node of the new layer. The features of each node are iteratively calculated in the form of matrix, then convolution is carried out through layer propagation, and finally the features of node are updated.


## Data
Using Facebook Large Page-Page Network dataset:
- 22470 nodes
- 171002 edges
- 128 dim vectors
- [Partially processed dataset](https://graphmining.ai/datasets/ptg/facebook.npz) will be used, which contains three npy files.

## Implement
- Load data
- N * N adjacency matrix (N: number of nodes)
- N * D features matrix (E: number of features)
- labels to one hot
- GCN layers
- similar to the CNN layers
- reset_parameters function to ensure the inital parameters are the same for every test
- forward function uses A * X * W
- GCN model
- two layers of GCN model
- first layers input dimension: 128; output dimension: 16
- first layers input dimension: 16; output dimension: 4
- forward function to getpPropagation mode: Relu -> Fropout -> gc2 -> softmax
- Train
- loss function only calculate the nodes in train set
- back propagation to optimize loss function

## Requirements
- PyTorch 0.4 or 0.5
- python 2.7 or 3.6

## Run the model
```
cd s4571084

python gcn.py
```
## Visualization
Rnning the training script:<br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/shotScreen5.jpeg)<br />
Visualize the loss and accuracy <br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot4.png)<br />
TSNE Result:<br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot3.png)<br />




82 changes: 82 additions & 0 deletions recognition/s4571084/gcn.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
"""gcn.ipynb

Automatically generated by Colaboratory.

Original file is located at
https://colab.research.google.com/drive/19CHiNNkww39vm_KI_Ov0GMJekRuEg3BF
"""

import numpy as np
import scipy.sparse as sp
import torch
from sklearn.preprocessing import LabelBinarizer
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import model as M

#train the model
learning_rate = 0.1
weight_decay = 5e-4
epochs = 200

#define some arguments
device = "cuda" if torch.cuda.is_available() else "cpu"
model = M.GcnNet().to(device)
criterion = nn.CrossEntropyLoss().to(device)
optimizer = optim.Adam(model.parameters(), lr=learning_rate, weight_decay=weight_decay)

adjacency, features, labels, train_mask, val_mask, test_mask= M.load_data()
tensor_x = features.to(device)
tensor_y = labels.to(device)
tensor_train_mask = torch.from_numpy(train_mask).to(device)
tensor_val_mask = torch.from_numpy(val_mask).to(device)
tensor_test_mask = torch.from_numpy(test_mask).to(device)
indices = torch.from_numpy(np.asarray([adjacency.row, adjacency.col]).astype('int64')).long()
values = torch.from_numpy(adjacency.data.astype(np.float32))
tensor_adjacency = torch.sparse.FloatTensor(indices, values, (22470, 22470)).to(device)

#train function
def train():
loss_history = []
val_acc_history = []
model.train()
train_y = tensor_y[tensor_train_mask]
for epoch in range(epochs):
logits = model(tensor_adjacency, tensor_x)
#only choose node to train
train_mask_logits = logits[tensor_train_mask]
loss = criterion(train_mask_logits, train_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_acc, _, _ = test(tensor_train_mask)
val_acc, _, _ = test(tensor_val_mask)
loss_history.append(loss.item())
val_acc_history.append(val_acc.item())
print("Epoch {:03d}: Loss {:.4f}, TestAcc {:.4}, TrainAcc {:.4f}".format(
epoch, loss.item(), train_acc.item(), val_acc.item()))

def test(mask):
model.eval()
with torch.no_grad():
logits = model(tensor_adjacency, tensor_x)
test_mask_logits = logits[mask]
predict_y = test_mask_logits.max(1)[1]
accuarcy = torch.eq(predict_y, tensor_y[mask]).float().mean()
return accuarcy, test_mask_logits.cpu().numpy(), tensor_y[mask].cpu().numpy()

train()

"""
TSNE part for embeddings plot.
"""
from sklearn.manifold import TSNE
test_accuracy, test_data, test_labels = test(tensor_test_mask)
tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000)
low_dim_embs = tsne.fit_transform(test_data)
plt.title('tsne result')
plt.scatter(low_dim_embs[:,0], low_dim_embs[:,1], marker='o', c=test_labels)
plt.savefig("tsne.png")
130 changes: 130 additions & 0 deletions recognition/s4571084/model.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
"""gcn.ipynb

Automatically generated by Colaboratory.

Original file is located at
https://colab.research.google.com/drive/19CHiNNkww39vm_KI_Ov0GMJekRuEg3BF
"""

import numpy as np
import scipy.sparse as sp
import torch
from sklearn.preprocessing import LabelBinarizer
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import matplotlib.pyplot as plt

"""
normalize the data to 1.
"""
def normalize_adj(adjacency):
adjacency += sp.eye(adjacency.shape[0])
degree = np.array(adjacency.sum(1))
d_hat = sp.diags(np.power(degree, -0.5).flatten())
return d_hat.dot(adjacency).dot(d_hat).tocoo()


def normalize_features(features):
return features / features.sum(1)

"""
load data from facebook.npz and set adjacency/features/labels
"""
def load_data():
dataset = np.load('./facebook.npz')
edges = dataset['edges']
features = dataset['features']
target = dataset['target']

# set adjacency
n = len(target)
x = np.zeros((n, n), dtype=np.float32)
for i in edges:
x[i[0]][i[1]] = 1

edges = sp.csr_matrix(x)
adjacency = normalize_adj(edges)

# set features
features = sp.csr_matrix(features, dtype=np.float32)
features = normalize_features(features)
features = torch.FloatTensor(np.array(features))

# set labels
encode_onehot = LabelBinarizer()
labels = encode_onehot.fit_transform(target)
labels = torch.LongTensor(np.where(labels)[1])

num_nodes = features.shape[0]
train_mask = np.zeros(num_nodes, dtype=np.bool)
val_mask = np.zeros(num_nodes, dtype=np.bool)
test_mask = np.zeros(num_nodes, dtype=np.bool)

train_mask[0:140] = True
val_mask[200:500] = True
test_mask[500:1500] = True

return adjacency, features, labels, train_mask, val_mask, test_mask

"""
Graph Convalution
"""
class GraphConvolution(nn.Module):
def __init__(self, input_dim, output_dim, use_bias=True):
"""
input_dim: int. Dimension of input nodes
output_dim: int. Dimension of output nodes
use_bias : bool, optional.
"""
super(GraphConvolution, self).__init__()
self.input_dim = input_dim
self.output_dim = output_dim
self.use_bias = use_bias
self.weight = nn.Parameter(torch.Tensor(input_dim, output_dim))
if self.use_bias:
self.bias = nn.Parameter(torch.Tensor(output_dim))
else:
self.register_parameter('bias', None)
self.reset_parameters()

def reset_parameters(self):
init.kaiming_uniform_(self.weight)
if self.use_bias:
init.zeros_(self.bias)

def forward(self, adjacency, input_feature):
"""
adjacency matrix is sparse matrix, thus this will use sparse when
calculate.
adjacency: torch.sparse.FloatTensor
input_feature: torch.Tensor
"""
device = "cuda" if torch.cuda.is_available() else "cpu"
support = torch.mm(input_feature, self.weight.to(device))
output = torch.sparse.mm(adjacency, support)
if self.use_bias:
output += self.bias.to(device)
return output

def __repr__(self):
return self.__class__.__name__ + ' (' + str(self.in_features) + ' -> ' + str(self.out_features) + ')'

"""
define model
"""
class GcnNet(nn.Module):

def __init__(self, input_dim=128):
super(GcnNet, self).__init__()
self.gcn1 = GraphConvolution(input_dim, 16)
self.gcn2 = GraphConvolution(16, 4)

def forward(self, adjacency, feature):
h = F.relu(self.gcn1(adjacency, feature))
logits = self.gcn2(adjacency, h)
return logits


, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file addedrecognition/s4571084/.DS_Store
Binary file not shown.
64 changes: 64 additions & 0 deletions recognition/s4571084/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
# Graph Convolutional Networks
This is a PyTorch implementation of GCN model to carry out a semi supervised multi-class
node classification using Facebook Large Page-Page Network dataset.

References:
Thomas N. Kipf, Max Welling, [Semi-Supervised Classification with Graph Convolutional Networks (ICLR 2017)](https://arxiv.org/abs/1609.02907)

## Description of algorithm
GCN is the first-order local approximation of spectral convolution. It is a multi-layer graph convolution neural network. Each convolution layer only processes the first-order neighborhood information. The information transmission of multi-order neighborhood can be realized by stacking several convolution layers. The magic of GCN is that it can aggregate the node features near a node and learn the node features through weighted aggregation, so as to do a series of prediction tasks.
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot.png)
Reference: [https://paperswithcode.com/method/gcn](https://paperswithcode.com/method/gcn)<br />
Suppose we have a batch of graph data, in which there are n nodes, and each node has its own feature dimensions, we set the features of these nodes to form an n * d dimensional characteristic matrix X, and then the relationship between each node will also form an n × n dimension adjacency matrix A. X and A are the inputs to our model.
For all nodes, H<sup>(l)</sup> represents the eigenvector matrix of all stages in layer l, H<sup>(l+1)</sup> represents all eigenvector matrices after one convolution operation. The formula of one-time convolution operation is as follows:
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot2.png)<br />
Reference: [https://medium.com/skylab-air/text-classification-using-graph-convolutional-networks-9a3b0479ada1](https://medium.com/skylab-air/text-classification-using-graph-convolutional-networks-9a3b0479ada1)<br />
In fact, it realizes the weighted summation of the neighbor nodes of each node in the graph, and uses the multiplication with the parameter matrix to obtain the characteristics of the node of the new layer. The features of each node are iteratively calculated in the form of matrix, then convolution is carried out through layer propagation, and finally the features of node are updated.


## Data
Using Facebook Large Page-Page Network dataset:
- 22470 nodes
- 171002 edges
- 128 dim vectors
- [Partially processed dataset](https://graphmining.ai/datasets/ptg/facebook.npz) will be used, which contains three npy files.

## Implement
- Load data
- N * N adjacency matrix (N: number of nodes)
- N * D features matrix (E: number of features)
- labels to one hot
- GCN layers
- similar to the CNN layers
- reset_parameters function to ensure the inital parameters are the same for every test
- forward function uses A * X * W
- GCN model
- two layers of GCN model
- first layers input dimension: 128; output dimension: 16
- first layers input dimension: 16; output dimension: 4
- forward function to getpPropagation mode: Relu -> Fropout -> gc2 -> softmax
- Train
- loss function only calculate the nodes in train set
- back propagation to optimize loss function

## Requirements
- PyTorch 0.4 or 0.5
- python 2.7 or 3.6

## Run the model
```
cd s4571084

python gcn.py
```
## Visualization
Rnning the training script:<br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/shotScreen5.jpeg)<br />
Visualize the loss and accuracy <br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot4.png)<br />
TSNE Result:<br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot3.png)<br />




82 changes: 82 additions & 0 deletions recognition/s4571084/gcn.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
"""gcn.ipynb

Automatically generated by Colaboratory.

Original file is located at
https://colab.research.google.com/drive/19CHiNNkww39vm_KI_Ov0GMJekRuEg3BF
"""

import numpy as np
import scipy.sparse as sp
import torch
from sklearn.preprocessing import LabelBinarizer
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import model as M

#train the model
learning_rate = 0.1
weight_decay = 5e-4
epochs = 200

#define some arguments
device = "cuda" if torch.cuda.is_available() else "cpu"
model = M.GcnNet().to(device)
criterion = nn.CrossEntropyLoss().to(device)
optimizer = optim.Adam(model.parameters(), lr=learning_rate, weight_decay=weight_decay)

adjacency, features, labels, train_mask, val_mask, test_mask= M.load_data()
tensor_x = features.to(device)
tensor_y = labels.to(device)
tensor_train_mask = torch.from_numpy(train_mask).to(device)
tensor_val_mask = torch.from_numpy(val_mask).to(device)
tensor_test_mask = torch.from_numpy(test_mask).to(device)
indices = torch.from_numpy(np.asarray([adjacency.row, adjacency.col]).astype('int64')).long()
values = torch.from_numpy(adjacency.data.astype(np.float32))
tensor_adjacency = torch.sparse.FloatTensor(indices, values, (22470, 22470)).to(device)

#train function
def train():
loss_history = []
val_acc_history = []
model.train()
train_y = tensor_y[tensor_train_mask]
for epoch in range(epochs):
logits = model(tensor_adjacency, tensor_x)
#only choose node to train
train_mask_logits = logits[tensor_train_mask]
loss = criterion(train_mask_logits, train_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_acc, _, _ = test(tensor_train_mask)
val_acc, _, _ = test(tensor_val_mask)
loss_history.append(loss.item())
val_acc_history.append(val_acc.item())
print("Epoch {:03d}: Loss {:.4f}, TestAcc {:.4}, TrainAcc {:.4f}".format(
epoch, loss.item(), train_acc.item(), val_acc.item()))

def test(mask):
model.eval()
with torch.no_grad():
logits = model(tensor_adjacency, tensor_x)
test_mask_logits = logits[mask]
predict_y = test_mask_logits.max(1)[1]
accuarcy = torch.eq(predict_y, tensor_y[mask]).float().mean()
return accuarcy, test_mask_logits.cpu().numpy(), tensor_y[mask].cpu().numpy()

train()

"""
TSNE part for embeddings plot.
"""
from sklearn.manifold import TSNE
test_accuracy, test_data, test_labels = test(tensor_test_mask)
tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000)
low_dim_embs = tsne.fit_transform(test_data)
plt.title('tsne result')
plt.scatter(low_dim_embs[:,0], low_dim_embs[:,1], marker='o', c=test_labels)
plt.savefig("tsne.png")
130 changes: 130 additions & 0 deletions recognition/s4571084/model.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
"""gcn.ipynb

Automatically generated by Colaboratory.

Original file is located at
https://colab.research.google.com/drive/19CHiNNkww39vm_KI_Ov0GMJekRuEg3BF
"""

import numpy as np
import scipy.sparse as sp
import torch
from sklearn.preprocessing import LabelBinarizer
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import matplotlib.pyplot as plt

"""
normalize the data to 1.
"""
def normalize_adj(adjacency):
adjacency += sp.eye(adjacency.shape[0])
degree = np.array(adjacency.sum(1))
d_hat = sp.diags(np.power(degree, -0.5).flatten())
return d_hat.dot(adjacency).dot(d_hat).tocoo()


def normalize_features(features):
return features / features.sum(1)

"""
load data from facebook.npz and set adjacency/features/labels
"""
def load_data():
dataset = np.load('./facebook.npz')
edges = dataset['edges']
features = dataset['features']
target = dataset['target']

# set adjacency
n = len(target)
x = np.zeros((n, n), dtype=np.float32)
for i in edges:
x[i[0]][i[1]] = 1

edges = sp.csr_matrix(x)
adjacency = normalize_adj(edges)

# set features
features = sp.csr_matrix(features, dtype=np.float32)
features = normalize_features(features)
features = torch.FloatTensor(np.array(features))

# set labels
encode_onehot = LabelBinarizer()
labels = encode_onehot.fit_transform(target)
labels = torch.LongTensor(np.where(labels)[1])

num_nodes = features.shape[0]
train_mask = np.zeros(num_nodes, dtype=np.bool)
val_mask = np.zeros(num_nodes, dtype=np.bool)
test_mask = np.zeros(num_nodes, dtype=np.bool)

train_mask[0:140] = True
val_mask[200:500] = True
test_mask[500:1500] = True

return adjacency, features, labels, train_mask, val_mask, test_mask

"""
Graph Convalution
"""
class GraphConvolution(nn.Module):
def __init__(self, input_dim, output_dim, use_bias=True):
"""
input_dim: int. Dimension of input nodes
output_dim: int. Dimension of output nodes
use_bias : bool, optional.
"""
super(GraphConvolution, self).__init__()
self.input_dim = input_dim
self.output_dim = output_dim
self.use_bias = use_bias
self.weight = nn.Parameter(torch.Tensor(input_dim, output_dim))
if self.use_bias:
self.bias = nn.Parameter(torch.Tensor(output_dim))
else:
self.register_parameter('bias', None)
self.reset_parameters()

def reset_parameters(self):
init.kaiming_uniform_(self.weight)
if self.use_bias:
init.zeros_(self.bias)

def forward(self, adjacency, input_feature):
"""
adjacency matrix is sparse matrix, thus this will use sparse when
calculate.
adjacency: torch.sparse.FloatTensor
input_feature: torch.Tensor
"""
device = "cuda" if torch.cuda.is_available() else "cpu"
support = torch.mm(input_feature, self.weight.to(device))
output = torch.sparse.mm(adjacency, support)
if self.use_bias:
output += self.bias.to(device)
return output

def __repr__(self):
return self.__class__.__name__ + ' (' + str(self.in_features) + ' -> ' + str(self.out_features) + ')'

"""
define model
"""
class GcnNet(nn.Module):

def __init__(self, input_dim=128):
super(GcnNet, self).__init__()
self.gcn1 = GraphConvolution(input_dim, 16)
self.gcn2 = GraphConvolution(16, 4)

def forward(self, adjacency, feature):
h = F.relu(self.gcn1(adjacency, feature))
logits = self.gcn2(adjacency, h)
return logits


, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file addedrecognition/s4571084/.DS_Store
Binary file not shown.
64 changes: 64 additions & 0 deletions recognition/s4571084/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
# Graph Convolutional Networks
This is a PyTorch implementation of GCN model to carry out a semi supervised multi-class
node classification using Facebook Large Page-Page Network dataset.

References:
Thomas N. Kipf, Max Welling, [Semi-Supervised Classification with Graph Convolutional Networks (ICLR 2017)](https://arxiv.org/abs/1609.02907)

## Description of algorithm
GCN is the first-order local approximation of spectral convolution. It is a multi-layer graph convolution neural network. Each convolution layer only processes the first-order neighborhood information. The information transmission of multi-order neighborhood can be realized by stacking several convolution layers. The magic of GCN is that it can aggregate the node features near a node and learn the node features through weighted aggregation, so as to do a series of prediction tasks.
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot.png)
Reference: [https://paperswithcode.com/method/gcn](https://paperswithcode.com/method/gcn)<br />
Suppose we have a batch of graph data, in which there are n nodes, and each node has its own feature dimensions, we set the features of these nodes to form an n * d dimensional characteristic matrix X, and then the relationship between each node will also form an n × n dimension adjacency matrix A. X and A are the inputs to our model.
For all nodes, H<sup>(l)</sup> represents the eigenvector matrix of all stages in layer l, H<sup>(l+1)</sup> represents all eigenvector matrices after one convolution operation. The formula of one-time convolution operation is as follows:
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot2.png)<br />
Reference: [https://medium.com/skylab-air/text-classification-using-graph-convolutional-networks-9a3b0479ada1](https://medium.com/skylab-air/text-classification-using-graph-convolutional-networks-9a3b0479ada1)<br />
In fact, it realizes the weighted summation of the neighbor nodes of each node in the graph, and uses the multiplication with the parameter matrix to obtain the characteristics of the node of the new layer. The features of each node are iteratively calculated in the form of matrix, then convolution is carried out through layer propagation, and finally the features of node are updated.


## Data
Using Facebook Large Page-Page Network dataset:
- 22470 nodes
- 171002 edges
- 128 dim vectors
- [Partially processed dataset](https://graphmining.ai/datasets/ptg/facebook.npz) will be used, which contains three npy files.

## Implement
- Load data
- N * N adjacency matrix (N: number of nodes)
- N * D features matrix (E: number of features)
- labels to one hot
- GCN layers
- similar to the CNN layers
- reset_parameters function to ensure the inital parameters are the same for every test
- forward function uses A * X * W
- GCN model
- two layers of GCN model
- first layers input dimension: 128; output dimension: 16
- first layers input dimension: 16; output dimension: 4
- forward function to getpPropagation mode: Relu -> Fropout -> gc2 -> softmax
- Train
- loss function only calculate the nodes in train set
- back propagation to optimize loss function

## Requirements
- PyTorch 0.4 or 0.5
- python 2.7 or 3.6

## Run the model
```
cd s4571084

python gcn.py
```
## Visualization
Rnning the training script:<br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/shotScreen5.jpeg)<br />
Visualize the loss and accuracy <br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot4.png)<br />
TSNE Result:<br />
![Alt text](https://github.com/shunyuLiu/pract/blob/master/Screenshot3.png)<br />




82 changes: 82 additions & 0 deletions recognition/s4571084/gcn.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
"""gcn.ipynb

Automatically generated by Colaboratory.

Original file is located at
https://colab.research.google.com/drive/19CHiNNkww39vm_KI_Ov0GMJekRuEg3BF
"""

import numpy as np
import scipy.sparse as sp
import torch
from sklearn.preprocessing import LabelBinarizer
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import model as M

#train the model
learning_rate = 0.1
weight_decay = 5e-4
epochs = 200

#define some arguments
device = "cuda" if torch.cuda.is_available() else "cpu"
model = M.GcnNet().to(device)
criterion = nn.CrossEntropyLoss().to(device)
optimizer = optim.Adam(model.parameters(), lr=learning_rate, weight_decay=weight_decay)

adjacency, features, labels, train_mask, val_mask, test_mask= M.load_data()
tensor_x = features.to(device)
tensor_y = labels.to(device)
tensor_train_mask = torch.from_numpy(train_mask).to(device)
tensor_val_mask = torch.from_numpy(val_mask).to(device)
tensor_test_mask = torch.from_numpy(test_mask).to(device)
indices = torch.from_numpy(np.asarray([adjacency.row, adjacency.col]).astype('int64')).long()
values = torch.from_numpy(adjacency.data.astype(np.float32))
tensor_adjacency = torch.sparse.FloatTensor(indices, values, (22470, 22470)).to(device)

#train function
def train():
loss_history = []
val_acc_history = []
model.train()
train_y = tensor_y[tensor_train_mask]
for epoch in range(epochs):
logits = model(tensor_adjacency, tensor_x)
#only choose node to train
train_mask_logits = logits[tensor_train_mask]
loss = criterion(train_mask_logits, train_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_acc, _, _ = test(tensor_train_mask)
val_acc, _, _ = test(tensor_val_mask)
loss_history.append(loss.item())
val_acc_history.append(val_acc.item())
print("Epoch {:03d}: Loss {:.4f}, TestAcc {:.4}, TrainAcc {:.4f}".format(
epoch, loss.item(), train_acc.item(), val_acc.item()))

def test(mask):
model.eval()
with torch.no_grad():
logits = model(tensor_adjacency, tensor_x)
test_mask_logits = logits[mask]
predict_y = test_mask_logits.max(1)[1]
accuarcy = torch.eq(predict_y, tensor_y[mask]).float().mean()
return accuarcy, test_mask_logits.cpu().numpy(), tensor_y[mask].cpu().numpy()

train()

"""
TSNE part for embeddings plot.
"""
from sklearn.manifold import TSNE
test_accuracy, test_data, test_labels = test(tensor_test_mask)
tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000)
low_dim_embs = tsne.fit_transform(test_data)
plt.title('tsne result')
plt.scatter(low_dim_embs[:,0], low_dim_embs[:,1], marker='o', c=test_labels)
plt.savefig("tsne.png")
130 changes: 130 additions & 0 deletions recognition/s4571084/model.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
"""gcn.ipynb

Automatically generated by Colaboratory.

Original file is located at
https://colab.research.google.com/drive/19CHiNNkww39vm_KI_Ov0GMJekRuEg3BF
"""

import numpy as np
import scipy.sparse as sp
import torch
from sklearn.preprocessing import LabelBinarizer
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init
import torch.optim as optim
import matplotlib.pyplot as plt

"""
normalize the data to 1.
"""
def normalize_adj(adjacency):
adjacency += sp.eye(adjacency.shape[0])
degree = np.array(adjacency.sum(1))
d_hat = sp.diags(np.power(degree, -0.5).flatten())
return d_hat.dot(adjacency).dot(d_hat).tocoo()


def normalize_features(features):
return features / features.sum(1)

"""
load data from facebook.npz and set adjacency/features/labels
"""
def load_data():
dataset = np.load('./facebook.npz')
edges = dataset['edges']
features = dataset['features']
target = dataset['target']

# set adjacency
n = len(target)
x = np.zeros((n, n), dtype=np.float32)
for i in edges:
x[i[0]][i[1]] = 1

edges = sp.csr_matrix(x)
adjacency = normalize_adj(edges)

# set features
features = sp.csr_matrix(features, dtype=np.float32)
features = normalize_features(features)
features = torch.FloatTensor(np.array(features))

# set labels
encode_onehot = LabelBinarizer()
labels = encode_onehot.fit_transform(target)
labels = torch.LongTensor(np.where(labels)[1])

num_nodes = features.shape[0]
train_mask = np.zeros(num_nodes, dtype=np.bool)
val_mask = np.zeros(num_nodes, dtype=np.bool)
test_mask = np.zeros(num_nodes, dtype=np.bool)

train_mask[0:140] = True
val_mask[200:500] = True
test_mask[500:1500] = True

return adjacency, features, labels, train_mask, val_mask, test_mask

"""
Graph Convalution
"""
class GraphConvolution(nn.Module):
def __init__(self, input_dim, output_dim, use_bias=True):
"""
input_dim: int. Dimension of input nodes
output_dim: int. Dimension of output nodes
use_bias : bool, optional.
"""
super(GraphConvolution, self).__init__()
self.input_dim = input_dim
self.output_dim = output_dim
self.use_bias = use_bias
self.weight = nn.Parameter(torch.Tensor(input_dim, output_dim))
if self.use_bias:
self.bias = nn.Parameter(torch.Tensor(output_dim))
else:
self.register_parameter('bias', None)
self.reset_parameters()

def reset_parameters(self):
init.kaiming_uniform_(self.weight)
if self.use_bias:
init.zeros_(self.bias)

def forward(self, adjacency, input_feature):
"""
adjacency matrix is sparse matrix, thus this will use sparse when
calculate.
adjacency: torch.sparse.FloatTensor
input_feature: torch.Tensor
"""
device = "cuda" if torch.cuda.is_available() else "cpu"
support = torch.mm(input_feature, self.weight.to(device))
output = torch.sparse.mm(adjacency, support)
if self.use_bias:
output += self.bias.to(device)
return output

def __repr__(self):
return self.__class__.__name__ + ' (' + str(self.in_features) + ' -> ' + str(self.out_features) + ')'

"""
define model
"""
class GcnNet(nn.Module):

def __init__(self, input_dim=128):
super(GcnNet, self).__init__()
self.gcn1 = GraphConvolution(input_dim, 16)
self.gcn2 = GraphConvolution(16, 4)

def forward(self, adjacency, feature):
h = F.relu(self.gcn1(adjacency, feature))
logits = self.gcn2(adjacency, h)
return logits