From afaf653fb3c2b2b1105eb4d0c5c1b830e1142dd4 Mon Sep 17 00:00:00 2001 From: Paula Date: Wed, 25 Jun 2025 15:41:31 +0200 Subject: [PATCH 1/6] reworked graphml documentation --- site/content/3.13/data-science/_index.md | 6 +- .../3.13/data-science/arangographml/deploy.md | 78 ------ .../3.13/data-science/arangographml/ui.md | 264 ------------------ .../{arangographml => graphml}/_index.md | 66 +++-- .../notebooks-api.md} | 225 ++++----------- .../3.13/data-science/graphml/quickstart.md | 60 ++++ site/content/3.13/data-science/graphml/ui.md | 254 +++++++++++++++++ .../images/graphml-ui-confusion-matrix.png | Bin 0 -> 88842 bytes 8 files changed, 406 insertions(+), 547 deletions(-) delete mode 100644 site/content/3.13/data-science/arangographml/deploy.md delete mode 100644 site/content/3.13/data-science/arangographml/ui.md rename site/content/3.13/data-science/{arangographml => graphml}/_index.md (70%) rename site/content/3.13/data-science/{arangographml/getting-started.md => graphml/notebooks-api.md} (79%) create mode 100644 site/content/3.13/data-science/graphml/quickstart.md create mode 100644 site/content/3.13/data-science/graphml/ui.md create mode 100644 site/content/images/graphml-ui-confusion-matrix.png diff --git a/site/content/3.13/data-science/_index.md b/site/content/3.13/data-science/_index.md index c543cebc6e..bed670b505 100644 --- a/site/content/3.13/data-science/_index.md +++ b/site/content/3.13/data-science/_index.md @@ -1,6 +1,6 @@ --- -title: Data Science -menuTitle: Data Science +title: Data Science & GenAI +menuTitle: Data Science & GenAI weight: 115 description: >- ArangoDB lets you apply analytics and machine learning to graph data at scale @@ -69,7 +69,7 @@ GraphML can answer questions like: ![Graph ML](../../images/graph-ml.png) For ArangoDB's enterprise-ready, graph-powered machine learning offering, -see [ArangoGraphML](arangographml/_index.md). +see [ArangoGraphML](graphml/_index.md). ## Use Cases diff --git a/site/content/3.13/data-science/arangographml/deploy.md b/site/content/3.13/data-science/arangographml/deploy.md deleted file mode 100644 index 0d62cb12f6..0000000000 --- a/site/content/3.13/data-science/arangographml/deploy.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: Deploy ArangoGraphML -menuTitle: Deploy -weight: 5 -description: >- - You can deploy ArangoGraphML in your own Kubernetes cluster or use the managed - cloud service that comes with a ready-to-go, pre-configured environment ---- - -## Managed cloud service versus self-managed - -ArangoDB offers two deployment options, tailored to suit diverse requirements -and infrastructure preferences: -- Managed cloud service via the [ArangoGraph Insights Platform](https://dashboard.arangodb.cloud/home?utm_source=docs&utm_medium=cluster_pages&utm_campaign=docs_traffic) -- Self-managed solution via the [ArangoDB Kubernetes Operator](https://github.com/arangodb/kube-arangodb) - -### ArangoGraphML - -ArangoGraphML provides enterprise-ready Graph Machine Learning as a -Cloud Service via Jupyter Notebooks that run on the -[ArangoGraph Insights Platform](https://dashboard.arangodb.cloud/home?utm_source=docs&utm_medium=cluster_pages&utm_campaign=docs_traffic). - -{{< tip >}} -To get access to ArangoGraphML services and packages, -[get in touch](https://www.arangodb.com/contact/) -with the ArangoDB team. -{{< /tip >}} - -- **Accessible at all levels** - - Low code UI - - Notebooks - - APIs -- **Full usability** - - MLOps lifecycle - - Metrics - - Metadata capture - - Model management - -![ArangoGraphML Pipeline](../../../images/ArangoGraphML_Pipeline.png) - -#### Setup - -The ArangoGraphML managed-service runs on the -[ArangoGraph Insights Platform](https://dashboard.arangodb.cloud/home?utm_source=docs&utm_medium=cluster_pages&utm_campaign=docs_traffic). -It offers a pre-configured environment where everything, -including necessary components and configurations, comes preloaded. You don't -need to set up or configure the infrastructure, and can immediately start using the -GraphML functionalities. - -To summarize, all you need to do is: -1. Sign up for an [ArangoGraph account](https://dashboard.arangodb.cloud/home?utm_source=docs&utm_medium=cluster_pages&utm_campaign=docs_traffic). -2. Create a new [deployment in ArangoGraph](../../arangograph/deployments/_index.md#how-to-create-a-new-deployment). -3. Start using the ArangoGraphML functionalities. - -### Self-managed ArangoGraphML - -{{< tag "ArangoDB Enterprise Edition" >}} - -The self-managed solution enables you to deploy and manage ArangoML within your -Kubernetes cluster using the [ArangoDB Kubernetes Operator](https://github.com/arangodb/kube-arangodb). - -The self-managed package includes the same features as in ArangoGraphML. -The primary distinction lies in the environment setup: with the self-managed -solution, you have direct control over configuring your environment. - -#### Setup - -You can run ArangoGraphML in your Kubernetes -cluster provided you already have a running `ArangoDeployment`. If you don't -have one yet, consider checking the installation guide of the -[ArangoDB Kubernetes Operator](https://arangodb.github.io/kube-arangodb/docs/using-the-operator.html) -and the [ArangoDeployment Custom Resource](https://arangodb.github.io/kube-arangodb/docs/deployment-resource-reference.html) -description. - -To start ArangoGraphML in your Kubernetes cluster, follow the instructions provided -in the [ArangoMLExtension Custom Resource](https://arangodb.github.io/kube-arangodb/docs/mlextension-resource.html) -description. Once the `CustomResource` has been created and the ArangoGraphML extension -is ready, you can start using it. \ No newline at end of file diff --git a/site/content/3.13/data-science/arangographml/ui.md b/site/content/3.13/data-science/arangographml/ui.md deleted file mode 100644 index bbe6f9df88..0000000000 --- a/site/content/3.13/data-science/arangographml/ui.md +++ /dev/null @@ -1,264 +0,0 @@ ---- -title: GraphML -menuTitle: GraphML -weight: 15 -description: >- - Learn how to create, configure, and run a full machine learning workflow for ArangoGraphML using the steps and features in the ArangoDB web interface ---- -Solve high-computational graph problems with Graph Machine Learning. Apply ML on a selected graph to predict connections, get better product recommendations, classify nodes, and perform node embeddings. Configure and run the whole machine learning flow entirely in the web interface. - -## What You Can Do with GraphML - -GraphML directly supports two primary machine learning tasks: - -* **Node Classification:** Automatically assign a label to nodes in your graph. For example, you can classify customers as "likely to churn" or "high value," or identify fraudulent transactions. -* **Node Embeddings:** Generate numerical representations (vectors) for each node. The main purpose of these embeddings is to measure similarity: nodes that are alike are placed close together in a high-dimensional space. By comparing the embeddings of two nodes, you can calculate their similarity. This is the foundation for advanced tasks like link prediction (predicting a future connection), building recommendation engines, and finding similar items. - -## How ArangoGraphML Works - -The underlying framework for ArangoGraphML is **[GraphSAGE](https://snap.stanford.edu/graphsage/)**. GraphSAGE (Graph Sample and AggreGatE) is a powerful Graph Neural Network (GNN) **framework** designed for inductive representation learning on large graphs. It is used to generate low-dimensional vector representations for nodes and is especially useful for graphs that have rich node attribute information. The overall process involves two main stages: - -1. **Featurization**: Your raw graph data is transformed into numerical representations that the model can understand. - * The system iterates over your selected vertices and converts their attributes: booleans become `0` or `1`, numbers are normalized, and text attributes are converted into numerical vectors using sentence transformers. - * All of these numerical features are then combined (concatenated). - * Finally, **Incremental PCA** (Incremental Principal Component Analysis a dimensionality reduction technique) is used to reduce the size of the combined features, which helps remove noise and keep only the most important information. - -2. **Training**: The model learns from the graph's structure by sampling and aggregating information from each node's local neighborhood. - * For each node, GraphSAGE looks at connections up to **2 hops away**. - * Specifically, it uniformly samples up to **25 direct neighbors** (depth 1) and for each of those, it samples up to **10 of their neighbors** (depth 2). - * By aggregating feature information from this sampled neighborhood, the model creates a rich "embedding" for each node that captures both its own features and its role in the graph. - -## Limitations - -* **Edge Attributes**: The current version of ArangoGraphML does not support the use of edge attributes as features. -* **Dangling Edges**: Edges that point to non-existent vertices ("dangling edges") are not caught during the featurization analysis. They may cause errors later, during the Training phase. -* **Prediction Scope**: Predictions can only be run in batches on the graph. It is not possible to request a prediction for a single document on-the-fly (Example, via an AQL query). -* **Memory Usage**: Both featurization and training can be memory-intensive. Out-of-memory errors can occur on large graphs with insufficient system resources. - -## The GraphML Workflow - -The entire process is organized into sequential steps within a **Project**, giving you a clear path from data to prediction: - -1. **Featurization:** Select your data and convert it into numerical features. -2. **Training:** Train a GraphSAGE model on the features and graph structure. -3. **Model Selection:** Evaluate the trained models and choose the best one. -4. **Prediction:** Use the selected model to generate predictions on your data. -5. **Scheduling (Optional):** Automate the prediction process to run at regular intervals. - -## Creating a GraphML Project - -To create a new GraphML project using the ArangoDB Web Interface, follow these steps: - -1. From the left-hand sidebar, select the database where you want to create the project. -2. In the left-hand navigation menu, click **Data Science Suite** to open the GraphML project management interface, then click Run GraphML. - ![Navigate to Data Science](../../../images/datascience-intro.jpg) -3. In the **GraphML projects** view, click **Add new project**. -4. The **Create ML project** modal opens. Enter a **Name** for your machine learning project. -5. Click the **Create project** button to finalize the creation. -6. After creation, the new project appears in the list under **GraphML projects**. Click the project name to begin with a Featurization job. - -## Featurization Phase - -After clicking on a project name, you are taken to a screen where you can configure and start a new Featurization job. Follow these steps: -- **Select a Graph** – In the **Features** section, choose your target graph from the **Select a graph** dropdown. -- **Choose Vertex Collections** – Pick the vertex collections that you want to include for feature extraction. -- **Select Attributes** – Choose the attributes from your vertex collection to convert into machine-understandable features. - -{{< info >}} -Attributes cannot be used if their values are lists or arrays. -{{< /info >}} - -{{< info >}} -A metagraph is basically just the configured subset of a graph (the vertex and edge collections and the specified attributes). This is what you see represented in the metagraph object in the JSON specification on the right. -{{< /info >}} - -The featurization process has several configurable options, grouped into Configuration and Advanced settings. These are also shown in a JSON format on the right side of the screen for transparency. - -**Configuration** -These settings control the overall featurization job and how features are stored. - -- **Batch size** – The number of documents to process in a single batch. -- **Run analysis checks** – Whether to run analysis checks to perform a high-level analysis of the data quality before proceeding. Default is `true`. -- **Skip labels** – Skip the featurization process for attributes marked as labels. Default is `false`. - -**Feature Storage Options** -These settings control where the generated features are stored. - -- **Overwrite FS graph** – Whether to overwrite the Feature Store graph if features were previously generated. Default is `false`, therefore features are written to an existing graph Featere store graph. -- **Write to source graph** – Whether to store the generated features on the Source Graph. Default is `true`. -- **Use feature store** – Enable the use of the Feature Store database, which allows you to store features separately from your Source Database. Default is `false`, therefore feature are written to the source graph. - -**Advanced Settings: Handling Imperfect Data** - -Real-world data is often messy. It can have missing values or mismatched data types. This section allows you to define default rules for how the featurization process should handle these data quality issues for each feature type, preventing the job from failing unexpectedly. - -For each feature type (Text, Numeric, Category, and Label), you can set two types of strategies: - -**Missing Strategy:** Defines what to do when an attribute is completely missing from a document. - -**Mismatch Strategy:** Defines what to do when an attribute exists but has the wrong data type. - -**Missing Value Strategies** - -**Raise:** The job immediately stops and reports an error if a data type mismatch is found. Use this when any type mismatch indicates a critical data error. - -**Replace:** Replaces a missing value with a default you provide (e.g., 0 for numbers, "unknown" for text). The job then continues. Use this when missing values are expected. - -**Mismatch Value Strategies** - -**Raise:** The strictest option. The job will immediately stop and report an error if a data type mismatch is found. Use this when any type mismatch indicates a critical data error. - -**Replace:** If a mismatch is found, the system immediately replaces the value with the default you specify, without attempting to convert it first. Use this when you don't trust the mismatched data and prefer to substitute it directly. - -**Coerce and raise:** A balanced approach. The system first tries to convert (coerce) the value to the correct type (e.g., string "123" to number 123). If the conversion is successful, it uses the new value. If it fails, the job stops. This is often the best default strategy. - -**Coerce and replace:** The most forgiving option. The system first tries to convert the value. If the conversion fails, it replaces the value with the default you specify and continues the job. Use this for very "dirty" datasets where completing the job is the highest priority. - -Once all selections are done, click the **Begin featurization** button. This triggers a **node embedding-compatible featurization job**. Once the job status changes to **Ready for training**, you can start the **ML Training** step. - -![Navigate to Featurization](../../../images/graph-ml-ui-featurization.png) - -## Training Phase - -The training is the second step in the ML workflow after featurization. In the training phase, you configure and launch a machine learning training job on your graph data. - - -- **Select a training job type** – From the **Select a type of training job** dropdown, choose the type of model you want to train (Example, Node Classification, Node Embedding). - - -#### Node Classification - -Node Classification is used to categorize the nodes in your graph based on their features and structural connections within the graph. - -**Use cases include:** -- Entity categorization (Example, movies into genres, users into segments) -- Fraud detection in transaction networks - -**Configuration Parameters:** -- **Type of Training Job:** Node classification -- **Target Vertex Collection:** Choose the collection to classify (Example, `movie`) -- **Batch Size:** The nummer of documents processed in a single training iteration. (Example, 256) -- **Data Load Batch Size:** The number of documents loaded from ArangoDB into memory in a single batch during the data loading phase. (Example, 50000) -- **Data Load Parallelism:** The number of parallel processes used when loading data from ArangoDB into memory for trainnig. (Example, 10) - -After setting these values, click the **Begin training** button to start the job. - -![Node Classification](../../../images/ml-nodeclassification.png) - - #### Node Embedding - -Node Embedding is used to generate vector embeddings (dense numerical representations) of graph nodes that capture structural and feature-based information. - -**Use cases include:** -- Similarity search (Example, finding similar products, users, or documents) -- Link prediction (Example, suggesting new connections) - -**Configuration Parameters:** -- **Type of Training Job:** Node embeddings -- **Target Vertex Collection:** Select the collection to generate embeddings for (Example, `movie` or `person`) -- No label is required for training in this mode - -Once the configuration is complete, click **Begin training** to launch the embedding job. - -![Node Embeddings](../../../images/ml-node-embedding.png) - - -After training is complete, the next step in the ArangoGraphML workflow is **Model Selection**. - -## Model Selection Phase - -Once the training is finished, the job status updates to **READY FOR MODEL SELECTION**. This means the model has been trained using the provided vertex and edge data and is now ready for evaluation. - -**Understanding Vertex Collections:** - -**X Vertex Collection:** These are the source nodes used during training. They represent the full set of nodes on which features were computed (Example, person, movie). - -**Y Vertex Collection:** These are the target nodes that contain labeled data. The labels in this collection are used to supervise the training process and are the basis for evaluating prediction quality. - -The target collection is where the model's predictions are stored when running a prediction job. - -**Model Selection Interface:** - -A list of trained models is displayed, along with performance metrics (Accuracy, Precision, Recall, F1 score, Loss). -Review the results of different model runs and configurations. - -Select the best performing model suitable for your prediction task. - -![Model Selection](../../../images/graph-ml-model.png) - -## Prediction Phase - -After selecting a model, you can create a Prediction Job. The Prediction Job generates predictions and persists them to the source graph, either in a new collection or within the source documents. - -### Overview - -The Prediction interface allows inference to be run using the selected model. It enables configuration of how predictions are executed, which collections are involved, and whether new or outdated documents should be automatically featurized before prediction. - -You have two important options for this: - -**Featurize new documents:** Enable this option to generate features for documents that have been added since the model was trained. This is useful for getting predictions on new data without having to retrain the model. - -**Featurize outdated documents:** Enable this option to re-generate features for documents that have been modified since the last featurization. This ensures your predictions reflect the latest changes to your data. -In addition to these settings, you will also define the target data, where to store results, and whether to run the job on a recurring schedule. - -In addition to these settings, you also define the target data, where to store results, and whether to run the job on a recurring schedule. - -![prediction phase](../../../images/graph-prediction.png) - -### Configuration Options -The Prediction screen displays the following configuration options: - -- Selected Model: Displays the model selected during the Model Selection phase. This model will be used to perform inference. - -- Target Vertex Collection: This is the vertex collection on which predictions are applied. - -- Prediction Type: Depending on the training job (for example, classification or embedding), the prediction outputs class labels or updated embeddings. - -### Featurization Settings -Two toggles are available to control automatic featurization during prediction - -**Featurize New Documents:** -This option controls whether newly added documents are automatically featurized. It is useful when new data arrives after training, allowing predictions to continue without requiring a full retraining process. - -**Featurize Outdated Documents:** -Enable or disable the featurization of outdated documents. Outdated documents are those whose attributes (used during featurization) have changed since the last feature computation. This ensures prediction results are based on up-to-date information. - -These options provide flexibility in handling dynamic graph data and keeping predictions relevant without repeating the entire ML workflow. - -**Data load batch size** – Specifies the number of documents to load in a single batch (Example, 500000). - -**Data load parallelism** – Number of parallel threads used to process the prediction workload (Example, 10). - -**Prediction field** – The field in the documents where the predicted values are stored. - -### Enable Scheduling - -You can configure automatic predictions using the **Enable scheduling** checkbox. - -When scheduling is turned on, predictions run automatically based on a set CRON expression. This helps keep prediction results up to date as new data is added to the system. - -#### Schedule (CRON expression) - -You can define a CRON expression that sets when the prediction job should run. For example: -0 0 1 1 * -This CRON pattern executes the prediction every year on January 1st at 00:00. - -Below the CRON field, a user-friendly scheduling interface helps translate it: - -- **Period**: Options include *Hourly*, *Daily*, *Weekly*, *Monthly*, or *Yearly*. -- **Month**: *(Example, January)* -- **Day of Month**: *(Example, 1)* -- **Day of Week**: *(optional)* -- **Hours and Minutes**: Set the exact time for execution *(Example, 0:00)* - - -### Execute Prediction -After reviewing the configuration, click the Run Prediction button. ArangoGraphML then: - -- Perform featurization - -- Run inference using the selected model - -- Write prediction results into the target vertex collection or a specified output location - -Once prediction is complete, you can analyze the results directly in the Web Interface or export them for downstream use. diff --git a/site/content/3.13/data-science/arangographml/_index.md b/site/content/3.13/data-science/graphml/_index.md similarity index 70% rename from site/content/3.13/data-science/arangographml/_index.md rename to site/content/3.13/data-science/graphml/_index.md index 2d5d3324de..291f728b65 100644 --- a/site/content/3.13/data-science/arangographml/_index.md +++ b/site/content/3.13/data-science/graphml/_index.md @@ -1,18 +1,23 @@ --- -title: ArangoGraphML -menuTitle: ArangoGraphML +title: GraphML +menuTitle: GraphML weight: 125 description: >- - Enterprise-ready, graph-powered machine learning as a cloud service or self-managed + Boost your machine learning models with graph data using ArangoDB's advanced GraphML capabilities and predict anything aliases: - - graphml + - arangographml --- Traditional Machine Learning (ML) overlooks the connections and relationships between data points, which is where graph machine learning excels. However, accessibility to GraphML has been limited to sizable enterprises equipped with -specialized teams of data scientists. ArangoGraphML simplifies the utilization of GraphML, +specialized teams of data scientists. ArangoDB simplifies the utilization of Graph Machine Learning, enabling a broader range of personas to extract profound insights from their data. +With ArangoDB, you can solve high-computational graph problems using Graph Machine +Learning. Apply it on a selected graph to predict connections, get better product +recommendations, classify nodes, and perform node embeddings. You can configure and run +the whole machine learning flow entirely through the web interface or programmatically. + ## How GraphML works Graph machine learning leverages the inherent structure of graph data, where @@ -21,18 +26,29 @@ traditional ML, which primarily operates on tabular data, GraphML applies specialized algorithms like Graph Neural Networks (GNNs), node embeddings, and link prediction to uncover complex patterns and insights. +The underlying framework for ArangoDB's GraphML is **[GraphSAGE](https://snap.stanford.edu/graphsage/)**. +GraphSAGE (Graph Sample and AggreGatE) is a powerful Graph Neural Network (GNN) +**framework** designed for inductive representation learning on large graphs. +It is used to generate low-dimensional vector representations for nodes and is +especially useful for graphs that have rich node attribute information. +The overall process involves the following steps: + 1. **Graph Construction**: - Raw data is transformed into a graph structure, defining nodes and edges based + - Raw data is transformed into a graph structure, defining nodes and edges based on real-world relationships. -2. **Featurization**: - Nodes and edges are enriched with features that help in training predictive models. -3. **Model Training**: - Machine learning techniques are applied on GNNs to identify patterns and make predictions. +2. **Featurization**: Your raw graph data is transformed into numerical representations that the model can understand. + - The system iterates over your selected vertices and converts their attributes: booleans become `0` or `1`, numbers are normalized, and text attributes are converted into numerical vectors using sentence transformers. + - All of these numerical features are then combined (concatenated). + - Finally, **Incremental PCA** (Incremental Principal Component Analysis a dimensionality reduction technique) is used to reduce the size of the combined features, which helps remove noise and keep only the most important information. +3. **Training**: The model learns from the graph's structure by sampling and aggregating information from each node's local neighborhood. + - For each node, GraphSAGE looks at connections up to **2 hops away**. + - Specifically, it uniformly samples up to **25 direct neighbors** (depth 1) and for each of those, it samples up to **10 of their neighbors** (depth 2). + - By aggregating feature information from this sampled neighborhood, the model creates a rich "embedding" for each node that captures both its own features and its role in the graph. 4. **Inference & Insights**: - The trained model is used to classify nodes, detect anomalies, recommend items, + - The trained model is used to classify nodes, detect anomalies, recommend items, or predict future connections. -ArangoGraphML streamlines these steps, providing an intuitive and scalable +ArangoDB streamlines these steps, providing an intuitive and scalable framework to integrate GraphML into various applications, from fraud detection to recommendation systems. @@ -40,16 +56,18 @@ to recommendation systems. ![GraphML Workflow](../../../images/GraphML-How-it-works.webp) -It is no longer necessary to understand the complexities involved with graph -machine learning, thanks to the accessibility of the ArangoML package. -Solutions with ArangoGraphML only require input from a user about -their data, and the ArangoGraphML managed service handles the rest. +You no longer need to understand the complexities of graph machine learning to +benefit from it. Solutions with ArangoDB's GraphML only require input from a user about +their data, and the GraphML managed service handles the rest. The platform comes preloaded with all the tools needed to prepare your graph for machine learning, high-accuracy training, and persisting predictions back to the database for application use. -## Supported Tasks +## What you can do with GraphML + +GraphML directly supports two primary machine learning tasks: +**Node Classification** and **Node Embeddings**. ### Node Classification @@ -58,7 +76,7 @@ predict the label of a node based on both its own features and its relationships within the graph. It requires a set of labeled nodes to train a model, which then classifies unlabeled nodes based on learned patterns. -**How it works in ArangoGraphML** +**How it works in ArangoDB** - A portion of the nodes in a graph is labeled for training. - The model learns patterns from both **node features** and @@ -97,7 +115,7 @@ into numerical vector representations, preserving their **structural relationshi within the graph. Unlike simple feature aggregation, node embeddings **capture the influence of neighboring nodes and graph topology**, making them powerful for downstream tasks like clustering, anomaly detection, -and link prediction. These combinations can provide valuable insights. +and link prediction. This combination provides valuable insights. Consider using [ArangoDB's Vector Search](https://arangodb.com/2024/11/vector-search-in-arangodb-practical-insights-and-hands-on-examples/) capabilities to find similar nodes based on their embeddings. @@ -116,7 +134,7 @@ Essentially, they aggregate both the node's attributes and the connectivity patt within the graph. This fusion helps capture not only the individual properties of a node but also its position and role within the network. -**How it works in ArangoGraphML** +**How it works in ArangoDB** - The model learns an embedding (a vector representation) for each node based on its **position within the graph and its connections**. @@ -161,12 +179,12 @@ a node but also its position and role within the network. | **Key Advantage** | Learns labels based on node connections and attributes | Learns structural patterns and node relationships | | **Use Cases** | Fraud detection, customer segmentation, disease classification | Recommendations, anomaly detection, link prediction | -ArangoGraphML provides the infrastructure to efficiently train and apply these +GraphML provides the infrastructure to efficiently train and apply these models, helping users extract meaningful insights from complex graph data. ## Metrics and Compliance -ArangoGraphML supports tracking your ML pipeline by storing all relevant metadata +GraphML supports tracking your ML pipeline by storing all relevant metadata and metrics in a Graph called ArangoPipe. This is only available to you and is never viewable by ArangoDB. This metadata graph links all experiments to the source data, feature generation activities, training runs, and prediction @@ -174,8 +192,8 @@ jobs, allowing you to track the entire ML pipeline without having to leave Arang ### Security -Each deployment that uses ArangoGraphML has an `arangopipe` database created, +Each deployment that uses GraphML has an `arangopipe` database created, which houses all ML Metadata information. Since this data lives within the deployment, it benefits from the ArangoGraph SOC 2 compliance and Enterprise security features. -All ArangoGraphML services live alongside the ArangoGraph deployment and are only +All GraphML services live alongside the ArangoGraph deployment and are only accessible within that organization. diff --git a/site/content/3.13/data-science/arangographml/getting-started.md b/site/content/3.13/data-science/graphml/notebooks-api.md similarity index 79% rename from site/content/3.13/data-science/arangographml/getting-started.md rename to site/content/3.13/data-science/graphml/notebooks-api.md index 6bd614167e..06a0b6a9b4 100644 --- a/site/content/3.13/data-science/arangographml/getting-started.md +++ b/site/content/3.13/data-science/graphml/notebooks-api.md @@ -1,29 +1,39 @@ --- -title: Getting Started with ArangoGraphML -menuTitle: Getting Started -weight: 10 +title: How to use GraphML in a scriptable manner +menuTitle: Notebooks & API +weight: 15 description: >- - How to control all resources inside ArangoGraphML in a scriptable manner + Control all resources inside GraphML via Notebooks or API aliases: - getting-started-with-arangographml + - ../arangographml/getting-started + - ../arangographml-getting-started-with-arangographml --- -ArangoGraphML provides an easy-to-use & scalable interface to run Graph Machine Learning on ArangoDB Data. Since all of the orchestration and ML logic is managed by ArangoGraph, all that is typically required are JSON specifications outlining individual processes to solve an ML Task. If you are using the self-managed solution, additional configurations may be required. -The `arangoml` is a Python Package allowing you to manage all of the necessary ArangoGraphML components, including: -- **Project Management**: Projects are a metadata-tracking entity that sit at the top level of ArangoGraphML. All activities must link to a project. -- **Featurization**: The step of converting human-understandable data to machine-understandable data (i.e features), such that it can be used to train Graph Neural Networks (GNNs). -- **Training**: Train a set of models based on the name of the generated/existing features, and a definition of the ML Task we want to solve (e.g Node Classification, Embedding Generation). +{{< tag "ArangoDB Platform" >}} + +ArangoDB Platform provides an easy-to-use & scalable interface to run Graph Machine +Learning on ArangoDB data. Since all the orchestration and Machine Learning logic is +managed by ArangoDB, all that is typically required are JSON specifications outlining +individual processes to solve a Machine Learning Task. + +The `arangoml` is a Python Package allowing you to manage all the necessary +GraphML components, including: +- **Project Management**: Projects are a metadata-tracking entity that sit at + the top level of GraphML. All activities must link to a project. +- **Featurization**: The step of converting human-understandable data to + machine-understandable data (e.g. features), such that it can be used to + train Graph Neural Networks (GNNs). +- **Training**: Train a set of models based on the name of the generated/existing + features, and a definition of the ML task we want to solve (e.g. Node Classification, Embedding Generation). - **Model Selection**: Select the best model based on the metrics generated during training. -- **Predictions**: Generate predictions based on the selected model, and persit the results to the source graph (either in the source document, or in a new collection). +- **Predictions**: Generate predictions based on the selected model, and persist + the results to the source graph (either in the source document, or in a new collection). -{{< tip >}} -To enable the ArangoGraphML services in the ArangoGraph platform, -[get in touch](https://www.arangodb.com/contact/) -with the ArangoDB team. Regular notebooks in ArangoGraph don't include the -`arangoml` package. -{{< /tip >}} - -ArangoGraphML's suite of services and packages is driven by **"specifications"**. These specifications are standard Python dictionaries that describe the task being performed, & the data being used. The ArangoGraphML services work closely together, with the previous task being used as the input for the next. +GraphML's suite of services and packages is driven by **"specifications"**. +These specifications are standard Python dictionaries that describe the task +being performed, and the data being used. The GraphML services work closely +together, with the previous task being used as the input for the next. Let's take a look at using the `arangoml` package to: @@ -35,13 +45,9 @@ Let's take a look at using the `arangoml` package to: ## Initialize ArangoML -{{< tabs "arangoml" >}} - -{{< tab "ArangoGraphML" >}} - **API Documentation: [arangoml.ArangoMLMagics.enable_arangoml](https://arangoml.github.io/arangoml/magics.html#arangoml.magic.ArangoMLMagics.enable_arangoml)** -The `arangoml` package comes pre-loaded with every ArangoGraphML notebook environment. +The `arangoml` package comes pre-loaded with every GraphML notebook environment. To start using it, simply import it, and enable it via a Jupyter Magic Command. ```py @@ -49,116 +55,12 @@ arangoml = %enable_arangoml ``` {{< tip >}} -ArangoGraphML comes with other ArangoDB Magic Commands! See the full list [here](https://arangoml.github.io/arangoml/magics.html). +GraphML comes with other ArangoDB Magic Commands! See the full list [here](https://arangoml.github.io/arangoml/magics.html). {{< /tip >}} -{{< /tab >}} - -{{< tab "Self-managed" >}} - -**API Documentation: [arangoml.ArangoML](https://arangoml.github.io/arangoml/client.html#arangoml.main.ArangoML)** - -The `ArangoML` class is the main entry point for the `arangoml` package. -It has the following parameters: -- `client`: An instance of arango.client.ArangoClient. Defaults to `None`. If not provided, the **hosts** argument must be provided. -- `hosts`: The ArangoDB host(s) to connect to. This can be a single host, or a - list of hosts. -- `username`: The ArangoDB username to use for authentication. -- `password`: The ArangoDB password to use for authentication. -- `user_token`: The ArangoDB user token to use for authentication. - This is an alternative to username/password authentication. -- `ca_cert_file`: The path to the CA certificate file to use for TLS - verification. Defaults to `None`. -- `api_endpoint`: The URL to the ArangoGraphML API Service. -- `settings_files`: A list of secrets files to be loaded as settings. Parameters provided as arguments will override those in the settings files (e.g `settings.toml`). -- `version`: The ArangoML API date version. Defaults to the latest version. - -It is possible to instantiate an ArangoML object in multiple ways: - -1. Via parameters -```py -from arangoml import ArangoML - -arangoml = ArangoML( - hosts="http://localhost:8529" - username="root", - password="password", - # ca_cert_file="/path/to/ca.pem", - # user_token="..." - api_endpoint="http://localhost:8501", -) -``` - -2. Via parameters and a custom `ArangoClient` instance -```py -from arangoml import ArangoML -from arango import ArangoClient - -client = ArangoClient( - hosts="http://localhost:8529", - verify_override="/path/to/ca.pem", - hosts_resolver=..., - ... -) - -arangoml = ArangoML( - client=client, - username="root", - password="password", - # user_token="..." - api_endpoint="http://localhost:8501", -) -``` - -3. Via environment variables -```py -import os -from arangoml import ArangoML - -os.environ["ARANGODB_HOSTS"] = "http://localhost:8529" -os.environ["ARANGODB_CA_CERT_FILE"]="/path/to/ca.pem" -os.environ["ARANGODB_USER"] = "root" -os.environ["ARANGODB_PW"] = "password" -# os.environ["ARANGODB_USER_TOKEN"] = "..." -os.environ["ML_API_SERVICES_ENDPOINT"] = "http://localhost:8501" - -arangoml = ArangoML() -``` - -4. Via configuration files -```py -import os -from arangoml import ArangoML - -arangoml = ArangoML(settings_files=["settings_1.toml", "settings_2.toml"]) -``` - -5. Via a Jupyter Magic Command - -**API Documentation: [arangoml.ArangoMLMagics.enable_arangoml](https://arangoml.github.io/arangoml/magics.html#arangoml.magic.ArangoMLMagics.enable_arangoml)** - -``` -%load_ext arangoml -%enable_arangoml -``` -{{< info >}} -This assumes you are working out of a Jupyter Notebook environment, and -have set the environment variables in the notebook environment with user -authentication that has **_system** access. -{{< /info >}} - -{{< tip >}} -Running `%load_ext arangoml` also provides access to other [ArangoGraphML -Jupyter Magic Commands](https://arangoml.github.io/arangoml/magics.html). -{{< /tip >}} - -{{< /tab >}} - -{{< /tabs >}} - ## Load the database -This example is using ArangoML to predict the **class** of `Events` in a +This example is using GraphML to predict the **class** of `Events` in a Knowledge Graph constructed from the [GDELT Project](https://www.gdeltproject.org/). > GDELT monitors the world's news media from nearly every corner of every @@ -180,15 +82,9 @@ news sources, and locations are interconnected into a large graph. ![Example Event](../../../images/ArangoML_open_intelligence_visualization.png) -Let's get started! - -{{< tabs "arangoml" >}} - -{{< tab "ArangoGraphML" >}} - The [`arango-datasets`](../../components/tools/arango-datasets.md) Python package -allows you to load pre-defined datasets into ArangoDB. It comes pre-installed in the -ArangoGraphML notebook environment. +allows you to load pre-defined datasets into ArangoDB Platform. It comes pre-installed in the +GraphML notebook environment. ```py DATASET_NAME = "OPEN_INTELLIGENCE_ANGOLA" @@ -199,42 +95,11 @@ DATASET_NAME = "OPEN_INTELLIGENCE_ANGOLA" %load_dataset {DATASET_NAME} ``` -{{< /tab >}} - -{{< tab "Self-managed" >}} - -The [`arango-datasets`](../../components/tools/arango-datasets.md) Python package -allows you to load pre-defined datasets into ArangoDB. It can be installed with the -following command: - -``` -pip install arango-datasets -``` - -```py -from arango_datasets.datasets import Datasets - -DATASET_NAME = "OPEN_INTELLIGENCE_ANGOLA" - -db = arangoml.client.db( - name=DATASET_NAME, - username=arangoml.settings.get("ARANGODB_USER"), - password=arangoml.settings.get("ARANGODB_PW"), - user_token=arangoml.settings.get("ARANGODB_USER_TOKEN"), - verify=True -) - -Datasets(dataset_db).load(DATASET_NAME) -``` -{{< /tab >}} - -{{< /tabs >}} - ## Projects **API Documentation: [ArangoML.projects](https://arangoml.github.io/arangoml/api.html#projects)** -Projects are an important reference used throughout the entire ArangoGraphML +Projects are an important reference used throughout the entire GraphML lifecycle. All activities link back to a project. The creation of the project is very simple. @@ -410,7 +275,6 @@ Once a Featurization Job has been submitted, you can wait for it to complete usi featurization_job_result = arangoml.wait_for_featurization(featurization_job.job_id) ``` - **Example Output:** ```py { @@ -511,12 +375,11 @@ You can also cancel a Featurization Job using the `arangoml.jobs.cancel_job` met arangoml.jobs.cancel_job(prediction_job.job_id) ``` - ## Training **API Documentation: [ArangoML.jobs.train](https://arangoml.github.io/arangoml/api.html#agml_api.jobs.v1.api.jobs_api.JobsApi.train)** -Training Graph Machine Learning Models with ArangoGraphML requires two steps: +Training Graph Machine Learning Models with GraphML requires two steps: 1. Describe which data points should be included in the Training Job. 2. Pass the Training Specification to the Training Service. @@ -553,12 +416,13 @@ A Training Specification allows for concisely defining your training task in a single object and then passing that object to the training service using the Python API client, as shown below. -The ArangoGraphML Training Service is responsible for training a series of +The GraphML Training Service is responsible for training a series of Graph Machine Learning Models using the data provided in the Training Specification. It assumes that the data has been featurized and is ready to be used for training. -Given that we have run a Featurization Job, we can create the Training Specification using the `featurization_job_result` object returned from the Featurization Job: +Given that we have run a Featurization Job, we can create the Training Specification +using the `featurization_job_result` object returned from the Featurization Job: ```py # 1. Define the Training Specification @@ -736,10 +600,11 @@ arangoml.jobs.cancel_job(training_job.job_id) ## Model Selection Model Statistics can be observed upon completion of a Training Job. -To select a Model, the ArangoGraphML Projects Service can be used to gather +To select a Model, the GraphML Projects Service can be used to gather all relevant models and choose the preferred model for a Prediction Job. -First, let's list all the trained models using [ArangoML.list_models](https://arangoml.github.io/arangoml/client.html#arangoml.main.ArangoML.list_models): +First, let's list all the trained models using +[ArangoML.list_models](https://arangoml.github.io/arangoml/client.html#arangoml.main.ArangoML.list_models): ```py # 1. List all trained Models @@ -752,7 +617,11 @@ models = arangoml.list_models( print(len(models)) ``` -The cell below selects the model with the highest **test accuracy** using [ArangoML.get_best_model](https://arangoml.github.io/arangoml/client.html#arangoml.main.ArangoML.get_best_model), but there may be other factors that motivate you to choose another model. See the `model_statistics` in the output field below for more information on the full list of available metrics. +The cell below selects the model with the highest **test accuracy** using +[ArangoML.get_best_model](https://arangoml.github.io/arangoml/client.html#arangoml.main.ArangoML.get_best_model), +but there may be other factors that motivate you to choose another model. See +the `model_statistics` in the output field below for more information on the +full list of available metrics. ```py @@ -848,7 +717,7 @@ collection, or within the source documents. - `modelID`: The model ID to use for generating predictions. - `featurizeNewDocuments`: Boolean for enabling or disabling the featurization of new documents. Useful if you don't want to re-train the model upon new data. Default is `false`. - `featurizeOutdatedDocuments`: Boolean for enabling or disabling the featurization of outdated documents. Outdated documents are those whose features have changed since the last featurization. Default is `false`. -- `schedule`: A cron expression to schedule the prediction job (e.g `0 0 * * *` for daily predictions). Default is `None`. +- `schedule`: A cron expression to schedule the prediction job (e.g. `0 0 * * *` for daily predictions). Default is `None`. - `embeddingsField`: The name of the field to store the generated embeddings. This is only used for Graph Embedding tasks. Default is `None`. ```py diff --git a/site/content/3.13/data-science/graphml/quickstart.md b/site/content/3.13/data-science/graphml/quickstart.md new file mode 100644 index 0000000000..132ee1fe14 --- /dev/null +++ b/site/content/3.13/data-science/graphml/quickstart.md @@ -0,0 +1,60 @@ +--- +title: How to get started with GraphML +menuTitle: Quickstart +weight: 5 +description: >- + You can use GraphML straight within the ArangoDB Platform, via the web interface + or via Notebooks +aliases: + - ../arangographml/deploy +--- + +## Web interface versus Jupyter Notebooks + +ArangoDB Platform provides enterprise-ready Graph Machine Learning in two options, +tailored to suit diverse requirements and preferences: +- Using the web interface +- In a scriptable manner, using the integrated Jupyter Notebooks and APIs + +## Setup + +{{< tabs "graphml-setup" >}} + +{{< tab "Web Interface" >}} + +The web interface within ArangoDB Platform allows you to create, configure, and +run a full machine learning workflow for GraphML. To get started, see the +[Web interface for GraphML](./ui.md) page. + +{{< /tab >}} + +{{< tab "Notebooks" >}} +The ArangoDB Notebooks service runs on the +[ArangoGraph Insights Platform](https://dashboard.arangodb.cloud/home?utm_source=docs&utm_medium=cluster_pages&utm_campaign=docs_traffic). +It offers a pre-configured environment where everything, +including necessary components and configurations, comes preloaded. You don't +need to set up or configure the infrastructure, and can immediately start using the +GraphML functionalities in a scriptable manner. To get started, see the +[GraphML Notebooks & API](./notebooks-api.md) reference documentation. + +{{< tip >}} +To get access to GraphML services and packages, +[get in touch](https://www.arangodb.com/contact/) +with the ArangoDB team. +{{< /tip >}} + +- **Accessible at all levels** + - Low code UI + - Notebooks + - APIs +- **Full usability** + - MLOps lifecycle + - Metrics + - Metadata capture + - Model management + +![ArangoGraphML Pipeline](../../../images/ArangoGraphML_Pipeline.png) + +{{< /tab >}} + +{{< /tabs >}} \ No newline at end of file diff --git a/site/content/3.13/data-science/graphml/ui.md b/site/content/3.13/data-science/graphml/ui.md new file mode 100644 index 0000000000..4d6f85d18e --- /dev/null +++ b/site/content/3.13/data-science/graphml/ui.md @@ -0,0 +1,254 @@ +--- +title: How to use GraphML in the Web Interface +menuTitle: Web Interface +weight: 10 +description: >- + Learn how to create, configure, and run a full machine learning workflow for + GraphML using the steps and features in the ArangoDB web interface +--- + +{{< tag "ArangoDB Platform" >}} + +## The GraphML workflow in the web interface + +The entire process is organized into sequential steps within a **Project**, +giving you a clear path from data to prediction: + +1. **Featurization:** Select your data and convert it into numerical features. +2. **Training:** Train a GraphSAGE model on the features and graph structure. +3. **Model Selection:** Evaluate the trained models and choose the best one. +4. **Prediction:** Use the selected model to generate predictions on your data. You can also automate the prediction process to run at regular intervals. + +## Creating a GraphML project + +To create a new GraphML project using the ArangoDB Web Interface, follow these steps: + +1. From the left-hand sidebar, select the database where you want to create the project. +2. In the left-hand navigation menu, click **GenAI** to open the GraphML project management interface, then click **Run GraphML**. + ![Navigate to Data Science](../../../images/datascience-intro.jpg) +3. In the **GraphML projects** view, click **Add new project**. +4. The **Create ML project** modal opens. Enter a **Name** for your machine learning project. +5. Click the **Create project** button to finalize the creation. +6. After creation, the new project appears in the list under **GraphML projects**. Click the project name to begin with a Featurization job. + +## Featurization phase + +After clicking on a project name, you are taken to a screen where you can +configure and start a new Featurization job. Follow these steps: +1. **Select a Graph**: In the **Features** section, choose your target graph from the **Select a graph** dropdown. +2. **Select Vertex Collections**: Pick the vertex collections that you want to include for feature extraction. +3. **Select Attributes**: Choose the attributes from your vertex collection to + convert into machine-understandable features. Attributes cannot be used if their values are lists or arrays. + +{{< info >}} +A metagraph is basically just the configured subset of a graph (the vertex and +edge collections and the specified attributes). This is what you see represented +in the metagraph object in the JSON specification on the right. +{{< /info >}} + +### Configuration options + +The featurization process has several configurable options, grouped into +**Configuration** and **Advanced** settings. These are also shown in a JSON +format on the right side of the screen for transparency. + +In the **Configuration** tab, you can control the overall featurization job and +how features are stored. +- **Batch size** – The number of documents to process in a single batch. +- **Run analysis checks** – Whether to run analysis checks to perform a high-level + analysis of the data quality before proceeding. The default value is `true`. +- **Skip labels** – Skip the featurization process for attributes marked as labels. + The default value is `false`. +- **Overwrite FS graph** – Whether to overwrite the Feature Store graph if features + were previously generated. The default value is `false`, therefore features are + written to an existing Feature Store graph. +- **Write to source graph** – Whether to store the generated features on the Source + Graph. The default value is `true`. +- **Use feature store** – Enable the use of the Feature Store database, which + allows you to store features separately from your Source Database. The default + value is `false`, therefore features are written to the source graph. + +### Handling imperfect data + +Real-world data is often messy. It can have missing values or mismatched data +types. This section allows you to define default rules for how the featurization +process should handle these data quality issues for each feature type, preventing +the job from failing unexpectedly. + +For each feature type (Text, Numeric, Category, and Label), you can set two types of strategies: +- **Missing Strategy:** Defines what to do when an attribute is completely missing from a document. +- **Mismatch Strategy:** Defines what to do when an attribute exists but has the wrong data type. + +**Missing Value strategies** + +- **Raise:** The job immediately stops and reports an error if a data type + mismatch is found. Use this when any type mismatch indicates a critical data error. +- **Replace:** Replaces a missing value with a default you provide (e.g., 0 for + numbers, "unknown" for text). The job then continues. Use this when missing values are expected. + +**Mismatch Value strategies** +- **Raise:** The strictest option. The job will immediately stop and report an + error if a data type mismatch is found. Use this when any type mismatch indicates + a critical data error. +- **Replace:** If a mismatch is found, the system immediately replaces the value + with the default you specify, without attempting to convert it first. Use this + when you don't trust the mismatched data and prefer to substitute it directly. +- **Coerce and raise:** A balanced approach. The system first tries to convert + (coerce) the value to the correct type (e.g., string "123" to number 123). If + the conversion is successful, it uses the new value. If it fails, the job stops. + This is often the best default strategy. +- **Coerce and replace:** The most forgiving option. The system first tries to + convert the value. If the conversion fails, it replaces the value with the default + you specify and continues the job. Use this for very "dirty" datasets where + completing the job is the highest priority. + +Once all selections are done, click the **Begin featurization** button. +This triggers a **node embedding-compatible featurization job**. Once the job +status changes to **Ready for training**, you can start the **Training** step. + +![Navigate to Featurization](../../../images/graph-ml-ui-featurization.png) + +## Training phase + +The training is the second step in the ML workflow after featurization. +In the training phase, you configure and launch a machine learning training +job on your graph data. + +From the **Select a type of training job** dropdown menu, choose the type of +model you want to train (Node Classification or Node Embeddings). + +#### Node classification + +Node Classification is used to categorize the nodes in your graph based on their +features and structural connections within the graph. + +**Use cases include:** +- Entity categorization (e.g. movies into genres, users into segments) +- Fraud detection in transaction networks + +**Configuration parameters:** +- **Type of Training Job:** Node classification +- **Target Vertex Collection:** Choose the collection to classify (e.g. `movie`) +- **Batch Size:** The number of documents processed in a single training iteration. (e.g. `256`) +- **Data Load Batch Size:** The number of documents loaded from ArangoDB into memory in a single batch during the data loading phase. (e.g. `50000`) +- **Data Load Parallelism:** The number of parallel processes used when loading data from ArangoDB into memory for training. (e.g. `10`) + +After setting these values, click the **Begin training** button to start the job. + +![Node Classification](../../../images/ml-nodeclassification.png) + +#### Node embeddings + +Node Embeddings are used to generate vector embeddings (dense numerical representations) +of graph nodes that capture structural and feature-based information. + +**Use cases include:** +- Similarity search (e.g. finding similar products, users, or documents) +- Link prediction (e.g. suggesting new connections) + +**Configuration parameters:** +- **Type of Training Job:** Node embeddings +- **Target Vertex Collection:** Select the collection to generate embeddings for (e.g. `movie` or `person`) +- No label is required for training in this mode + +**Understanding Vertex Collections:** + +- **X Vertex Collection:** These are the source nodes used during training. + They represent the full set of nodes on which features were computed (e.g. `person`, `movie`). +- **Y Vertex Collection:** These are the target nodes that contain labeled data. + The labels in this collection are used to supervise the training process and + are the basis for evaluating prediction quality. + +The target collection is where the model's predictions are stored when running a prediction job. + +Once the configuration is complete, click **Begin training** to start the embedding job. + +![Node Embeddings](../../../images/ml-node-embedding.png) + +## Model selection phase + +Once the training is finished, the job status updates to **READY FOR MODEL SELECTION**. +This means the model has been trained using the provided vertex and edge data +and is now ready for evaluation. + +A list of trained models is displayed, along with performance metrics +(Accuracy, Precision, Recall, F1 score, Loss). Review the results of different +model runs and configurations. + +![GraphML Model Selection](../../../images/graph-ml-model.png) + +Select the best performing model suitable for your prediction task. You can also +open the **Confusion Matrix** to compare predicted values versus actual values. + +![GraphML Confusion Matrix](../../../images/graphml-ui-confusion-matrix.png) + +## Prediction phase + +After selecting a model, you can create a Prediction Job. The Prediction Job +generates predictions and persists them to the source graph, either in a new +collection or within the source documents. + +The Prediction interface allows inference to be run using the selected model. +It enables configuration of how predictions are executed, which collections are +involved, and whether new or outdated documents should be automatically featurized +before prediction. + +You have two important options: + +- **Featurize new documents:** Enable this option to generate features for + documents that have been added since the model was trained. This is useful + for getting predictions on new data without having to retrain the model. +- **Featurize outdated documents:** Enable this option to re-generate features + for documents that have been modified. Outdated documents are those whose + attributes (used during featurization) have changed since the last feature + computation. This ensures your predictions reflect the latest changes to your data. + +In addition to these settings, you can also define the target data, where to store +results, and whether to run the job on a recurring schedule. + +These options provide flexibility in handling dynamic graph data and keeping +predictions relevant without repeating the entire ML workflow. + +- **Data load batch size**: Specifies the number of documents to load in a + single batch (e.g. `500000`). +- **Data load parallelism**: The number of parallel threads used to process + the prediction workload (e.g. `10`). +- **Prediction field**: The field in the documents where the predicted values are stored. + +![GraphML prediction phase](../../../images/graph-prediction.png) + +### Configuration options + +The Prediction screen displays the following configuration options: +- **Selected Model**: Displays the model selected during the Model Selection phase. This model will be used to perform inference. +- **Target Vertex Collection**: This is the vertex collection on which predictions are applied. +- **Prediction Type**: Depending on the training job (for example, classification or embedding), the prediction outputs class labels or updated embeddings. + +### Enable scheduling + +You can configure automatic predictions using the **Enable scheduling** checkbox. +When scheduling is turned on, predictions run automatically based on a set CRON +expression. This helps keep prediction results up-to-date as new data is added to the system. + +You can define a CRON expression that sets when the prediction job should run. +For example, `0 0 1 1 *`. +This CRON pattern executes the prediction every year on January 1st at 00:00. + +Below the CRON field, a user-friendly scheduling interface helps translate it: +- **Period**: Options include *Hourly*, *Daily*, *Weekly*, *Monthly*, or *Yearly*. +- **Month**: *(e.g. January)* +- **Day of Month**: *(e.g 1)* +- **Day of Week**: *(optional)* +- **Hours and Minutes**: Set the exact time for execution *(e.g. 0:00)* + +### Execute prediction + +After reviewing the configuration, click the **Run Prediction** button. +Once prediction is complete, you can analyze the results directly in +the Web Interface or export them for downstream use. + +## Limitations + +- **Edge Attributes**: The current version of GraphML does not support the use of edge attributes as features. +- **Dangling Edges**: Edges that point to non-existent vertices ("dangling edges") are not caught during the featurization analysis. They may cause errors later, during the Training phase. +- **Memory Usage**: Both featurization and training can be memory-intensive. Out-of-memory errors can occur on large graphs with insufficient system resources. \ No newline at end of file diff --git a/site/content/images/graphml-ui-confusion-matrix.png b/site/content/images/graphml-ui-confusion-matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..1ddfa5f9b82451dced12e77cd57d9a5bcbb5e2f2 GIT binary patch literal 88842 zcmeFZXIN9))&>fK^p4WIQWT^rp@h&urHcYe7XcLrC6v$t2vVdfA|O?fA|OqA??~@G z^b+YMK!8y0V()Xly^s3bpZC}G36DwUDw$)>Ip!Ge`;HZ&t*J^*!bF0DgF}AruF?Y> z96S^b&Lwvu0^pmjC3$Ha99*>*ii+Cz6cstNo$Re%*jnP?FvM6!6{6X6Fb}%*&>xN(4Kvd=3pA zQ&y|amycd;*g;?%I0Xj` zmjd3}Svs3@!0l|IFe$hU*I!3S0q-xq7UJUg>kwxf87>12Z4O0yCrb`-L195*E(i$+ z2M5^cnU&N7C6)i24t$c~dhYD(ASEQ^=H@2oCMIa_WGy5jDJdx=EGi@_dJ8z>7R(*$ zY!1H#g4k&y3wtQX#dXb}+PgT*aB*GS=>Pux{W~q;FaEg`6!xEP z0UZ>&Xb}<-6c+lwYXhf(FTR!1egU_%eXR7t4loaJ4~V#!82GQ_e{1>Yj{iE-;GZ*r z{`~vQf3^I2<|CM;lcK#Ha7$;%KO^&>lmFfLpA*4C7d`)%CH^+^U*7_DhLC`T{%_16 zB)ZZEa2y;toO??058=4$NrdHBlt%(X1s=;$zvpAn5#h)RHtS)8H5RYEd1?Tourbir z*KbKwZcY^Dy`pZWBFw?4tfM0*+97K{7hm_n!^&e>vl4>{LWzn^4PTvJU0KQR`MS!a zM!Wosi{sMoFYasF-=A^km{C*V{{GUEn>XTpMHKA&>+5}-Z?k)dv|LN*Z%x6z%@sX= zxE3{^2yO_P#?AK){U2IGU`cqt|2Qt~rPk@*OaX7b`kpS(0UwLsi zq5XP~81(?+cQaBGQUB~u3#!^_WgaAP4v@S!_NDdx)YCuonB!6>t;Eq{^eI6z$SUPD zsM&|7i;#?=pb?@*>lGtGInd|%hZ_XntH*aYNVVqgx}iA_=5gkx{oTfX!hQvE*0N7! z^4gHQ(GnmUkE?$;h=nNNvPWrSB3B8A5yh-a@E`2480`y29ew#){j25W@UvO{W?5iN z?8*LYgu%`3EcfqU`@_Id{e1QS3F}=%J*}?Ts>LF!-O((Tn6=S-2IXwBUnklRe!qA5BM`4c z2e)Z=R0TWY(EN4aotHyDjD1e`^R!YR{OkOCfrFaT-Q=uNw;y*UxlKRb_DFQEzJ7&ULH`%%i)gx%<+lO z%Av@5KfF_OvYz#lQIo>&V^2eGA%*Hr=Jg(1Ud2p%ZnZ!(iIsnG0jWkr?{DX_YVC-$ zv3ySywjV1ne@D)?Uw67;zv)NFb{ERun($&UgZs66_s2U^D$I6^_g5N(R1y~6@ARv( ze~VgQh?F%kHxy*16Eyk>tyrQvx5m3tv6yD@>r_Y;rQwU0NRjoa#g5jU#XKvWa-FOI z&3FxZAFpI?Hc`qtx3T+F&y+c@JaAiz(XVWe=H(rvJJ#q6_1SJ9tl~|EH`*1o^Sl-u zF?MXZdN#KQPOa_Z+#A1b6@F)AyS*)fb?qnUdkW&XT_mb-;68uFa(BYy^5?qR!}-u; z=6um#hbJox|GqftBtm*~8{O&Gdg4@_0a$XEZ0A9W%gAHs-p~HcQ_PH;&&f`Z=jzu? ztx5?cA}4lI;O%TV*qd&34|D zJv?0aG6?CQ=r$k1TUly1d2F#+caD&iFk|%EX`$a-&B;GNXMJVgZ}r$;T{@U@GgVs- zz64)Mk3F$omRYa^F-tmqsangcIyu>Ay7RIeI#nsTY>(NTnk;$V-}vsxLqx(a3DzT- zXW99o-TU=R`y$`l_IiePcV-C5p7ilfRk>llc1KT_*yw{0Cx<^+vOryNYUjX+RRUdX zqjcU|A0PZywOM(_Kw$am)AmAZIJ(OX>8M&ZZk2EZGjD pOae=CjwYDq~Mz_hT!N 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 N`cRZOy_iY;-h+`w-WcZpZt*Gim?t`P*z12 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 Date: Wed, 25 Jun 2025 16:09:46 +0200 Subject: [PATCH 2/6] fix merge related errors --- site/content/3.13/aql/functions/vector.md | 2 +- site/content/3.13/data-science/graphml/_index.md | 2 +- .../indexing/working-with-indexes/vector-indexes.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/site/content/3.13/aql/functions/vector.md b/site/content/3.13/aql/functions/vector.md index e6a6de0134..45341b8ea8 100644 --- a/site/content/3.13/aql/functions/vector.md +++ b/site/content/3.13/aql/functions/vector.md @@ -12,7 +12,7 @@ To use vector search, you need to have vector embeddings stored in documents and the attribute that stores them needs to be indexed by a [vector index](../../index-and-search/indexing/working-with-indexes/vector-indexes.md). -You can calculate vector embeddings using [ArangoDB's GraphML](../../data-science/arangographml/_index.md) +You can calculate vector embeddings using [ArangoDB's GraphML](../../data-science/graphml/_index.md) capabilities (available in ArangoGraph) or using external tools. {{< warning >}} diff --git a/site/content/3.13/data-science/graphml/_index.md b/site/content/3.13/data-science/graphml/_index.md index 291f728b65..02de5f9c83 100644 --- a/site/content/3.13/data-science/graphml/_index.md +++ b/site/content/3.13/data-science/graphml/_index.md @@ -190,7 +190,7 @@ viewable by ArangoDB. This metadata graph links all experiments to the source data, feature generation activities, training runs, and prediction jobs, allowing you to track the entire ML pipeline without having to leave ArangoDB. -### Security +## Security Each deployment that uses GraphML has an `arangopipe` database created, which houses all ML Metadata information. Since this data lives within the deployment, diff --git a/site/content/3.13/index-and-search/indexing/working-with-indexes/vector-indexes.md b/site/content/3.13/index-and-search/indexing/working-with-indexes/vector-indexes.md index 90b82edbf9..236093878b 100644 --- a/site/content/3.13/index-and-search/indexing/working-with-indexes/vector-indexes.md +++ b/site/content/3.13/index-and-search/indexing/working-with-indexes/vector-indexes.md @@ -33,7 +33,7 @@ startup option needs to be enabled on the deployment you want to restore to. {{< /warning >}} 1. Enable the experimental vector index feature. -2. Calculate vector embeddings using [ArangoDB's GraphML](../../../data-science/arangographml/_index.md) +2. Calculate vector embeddings using [ArangoDB's GraphML](../../../data-science/graphml/_index.md) capabilities (available in ArangoGraph) or using external tools. Store each vector as an attribute in the respective document. 3. Create a vector index over this attribute. You need to choose which From 5bdb8d8ac014b777e656207ba5d55780e8ee3422 Mon Sep 17 00:00:00 2001 From: Paula Mihu <97217318+nerpaula@users.noreply.github.com> Date: Thu, 26 Jun 2025 09:46:38 +0200 Subject: [PATCH 3/6] Apply suggestions from code review Co-authored-by: Simran --- site/content/3.13/data-science/_index.md | 2 +- .../3.13/data-science/graphml/_index.md | 2 +- .../data-science/graphml/notebooks-api.md | 8 ++-- .../3.13/data-science/graphml/quickstart.md | 11 ++---- site/content/3.13/data-science/graphml/ui.md | 39 ++++++++++--------- 5 files changed, 30 insertions(+), 32 deletions(-) diff --git a/site/content/3.13/data-science/_index.md b/site/content/3.13/data-science/_index.md index bed670b505..21c683bdf2 100644 --- a/site/content/3.13/data-science/_index.md +++ b/site/content/3.13/data-science/_index.md @@ -1,5 +1,5 @@ --- -title: Data Science & GenAI +title: Data Science and GenAI menuTitle: Data Science & GenAI weight: 115 description: >- diff --git a/site/content/3.13/data-science/graphml/_index.md b/site/content/3.13/data-science/graphml/_index.md index 02de5f9c83..7626187b6f 100644 --- a/site/content/3.13/data-science/graphml/_index.md +++ b/site/content/3.13/data-science/graphml/_index.md @@ -194,6 +194,6 @@ jobs, allowing you to track the entire ML pipeline without having to leave Arang Each deployment that uses GraphML has an `arangopipe` database created, which houses all ML Metadata information. Since this data lives within the deployment, -it benefits from the ArangoGraph SOC 2 compliance and Enterprise security features. +it benefits from the ArangoGraph security features and SOC 2 compliance. All GraphML services live alongside the ArangoGraph deployment and are only accessible within that organization. diff --git a/site/content/3.13/data-science/graphml/notebooks-api.md b/site/content/3.13/data-science/graphml/notebooks-api.md index 06a0b6a9b4..56d396e6bf 100644 --- a/site/content/3.13/data-science/graphml/notebooks-api.md +++ b/site/content/3.13/data-science/graphml/notebooks-api.md @@ -12,12 +12,12 @@ aliases: {{< tag "ArangoDB Platform" >}} -ArangoDB Platform provides an easy-to-use & scalable interface to run Graph Machine -Learning on ArangoDB data. Since all the orchestration and Machine Learning logic is +The ArangoDB Platform provides an easy-to-use & scalable interface to run +Graph Machine Learning on ArangoDB data. Since all the orchestration and Machine Learning logic is managed by ArangoDB, all that is typically required are JSON specifications outlining individual processes to solve a Machine Learning Task. -The `arangoml` is a Python Package allowing you to manage all the necessary +The `arangoml` Python package allows you to manage all the necessary GraphML components, including: - **Project Management**: Projects are a metadata-tracking entity that sit at the top level of GraphML. All activities must link to a project. @@ -25,7 +25,7 @@ GraphML components, including: machine-understandable data (e.g. features), such that it can be used to train Graph Neural Networks (GNNs). - **Training**: Train a set of models based on the name of the generated/existing - features, and a definition of the ML task we want to solve (e.g. Node Classification, Embedding Generation). + features, and a definition of the ML task you want to solve (e.g. Node Classification, Embedding Generation). - **Model Selection**: Select the best model based on the metrics generated during training. - **Predictions**: Generate predictions based on the selected model, and persist the results to the source graph (either in the source document, or in a new collection). diff --git a/site/content/3.13/data-science/graphml/quickstart.md b/site/content/3.13/data-science/graphml/quickstart.md index 132ee1fe14..8c45a95d00 100644 --- a/site/content/3.13/data-science/graphml/quickstart.md +++ b/site/content/3.13/data-science/graphml/quickstart.md @@ -11,7 +11,7 @@ aliases: ## Web interface versus Jupyter Notebooks -ArangoDB Platform provides enterprise-ready Graph Machine Learning in two options, +The ArangoDB Platform provides enterprise-ready Graph Machine Learning in two options, tailored to suit diverse requirements and preferences: - Using the web interface - In a scriptable manner, using the integrated Jupyter Notebooks and APIs @@ -21,11 +21,9 @@ tailored to suit diverse requirements and preferences: {{< tabs "graphml-setup" >}} {{< tab "Web Interface" >}} - -The web interface within ArangoDB Platform allows you to create, configure, and +The web interface of the ArangoDB Platform allows you to create, configure, and run a full machine learning workflow for GraphML. To get started, see the -[Web interface for GraphML](./ui.md) page. - +[Web interface for GraphML](ui.md) page. {{< /tab >}} {{< tab "Notebooks" >}} @@ -35,7 +33,7 @@ It offers a pre-configured environment where everything, including necessary components and configurations, comes preloaded. You don't need to set up or configure the infrastructure, and can immediately start using the GraphML functionalities in a scriptable manner. To get started, see the -[GraphML Notebooks & API](./notebooks-api.md) reference documentation. +[GraphML Notebooks & API](notebooks-api.md) reference documentation. {{< tip >}} To get access to GraphML services and packages, @@ -54,7 +52,6 @@ with the ArangoDB team. - Model management ![ArangoGraphML Pipeline](../../../images/ArangoGraphML_Pipeline.png) - {{< /tab >}} {{< /tabs >}} \ No newline at end of file diff --git a/site/content/3.13/data-science/graphml/ui.md b/site/content/3.13/data-science/graphml/ui.md index 4d6f85d18e..62ffa056b7 100644 --- a/site/content/3.13/data-science/graphml/ui.md +++ b/site/content/3.13/data-science/graphml/ui.md @@ -6,7 +6,6 @@ description: >- Learn how to create, configure, and run a full machine learning workflow for GraphML using the steps and features in the ArangoDB web interface --- - {{< tag "ArangoDB Platform" >}} ## The GraphML workflow in the web interface @@ -14,14 +13,15 @@ description: >- The entire process is organized into sequential steps within a **Project**, giving you a clear path from data to prediction: -1. **Featurization:** Select your data and convert it into numerical features. -2. **Training:** Train a GraphSAGE model on the features and graph structure. -3. **Model Selection:** Evaluate the trained models and choose the best one. -4. **Prediction:** Use the selected model to generate predictions on your data. You can also automate the prediction process to run at regular intervals. +1. **Featurization**: Select your data and convert it into numerical features. +2. **Training**: Train a GraphSAGE model on the features and graph structure. +3. **Model Selection**: Evaluate the trained models and choose the best one. +4. **Prediction**: Use the selected model to generate predictions on your data. + You can also automate the prediction process to run at regular intervals. ## Creating a GraphML project -To create a new GraphML project using the ArangoDB Web Interface, follow these steps: +To create a new GraphML project using the ArangoDB Platform web interface, follow these steps: 1. From the left-hand sidebar, select the database where you want to create the project. 2. In the left-hand navigation menu, click **GenAI** to open the GraphML project management interface, then click **Run GraphML**. @@ -29,7 +29,8 @@ To create a new GraphML project using the ArangoDB Web Interface, follow these s 3. In the **GraphML projects** view, click **Add new project**. 4. The **Create ML project** modal opens. Enter a **Name** for your machine learning project. 5. Click the **Create project** button to finalize the creation. -6. After creation, the new project appears in the list under **GraphML projects**. Click the project name to begin with a Featurization job. +6. After creation, the new project appears in the list under **GraphML projects**. + Click the project name to begin with a Featurization job. ## Featurization phase @@ -127,11 +128,11 @@ features and structural connections within the graph. - Fraud detection in transaction networks **Configuration parameters:** -- **Type of Training Job:** Node classification -- **Target Vertex Collection:** Choose the collection to classify (e.g. `movie`) -- **Batch Size:** The number of documents processed in a single training iteration. (e.g. `256`) -- **Data Load Batch Size:** The number of documents loaded from ArangoDB into memory in a single batch during the data loading phase. (e.g. `50000`) -- **Data Load Parallelism:** The number of parallel processes used when loading data from ArangoDB into memory for training. (e.g. `10`) +- **Type of Training Job**: Node classification +- **Target Vertex Collection**: Choose the collection to classify (e.g. `movie`) +- **Batch Size**: The number of documents processed in a single training iteration. (e.g. `256`) +- **Data Load Batch Size**: The number of documents loaded from ArangoDB into memory in a single batch during the data loading phase (e.g. `50000`). +- **Data Load Parallelism**: The number of parallel processes used when loading data from ArangoDB into memory for training (e.g. `10`). After setting these values, click the **Begin training** button to start the job. @@ -153,9 +154,9 @@ of graph nodes that capture structural and feature-based information. **Understanding Vertex Collections:** -- **X Vertex Collection:** These are the source nodes used during training. +- **Vertex Collection**: These are the source nodes used during training. They represent the full set of nodes on which features were computed (e.g. `person`, `movie`). -- **Y Vertex Collection:** These are the target nodes that contain labeled data. +- **Vertex Collection**: These are the target nodes that contain labeled data. The labels in this collection are used to supervise the training process and are the basis for evaluating prediction quality. @@ -199,9 +200,9 @@ You have two important options: documents that have been added since the model was trained. This is useful for getting predictions on new data without having to retrain the model. - **Featurize outdated documents:** Enable this option to re-generate features - for documents that have been modified. Outdated documents are those whose - attributes (used during featurization) have changed since the last feature - computation. This ensures your predictions reflect the latest changes to your data. + for documents that have been modified. Outdated documents are those whose + attributes (used during featurization) have changed since the last feature + computation. This ensures your predictions reflect the latest changes to your data. In addition to these settings, you can also define the target data, where to store results, and whether to run the job on a recurring schedule. @@ -210,9 +211,9 @@ These options provide flexibility in handling dynamic graph data and keeping predictions relevant without repeating the entire ML workflow. - **Data load batch size**: Specifies the number of documents to load in a - single batch (e.g. `500000`). + single batch (e.g. `500000`). - **Data load parallelism**: The number of parallel threads used to process - the prediction workload (e.g. `10`). + the prediction workload (e.g. `10`). - **Prediction field**: The field in the documents where the predicted values are stored. ![GraphML prediction phase](../../../images/graph-prediction.png) From 405a9bf4d1ca5f02c51f6d4cc9f4d74d17390c85 Mon Sep 17 00:00:00 2001 From: Paula Date: Thu, 26 Jun 2025 12:46:49 +0200 Subject: [PATCH 4/6] review --- .../3.13/data-science/graphml/_index.md | 2 +- .../data-science/graphml/notebooks-api.md | 11 ++-- .../3.13/data-science/graphml/quickstart.md | 4 +- site/content/3.13/data-science/graphml/ui.md | 61 +++++++------------ 4 files changed, 31 insertions(+), 47 deletions(-) diff --git a/site/content/3.13/data-science/graphml/_index.md b/site/content/3.13/data-science/graphml/_index.md index 7626187b6f..26b2db3026 100644 --- a/site/content/3.13/data-science/graphml/_index.md +++ b/site/content/3.13/data-science/graphml/_index.md @@ -3,7 +3,7 @@ title: GraphML menuTitle: GraphML weight: 125 description: >- - Boost your machine learning models with graph data using ArangoDB's advanced GraphML capabilities and predict anything + Boost your machine learning models with graph data using ArangoDB's advanced GraphML capabilities aliases: - arangographml --- diff --git a/site/content/3.13/data-science/graphml/notebooks-api.md b/site/content/3.13/data-science/graphml/notebooks-api.md index 56d396e6bf..08f9dad30b 100644 --- a/site/content/3.13/data-science/graphml/notebooks-api.md +++ b/site/content/3.13/data-science/graphml/notebooks-api.md @@ -3,7 +3,7 @@ title: How to use GraphML in a scriptable manner menuTitle: Notebooks & API weight: 15 description: >- - Control all resources inside GraphML via Notebooks or API + Control all resources inside GraphML via Jupyter Notebooks or HTTP REST API aliases: - getting-started-with-arangographml - ../arangographml/getting-started @@ -15,12 +15,12 @@ aliases: The ArangoDB Platform provides an easy-to-use & scalable interface to run Graph Machine Learning on ArangoDB data. Since all the orchestration and Machine Learning logic is managed by ArangoDB, all that is typically required are JSON specifications outlining -individual processes to solve a Machine Learning Task. +individual processes to solve a Machine Learning task. The `arangoml` Python package allows you to manage all the necessary GraphML components, including: - **Project Management**: Projects are a metadata-tracking entity that sit at - the top level of GraphML. All activities must link to a project. + the top level of ArangoDB GraphML. All activities must link to a project. - **Featurization**: The step of converting human-understandable data to machine-understandable data (e.g. features), such that it can be used to train Graph Neural Networks (GNNs). @@ -717,7 +717,10 @@ collection, or within the source documents. - `modelID`: The model ID to use for generating predictions. - `featurizeNewDocuments`: Boolean for enabling or disabling the featurization of new documents. Useful if you don't want to re-train the model upon new data. Default is `false`. - `featurizeOutdatedDocuments`: Boolean for enabling or disabling the featurization of outdated documents. Outdated documents are those whose features have changed since the last featurization. Default is `false`. -- `schedule`: A cron expression to schedule the prediction job (e.g. `0 0 * * *` for daily predictions). Default is `None`. +- `schedule`: A cron expression to schedule the prediction job. The cron syntax is a set of + five fields in a line, indicating when the job should be executed. The format must follow + the following order: `minute` `hour` `day-of-month` `month` `day-of-week` + (e.g. `0 0 * * *` for daily predictions at 00:00). Default is `None`. - `embeddingsField`: The name of the field to store the generated embeddings. This is only used for Graph Embedding tasks. Default is `None`. ```py diff --git a/site/content/3.13/data-science/graphml/quickstart.md b/site/content/3.13/data-science/graphml/quickstart.md index 8c45a95d00..97b9fabeb3 100644 --- a/site/content/3.13/data-science/graphml/quickstart.md +++ b/site/content/3.13/data-science/graphml/quickstart.md @@ -14,7 +14,7 @@ aliases: The ArangoDB Platform provides enterprise-ready Graph Machine Learning in two options, tailored to suit diverse requirements and preferences: - Using the web interface -- In a scriptable manner, using the integrated Jupyter Notebooks and APIs +- In a scriptable manner, using the integrated Jupyter Notebooks and the HTTP API for GraphML ## Setup @@ -36,7 +36,7 @@ GraphML functionalities in a scriptable manner. To get started, see the [GraphML Notebooks & API](notebooks-api.md) reference documentation. {{< tip >}} -To get access to GraphML services and packages, +To get access to GraphML services and packages in ArangoGraph Insights Platform, [get in touch](https://www.arangodb.com/contact/) with the ArangoDB team. {{< /tip >}} diff --git a/site/content/3.13/data-science/graphml/ui.md b/site/content/3.13/data-science/graphml/ui.md index 62ffa056b7..d43f16410b 100644 --- a/site/content/3.13/data-science/graphml/ui.md +++ b/site/content/3.13/data-science/graphml/ui.md @@ -24,7 +24,7 @@ giving you a clear path from data to prediction: To create a new GraphML project using the ArangoDB Platform web interface, follow these steps: 1. From the left-hand sidebar, select the database where you want to create the project. -2. In the left-hand navigation menu, click **GenAI** to open the GraphML project management interface, then click **Run GraphML**. +2. In the left-hand sidebar, click **GenAI** to open the GraphML project management interface, then click **Run GraphML**. ![Navigate to Data Science](../../../images/datascience-intro.jpg) 3. In the **GraphML projects** view, click **Add new project**. 4. The **Create ML project** modal opens. Enter a **Name** for your machine learning project. @@ -71,41 +71,22 @@ how features are stored. ### Handling imperfect data -Real-world data is often messy. It can have missing values or mismatched data -types. This section allows you to define default rules for how the featurization -process should handle these data quality issues for each feature type, preventing -the job from failing unexpectedly. - -For each feature type (Text, Numeric, Category, and Label), you can set two types of strategies: -- **Missing Strategy:** Defines what to do when an attribute is completely missing from a document. -- **Mismatch Strategy:** Defines what to do when an attribute exists but has the wrong data type. - -**Missing Value strategies** - -- **Raise:** The job immediately stops and reports an error if a data type - mismatch is found. Use this when any type mismatch indicates a critical data error. -- **Replace:** Replaces a missing value with a default you provide (e.g., 0 for - numbers, "unknown" for text). The job then continues. Use this when missing values are expected. - -**Mismatch Value strategies** -- **Raise:** The strictest option. The job will immediately stop and report an - error if a data type mismatch is found. Use this when any type mismatch indicates - a critical data error. -- **Replace:** If a mismatch is found, the system immediately replaces the value - with the default you specify, without attempting to convert it first. Use this - when you don't trust the mismatched data and prefer to substitute it directly. -- **Coerce and raise:** A balanced approach. The system first tries to convert - (coerce) the value to the correct type (e.g., string "123" to number 123). If - the conversion is successful, it uses the new value. If it fails, the job stops. - This is often the best default strategy. -- **Coerce and replace:** The most forgiving option. The system first tries to - convert the value. If the conversion fails, it replaces the value with the default - you specify and continues the job. Use this for very "dirty" datasets where - completing the job is the highest priority. - -Once all selections are done, click the **Begin featurization** button. -This triggers a **node embedding-compatible featurization job**. Once the job -status changes to **Ready for training**, you can start the **Training** step. +Real-world datasets often contain missing values or mismatched data types. Use +the strategies below to control how each feature type (**Text**, **Numeric**, +**Category**, **Label**) handles these issues during featurization. + +| **Strategy type** | **Option** | **Description** | **When to use** | +|-------------------|-----------------------|-----------------------------------------------------------------------------------------------------|---------------------------------------------------------------| +| Missing | **Raise** | Stops the job and reports an error when a value is missing. | When missing data indicates a critical issue. | +| | **Replace** | Substitutes missing values with a default you provide (e.g., `0` for numbers, `"unknown"` for text). | When missing values are expected. | +| Mismatch | **Raise** | The strictest option. Stops the job on any data type mismatch. | When any data type mismatch indicates a critical error. | +| | **Replace** | Replaces mismatched values with a default you provide, without trying to convert it first. | When mismatched values are unreliable, and you prefer to substitute it directly. | +| | **Coerce and Raise** | Attempts to convert (coerce) the value to the correct type (e.g. string "123" to number `123`). If the conversion is successful, it uses the new value. If it fails, the job stops. | A balanced approach, often the best default strategy. | +| | **Coerce and Replace**| The most forgiving option. The system first tries to convert the value. If it fails, it replaces the value with the specified default and continues the job. | For very dirty datasets where completing the job is the highest priority. | + +Once you’ve set your strategies, click **Begin featurization** to start the node +embedding-compatible featurization job. When the job status updates to +**Ready for training**, proceed to the **Training** step. ![Navigate to Featurization](../../../images/graph-ml-ui-featurization.png) @@ -116,7 +97,7 @@ In the training phase, you configure and launch a machine learning training job on your graph data. From the **Select a type of training job** dropdown menu, choose the type of -model you want to train (Node Classification or Node Embeddings). +model you want to train (**Node Classification** or **Node Embeddings**). #### Node classification @@ -173,7 +154,7 @@ This means the model has been trained using the provided vertex and edge data and is now ready for evaluation. A list of trained models is displayed, along with performance metrics -(Accuracy, Precision, Recall, F1 score, Loss). Review the results of different +(**Accuracy**, **Precision**, **Recall**, **F1 score**, **Loss**). Review the results of different model runs and configurations. ![GraphML Model Selection](../../../images/graph-ml-model.png) @@ -221,7 +202,7 @@ predictions relevant without repeating the entire ML workflow. ### Configuration options The Prediction screen displays the following configuration options: -- **Selected Model**: Displays the model selected during the Model Selection phase. This model will be used to perform inference. +- **Select Model**: Displays the model selected during the Model Selection phase. This model will be used to perform inference. - **Target Vertex Collection**: This is the vertex collection on which predictions are applied. - **Prediction Type**: Depending on the training job (for example, classification or embedding), the prediction outputs class labels or updated embeddings. @@ -238,7 +219,7 @@ This CRON pattern executes the prediction every year on January 1st at 00:00. Below the CRON field, a user-friendly scheduling interface helps translate it: - **Period**: Options include *Hourly*, *Daily*, *Weekly*, *Monthly*, or *Yearly*. - **Month**: *(e.g. January)* -- **Day of Month**: *(e.g 1)* +- **Day of Month**: *(e.g. 1)* - **Day of Week**: *(optional)* - **Hours and Minutes**: Set the exact time for execution *(e.g. 0:00)* From c18589f2847e5c42c89fdbf0652f62906397815d Mon Sep 17 00:00:00 2001 From: Paula Date: Fri, 27 Jun 2025 16:31:53 +0200 Subject: [PATCH 5/6] minor changes --- .../data-science/graphml/notebooks-api.md | 2 +- site/content/3.13/data-science/graphml/ui.md | 24 ++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/site/content/3.13/data-science/graphml/notebooks-api.md b/site/content/3.13/data-science/graphml/notebooks-api.md index 08f9dad30b..c9ade4cacf 100644 --- a/site/content/3.13/data-science/graphml/notebooks-api.md +++ b/site/content/3.13/data-science/graphml/notebooks-api.md @@ -3,7 +3,7 @@ title: How to use GraphML in a scriptable manner menuTitle: Notebooks & API weight: 15 description: >- - Control all resources inside GraphML via Jupyter Notebooks or HTTP REST API + Control all resources inside GraphML via Jupyter Notebooks or Python API aliases: - getting-started-with-arangographml - ../arangographml/getting-started diff --git a/site/content/3.13/data-science/graphml/ui.md b/site/content/3.13/data-science/graphml/ui.md index d43f16410b..1e5d8c0792 100644 --- a/site/content/3.13/data-science/graphml/ui.md +++ b/site/content/3.13/data-science/graphml/ui.md @@ -212,16 +212,24 @@ You can configure automatic predictions using the **Enable scheduling** checkbox When scheduling is turned on, predictions run automatically based on a set CRON expression. This helps keep prediction results up-to-date as new data is added to the system. -You can define a CRON expression that sets when the prediction job should run. -For example, `0 0 1 1 *`. -This CRON pattern executes the prediction every year on January 1st at 00:00. +You can define a cron expression that sets when the prediction job should run. +The cron syntax is a set of five fields in a line, indicating when the job should +be executed. The format must follow the following order: `minute` `hour` `day-of-month` `month` `day-of-week` +(e.g. `0 0 * * *` for daily predictions at 00:00, or `0 0 1 1 *` to execute the prediction +on January 1st at 00:00). + +When a field is set to an asterisk `*`, it means that any value is allowed for that field, +whenever the other field conditions are met. Below the CRON field, a user-friendly scheduling interface helps translate it: -- **Period**: Options include *Hourly*, *Daily*, *Weekly*, *Monthly*, or *Yearly*. -- **Month**: *(e.g. January)* -- **Day of Month**: *(e.g. 1)* -- **Day of Week**: *(optional)* -- **Hours and Minutes**: Set the exact time for execution *(e.g. 0:00)* +- **Period**: Options include **Hourly**, **Daily**, **Weekly**, **Monthly**, or **Yearly**. +- **Month**: Indicates the month. For example, `1` for January. +- **Day of Month**: Indicates the day of the month. For example, `1` for + the first day of the month. +- **Day of Week** (optional): Indicates the day of the week. For example, + Monday is `1` and Tuesday is `2`. +- **Hours and Minutes**: Set the exact time for execution. For example, + if the hour is set to `8` and the minute to `0`, then the job runs at 8:00 AM. ### Execute prediction From 892b243fbb6e013b0226c727da7f30196a265723 Mon Sep 17 00:00:00 2001 From: Paula Date: Tue, 1 Jul 2025 12:43:12 +0200 Subject: [PATCH 6/6] review --- .../3.13/data-science/graphml/_index.md | 2 +- site/content/3.13/data-science/graphml/ui.md | 26 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/site/content/3.13/data-science/graphml/_index.md b/site/content/3.13/data-science/graphml/_index.md index 26b2db3026..ba8e2d6c46 100644 --- a/site/content/3.13/data-science/graphml/_index.md +++ b/site/content/3.13/data-science/graphml/_index.md @@ -1,5 +1,5 @@ --- -title: GraphML +title: ArangoDB GraphML menuTitle: GraphML weight: 125 description: >- diff --git a/site/content/3.13/data-science/graphml/ui.md b/site/content/3.13/data-science/graphml/ui.md index 1e5d8c0792..45ef75dd6a 100644 --- a/site/content/3.13/data-science/graphml/ui.md +++ b/site/content/3.13/data-science/graphml/ui.md @@ -1,10 +1,10 @@ --- -title: How to use GraphML in the Web Interface +title: How to use GraphML in the ArangoDB Platform web interface menuTitle: Web Interface weight: 10 description: >- Learn how to create, configure, and run a full machine learning workflow for - GraphML using the steps and features in the ArangoDB web interface + GraphML in four steps using the features in the ArangoDB web interface --- {{< tag "ArangoDB Platform" >}} @@ -13,13 +13,13 @@ description: >- The entire process is organized into sequential steps within a **Project**, giving you a clear path from data to prediction: -1. **Featurization**: Select your data and convert it into numerical features. +1. **Featurization**: Select your data and convert it into numerical representations. 2. **Training**: Train a GraphSAGE model on the features and graph structure. 3. **Model Selection**: Evaluate the trained models and choose the best one. 4. **Prediction**: Use the selected model to generate predictions on your data. You can also automate the prediction process to run at regular intervals. -## Creating a GraphML project +## Create a GraphML project To create a new GraphML project using the ArangoDB Platform web interface, follow these steps: @@ -36,8 +36,8 @@ To create a new GraphML project using the ArangoDB Platform web interface, follo After clicking on a project name, you are taken to a screen where you can configure and start a new Featurization job. Follow these steps: -1. **Select a Graph**: In the **Features** section, choose your target graph from the **Select a graph** dropdown. -2. **Select Vertex Collections**: Pick the vertex collections that you want to include for feature extraction. +1. **Select a Graph**: In the **Features** section, choose your target graph from the **Select a graph** dropdown menu. +2. **Select Vertex Collection(s)**: Pick the vertex collection(s) that you want to include for feature extraction. 3. **Select Attributes**: Choose the attributes from your vertex collection to convert into machine-understandable features. Attributes cannot be used if their values are lists or arrays. @@ -55,17 +55,17 @@ format on the right side of the screen for transparency. In the **Configuration** tab, you can control the overall featurization job and how features are stored. -- **Batch size** – The number of documents to process in a single batch. -- **Run analysis checks** – Whether to run analysis checks to perform a high-level +- **Batch size**: The number of documents to process in a single batch. +- **Run analysis checks**: Whether to run analysis checks to perform a high-level analysis of the data quality before proceeding. The default value is `true`. -- **Skip labels** – Skip the featurization process for attributes marked as labels. +- **Skip labels**: Skip the featurization process for attributes marked as labels. The default value is `false`. -- **Overwrite FS graph** – Whether to overwrite the Feature Store graph if features +- **Overwrite FS graph**: Whether to overwrite the Feature Store graph if features were previously generated. The default value is `false`, therefore features are written to an existing Feature Store graph. -- **Write to source graph** – Whether to store the generated features on the Source +- **Write to source graph**: Whether to store the generated features on the Source Graph. The default value is `true`. -- **Use feature store** – Enable the use of the Feature Store database, which +- **Use feature store**: Enable the use of the Feature Store database, which allows you to store features separately from your Source Database. The default value is `false`, therefore features are written to the source graph. @@ -209,7 +209,7 @@ The Prediction screen displays the following configuration options: ### Enable scheduling You can configure automatic predictions using the **Enable scheduling** checkbox. -When scheduling is turned on, predictions run automatically based on a set CRON +When scheduling is enabled, predictions run automatically based on a set CRON expression. This helps keep prediction results up-to-date as new data is added to the system. You can define a cron expression that sets when the prediction job should run.