Is there a manual about GCN(Graph Convolutional Networks) using DL4J

Can DL4J do graph convolutional networks? Wish there was a manual, I’m a newbie

@xiumuzidiao we don’t have a dedicated library for it. We have a graph library and some basic algorithms like deepwalk but you would need to setup the infrastructure yourself using the samediff building blocks.

A library would be possible to build but it’s not just a few lines of code you import. I haven’t looked in depth at it but we might be able to import some of the networks involved for it.

According to MinimalSameDiffDense.java of
deeplearning4j-examples, I wrote a simple graph convolutional neural network, but I don’t know if I wrote it right, could you please help me take a look?I will be very grateful!

import org.deeplearning4j.nn.conf.inputs.InputType;
import org.deeplearning4j.nn.conf.layers.samediff.SDLayerParams;
import org.deeplearning4j.nn.conf.layers.samediff.SameDiffLayer;
import org.deeplearning4j.nn.params.DefaultParamInitializer;
import org.deeplearning4j.nn.weights.WeightInit;
import org.nd4j.autodiff.samediff.SDVariable;
import org.nd4j.autodiff.samediff.SameDiff;
import org.nd4j.linalg.activations.Activation;
import org.nd4j.linalg.api.ndarray.INDArray;

import java.util.Map;

public class GCNSameDiffDense extends SameDiffLayer {
    //特征数 FeatureNum
    private Integer inputNum;
    //神经元数 neurons num
    private Integer outputNum;
    //活化函数
    private Activation activation;
    //度数矩阵*邻接矩阵*...   DegreeMatrxi^-0.5  x  Adjacent  x DegreeMatrxi^-0.5
    private INDArray adda ;

    public GCNSameDiffDense() {

    }

    public GCNSameDiffDense(Integer inputNum, Integer outputNum, Activation activation, WeightInit weightInit, INDArray adda){
        this.inputNum = in...Adjacent  x DegreeMatrxi^-0.5  x InputMatrix x WeightMatrix
        SDVariable mulTwo = mulOne.mmul(weights);
        paramTable.remove(DefaultParamInitializer.BIAS_KEY);
        return mulTwo;
    }

    @Override
    public void defineParameters(SDLayerParams params) {
        // -1 分子数, inputNum:特征数,  outputNum:神经元数
        params.addWeightParam(DefaultParamInitializer.WEIGHT_KEY,inputNum,outputNum);
        //

        params.addBiasParam(DefaultParamInitializer.BIAS_KEY,adda.shape()[0],adda.shape()[1]);
    }

    @Override
    public void initializeParameters(Map<String, INDArray> params) {
        initWeights(inputNum, outputNum, weightInit, params.get(DefaultParamInitializer.WEIGHT_KEY));
        INDArray bias = params.get(DefaultParamInitializer.BIAS_KEY);
        // bias == DegreeMatrxi^-0.5  x  Adjacent  x DegreeMatrxi^-0.5
        bias = this.adda;

    }

    @Override
    public InputType getOutputType(int layerIndex, InputType inputType) {
        return null;
    }
}

And below is main

import com.xiumuzidiao.dp.d3.GCNSameDiffDense;
import org.deeplearning4j.datasets.iterator.utilty.ListDataSetIterator;
import org.deeplearning4j.nn.conf.MultiLayerConfiguration;
import org.deeplearning4j.nn.conf.NeuralNetConfiguration;
import org.deeplearning4j.nn.conf.layers.OutputLayer;
import org.deeplearning4j.nn.multilayer.MultiLayerNetwork;
import org.deeplearning4j.nn.weights.WeightInit;
import org.nd4j.linalg.activations.Activation;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.dataset.DataSet;
import org.nd4j.linalg.factory.Nd4j;
import org.nd4j.linalg.learning.config.Adam;
import org.nd4j.linalg.lossfunctions.LossFunctions;

import java.util.List;

public class GCNTest {
    public static Integer batch =1000;
    public static Integer featureOfNum =29;
    public static Integer inputOfFirstLayer = 1024;
    public static void main(String[] args) {

        MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
                .updater(new A... .seed(12345)
                .list()
                .layer(new GCNSameDiffDense(featureOfNum,inputOfFirstLayer, Activation.RELU, WeightInit.XAVIER, Nd4j.rand(batch,batch)))
                .layer(new OutputLayer.Builder().nIn(inputOfFirstLayer).nOut(1).activation(Activation.IDENTITY)
                        .weightInit(WeightInit.XAVIER)
                        .lossFunction(LossFunctions.LossFunction.MSE).build())
                .build();

        MultiLayerNetwork net = new MultiLayerNetwork(conf);
        //数据准备,随机
        INDArray inputNDArray1 = Nd4j.rand(batch,featureOfNum);
        INDArray outputNDArray = Nd4j.rand(batch,1);
        DataSet dataSet = new DataSet(inputNDArray1, outputNDArray);

        List<DataSet> listDs = dataSet.asList();

        ListDataSetIterator<DataSet> iterator = new ListDataSetIterator<>(listDs, batch);



        for (int i = 0;i<=1000;i++)
        {
            net.fit(iterator);
            System.out.println(net.score());
        }

    }
}

@xiumuzidiao this isn’t really a cnn…do you mean a simple multi layer perceptron? A graph convolutional network would use samediff.cnn().conv2d normally.

Beyond that, maybe you can use deeplearning4j-graph for the graph abstraction. We used that previously for graph embeddings using deepwalk.

I would try just sticking to use the samediff api itself if you want to do this. Once you have your network if you want feel free to wrap it in a samediff layer.

Graph Convolutional Neural Networks ,Its principle is convolution but in operation It is just a multiplication of the feature matrix and the weight matrix with the corresponding normalized coefficient matrix.
For exampl

the D matrix is the degree matrix ,a diagonal matrix ,left multiply adjancent matrix and multiply D matrix then multiply feature matrix and Weights matrix.
This is the graph convolutional network, no need to use convolution operations.
GCN is hot in artificial intelligence in chemistry Because atoms can be considered nodes and chemical bonds can be considered edges

Graph Convolutional Networks using only NumPy - YouTube this is youtube about Graph Convolutional Networks and implement it using numpy,he doesn’t use convolution operation

so,i really think it is a simple multi layer perceptron

So…I don’t knw whether i am true

anyone help me?

import org.deeplearning4j.nn.conf.layers.samediff.SDLayerParams;
import org.deeplearning4j.nn.conf.layers.samediff.SameDiffLayer;
import org.deeplearning4j.nn.params.DefaultParamInitializer;
import org.deeplearning4j.nn.weights.WeightInit;
import org.nd4j.autodiff.samediff.SDVariable;
import org.nd4j.autodiff.samediff.SameDiff;
import org.nd4j.linalg.activations.Activation;
import org.nd4j.linalg.api.ndarray.INDArray;

import java.util.Map;

public class GCNSameDiffDense extends SameDiffLayer {
    //特征数 FeatureNum
    private Integer inputNum;
    //神经元数 neurons num
    private Integer outputNum;
    //活化函数
    private Activation activation;
    //度数矩阵*邻接矩阵*...   DegreeMatrxi^-0.5  x  Adjacent  x DegreeMatrxi^-0.5
    private INDArray adda ;

    public GCNSameDiffDense() {

    }

    public GCNSameDiffDense(Integer inputNum, Integer outputNum, Activation activation, WeightInit weightInit, INDArray adda){
        this.inputNum = inputNum;
        this.outputNum = outputNum;
        ... Adjacent  x DegreeMatrxi^-0.5  x InputMatrix x WeightMatrix
        SDVariable mulTwo = mulOne.mmul(weights);
        paramTable.remove(DefaultParamInitializer.BIAS_KEY);
        return mulTwo;
    }

    @Override
    public void defineParameters(SDLayerParams params) {
        // -1 分子数, inputNum:特征数,  outputNum:神经元数
        params.addWeightParam(DefaultParamInitializer.WEIGHT_KEY,inputNum,outputNum);
        //

        params.addBiasParam(DefaultParamInitializer.BIAS_KEY,adda.shape()[0],adda.shape()[1]);
    }

    @Override
    public void initializeParameters(Map<String, INDArray> params) {
        initWeights(inputNum, outputNum, weightInit, params.get(DefaultParamInitializer.WEIGHT_KEY));
        INDArray bias = params.get(DefaultParamInitializer.BIAS_KEY);
        // bias == DegreeMatrxi^-0.5  x  Adjacent  x DegreeMatrxi^-0.5
        bias = this.adda;

    }

    @Override
    public InputType getOutputType(int layerIndex, InputType inputType) {
        return null;
    }
}

@xiumuzidiao define one layer at a time just like the example you’re following. Go straight to the numpy code here: blog_code/gcn_from_scratch.ipynb at master · zjost/blog_code · GitHub

I would also suggest stripping out dl4j for now. Just use samediff itself and make sure you get correct results.

I’ll try to go through the notebook if I have time to see if I can come up with an example based on the forward and backwards but don’t expect a response within a set period of time unless you’re paying for it :slight_smile: I can give you something rough till I have time to put together something more polished.

Graphs are enough of a topic that an example for this wouldn’t hurt to have out there.

I can tell you in general though that the forward there is a just the matrix multiply that you already implemented.

Since samediff has automatic differentiation we only need to focus on the forward method. The forward method should be reproducible as:

  double nIn = 5.0;
        SameDiff sameDiff = SameDiff.create();
        SDVariable w = sameDiff.var("w",new XavierFanInInitScheme('c',nIn));
        SDVariable a = sameDiff.placeHolder("a", DataType.FLOAT);
        SDVariable x = sameDiff.placeHolder("x", DataType.FLOAT);
        x = sameDiff.transpose(a.mmul(x));
        SDVariable relu = sameDiff.nn().relu(sameDiff.linalg().matmul(w,x),1.0);

Note that placeholders are something you have to define at runtime since those are inputs in to the graph. We also have the softmax layer for you there as well. It depends how much of the graph you want to implement in dl4j vs samediff though. This isn’t tested just meant to be an example for you to start from.

Thank you very much, your example has helped me a lot, I will improve it and introduce it to those around me

@xiumuzidiao sure appreciate any help for the community. Feel free to ask follow up questions and I’ll see if I can add more details when I get time.