Package: bijou
bijouA lightweight freamwork based onfastai coursefor training pytorch models conveniently. In particular, it is compatible with datasets and models ofpytorch_geometricandDGLforGraph Neural Networks.FeaturesCompatible with PyG and DGL for GNNGraph level learning: It is compatible withpytorch_geometricandDGLfor Graph Neural Networks of graph classification and other graph level learning.Node level learning: It can be used in node classification or other node level learning with dataset of singlepytorch_geometric DataorDGLGraph.Easy to UseIt likesFastAIbut far more lightweight.Installpip install bijouDependenciesPytorchMatplotlibNumpytqdmNetworkxtorch-geometric   (Optional)dgl               (Optional)UsingSee following examples, and more examples arehere.Examplesa. MNIST classificationimporttorch.nnasnn,torch.nn.functionalasF,torch.optimasoptimfrombijou.learnerimportLearnerfrombijou.dataimportDataset,DataLoader,DataBunchfrombijou.metricsimportaccuracyfrombijou.datasetsimportmnistimportmatplotlib.pyplotasplt# 1. datasetx_train,y_train,x_valid,y_valid,x_test,y_test=mnist()train_ds,valid_ds,test_ds=Dataset(x_train,y_train),Dataset(x_valid,y_valid),Dataset(x_test,y_test)train_dl=DataLoader(train_ds,batch_size=128,shuffle=True)valid_dl=DataLoader(valid_ds,batch_size=128)test_dl=DataLoader(test_ds,batch_size=128)# train_dl, valid_dl, test_dl = DataLoader.loaders(train_ds, valid_ds, test_ds, 128)train_db=DataBunch(train_dl,valid_dl)# 2. model and optimizerin_dim=train_db.train_ds.x.shape[1]out_dim=y_train.max().item()+1model=nn.Sequential(nn.Linear(in_dim,64),nn.ReLU(),nn.Linear(64,out_dim))opt=optim.SGD(model.parameters(),lr=0.35)# 3. learnerloss_func=F.cross_entropylearner=Learner(model,opt,loss_func,train_db,metrics=[accuracy])# 4. fitlearner.fit(10)# 5. testlearner.test(valid_dl)# 6. predictpred=learner.predict(x_valid)print(pred.size())# 7.  plotlearner.recorder.plot_metrics()plt.show()b. Graph Classification with PyGNOTE: Performance of this GNN model's is not good, as the dataset is highly unbalanced.importtorch,torch.nnasnn,torch.nn.functionalasF,torch.optimasoptimfromtorch_geometric.nnimportglobal_max_pool,TopKPooling,GCNConvfrombijou.learnerimportLearnerfrombijou.datasetsimportpyg_yoochoose_10kfrombijou.dataimportDataBunch,PyGDataLoaderfrombijou.metricsimportaccuracyfromexamples.pyg_datasetimportYooChooseBinaryDatasetimportmatplotlib.pyplotasplt# 1. datasetdataset=YooChooseBinaryDataset(root=pyg_yoochoose_10k()).shuffle()train_ds,val_ds,test_ds=dataset[:8000],dataset[8000:9000],dataset[9000:]train_dl=PyGDataLoader(train_ds,batch_size=64,shuffle=True)val_dl=PyGDataLoader(val_ds,batch_size=64)test_dl=PyGDataLoader(test_ds,batch_size=64)# train_dl, val_dl, test_dl = PyGDataLoader.loaders(train_ds, val_ds, test_ds, 64)train_db=DataBunch(train_dl,val_dl)# 2. mode and optimizerclassModel(nn.Module):def__init__(self,feature_dim,class_num,embed_dim=64,gcn_dims=(32,32),dense_dim=64):super().__init__()self.embedding=torch.nn.Embedding(num_embeddings=feature_dim,embedding_dim=embed_dim)self.gcns=nn.ModuleList()in_dim=embed_dimfordimingcn_dims:self.gcns.append(GCNConv(in_dim,dim))in_dim=dimself.graph_pooling=TopKPooling(gcn_dims[-1],ratio=0.8)self.dense=nn.Linear(gcn_dims[-1],dense_dim)self.out=nn.Linear(dense_dim,class_num)defforward(self,data):x,edge_index,batch=data.x,data.edge_index,data.batchx=self.embedding(x)x=x.squeeze(1)forgcninself.gcns:x=gcn(x,edge_index)x=F.relu(x)x,_,_,batch,_,_=self.graph_pooling(x,edge_index,None,batch)x=global_max_pool(x,batch)outputs=self.dense(x)outputs=F.relu(outputs)outputs=self.out(outputs)returnoutputsmodel=Model(dataset.item_num,2)opt=optim.SGD(model.parameters(),lr=0.5)# 3. learnerlearner=Learner(model,opt,F.cross_entropy,train_db,metrics=[accuracy])# 4. fitlearner.fit(3)# 5. testlearner.test(test_dl)# 6. predictpred=learner.predict(test_dl)print(pred.size())# 7. plotlearner.recorder.plot_metrics()plt.show()c. Node Classification with PyGfromtorch_geometric.datasetsimportPlanetoidimporttorch.nnasnn,torch.nn.functionalasF,torch.optimasoptimfromtorch_geometric.nnimportGCNConvfrombijou.dataimportPyGGraphLoader,DataBunchfrombijou.learnerimportLearnerfrombijou.metricsimportmasked_cross_entropy,masked_accuracyfrombijou.datasetsimportpyg_coraimportmatplotlib.pyplotasplt# 1. datasetdataset=Planetoid(root=pyg_cora(),name='Cora')train_dl=PyGGraphLoader(dataset,'train')val_dl=PyGGraphLoader(dataset,'val')test_dl=PyGGraphLoader(dataset,'test')# train_dl, val_dl, test_dl = PyGGraphLoader.loaders(dataset)data=DataBunch(train_dl,val_dl)# 2. model and optimizerclassModel(nn.Module):def__init__(self,feature_num,class_num):super().__init__()self.conv1=GCNConv(feature_num,16)self.conv2=GCNConv(16,class_num)defforward(self,data):x,edge_index=data.x,data.edge_indexx=self.conv1(x,edge_index)x=F.relu(x)x=self.conv2(x,edge_index)outputs=F.relu(x)returnoutputsmodel=Model(dataset.num_node_features,dataset.num_classes)opt=optim.SGD(model.parameters(),lr=0.5,weight_decay=0.01)# 3. learnerlearner=Learner(model,opt,masked_cross_entropy,data,metrics=[masked_accuracy])# 4. fitlearner.fit(100)# 5. testlearner.test(test_dl)# 6. predictpred=learner.predict(dataset[0])print(pred.size())# 7. plotlearner.recorder.plot_metrics()plt.show()d. Graph Classification with DGLimporttorch,torch.nnasnn,torch.nn.functionalasF,torch.optimasoptimimportdglimportdgl.functionasfnfromdgl.dataimportMiniGCDatasetfrombijou.dataimportDGLDataLoader,DataBunchfrombijou.metricsimportaccuracyfrombijou.learnerimportLearnerimportmatplotlib.pyplotasplt# 1. datasettrain_ds=MiniGCDataset(320,10,20)val_ds=MiniGCDataset(100,10,20)test_ds=MiniGCDataset(80,10,20)train_dl=DGLDataLoader(train_ds,batch_size=32,shuffle=True)val_dl=DGLDataLoader(val_ds,batch_size=32,shuffle=False)test_dl=DGLDataLoader(test_ds,batch_size=32,shuffle=False)data=DataBunch(train_dl,val_dl)# 2. mode and optimizermsg=fn.copy_src(src='h',out='m')# Sends a message of node feature h.defreduce(nodes):"""Take an average over all neighbor node features hu and use it tooverwrite the original node feature."""accum=torch.mean(nodes.mailbox['m'],1)return{'h':accum}classNodeApplyModule(nn.Module):"""Update the node feature hv with ReLU(Whv+b)."""def__init__(self,in_feats,out_feats,activation):super().__init__()self.linear=nn.Linear(in_feats,out_feats)self.activation=activationdefforward(self,node):h=self.linear(node.data['h'])h=self.activation(h)return{'h':h}classGCN(nn.Module):def__init__(self,in_feats,out_feats,activation):super().__init__()self.apply_mod=NodeApplyModule(in_feats,out_feats,activation)defforward(self,g,feature):# Initialize the node features with h.g.ndata['h']=featureg.update_all(msg,reduce)g.apply_nodes(func=self.apply_mod)returng.ndata.pop('h')classClassifier(nn.Module):def__init__(self,in_dim,hidden_dim,n_classes):super(Classifier,self).__init__()self.layers=nn.ModuleList([GCN(in_dim,hidden_dim,F.relu),GCN(hidden_dim,hidden_dim,F.relu)])self.classify=nn.Linear(hidden_dim,n_classes)defforward(self,g):# For undirected graphs, in_degree is the same as# out_degree.h=g.in_degrees().view(-1,1).float()forconvinself.layers:h=conv(g,h)g.ndata['h']=hhg=dgl.mean_nodes(g,'h')returnself.classify(hg)model=Classifier(1,256,train_ds.num_classes)optimizer=optim.Adam(model.parameters(),lr=0.001)# 3. learneloss_func=nn.CrossEntropyLoss()learner=Learner(model,optimizer,loss_func,data,metrics=accuracy)# 4. fitlearner.fit(80)# 5. testlearner.test(test_dl)# 6. predictlearner.predict(test_dl)# 7. plotlearner.recorder.plot_metrics()plt.show()e. Node Classification with DGLimporttorch.nn.functionalasF,torch.nnasnn,torchasthimportdgl.functionasfnfromdglimportDGLGraphfromdgl.dataimportcitation_graphascitegrhfrombijou.learnerimportLearnerfrombijou.dataimportGraphLoader,DataBunchfrombijou.metricsimportmasked_accuracy,masked_cross_entropyimportmatplotlib.pyplotaspltimportnetworkxasnx# 1. datasetdefload_cora_data():data=citegrh.load_cora()features=th.FloatTensor(data.features)labels=th.LongTensor(data.labels)train_mask=th.BoolTensor(data.train_mask)val_mask=th.BoolTensor(data.val_mask)test_mask=th.BoolTensor(data.test_mask)g=data.graph# add self loopg.remove_edges_from(nx.selfloop_edges(g))g=DGLGraph(g)g.add_edges(g.nodes(),g.nodes())returng,features,labels,train_mask,val_mask,test_maskg,features,labels,train_mask,val_mask,test_mask=load_cora_data()train_dl=GraphLoader(g,features=features,labels=labels,mask=train_mask)val_dl=GraphLoader(g,features=features,labels=labels,mask=val_mask)test_dl=GraphLoader(g,features=features,labels=labels,mask=test_mask)data=DataBunch(train_dl,val_dl)# 2. model and optimizergcn_msg=fn.copy_src(src='h',out='m')gcn_reduce=fn.sum(msg='m',out='h')classNodeApplyModule(nn.Module):def__init__(self,in_feats,out_feats,activation):super(NodeApplyModule,self).__init__()self.linear=nn.Linear(in_feats,out_feats)self.activation=activationdefforward(self,node):h=self.linear(node.data['h'])ifself.activationisnotNone:h=self.activation(h)return{'h':h}classGCN(nn.Module):def__init__(self,in_feats,out_feats,activation):super(GCN,self).__init__()self.apply_mod=NodeApplyModule(in_feats,out_feats,activation)defforward(self,g,feature):g.ndata['h']=featureg.update_all(gcn_msg,gcn_reduce)g.apply_nodes(func=self.apply_mod)returng.ndata.pop('h')classNet(nn.Module):def__init__(self):super(Net,self).__init__()self.gcn1=GCN(1433,16,F.relu)self.gcn2=GCN(16,7,None)defforward(self,g,features):x=self.gcn1(g,features)x=self.gcn2(g,x)returnxnet=Net()optimizer=th.optim.Adam(net.parameters(),lr=1e-3)# 3. learnerlearner=Learner(net,optimizer,masked_cross_entropy,data,metrics=masked_accuracy)# 4. fitlearner.fit(50)# 5. testlearner.test(test_dl)# 6. predictlearner.predict(test_dl)# 7. plotlearner.recorder.plot_metrics()plt.show()