stellargraph/stellargraph

View on GitHub
demos/connector/neo4j/directed-graphsage-on-cora-neo4j-example.ipynb

Summary

Maintainability
Test Coverage
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0",
   "metadata": {},
   "source": [
    "# Directed GraphSAGE with Neo4j\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1",
   "metadata": {
    "nbsphinx": "hidden",
    "tags": [
     "CloudRunner"
    ]
   },
   "source": [
    "<table><tr><td>Run the latest release of this notebook:</td><td><a href=\"https://mybinder.org/v2/gh/stellargraph/stellargraph/master?urlpath=lab/tree/demos/connector/neo4j/directed-graphsage-on-cora-neo4j-example.ipynb\" alt=\"Open In Binder\" target=\"_parent\"><img src=\"https://mybinder.org/badge_logo.svg\"/></a></td><td><a href=\"https://colab.research.google.com/github/stellargraph/stellargraph/blob/master/demos/connector/neo4j/directed-graphsage-on-cora-neo4j-example.ipynb\" alt=\"Open In Colab\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\"/></a></td></tr></table>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2",
   "metadata": {},
   "source": [
    "This example shows the application of *directed* GraphSAGE to a *directed* graph, where the in-node and out-node neighbourhoods are separately sampled and have different weights.\n",
    "\n",
    "Subgraphs are sampled directly from Neo4j, which eliminate the need to store the whole graph structure in NetworkX. In this demo, node features are cached in memory for faster access since the dataset is relatively small.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "3",
   "metadata": {
    "nbsphinx": "hidden",
    "tags": [
     "CloudRunner"
    ]
   },
   "outputs": [],
   "source": [
    "# install StellarGraph if running on Google Colab\n",
    "import sys\n",
    "if 'google.colab' in sys.modules:\n",
    "  %pip install -q stellargraph[demos]==1.3.0b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "4",
   "metadata": {
    "nbsphinx": "hidden",
    "tags": [
     "VersionCheck"
    ]
   },
   "outputs": [],
   "source": [
    "# verify that we're using the correct version of StellarGraph for this notebook\n",
    "import stellargraph as sg\n",
    "\n",
    "try:\n",
    "    sg.utils.validate_notebook_version(\"1.3.0b\")\n",
    "except AttributeError:\n",
    "    raise ValueError(\n",
    "        f\"This notebook requires StellarGraph version 1.3.0b, but a different version {sg.__version__} is installed.  Please see <https://github.com/stellargraph/stellargraph/issues/1172>.\"\n",
    "    ) from None"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "5",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import os\n",
    "\n",
    "import stellargraph as sg\n",
    "from stellargraph.connector.neo4j import Neo4jDirectedGraphSAGENodeGenerator, Neo4jStellarDiGraph\n",
    "from stellargraph.layer import DirectedGraphSAGE\n",
    "\n",
    "from tensorflow.keras import layers, optimizers, losses, metrics, Model\n",
    "from sklearn import preprocessing, feature_extraction, model_selection\n",
    "\n",
    "import time\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6",
   "metadata": {},
   "source": [
    "## Connect to Neo4j\n",
    "\n",
    "It is assumed that the Cora dataset has already been loaded into Neo4j. [This notebook](./load-cora-into-neo4j.ipynb) demonstrates how to load Cora dataset into Neo4j."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "7",
   "metadata": {},
   "outputs": [],
   "source": [
    "import py2neo\n",
    "\n",
    "default_host = os.environ.get(\"STELLARGRAPH_NEO4J_HOST\")\n",
    "\n",
    "# Create the Neo4j Graph database object; port, user, password parameters can be add to specify location and authentication\n",
    "neo4j_graphdb = py2neo.Graph(host=default_host)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8",
   "metadata": {},
   "source": [
    "## Create a Neo4jStellarDiGraph object\n",
    "\n",
    "Using the `py2neo.Graph` instance, we can create a `Neo4jStellarDiGraph` object that will be useful for the rest of the stellargraph workflow.\n",
    "\n",
    "For this demo, we introduce two additional details that help us execute the notebook faster:\n",
    "\n",
    "* Our Cora dataset loaded into Neo4j has an index on the `ID` property of `paper` entities. By specifying the `node_label` parameter when constructing `Neo4jStellarGraph`, this lets downstream queries to use this index and therefore run much faster, since many of the queries will require matching nodes by their `ID`.\n",
    "* We also cache the node features in memory. Since we know the graph is relatively small, we can safely cache the node features in memory for faster access. Note that this should be avoided for larger graphs where the node features are large."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "9",
   "metadata": {},
   "outputs": [],
   "source": [
    "neo4j_sg = Neo4jStellarDiGraph(neo4j_graphdb, node_label=\"paper\")\n",
    "neo4j_sg.cache_all_nodes_in_memory()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10",
   "metadata": {},
   "source": [
    "## Data Labels\n",
    "\n",
    "Each node has a \"subject\" property in the database, which we want to use as labels for training. We can load the labels into memory using a Cypher query and split them into training/test sets."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "11",
   "metadata": {},
   "outputs": [],
   "source": [
    "labels_query = \"\"\"\n",
    "    MATCH (node:paper)\n",
    "    WITH {ID: node.ID, label: node.subject} as node_data\n",
    "    RETURN node_data\n",
    "    \"\"\"\n",
    "rows = neo4j_graphdb.run(labels_query).data()\n",
    "labels = pd.Series(\n",
    "    [r[\"node_data\"][\"label\"] for r in rows], index=[r[\"node_data\"][\"ID\"] for r in rows]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "12",
   "metadata": {},
   "outputs": [],
   "source": [
    "# split the node labels into train/test\n",
    "\n",
    "train_labels, test_labels = model_selection.train_test_split(\n",
    "    labels, train_size=0.1, test_size=None, stratify=labels\n",
    ")\n",
    "\n",
    "target_encoding = preprocessing.LabelBinarizer()\n",
    "\n",
    "train_targets = target_encoding.fit_transform(train_labels)\n",
    "test_targets = target_encoding.transform(test_labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13",
   "metadata": {},
   "source": [
    "## Creating the GraphSAGE model in Keras"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "14",
   "metadata": {},
   "source": [
    "To feed data from the graph to the Keras model we need a data generator that feeds data from the graph to the model. The generators are specialized to the model and the learning task so we choose the `Neo4jDirectedGraphSAGENodeGenerator` as we are predicting node attributes with a `DirectedGraphSAGE` model, sampling directly from Neo4j database.\n",
    "\n",
    "We need two other parameters, the `batch_size` to use for training and the number of nodes to sample at each level of the model. Here we choose a two-level model with 10 nodes sampled in the first layer (5 in-nodes and 5 out-nodes), and 4 in the second layer (2 in-nodes and 2 out-nodes)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "15",
   "metadata": {
    "tags": [
     "parameters"
    ]
   },
   "outputs": [],
   "source": [
    "batch_size = 50\n",
    "in_samples = [5, 2]\n",
    "out_samples = [5, 2]\n",
    "epochs = 20"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16",
   "metadata": {},
   "source": [
    "A `Neo4jDirectedGraphSAGENodeGenerator` object is required to send the node features in sampled subgraphs to Keras."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "17",
   "metadata": {},
   "outputs": [],
   "source": [
    "generator = Neo4jDirectedGraphSAGENodeGenerator(\n",
    "    neo4j_sg, batch_size, in_samples, out_samples\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18",
   "metadata": {},
   "source": [
    "Using the `generator.flow()` method, we can create iterators over nodes that should be used to train, validate, or evaluate the model. For training we use only the training nodes returned from our splitter and the target values. The `shuffle=True` argument is given to the `flow` method to improve training."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "19",
   "metadata": {},
   "outputs": [],
   "source": [
    "train_gen = generator.flow(train_labels.index, train_targets, shuffle=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20",
   "metadata": {},
   "source": [
    "Now we can specify our machine learning model, we need a few more parameters for this:\n",
    "\n",
    " * the `layer_sizes` is a list of hidden feature sizes of each layer in the model. In this example we use 32-dimensional hidden node features at each layer, which corresponds to 12 weights for each head node, 10 for each in-node and 10 for each out-node.\n",
    " * The `bias` and `dropout` are internal parameters of the model. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "21",
   "metadata": {},
   "outputs": [],
   "source": [
    "graphsage_model = DirectedGraphSAGE(\n",
    "    layer_sizes=[32, 32], generator=generator, bias=False, dropout=0.5,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "22",
   "metadata": {},
   "source": [
    "Now we create a model to predict the 7 categories using Keras softmax layers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "23",
   "metadata": {},
   "outputs": [],
   "source": [
    "x_inp, x_out = graphsage_model.in_out_tensors()\n",
    "prediction = layers.Dense(units=train_targets.shape[1], activation=\"softmax\")(x_out)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24",
   "metadata": {},
   "source": [
    "### Training the model"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25",
   "metadata": {},
   "source": [
    "Now let's create the actual Keras model with the graph inputs `x_inp` provided by the `graph_model` and outputs being the predictions from the softmax layer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "26",
   "metadata": {},
   "outputs": [],
   "source": [
    "model = Model(inputs=x_inp, outputs=prediction)\n",
    "model.compile(\n",
    "    optimizer=optimizers.Adam(lr=0.005),\n",
    "    loss=losses.categorical_crossentropy,\n",
    "    metrics=[\"acc\"],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "27",
   "metadata": {},
   "source": [
    "Train the model, keeping track of its loss and accuracy on the training set, and its generalisation performance on the test set (we need to create another generator over the test data for this)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "28",
   "metadata": {},
   "outputs": [],
   "source": [
    "test_gen = generator.flow(test_labels.index, test_targets)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "29",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/20\n",
      "6/6 - 10s - loss: 1.9096 - acc: 0.2370 - val_loss: 1.7668 - val_acc: 0.3409\n",
      "Epoch 2/20\n",
      "6/6 - 9s - loss: 1.7038 - acc: 0.4741 - val_loss: 1.6493 - val_acc: 0.4606\n",
      "Epoch 3/20\n",
      "6/6 - 9s - loss: 1.5779 - acc: 0.5778 - val_loss: 1.5424 - val_acc: 0.6300\n",
      "Epoch 4/20\n",
      "6/6 - 9s - loss: 1.4403 - acc: 0.7259 - val_loss: 1.4561 - val_acc: 0.6727\n",
      "Epoch 5/20\n",
      "6/6 - 9s - loss: 1.3461 - acc: 0.7926 - val_loss: 1.3726 - val_acc: 0.6858\n",
      "Epoch 6/20\n",
      "6/6 - 9s - loss: 1.2412 - acc: 0.8519 - val_loss: 1.2986 - val_acc: 0.7059\n",
      "Epoch 7/20\n",
      "6/6 - 9s - loss: 1.1368 - acc: 0.8741 - val_loss: 1.2314 - val_acc: 0.7281\n",
      "Epoch 8/20\n",
      "6/6 - 9s - loss: 1.0673 - acc: 0.9111 - val_loss: 1.1734 - val_acc: 0.7391\n",
      "Epoch 9/20\n",
      "6/6 - 9s - loss: 0.9848 - acc: 0.9074 - val_loss: 1.1162 - val_acc: 0.7424\n",
      "Epoch 10/20\n",
      "6/6 - 9s - loss: 0.8907 - acc: 0.9296 - val_loss: 1.0722 - val_acc: 0.7408\n",
      "Epoch 11/20\n",
      "6/6 - 9s - loss: 0.8161 - acc: 0.9333 - val_loss: 1.0221 - val_acc: 0.7527\n",
      "Epoch 12/20\n",
      "6/6 - 9s - loss: 0.7358 - acc: 0.9519 - val_loss: 0.9911 - val_acc: 0.7572\n",
      "Epoch 13/20\n",
      "6/6 - 9s - loss: 0.7578 - acc: 0.9370 - val_loss: 0.9490 - val_acc: 0.7666\n",
      "Epoch 14/20\n",
      "6/6 - 9s - loss: 0.6305 - acc: 0.9667 - val_loss: 0.9255 - val_acc: 0.7662\n",
      "Epoch 15/20\n",
      "6/6 - 9s - loss: 0.6118 - acc: 0.9741 - val_loss: 0.8930 - val_acc: 0.7728\n",
      "Epoch 16/20\n",
      "6/6 - 9s - loss: 0.5467 - acc: 0.9704 - val_loss: 0.8725 - val_acc: 0.7748\n",
      "Epoch 17/20\n",
      "6/6 - 9s - loss: 0.5354 - acc: 0.9704 - val_loss: 0.8470 - val_acc: 0.7801\n",
      "Epoch 18/20\n",
      "6/6 - 9s - loss: 0.4920 - acc: 0.9852 - val_loss: 0.8328 - val_acc: 0.7785\n",
      "Epoch 19/20\n",
      "6/6 - 9s - loss: 0.4557 - acc: 0.9704 - val_loss: 0.8178 - val_acc: 0.7851\n",
      "Epoch 20/20\n",
      "6/6 - 9s - loss: 0.4396 - acc: 0.9630 - val_loss: 0.8101 - val_acc: 0.7830\n"
     ]
    }
   ],
   "source": [
    "history = model.fit(\n",
    "    train_gen, epochs=epochs, validation_data=test_gen, verbose=2, shuffle=False\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "30",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 504x576 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "sg.utils.plot_history(history)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "31",
   "metadata": {},
   "source": [
    "Now we have trained the model we can evaluate on the test set."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "32",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "49/49 [==============================] - 8s 164ms/step - loss: 0.8131 - acc: 0.7859\n",
      "\n",
      "Test Set Metrics:\n",
      "\tloss: 0.8131\n",
      "\tacc: 0.7859\n"
     ]
    }
   ],
   "source": [
    "test_metrics = model.evaluate(test_gen)\n",
    "print(\"\\nTest Set Metrics:\")\n",
    "for name, val in zip(model.metrics_names, test_metrics):\n",
    "    print(\"\\t{}: {:0.4f}\".format(name, val))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "33",
   "metadata": {},
   "source": [
    "### Making predictions with the model"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34",
   "metadata": {},
   "source": [
    "Now let's get the predictions themselves for all nodes using another node iterator:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "35",
   "metadata": {},
   "outputs": [],
   "source": [
    "all_nodes = labels.index\n",
    "all_mapper = generator.flow(all_nodes)\n",
    "all_predictions = model.predict(all_mapper)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36",
   "metadata": {},
   "source": [
    "These predictions will be the output of the softmax layer, so to get final categories we'll use the `inverse_transform` method of our target attribute specification to turn these values back to the original categories"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "37",
   "metadata": {},
   "outputs": [],
   "source": [
    "node_predictions = target_encoding.inverse_transform(all_predictions)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "38",
   "metadata": {},
   "source": [
    "Let's have a look at a few:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "39",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Predicted</th>\n",
       "      <th>True</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>31336</th>\n",
       "      <td>Neural_Networks</td>\n",
       "      <td>Neural_Networks</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1061127</th>\n",
       "      <td>Theory</td>\n",
       "      <td>Rule_Learning</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1106406</th>\n",
       "      <td>Reinforcement_Learning</td>\n",
       "      <td>Reinforcement_Learning</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13195</th>\n",
       "      <td>Reinforcement_Learning</td>\n",
       "      <td>Reinforcement_Learning</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>37879</th>\n",
       "      <td>Probabilistic_Methods</td>\n",
       "      <td>Probabilistic_Methods</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1126012</th>\n",
       "      <td>Probabilistic_Methods</td>\n",
       "      <td>Probabilistic_Methods</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1107140</th>\n",
       "      <td>Theory</td>\n",
       "      <td>Theory</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1102850</th>\n",
       "      <td>Neural_Networks</td>\n",
       "      <td>Neural_Networks</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>31349</th>\n",
       "      <td>Neural_Networks</td>\n",
       "      <td>Neural_Networks</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1106418</th>\n",
       "      <td>Theory</td>\n",
       "      <td>Theory</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                      Predicted                    True\n",
       "31336           Neural_Networks         Neural_Networks\n",
       "1061127                  Theory           Rule_Learning\n",
       "1106406  Reinforcement_Learning  Reinforcement_Learning\n",
       "13195    Reinforcement_Learning  Reinforcement_Learning\n",
       "37879     Probabilistic_Methods   Probabilistic_Methods\n",
       "1126012   Probabilistic_Methods   Probabilistic_Methods\n",
       "1107140                  Theory                  Theory\n",
       "1102850         Neural_Networks         Neural_Networks\n",
       "31349           Neural_Networks         Neural_Networks\n",
       "1106418                  Theory                  Theory"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df = pd.DataFrame({\"Predicted\": node_predictions, \"True\": labels})\n",
    "df.head(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40",
   "metadata": {},
   "source": [
    "Please refer to [the non-Neo4j directed GraphSAGE node classification demo](./../../node-classification/directed-graphsage-node-classification.ipynb) for **node embedding visualization**."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "41",
   "metadata": {
    "nbsphinx": "hidden",
    "tags": [
     "CloudRunner"
    ]
   },
   "source": [
    "<table><tr><td>Run the latest release of this notebook:</td><td><a href=\"https://mybinder.org/v2/gh/stellargraph/stellargraph/master?urlpath=lab/tree/demos/connector/neo4j/directed-graphsage-on-cora-neo4j-example.ipynb\" alt=\"Open In Binder\" target=\"_parent\"><img src=\"https://mybinder.org/badge_logo.svg\"/></a></td><td><a href=\"https://colab.research.google.com/github/stellargraph/stellargraph/blob/master/demos/connector/neo4j/directed-graphsage-on-cora-neo4j-example.ipynb\" alt=\"Open In Colab\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\"/></a></td></tr></table>"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}