import numpy as np
import pandas as pd
import pickle
import csv
import os
import torch
from torch_geometric.data import Data
from torch_geometric.data import InMemoryDataset
from torch_geometric.nn import GraphConv, TopKPooling, GatedGraphConv, SAGEConv, SGConv
from torch_geometric.nn import global_mean_pool as gap, global_max_pool as gmp
from torch_geometric.nn import SAGEConv, to_hetero
from torch_geometric.nn import GATConv, Linear, to_hetero
import torch.nn.functional as F
from tqdm import tqdm
import networkx as nx
seed = 0
np.random.seed(seed)
torch.manual_seed(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
!pip install tensorboard
%load_ext tensorboard
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
G = nx.balanced_tree(2, 5)
edge_nodes = [x for (x,d) in nx.degree(G) if d==1]
print("Total number of nodes: ",len(G.nodes))
print(" - Edge nodes: %i %0.3f%%"%(len(edge_nodes),len(edge_nodes)/len(G.nodes)))
pos=nx.spring_layout(G,seed=seed)
labels=nx.draw_networkx_labels(G,pos)
nx.draw(G,pos)
nx.draw_networkx_nodes(G,pos,nodelist=edge_nodes,node_color="r")
nx.draw_networkx_nodes(G,pos,nodelist=[0],node_size=500,node_color="g")
Total number of nodes: 63 - Edge nodes: 32 0.508%
<matplotlib.collections.PathCollection at 0x7fcb9123d3a0>
#Node Features
levels = nx.single_source_shortest_path_length(G,source=0) #get tier-level based on Cloud-node length
nLevel = max(levels.values())+1
centrality = nx.eigenvector_centrality(G)
features = []
for n in G.nodes():
hwr = (nLevel-levels[n])*10
watts = (0.2 if n%2==0 else 2.4)
features.append([n,centrality[n],levels[n],hwr,watts,int(n in edge_nodes)])
features = np.array(features)
num_features = features.shape[1]
print(num_features)
print(features[:3])
print(features[-3:])
6 [[ 0. 0.23190717 0. 60. 0.2 0. ] [ 1. 0.29549267 1. 50. 2.4 0. ] [ 2. 0.29549267 1. 50. 0.2 0. ]] [[6.00000000e+01 4.10005359e-02 5.00000000e+00 1.00000000e+01 2.00000000e-01 1.00000000e+00] [6.10000000e+01 4.10005359e-02 5.00000000e+00 1.00000000e+01 2.40000000e+00 1.00000000e+00] [6.20000000e+01 4.10005359e-02 5.00000000e+00 1.00000000e+01 2.00000000e-01 1.00000000e+00]]
#Test. Sample 1
alloc = 0 #the service is in node 0
neighs = [n for n in G.neighbors(alloc)]
edge_index = [[*[alloc]*len(neighs),*neighs],[*neighs,*[alloc]*len(neighs)]] #Doubt: bidirectional?
print(edge_index)
print(np.array(features[[alloc,*neighs]]).astype(float))
node_feats = torch.LongTensor(features[[alloc,*neighs]])
node_feats = torch.tensor(node_feats, dtype=torch.float)
y = np.zeros(len(node_feats))
best_choice = np.argmax(neighs) #Get the node with less watts.
y[best_choice] = 1
y = torch.FloatTensor(y)
print(y)
edge_index = torch.tensor(edge_index)
data = Data(x=node_feats, edge_index=edge_index,y=y) #Torch data
print(data) # Works :)
[[0, 0, 1, 2], [1, 2, 0, 0]] [[ 0. 0.23190717 0. 60. 0.2 0. ] [ 1. 0.29549267 1. 50. 2.4 0. ] [ 2. 0.29549267 1. 50. 0.2 0. ]] tensor([0., 1., 0.]) Data(x=[3, 6], edge_index=[2, 4], y=[3])
/tmp/ipykernel_781697/1953125285.py:10: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor). node_feats = torch.tensor(node_feats, dtype=torch.float)
#Test.sample 2
alloc = 2 #the service is in node 0
neighs = [n for n in G.neighbors(alloc)]
edge_index_old = [[*[alloc]*len(neighs),*neighs],[*neighs,*[alloc]*len(neighs)]] #Doubt: bidirectional?
#Note: I normalize the edge_index with respect to the nodes in this case
#Note: bidirecctional?
edge_index = [ [*np.zeros(len(neighs),dtype=int),*np.arange(1,len(neighs)+1)],
[*np.arange(1,len(neighs)+1),*np.zeros(len(neighs),dtype=int)] ]
node_feats = features[[alloc,*neighs]]
node_feats = torch.tensor(node_feats, dtype=torch.float)
y = np.zeros(len(node_feats))
best_choice = np.argmax(neighs) #Get the node with less watts.
y[best_choice] = 1
y = torch.FloatTensor(y)
edge_index = torch.tensor(edge_index, dtype=torch.long)
print(node_feats)
print(edge_index)
data = Data(x=node_feats, edge_index=edge_index,y=y) #Torch data
print(data) # Data.X HAS DIFFERENT SIZE
#Note: Data.Node_feature DO has the same shape for all cases.... I think so.
tensor([[ 2.0000, 0.2955, 1.0000, 50.0000, 0.2000, 0.0000], [ 0.0000, 0.2319, 0.0000, 60.0000, 0.2000, 0.0000], [ 5.0000, 0.2606, 2.0000, 40.0000, 2.4000, 0.0000], [ 6.0000, 0.2606, 2.0000, 40.0000, 0.2000, 0.0000]]) tensor([[0, 0, 0, 1, 2, 3], [1, 2, 3, 0, 0, 0]]) Data(x=[4, 6], edge_index=[2, 6], y=[4])
#Can feed this Data a simple Network? errors?
conv1 = GATConv(num_features, 1)
x = conv1(data.x, data.edge_index)
print(x)
tensor([[-4.5470], [ 0.3566], [-2.9737], [-4.7560]], grad_fn=<AsStridedBackward0>)
## FINAL TEST for previous dataset generation
#alloc = 0 #the service is in cloud
alloc = 48 #the service is in edge node
#alloc = 2 #service at intermediate node
alloc = 0 #service at intermediate node
neighs = np.array([n for n in G.neighbors(alloc)])
max_degree_graph = 3
diff = max_degree_graph - len(neighs)
print(diff)
node_feats = np.array(features[[alloc,*neighs]]).astype(float)
print(node_feats)
print(len(node_feats))
diff_row = (np.ones(num_features*diff)*-1).reshape(diff,num_features)
print(diff_row)
node_feats = np.vstack((node_feats, diff_row))
node_feats = torch.tensor(node_feats, dtype=torch.float)
print(node_feats)
#edge_index = [[*[alloc]*len(neighs),*neighs,*[-1]*diff*2],[*neighs,*[alloc]*len(neighs),*[-1]*diff*2]] #Doubt: bidirectional?
edge_index = [ [*np.zeros(len(neighs),dtype=int),*np.arange(1,len(neighs)+1),*[-1]*diff*2],
[*np.arange(1,len(neighs)+1),*np.zeros(len(neighs),dtype=int),*[-1]*diff*2 ]]
print(edge_index)
edge_index = torch.tensor(edge_index, dtype=torch.long)
y = np.zeros(len(node_feats))
best_choice = np.argmax(neighs) #Get the node with less watts.
y[best_choice] = 1 # where the service "migrates" #It will depends of our goal
y = torch.tensor(y, dtype=torch.int64)
print(y.dtype)
print(y)
if alloc==2 or alloc==14:
assert(y[2]==1)
if alloc==48:
assert(y[0]==1)
if alloc==0:
assert(y[1]==1)
1 [[ 0. 0.23190717 0. 60. 0.2 0. ] [ 1. 0.29549267 1. 50. 2.4 0. ] [ 2. 0.29549267 1. 50. 0.2 0. ]] 3 [[-1. -1. -1. -1. -1. -1.]] tensor([[ 0.0000, 0.2319, 0.0000, 60.0000, 0.2000, 0.0000], [ 1.0000, 0.2955, 1.0000, 50.0000, 2.4000, 0.0000], [ 2.0000, 0.2955, 1.0000, 50.0000, 0.2000, 0.0000], [-1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000]]) [[0, 0, 1, 2, -1, -1], [1, 2, 0, 0, -1, -1]] torch.int64 tensor([0, 1, 0, 0])
print(data.num_node_features)
print(data.x)
print(data.y)
6 tensor([[ 2.0000, 0.2955, 1.0000, 50.0000, 0.2000, 0.0000], [ 0.0000, 0.2319, 0.0000, 60.0000, 0.2000, 0.0000], [ 5.0000, 0.2606, 2.0000, 40.0000, 2.4000, 0.0000], [ 6.0000, 0.2606, 2.0000, 40.0000, 0.2000, 0.0000]]) tensor([0., 0., 1., 0.])
!rm -R processed
!rm -R raw
np.random.seed(0)
class SimpleMigrationDataset(InMemoryDataset):
def __init__(self, root, n_samples, max_degree_graph, transform=None, pre_transform=None):
self.n_samples = n_samples
self.max_degree_graph = max_degree_graph
super(SimpleMigrationDataset, self).__init__(root, transform, pre_transform)
self.data, self.slices = torch.load(self.processed_paths[0])
@property
def raw_file_names(self):
return []
@property
def processed_file_names(self):
return ['simple_migrations.dataset']
def download(self):
pass
def process(self):
#In this point, we could load them from the traces...
data_list = []
for x in range(self.n_samples):
# print("ID:",x)
alloc = np.random.choice(G.nodes,1)[0]
# print("\t alloc:",alloc)
neighs = np.array([n for n in G.neighbors(alloc)])
diff = max_degree_graph - len(neighs)
node_feats = np.array(features[[alloc,*neighs]]).astype(float)
diff_row = (np.ones(num_features*diff)*-1).reshape(diff,num_features)
node_feats = np.vstack((node_feats, diff_row))
node_feats = torch.tensor(node_feats, dtype=torch.float)
# print(node_feats)
# edge_index = [[*[alloc]*len(neighs),*neighs,*[-1]*diff],[*neighs,*[alloc]*len(neighs),*[-2]*diff]] #Doubt: bidirectional?
edge_index = [ [*np.zeros(len(neighs),dtype=int),*np.arange(1,len(neighs)+1),*[-1]*diff*2],
[*np.arange(1,len(neighs)+1),*np.zeros(len(neighs),dtype=int),*[-1]*diff*2 ]]
edge_index = torch.tensor(edge_index, dtype=torch.long)
# print(edge_index)
y = np.zeros(len(node_feats))
best_choice = np.argmax(neighs) #Get the node with less watts.
y[best_choice] = 1
# print(y)
y = torch.tensor(y, dtype=torch.int64)
data = Data(x=node_feats, edge_index=edge_index, y=y)
# print(data)
data_list.append(data)
data, slices = self.collate(data_list)
torch.save((data, slices), self.processed_paths[0])
dataset = SimpleMigrationDataset('',n_samples=1000,max_degree_graph=3)
Processing... Done!
!ls processed/
pre_filter.pt pre_transform.pt simple_migrations.dataset
len(dataset)
1000
dataset.num_classes, dataset.num_features
(2, 6)
dataset = dataset.shuffle()
one_tenth_length = int(len(dataset) * 0.1)
train_dataset = dataset[:one_tenth_length * 8]
val_dataset = dataset[one_tenth_length*8:one_tenth_length * 9]
test_dataset = dataset[one_tenth_length*9:]
len(train_dataset), len(val_dataset), len(test_dataset)
(800, 100, 100)
from torch_geometric.loader import DataLoader
batch_size= 40
train_loader = DataLoader(train_dataset, batch_size=batch_size)
val_loader = DataLoader(val_dataset, batch_size=batch_size)
test_loader = DataLoader(test_dataset, batch_size=batch_size)
len(train_loader), len(val_loader), len(test_loader)
(20, 3, 3)
from torch_geometric.nn import GCNConv
class Net2(torch.nn.Module):
def __init__(self,hidden_channels):
super(Net2, self).__init__()
self.conv1 = GCNConv(dataset.num_node_features, hidden_channels)
self.conv2 = GCNConv(hidden_channels, dataset.num_classes)
def forward(self, x, edge_index):
x = self.conv1(x, edge_index)
x = F.relu(x)
x = F.dropout(x, training=self.training)
x = self.conv2(x, edge_index)
return F.log_softmax(x, dim=1)
from torch_geometric.nn import global_mean_pool
class MLP(torch.nn.Module):
def __init__(self, hidden_channels):
super().__init__()
self.conv1 = GCNConv(dataset.num_node_features, hidden_channels)
self.conv2 = GCNConv(hidden_channels, hidden_channels)
self.conv3 = GCNConv(hidden_channels, hidden_channels)
self.lin = Linear(hidden_channels, dataset.num_classes)
def forward(self, x,edge_index,batch=None):
# 1. Obtain node embeddings
x = self.conv1(x, edge_index)
x = F.relu(x)
x = self.conv2(x, edge_index)
x = F.relu(x)
x = self.conv3(x, edge_index)
# 2. Readout layer
# print(x.shape)
# print(batch.shape)
# x = global_mean_pool(x, batch) # [batch_size, hidden_channels]
# 3. Apply a final classifier
x = F.dropout(x, p=0.05, training=self.training)
x = self.lin(x)
return x
MLP( (conv1): GCNConv(6, 16) (conv2): GCNConv(16, 16) (conv3): GCNConv(16, 16) (lin): Linear(16, 2, bias=True) )
np.random.seed(seed)
torch.manual_seed(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
from torch_geometric.nn import global_mean_pool
class MLP2(torch.nn.Module):
def __init__(self, hidden_channels):
super().__init__()
self.conv1 = GCNConv(dataset.num_node_features, hidden_channels)
self.conv2 = GCNConv(hidden_channels, hidden_channels)
# self.conv3 = GCNConv(hidden_channels, hidden_channels)
self.lin3 = Linear(hidden_channels, dataset.num_classes)
def forward(self, x,edge_index,batch=None):
# 1. Obtain node embeddings
x = self.conv1(x, edge_index)
x = F.relu(x)
x = self.conv2(x, edge_index)
x = F.dropout(x, p=0.6, training=self.training)
x = self.lin3(x)
return x
## TEST A:
# Epoch: 399, Loss: 0.28797, Train Auc: 0.82875, Val Auc: 0.81833, Test Auc: 0.79333
model = MLP(hidden_channels=8)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
loss_op = torch.nn.CrossEntropyLoss()
## TEST B:
# Epoch: 399, Loss: 0.38136, Train Auc: 0.73521, Val Auc: 0.69833, Test Auc: 0.77000
model = MLP(hidden_channels=16)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
loss_op = torch.nn.CrossEntropyLoss()
## TEST C:
# Epoch: 399, Loss: 0.38086, Train Auc: 0.76417, Val Auc: 0.74000, Test Auc: 0.77833
model = MLP2(hidden_channels=16)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
loss_op = torch.nn.CrossEntropyLoss()
model.train()
MLP2( (conv1): GCNConv(6, 16) (conv2): GCNConv(16, 16) (lin3): Linear(16, 2, bias=True) )
def train(writer,epoch):
model.train()
total_loss = 0
# first_time = False
for data in train_loader:
# print(data)
data = data.to(device)
# if not first_time:
# writer.add_graph(model, input_to_model=data, verbose=False)
# first_time = True
optimizer.zero_grad()
output = model(data.x, data.edge_index,data.batch) #.max(dim=1)
loss = loss_op(output, data.y)
loss.backward()
total_loss += data.num_graphs * loss.item()
optimizer.step()
epoch_loss = total_loss / len(train_loader.dataset)
writer.add_scalar('Loss/train', epoch_loss, global_step = epoch)
return epoch_loss
from sklearn.metrics import roc_auc_score
def evaluate(loader):
model.eval()
predictions = []
labels = []
with torch.no_grad():
for data in loader:
# print(data)
data = data.to(device)
_, pred = model(data.x,data.edge_index).max(dim=1)
# print(pred)
label = data.y.detach().cpu().numpy()
# print(label)
predictions.append(pred)
labels.append(label)
predictions = np.hstack(predictions)
labels = np.hstack(labels)
return roc_auc_score(labels, predictions)
from torch.utils.tensorboard import SummaryWriter
logging_dir = "/home/isaac/projects/tmp"
writer = SummaryWriter(logging_dir)
model_plotted = False
old_loss = 0
for epoch in range(1, 400):
loss = train(writer,epoch)
train_acc = evaluate(train_loader)
test_acc = evaluate(test_loader)
val_acc = evaluate(val_loader)
writer.add_scalar('Accuracy/train', train_acc, epoch)
writer.add_scalar('Accuracy/test', test_acc, epoch)
writer.add_scalar('Accuracy/val', val_acc, epoch)
if abs(loss-old_loss)>0.01 or epoch%25==0:
print('Epoch: {:03d}, Loss: {:.5f}, Train Auc: {:.5f}, Val Auc: {:.5f}, Test Auc: {:.5f}'.format(epoch, loss, train_acc, val_acc, test_acc))
old_loss = loss
print('Epoch: {:03d}, Loss: {:.5f}, Train Auc: {:.5f}, Val Auc: {:.5f}, Test Auc: {:.5f}'.format(epoch, loss, train_acc, val_acc, test_acc))
writer.close()
print("Done")
Epoch: 001, Loss: 1.76032, Train Auc: 0.50000, Val Auc: 0.50000, Test Auc: 0.50000 Epoch: 002, Loss: 1.21946, Train Auc: 0.50000, Val Auc: 0.50000, Test Auc: 0.50000 Epoch: 003, Loss: 0.87318, Train Auc: 0.50458, Val Auc: 0.50000, Test Auc: 0.50000 Epoch: 004, Loss: 0.68167, Train Auc: 0.53396, Val Auc: 0.51500, Test Auc: 0.53833 Epoch: 005, Loss: 0.61816, Train Auc: 0.52667, Val Auc: 0.51333, Test Auc: 0.52667 Epoch: 006, Loss: 0.56680, Train Auc: 0.53396, Val Auc: 0.51500, Test Auc: 0.53833 Epoch: 007, Loss: 0.54776, Train Auc: 0.51417, Val Auc: 0.50333, Test Auc: 0.52000 Epoch: 008, Loss: 0.52857, Train Auc: 0.50000, Val Auc: 0.50000, Test Auc: 0.50000 Epoch: 009, Loss: 0.51235, Train Auc: 0.51500, Val Auc: 0.50333, Test Auc: 0.51000 Epoch: 012, Loss: 0.48631, Train Auc: 0.50500, Val Auc: 0.50333, Test Auc: 0.50333 Epoch: 025, Loss: 0.45785, Train Auc: 0.51542, Val Auc: 0.51000, Test Auc: 0.51667 Epoch: 050, Loss: 0.44605, Train Auc: 0.55167, Val Auc: 0.54000, Test Auc: 0.53667 Epoch: 075, Loss: 0.44216, Train Auc: 0.57604, Val Auc: 0.56000, Test Auc: 0.56167 Epoch: 100, Loss: 0.43716, Train Auc: 0.60604, Val Auc: 0.58500, Test Auc: 0.58667 Epoch: 125, Loss: 0.43545, Train Auc: 0.61438, Val Auc: 0.60167, Test Auc: 0.60833 Epoch: 150, Loss: 0.43007, Train Auc: 0.57604, Val Auc: 0.56000, Test Auc: 0.56167 Epoch: 175, Loss: 0.41738, Train Auc: 0.58667, Val Auc: 0.57000, Test Auc: 0.56167 Epoch: 191, Loss: 0.42013, Train Auc: 0.71167, Val Auc: 0.68000, Test Auc: 0.74167 Epoch: 200, Loss: 0.40683, Train Auc: 0.61667, Val Auc: 0.60500, Test Auc: 0.61667 Epoch: 220, Loss: 0.41178, Train Auc: 0.71375, Val Auc: 0.68500, Test Auc: 0.73833 Epoch: 225, Loss: 0.40056, Train Auc: 0.59917, Val Auc: 0.58500, Test Auc: 0.57167 Epoch: 249, Loss: 0.40389, Train Auc: 0.68021, Val Auc: 0.65167, Test Auc: 0.71000 Epoch: 250, Loss: 0.39633, Train Auc: 0.64542, Val Auc: 0.62000, Test Auc: 0.66500 Epoch: 261, Loss: 0.38923, Train Auc: 0.73167, Val Auc: 0.72333, Test Auc: 0.74667 Epoch: 275, Loss: 0.38502, Train Auc: 0.60292, Val Auc: 0.59833, Test Auc: 0.57667 Epoch: 300, Loss: 0.39234, Train Auc: 0.73667, Val Auc: 0.71000, Test Auc: 0.76000 Epoch: 325, Loss: 0.38419, Train Auc: 0.68000, Val Auc: 0.68833, Test Auc: 0.67500 Epoch: 328, Loss: 0.39304, Train Auc: 0.76750, Val Auc: 0.75000, Test Auc: 0.77500 Epoch: 331, Loss: 0.39055, Train Auc: 0.73438, Val Auc: 0.72000, Test Auc: 0.74500 Epoch: 336, Loss: 0.38527, Train Auc: 0.76750, Val Auc: 0.75000, Test Auc: 0.77500 Epoch: 350, Loss: 0.37780, Train Auc: 0.68937, Val Auc: 0.69167, Test Auc: 0.67333 Epoch: 371, Loss: 0.38140, Train Auc: 0.57271, Val Auc: 0.55667, Test Auc: 0.55500 Epoch: 375, Loss: 0.37546, Train Auc: 0.73938, Val Auc: 0.74167, Test Auc: 0.73500 Epoch: 397, Loss: 0.37659, Train Auc: 0.77437, Val Auc: 0.77167, Test Auc: 0.78833 Epoch: 399, Loss: 0.38086, Train Auc: 0.76417, Val Auc: 0.74000, Test Auc: 0.77833 Done
## TESTING THE TRAINING MODEL
# A simple sample
alloc = 31 #the service goes to node 0
alloc = 14#the service goes to node 2
alloc = 0 #the service goes to node 1
alloc = 0
print(alloc)
neighs = np.array([n for n in G.neighbors(alloc)])
diff = max_degree_graph - len(neighs)
node_feats = np.array(features[[alloc,*neighs]]).astype(float)
diff_row = (np.ones(num_features*diff)*-1).reshape(diff,num_features)
node_feats = np.vstack((node_feats, diff_row))
print(node_feats)
node_feats = torch.tensor(node_feats, dtype=torch.float)
edge_index = [ [*np.zeros(len(neighs),dtype=int),*np.arange(1,len(neighs)+1),*[-1]*diff*2],
[*np.arange(1,len(neighs)+1),*np.zeros(len(neighs),dtype=int),*[-1]*diff*2 ]]
edge_index = torch.tensor(edge_index, dtype=torch.long)
y = np.zeros(len(node_feats))
best_choice = np.argmax(neighs) #Get the node with less watts.
y[best_choice] = 1
y = torch.tensor(y, dtype=torch.int64)
data = Data(x=node_feats, edge_index=edge_index, y=y)
print(data)
_ , pred = model(data.x, data.edge_index).max(dim=1)
print(pred)
0 [[ 0. 0.23190717 0. 60. 0.2 0. ] [ 1. 0.29549267 1. 50. 2.4 0. ] [ 2. 0.29549267 1. 50. 0.2 0. ] [-1. -1. -1. -1. -1. -1. ]] Data(x=[4, 6], edge_index=[2, 6], y=[4]) tensor([0, 1, 0, 0])
!lsof -i:8008
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME tensorboa 2833806 isaac 8u IPv4 145673214 0t0 TCP localhost:8008 (LISTEN)
!kill -9 2833806
%tensorboard --logdir {logging_dir} --port=8008