{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# GraphSAGE with Neo4j" ] }, { "cell_type": "markdown", "metadata": { "nbsphinx": "hidden", "tags": [ "CloudRunner" ] }, "source": [ "
Run the latest release of this notebook:
" ] }, { "cell_type": "code", "execution_count": 1, "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.0.0" ] }, { "cell_type": "code", "execution_count": 2, "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.0.0\")\n", "except AttributeError:\n", " raise ValueError(\n", " f\"This notebook requires StellarGraph version 1.0.0, but a different version {sg.__version__} is installed. Please see .\"\n", " ) from None" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import os\n", "\n", "import stellargraph as sg\n", "from stellargraph.connector.neo4j import Neo4JGraphSAGENodeGenerator\n", "from stellargraph.layer import GraphSAGE\n", "\n", "import numpy as np\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", "metadata": {}, "source": [ "## Loading the CORA data from 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.\n", "\n", "It is still required to load the node features into memory. We use ```py2neo```, which provides tools to connect to Neo4j databases from Python applications. ```py2neo``` documentation could be found [here](https://py2neo.org/v4/)." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "import py2neo\n", "\n", "default_host = os.environ.get(\"STELLARGRAPH_NEO4J_HOST\")\n", "\n", "# Create the Neo4j Graph database object; the arguments can be edited to specify location and authentication\n", "neo4j_graphdb = py2neo.Graph(host=default_host, port=None, user=None, password=None)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3.07 s: Loaded node data from neo4j database to memory\n" ] } ], "source": [ "def get_node_data_from_neo4j(neo4j_graphdb):\n", " fetch_node_query = \"MATCH (node) RETURN id(node), properties(node)\"\n", " # run the query\n", " node_records = neo4j_graphdb.run(fetch_node_query)\n", " # convert the node records into pandas dataframe\n", " return pd.DataFrame(node_records).rename(columns={0: \"id\", 1: \"attr\"})\n", "\n", "\n", "start = time.time()\n", "node_data = get_node_data_from_neo4j(neo4j_graphdb)\n", "end = time.time()\n", "\n", "print(f\"{end - start:.2f} s: Loaded node data from neo4j database to memory\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Extract the node_features:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
featuressubject
id
0[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...Neural_Networks
1[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, ...Rule_Learning
2[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...Reinforcement_Learning
3[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...Reinforcement_Learning
4[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...Probabilistic_Methods
\n", "
" ], "text/plain": [ " features subject\n", "id \n", "0 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... Neural_Networks\n", "1 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, ... Rule_Learning\n", "2 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... Reinforcement_Learning\n", "3 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... Reinforcement_Learning\n", "4 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... Probabilistic_Methods" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "node_data = node_data.set_index(\"id\")\n", "attribute_df = node_data[\"attr\"].apply(pd.Series)\n", "node_data = attribute_df.drop(labels=[\"ID\"], axis=1)\n", "node_features = pd.DataFrame(node_data[\"features\"].values.tolist(), index=node_data.index)\n", "node_data.head(5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We aim to train a graph-ML model that will predict the \"subject\" attribute on the nodes. These subjects are one of 7 categories:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'Case_Based',\n", " 'Genetic_Algorithms',\n", " 'Neural_Networks',\n", " 'Probabilistic_Methods',\n", " 'Reinforcement_Learning',\n", " 'Rule_Learning',\n", " 'Theory'}" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "labels = np.array(node_data[\"subject\"])\n", "set(labels)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Splitting the data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For machine learning we want to take a subset of the nodes for training, and use the rest for testing. We'll use scikit-learn again to do this." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "train_data, test_data = model_selection.train_test_split(\n", " node_data, train_size=0.1, test_size=None, stratify=node_data[\"subject\"]\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Converting to numeric arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For our categorical target, we will use one-hot vectors that will be fed into a soft-max Keras layer during training." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "target_encoding = feature_extraction.DictVectorizer(sparse=False)\n", "\n", "train_targets = target_encoding.fit_transform(train_data[[\"subject\"]].to_dict(\"records\"))\n", "test_targets = target_encoding.transform(test_data[[\"subject\"]].to_dict(\"records\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating the GraphSAGE model in Keras" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now create a *undirected* StellarGraph object storing node data. Since the subgraph is sampled directly from Neo4j, we only need to retain the node features and do not need to store any edges." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "G = sg.StellarGraph(nodes={\"paper\": node_features}, edges={})" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "StellarGraph: Undirected multigraph\n", " Nodes: 2708, Edges: 0\n", "\n", " Node types:\n", " paper: [2708]\n", " Features: float32 vector, length 1433\n", " Edge types: none\n", "\n", " Edge types:\n" ] } ], "source": [ "print(G.info())" ] }, { "cell_type": "markdown", "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 `Neo4JGraphSAGENodeGenerator` as we are predicting node attributes with a GraphSAGE 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, and 5 in the second." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "batch_size = 50\n", "num_samples = [10, 5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A `Neo4JGraphSAGENodeGenerator` object is required to send the node features in sampled subgraphs to Keras." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "generator = Neo4JGraphSAGENodeGenerator(G, batch_size, num_samples, neo4j_graphdb)" ] }, { "cell_type": "markdown", "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": 14, "metadata": {}, "outputs": [], "source": [ "train_gen = generator.flow(train_data.index, train_targets, shuffle=True)" ] }, { "cell_type": "markdown", "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.\n", " * The `bias` and `dropout` are internal parameters of the model. " ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "graphsage_model = GraphSAGE(\n", " layer_sizes=[32, 32], generator=generator, bias=True, dropout=0.5,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we create a model to predict the 7 categories using Keras softmax layers." ] }, { "cell_type": "code", "execution_count": 16, "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", "metadata": {}, "source": [ "### Training the model" ] }, { "cell_type": "markdown", "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": 17, "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", "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": 18, "metadata": {}, "outputs": [], "source": [ "test_gen = generator.flow(test_data.index, test_targets)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " ['...']\n", " ['...']\n", "Train for 6 steps, validate for 49 steps\n", "Epoch 1/20\n", "6/6 - 5s - loss: 1.8584 - acc: 0.2741 - val_loss: 1.7075 - val_acc: 0.3113\n", "Epoch 2/20\n", "6/6 - 4s - loss: 1.6480 - acc: 0.3444 - val_loss: 1.5590 - val_acc: 0.4454\n", "Epoch 3/20\n", "6/6 - 4s - loss: 1.4776 - acc: 0.6148 - val_loss: 1.3975 - val_acc: 0.6862\n", "Epoch 4/20\n", "6/6 - 4s - loss: 1.3258 - acc: 0.7778 - val_loss: 1.2655 - val_acc: 0.7441\n", "Epoch 5/20\n", "6/6 - 4s - loss: 1.1810 - acc: 0.8556 - val_loss: 1.1604 - val_acc: 0.7724\n", "Epoch 6/20\n", "6/6 - 4s - loss: 1.0659 - acc: 0.8778 - val_loss: 1.0739 - val_acc: 0.7929\n", "Epoch 7/20\n", "6/6 - 4s - loss: 0.9628 - acc: 0.9037 - val_loss: 0.9981 - val_acc: 0.8048\n", "Epoch 8/20\n", "6/6 - 4s - loss: 0.8450 - acc: 0.9444 - val_loss: 0.9395 - val_acc: 0.8130\n", "Epoch 9/20\n", "6/6 - 4s - loss: 0.7773 - acc: 0.9259 - val_loss: 0.8995 - val_acc: 0.8171\n", "Epoch 10/20\n", "6/6 - 4s - loss: 0.6998 - acc: 0.9556 - val_loss: 0.8573 - val_acc: 0.8072\n", "Epoch 11/20\n", "6/6 - 4s - loss: 0.6176 - acc: 0.9593 - val_loss: 0.8223 - val_acc: 0.8039\n", "Epoch 12/20\n", "6/6 - 4s - loss: 0.5801 - acc: 0.9593 - val_loss: 0.7851 - val_acc: 0.8035\n", "Epoch 13/20\n", "6/6 - 4s - loss: 0.5087 - acc: 0.9704 - val_loss: 0.7575 - val_acc: 0.8056\n", "Epoch 14/20\n", "6/6 - 4s - loss: 0.4625 - acc: 0.9741 - val_loss: 0.7380 - val_acc: 0.7994\n", "Epoch 15/20\n", "6/6 - 4s - loss: 0.4325 - acc: 0.9667 - val_loss: 0.7331 - val_acc: 0.8019\n", "Epoch 16/20\n", "6/6 - 4s - loss: 0.4018 - acc: 0.9852 - val_loss: 0.7020 - val_acc: 0.8089\n", "Epoch 17/20\n", "6/6 - 4s - loss: 0.3651 - acc: 0.9704 - val_loss: 0.6780 - val_acc: 0.8072\n", "Epoch 18/20\n", "6/6 - 4s - loss: 0.3333 - acc: 0.9815 - val_loss: 0.6943 - val_acc: 0.7990\n", "Epoch 19/20\n", "6/6 - 4s - loss: 0.3019 - acc: 0.9889 - val_loss: 0.6841 - val_acc: 0.8043\n", "Epoch 20/20\n", "6/6 - 4s - loss: 0.2742 - acc: 0.9815 - val_loss: 0.6906 - val_acc: 0.8019\n" ] } ], "source": [ "history = model.fit(\n", " train_gen, epochs=20, validation_data=test_gen, verbose=2, shuffle=False\n", ")" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "image/png": "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lbhcLhobGzl06FCfD4irrKwkPz+fM888s8P2s846i+XLl1NbW0tYmLPy5pw5czq0GTt2LDk5OX3+XP3hdUehW2sfs9ZOAu4AftxDmyestWnW2rS4uL6tlHYi3tt1mO/+dTPzk0byx2vmExTgdb82ERGv8qUvfYnY2FheeuklmpqaWLFiBddffz0AK1eu5JZbbuHyyy9n9erVbN68mXvvvZempqYBrSEjI4NLL72UM888k7///e9s2rSJP/7xjwA0NjYO6HsdERQU1OGxMaZfX0z6w5098AJgfLvH41q39WQF8IdBragPMnNLufmFjUyJj+DPSxYQGuTv6ZJERLyev78/V199Nc8//zwTJ06koqKCK664AoB169Yxd+5cbr/99rb2ubm5/Xr9GTNm8Pzzz9PS0oK/v/P/8ocfftihzQcffEBsbCw///nP27a9+uqrHdocCdz2r9NZZGQk48aNY926dSxatKht+9q1a0lJSWnrfbubO7uSG4ApxpgUY0wQcAWwqn0DY8yUdg8vAPa4sb4uth+o4IZnNjA2KtS5LGioLgsqItJX1113HZs2beInP/kJixYtIibGubJjamoqW7du5Y033iArK4vly5fz2muv9eu1v/Wtb1FUVMTSpUvZuXMn77zzDnfffXeHNqmpqRQVFfHnP/+Z7OxsnnvuOR5//PEObVJSUgBYtWoVRUVFVFdXd/t+P/rRj/jd737Hk08+yZ49e/jTn/7EH/7wB+66665+1T2Q3Bbg1tpm4FbgTWAn8Iq1drsx5v7WI84BbjXGbDfGfIIzD369u+rrLLuomuufXk9EcADPf3MhsSOCPVWKiIhPmjVrFnPmzOGTTz7huuuua9u+bNkyrr32Wm644Qbmzp1LRkYG9913X79eOzExkX/84x+sX7+eOXPmcNttt/Gb3/ymQ5tFixZx9913c9ddd3HyySezYsUKHn744Q5tFixYwG233cayZcuIj4/n1lu7vwLlt771Le6//35+8YtfMGPGDB566CEefPDBQTk4ra+MtV51oHe/paWl2czMzAF9zQPldXz9D/+lodnFKzefyqS4EQP6+iIifbFz506mT5/u6TJkkB3rz9kYs9Fam9Z5u47G6qS4uoFr/pxBVX0zz34jXeEtIiJeSUuptlNZ38T1T6+noKyO529cyMxE719KT0REhif1wNv5f2/vYdeh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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "sg.utils.plot_history(history)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we have trained the model we can evaluate on the test set." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " ['...']\n", "\n", "Test Set Metrics:\n", "\tloss: 0.6876\n", "\tacc: 0.8052\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", "metadata": {}, "source": [ "### Making predictions with the model" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's get the predictions themselves for all nodes using another node iterator:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [] } ], "source": [ "all_nodes = node_data.index\n", "all_mapper = generator.flow(all_nodes)\n", "all_predictions = model.predict(all_mapper)" ] }, { "cell_type": "markdown", "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 specifcation to turn these values back to the original categories" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "node_predictions = target_encoding.inverse_transform(all_predictions)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's have a look at a few:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
PredictedTrue
id
0subject=TheoryNeural_Networks
1subject=Rule_LearningRule_Learning
2subject=Reinforcement_LearningReinforcement_Learning
3subject=Reinforcement_LearningReinforcement_Learning
4subject=Probabilistic_MethodsProbabilistic_Methods
5subject=Probabilistic_MethodsProbabilistic_Methods
6subject=Reinforcement_LearningTheory
7subject=Neural_NetworksNeural_Networks
8subject=TheoryNeural_Networks
9subject=TheoryTheory
\n", "
" ], "text/plain": [ " Predicted True\n", "id \n", "0 subject=Theory Neural_Networks\n", "1 subject=Rule_Learning Rule_Learning\n", "2 subject=Reinforcement_Learning Reinforcement_Learning\n", "3 subject=Reinforcement_Learning Reinforcement_Learning\n", "4 subject=Probabilistic_Methods Probabilistic_Methods\n", "5 subject=Probabilistic_Methods Probabilistic_Methods\n", "6 subject=Reinforcement_Learning Theory\n", "7 subject=Neural_Networks Neural_Networks\n", "8 subject=Theory Neural_Networks\n", "9 subject=Theory Theory" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "results = pd.DataFrame(node_predictions, index=all_nodes).idxmax(axis=1)\n", "df = pd.DataFrame({\"Predicted\": results, \"True\": node_data[\"subject\"]})\n", "df.head(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Please refer to [graphsage-node-classification.ipynb](./../../node-classification/graphsage-node-classification.ipynb) for **node embedding visualization**." ] }, { "cell_type": "markdown", "metadata": { "nbsphinx": "hidden", "tags": [ "CloudRunner" ] }, "source": [ "
Run the latest release of this notebook:
" ] } ], "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.6.9" } }, "nbformat": 4, "nbformat_minor": 4 }