{"id":52153,"date":"2025-06-13T00:00:00","date_gmt":"2025-06-13T07:00:00","guid":{"rendered":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/blog\/iot-intrusion-detection\/"},"modified":"2025-06-13T00:00:00","modified_gmt":"2025-06-13T07:00:00","slug":"iot-intrusion-detection","status":"publish","type":"post","link":"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/","title":{"rendered":"IoT Intrusion Detection"},"content":{"rendered":"<p>Intrusions refer to unauthorized activities that exploit vulnerabilities in IoT devices, which can compromise sensitive data or disrupt essential services. Detecting these intrusions is crucial to maintaining the security and integrity of IoT networks.<\/p>\n<p>This article demonstrates how to develop a robust intrusion detection system for IoT environments using a machine learning and deep learning approach with the <a href=\"https:\/\/griddb.net\/en\/\">GridDB database<\/a> . We will begin by retrieving IoT intrusion detection data from Kaggle, store it in a GridDB container, and use this data to train machine learning and deep learning models to identify different types of intrusions.<\/p>\n<p>GridDB, a high-performance NoSQL database, is particularly suited for handling the large-scale, real-time data generated by IoT systems due to its efficient in-memory processing and time series capabilities.<\/p>\n<p>Using GridDB\u00e2\u0080\u0099s powerful IoT data management features along with advanced machine learning and deep learning, we will build a predictive model that identifies potential threats to IoT devices.<\/p>\n<p><strong>Note:<\/strong> You can find codes for the tutorial in my <a href=\"https:\/\/github.com\/usmanmalik57\/GridDB-Blogs\/tree\/main\/IOT%20Intrusion%20Detection%20Using%20Machine%20Learning%20with%20GridDB%20As%20Database\">GridDB Blogs GitHub repository<\/a>.<\/p>\n<h2>Prerequisites<\/h2>\n<p>You need to install the following libraries to run codes in this article.<\/p>\n<ol>\n<li>GridDB C Client<\/li>\n<li>GridDB Python client<\/li>\n<\/ol>\n<p>To install these libraries, follow the installation instructions on <a href=\"https:\/\/pypi.org\/project\/griddb-python\/\">GridDB Python Package Index (Pypi)<\/a>.<\/p>\n<p>Since the code is executed in <a href=\"https:\/\/colab.research.google.com\/\">Google Colab<\/a>, you do not need to install any other libraries.<\/p>\n<p>Run the following script to import required libraries into your Python application.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">import pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.ensemble import RandomForestClassifier\nfrom sklearn.metrics import accuracy_score,  classification_report\nfrom sklearn.preprocessing import LabelEncoder\n\nimport tensorflow as tf\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Dense, Dropout, BatchNormalization\nfrom tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.models import load_model\n\nimport griddb_python as griddb<\/code><\/pre>\n<\/div>\n<h2>Inserting IoT Data into GridDB<\/h2>\n<p>In this section, you will see how to download IoT data from kaggle, import it into your Python application and store it in the GridDB database. Along the way you, you will learn to connect your Python application to GridDB, create a GridDB container and insert data into the GridDB container you created.<\/p>\n<h3>Downloading and Importing the IoT Dataset From Kaggle<\/h3>\n<p>We will insert the <a href=\"https:\/\/www.kaggle.com\/datasets\/subhajournal\/iotintrusion?resource=download\">IoT Intrusion Detection dataset from Kaggle<\/a> into the GridDB.<\/p>\n<p>The dataset consists of different types of IoT intrusions. the following script imports the dataset into a Pandas dataframe.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\"># Dataset download link\n#https:\/\/www.kaggle.com\/datasets\/subhajournal\/iotintrusion\/data\n\ndataset = pd.read_csv(\"IoT_Intrusion.csv\")\nprint(f\"The dataset consists of {dataset.shape[0]} rows and {dataset.shape[1]} columns\")\ndataset.head()<\/code><\/pre>\n<\/div>\n<p><strong>Output:<\/strong><br \/>\n<a href=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/06\/img1-iot-dataset.png\"><img fetchpriority=\"high\" decoding=\"async\" src=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/06\/img1-iot-dataset.png\" alt=\"\" width=\"1398\" height=\"412\" class=\"aligncenter size-full wp-image-31614\" srcset=\"\/wp-content\/uploads\/2025\/06\/img1-iot-dataset.png 1398w, \/wp-content\/uploads\/2025\/06\/img1-iot-dataset-300x88.png 300w, \/wp-content\/uploads\/2025\/06\/img1-iot-dataset-1024x302.png 1024w, \/wp-content\/uploads\/2025\/06\/img1-iot-dataset-768x226.png 768w, \/wp-content\/uploads\/2025\/06\/img1-iot-dataset-600x177.png 600w\" sizes=\"(max-width: 1398px) 100vw, 1398px\" \/><\/a><\/p>\n<p>The above output shows that the dataset consists of 1048575 rows and 47 columns.<br \/>\nFor the sake of simplicity, we will train our machine learning models on 200k records.  The following script randomly selects 200k records from the original dataset.<\/p>\n<p>The <code>label<\/code> column contains the intrusion types. We will also plot the count for each intrusion type.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">dataset = dataset.sample(n=200000, random_state=42)\nprint(f\"The dataset consists of {dataset.shape[0]} rows and {dataset.shape[1]} columns\")\nprint(f\"The total number of output labels are {dataset['label'].nunique()}\")\ndataset['label'].value_counts()<\/code><\/pre>\n<\/div>\n<p><strong>Output:<\/strong><br \/>\n<a href=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/06\/img2-iot-label-distribution.png\"><img decoding=\"async\" src=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/06\/img2-iot-label-distribution.png\" alt=\"\" width=\"452\" height=\"730\" class=\"aligncenter size-full wp-image-31615\" srcset=\"\/wp-content\/uploads\/2025\/06\/img2-iot-label-distribution.png 452w, \/wp-content\/uploads\/2025\/06\/img2-iot-label-distribution-186x300.png 186w\" sizes=\"(max-width: 452px) 100vw, 452px\" \/><\/a><\/p>\n<p>The above output shows all the 34 intrusion types in our dataset with <code>DDoS-ICMP_FLOOD<\/code> being the most frequently occurring intrusion while <code>Uploading_Attack<\/code> is the least frequently occurring intrusion type.<\/p>\n<p>For simplification&#8217;s sake we will group the 34 categories into 9 major categories using the following script.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">category_map = {\n    'DDoS': [\n        'DDoS-ICMP_Flood', 'DDoS-UDP_Flood', 'DDoS-TCP_Flood', 'DDoS-PSHACK_Flood',\n        'DDoS-SYN_Flood', 'DDoS-RSTFINFlood', 'DDoS-SynonymousIP_Flood', 'DDoS-ICMP_Fragmentation',\n        'DDoS-ACK_Fragmentation', 'DDoS-UDP_Fragmentation', 'DDoS-HTTP_Flood', 'DDoS-SlowLoris'\n    ],\n    'DoS': [\n        'DoS-UDP_Flood', 'DoS-TCP_Flood', 'DoS-SYN_Flood', 'DoS-HTTP_Flood'\n    ],\n    'Brute Force': [\n        'DictionaryBruteForce'\n    ],\n    'Spoofing': [\n        'MITM-ArpSpoofing', 'DNS_Spoofing'\n    ],\n    'Recon': [\n        'Recon-HostDiscovery', 'Recon-OSScan', 'Recon-PortScan', 'Recon-PingSweep'\n    ],\n    'Web-based': [\n        'SqlInjection', 'CommandInjection', 'XSS', 'BrowserHijacking', 'Uploading_Attack'\n    ],\n    'Mirai': [\n        'Mirai-greeth_flood', 'Mirai-udpplain', 'Mirai-greip_flood'\n    ],\n    'Other': [\n        'VulnerabilityScan', 'Backdoor_Malware'\n    ],\n    'Benign-trafic': [\n        'BenignTraffic'\n    ]\n}\n\n# Reverse the mapping to allow lookup by subcategory\nsubcategory_to_parent = {subcat: parent for parent, subcats in category_map.items() for subcat in subcats}\n\n# Add the 'class' column using the mapping\ndataset['class'] = dataset['label'].map(subcategory_to_parent)\n\ndataset['class'].value_counts()\n<\/code><\/pre>\n<\/div>\n<p><strong>Output:<\/strong><br \/>\n<a href=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/06\/img3-processed-iot-class-labels.png\"><img decoding=\"async\" src=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/06\/img3-processed-iot-class-labels.png\" alt=\"\" width=\"240\" height=\"218\" class=\"aligncenter size-full wp-image-31616\" \/><\/a><\/p>\n<p>You can now see that <code>DDoS<\/code> intrusion is the most frequently occuring intrusion followed by <code>DoS<\/code> and <code>Mirai<\/code>.<\/p>\n<p>Let&#8217;s plot a bar plot for the class distribution.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">class_counts = dataset['class'].value_counts()\n\nsns.set(style=\"darkgrid\")\n\nplt.figure(figsize=(10, 6))\nsns.barplot(x=class_counts.index, y=class_counts.values)\nplt.title(\"Class Distribution\")\nplt.xlabel(\"Class\")\nplt.ylabel(\"Count\")\nplt.xticks(rotation=45)\nplt.show()<\/code><\/pre>\n<\/div>\n<p><strong>Output:<\/strong><br \/>\n<a href=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/06\/img4-processed-iot-class-labels-bar-plot.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/06\/img4-processed-iot-class-labels-bar-plot.png\" alt=\"\" width=\"889\" height=\"621\" class=\"aligncenter size-full wp-image-31617\" srcset=\"\/wp-content\/uploads\/2025\/06\/img4-processed-iot-class-labels-bar-plot.png 889w, \/wp-content\/uploads\/2025\/06\/img4-processed-iot-class-labels-bar-plot-300x210.png 300w, \/wp-content\/uploads\/2025\/06\/img4-processed-iot-class-labels-bar-plot-768x536.png 768w, \/wp-content\/uploads\/2025\/06\/img4-processed-iot-class-labels-bar-plot-600x419.png 600w\" sizes=\"(max-width: 889px) 100vw, 889px\" \/><\/a><\/p>\n<p>The above output shows that our dataset is highly imbalanced.<\/p>\n<p>In the next section, we will insert this data into GridDB.<\/p>\n<h3>Connect to GridDB<\/h3>\n<p>You need to perform the following steps to connect your Python application to a GridDB instance.<\/p>\n<ol>\n<li>\n<p>Create an instance of the <code>griddb.StoreFactory<\/code> object using the <code>get_instance()<\/code> method.<\/p>\n<\/li>\n<li>\n<p>Create a GridDB store factory object by calling the <code>get_store()<\/code> method on the <code>StoreFactory<\/code> object. You need to pass the GridDB host and cluster name, and the user and password that you use to connect to the GridDB instance. This should establish a connection to your GridDB instance.<\/p>\n<\/li>\n<li>\n<p>To test the connection create a container by calling the <code>get_container()<\/code> method and pass to it a dummy container name. This step is optional and only tests the connection.<\/p>\n<\/li>\n<\/ol>\n<p>The following script shows how to connect to GridDB.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\"># GridDB connection details\nDB_HOST = \"127.0.0.1:10001\"\nDB_CLUSTER = \"myCluster\"\nDB_USER = \"admin\"\nDB_PASS = \"admin\"\n\n# creating a connection\n\nfactory = griddb.StoreFactory.get_instance()\n\ntry:\n    gridstore = factory.get_store(\n        notification_member = DB_HOST,\n        cluster_name = DB_CLUSTER,\n        username = DB_USER,\n        password = DB_PASS\n    )\n\n    container1 = gridstore.get_container(\"container1\")\n    if container1 == None:\n        print(\"Container does not exist\")\n    print(\"Successfully connected to GridDB\")\n\nexcept griddb.GSException as e:\n    for i in range(e.get_error_stack_size()):\n        print(\"[\", i, \"]\")\n        print(e.get_error_code(i))\n        print(e.get_location(i))\n        print(e.get_message(i))<\/code><\/pre>\n<\/div>\n<p><strong>Output:<\/strong><\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-sh\">Container does not exist\nSuccessfully connected to GridDB<\/code><\/pre>\n<\/div>\n<p>If you see the above output, you have successfully connected to GridB.<\/p>\n<h3>Create Container for IoT Data in GridDB<\/h3>\n<p>GridDB stores data in containers. Therefore, you need to create a container to store your IoT data.<\/p>\n<p>You will need to perform the following steps to create a GridDb container capable of storing your IoT data.<\/p>\n<ol>\n<li>\n<p>First, check if a container with the name you want to use already exists. If it does, either delete the existing container or choose a different name for your new container.<\/p>\n<\/li>\n<li>\n<p>Convert your dataset into a format that the GridDB expects. For example, GridDB expects the dataset to have a unique ID, therefore you need to add unique add if it doesn&#8217;t already exist. Similarly GriDB doesn&#8217;t accept spaces in column names. You will need to preprocess the column names too.<\/p>\n<\/li>\n<li>\n<p>GridDB data types are different than the Pandas dataframe types. You need to Define mapping from Pandas dataframe column types to GridDB data types.<\/p>\n<\/li>\n<li>\n<p>You need to create a column info list that contains column names and their corresponding mapped data type.<\/p>\n<\/li>\n<li>\n<p>Finally, you need to call the <code>put_container()<\/code> method on the store factory object and pass it the container name, the <code>column_info<\/code> list and the container type (<code>griddb.ContainerType.COLLECTION<\/code> in this case). The <code>row_key<\/code> is set to true since each row has unique ID.<\/p>\n<\/li>\n<li>\n<p>To test if the container is successfully created, call the <code>get_container()<\/code> method to retrieve the container you created.<\/p>\n<\/li>\n<\/ol>\n<p>The following script creates the <code>IoT_Data<\/code> container in our GridB instance.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\"># drop container if already exists\ngridstore.drop_container(\"IoT_Data\")\n\n# Add an primary key column\ndataset.insert(0, 'ID', range(1, len(dataset) + 1))\n\n# Clean column names to remove spaces or forbidden characters in the GridDB container\ndataset.columns = [col.strip().replace(\" \", \"_\") for col in dataset.columns]\n\n# Mapping from pandas data types to GridDB data types\ntype_mapping = {\n   'float64': griddb.Type.DOUBLE,\n   'int64': griddb.Type.INTEGER,\n   'object': griddb.Type.STRING\n}\n\n\n# Generate column_info dynamically, adding ID as the first entry\ncolumn_info = [[\"ID\", griddb.Type.INTEGER]] + [\n   [col, type_mapping[str(dtype)]] for col, dtype in dataset.dtypes.items() if col != \"ID\"\n]\n\n# Define the container info with ID as the primary key and as a collection container\ncontainer_name = \"IoT_Data\"\ncontainer_info = griddb.ContainerInfo(\n   container_name, column_info, griddb.ContainerType.COLLECTION, row_key=True\n)\n\n# Connecting to GridDB and creating the container\ntry:\n   gridstore.put_container(container_info)\n   container = gridstore.get_container(container_name)\n   if container is None:\n       print(f\"Failed to create container: {container_name}\")\n   else:\n       print(f\"Successfully created container: {container_name}\")\n\nexcept griddb.GSException as e:\n   print(f\"Error creating container {container_name}:\")\n   for i in range(e.get_error_stack_size()):\n       print(f\"[{i}] Error code: {e.get_error_code(i)}, Message: {e.get_message(i)}\")<\/code><\/pre>\n<\/div>\n<h3>Insert IoT Data into GridDB<\/h3>\n<p>We are now ready to insert the dataframe into the GridDB container we just created.<br \/>\nTo do so, we iterate through all the rows in our dataset, fetch the column data and column type for each row and insert the data using the <code>container.put()<\/code> method.<\/p>\n<p>The following script inserts our IoT data from the Pandas dataframe into the GriDB <code>IoT_Data<\/code> container.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">\n\ntry:\n    for _, row in dataset.iterrows():\n        # Prepare each row's data in the exact order as defined in `column_info`\n        row_data = [\n            int(row[col]) if dtype == griddb.Type.INTEGER else\n            float(row[col]) if dtype == griddb.Type.DOUBLE else\n            str(row[col])\n            for col, dtype in column_info\n        ]\n        # Insert the row data into the container\n        container.put(row_data)\n\n    print(f\"Successfully inserted {len(dataset)} rows of data into {container_name}\")\n\nexcept griddb.GSException as e:\n    print(f\"Error inserting data into container {container_name}:\")\n    for i in range(e.get_error_stack_size()):\n        print(f\"[{i}] Error code: {e.get_error_code(i)}, Message: {e.get_message(i)}\")<\/code><\/pre>\n<\/div>\n<p><strong>Output:<\/strong><\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-sh\">Successfully inserted 200000 rows of data into IoT_Data<\/code><\/pre>\n<\/div>\n<p>In the next sections we will retrieve the IoT data from GridDB and will train machine learning and deep learning models for predicting intrusion type.<\/p>\n<h2>Forecasting IoT Intrusion Using Machine Learning and Deep Learning<\/h2>\n<p>We will try both machine learning and deep learning approaches for predicting intrusion type. But first we will see how to retrieve data from GridDB.<\/p>\n<h3>Retrieving Data From GridDB<\/h3>\n<p>To retrieve GridDB data you have to retrieve the container, call <code>query()<\/code> method and pass the <code>SELECT *<\/code> query to the method. This will retrieve all the records from the container. Next, call the <code>fetch()<\/code> method to retrieve the data from the container. Finally, call the <code>fetch_rows()<\/code> method to store data in a Pandas dataframe.<\/p>\n<p>The following script defines the <code>retrieve_data_from_griddb()<\/code> method that retrieves data from a GridDB container into a Pandas dataframe.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">def retrieve_data_from_griddb(container_name):\n\n    try:\n        data_container = gridstore.get_container(container_name)\n\n        # Query all data from the container\n        query = data_container.query(\"select *\")\n        rs = query.fetch()  \n\n        data = rs.fetch_rows()\n        data .set_index(\"ID\", inplace=True)\n        return data\n\n    except griddb.GSException as e:\n        print(f\"Error retrieving data from GridDB: {e.get_message()}\")\n        return None\n\n\niot_data = retrieve_data_from_griddb(\"IoT_Data\")\niot_data.head()<\/code><\/pre>\n<\/div>\n<p><strong>Output:<\/strong><br \/>\n<a href=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/06\/img5-iot-data-from-griddb.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/06\/img5-iot-data-from-griddb.png\" alt=\"\" width=\"846\" height=\"441\" class=\"aligncenter size-full wp-image-31618\" srcset=\"\/wp-content\/uploads\/2025\/06\/img5-iot-data-from-griddb.png 846w, \/wp-content\/uploads\/2025\/06\/img5-iot-data-from-griddb-300x156.png 300w, \/wp-content\/uploads\/2025\/06\/img5-iot-data-from-griddb-768x400.png 768w, \/wp-content\/uploads\/2025\/06\/img5-iot-data-from-griddb-600x313.png 600w\" sizes=\"(max-width: 846px) 100vw, 846px\" \/><\/a><\/p>\n<h3>IoT Data Classification Using Machine Learning<\/h3>\n<p>We will first use the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Random_forest\">Random Forest Classification<\/a> algorithm, a classification machine learning algorithm and predict the intrusion type.<\/p>\n<p>To do so, we will divide our dataset into features and labels set and then train and test sets. We will also standardize our dataset.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\"># Separate the features (X) and the output class (y)\n\nX = iot_data.drop(columns=['label', 'class'])  # Dropping both `label` and `class` columns as `class` is the target\ny = iot_data['class']  # Output target\n\nX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n\nscaler = StandardScaler()\nX_train = scaler.fit_transform(X_train)\nX_test = scaler.transform(X_test)\n<\/code><\/pre>\n<\/div>\n<p>Next, we will use the <code>RandomForestClassifier()<\/code> class from the <a href=\"https:\/\/scikit-learn.org\/stable\/\">sklearn<\/a> module and call its <code>fit()<\/code> method to train the algorithm on the training data.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">rf_model = RandomForestClassifier(random_state=42)\nrf_model.fit(X_train, y_train)<\/code><\/pre>\n<\/div>\n<p>Finally, we can make predictions using the <code>predict()<\/code> method and compare the predictions with the actual labels in the test set to calculate the model accuracy.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">\nrf_predictions = rf_model.predict(X_test)\nrf_accuracy = accuracy_score(y_test, rf_predictions)\n\nclassification_rep = classification_report(y_test, rf_predictions, zero_division=1)\nprint(\"Classification Report:n\", classification_rep)<\/code><\/pre>\n<\/div>\n<p><strong>Output:<\/strong><br \/>\n<a href=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/06\/img6-machine-learning-classification-report.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/06\/img6-machine-learning-classification-report.png\" alt=\"\" width=\"472\" height=\"317\" class=\"aligncenter size-full wp-image-31619\" srcset=\"\/wp-content\/uploads\/2025\/06\/img6-machine-learning-classification-report.png 472w, \/wp-content\/uploads\/2025\/06\/img6-machine-learning-classification-report-300x201.png 300w\" sizes=\"(max-width: 472px) 100vw, 472px\" \/><\/a><\/p>\n<p>The above output shows that our model achieves an accuracy of 99% on the test set. It is important to note that since <code>Brute Force<\/code> had only 47 instances in the training set, the model is not able to learn much about this category.<\/p>\n<h3>IoT Data Classification Using Deep Learning<\/h3>\n<p>Let&#8217;s now predict intrusion detection using a deep neural network implemented in <a href=\"https:\/\/www.tensorflow.org\/guide\/keras\">TensorFlow Keras<\/a> library. We will convert the output labels to numeric integers since deep learning algorithms work with numbers only. Next we will standardize the training and test sets as we did before.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\">label_encoder = LabelEncoder()\ny = label_encoder.fit_transform(iot_data['class'])  # Integer encoding\n\nX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n\nX_train = scaler.fit_transform(X_train)\nX_test = scaler.transform(X_test)\n<\/code><\/pre>\n<\/div>\n<p>The following script defines our model with four hidden layers followed by an output layer. Since we have a multiclass classification problem we use the <code>softmax<\/code> function in the final activation layer.<\/p>\n<p>We also use an adaptive learning rate to avoid overfitting.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\"># Define the model\nmodel = Sequential([\n    Dense(256, input_dim=X_train.shape[1], activation='relu'),\n    BatchNormalization(),\n    Dropout(0.4),\n    Dense(128, activation='relu'),\n    BatchNormalization(),\n    Dropout(0.4),\n    Dense(64, activation='relu'),\n    BatchNormalization(),\n    Dropout(0.3),\n    Dense(32, activation='relu'),\n    BatchNormalization(),\n    Dropout(0.3),\n    Dense(len(pd.unique(y)), activation='softmax')  # Softmax for multiclass classification\n])\n\n# Adaptive learning rate scheduler with exponential decay\ninitial_learning_rate = 0.001\nlr_schedule = tf.keras.optimizers.schedules.ExponentialDecay(\n    initial_learning_rate=initial_learning_rate,\n    decay_steps=10000,\n    decay_rate=0.9\n)\n\n# Compile the model with Adam optimizer with decayed learning rate\nmodel.compile(optimizer=Adam(learning_rate=lr_schedule),\n              loss='sparse_categorical_crossentropy',\n              metrics=['accuracy'])<\/code><\/pre>\n<\/div>\n<p>The script trains our deep learning model on the training set. We save the best model after each epoch.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\"># Define callbacks without ReduceLROnPlateau\nearly_stopping = EarlyStopping(monitor='val_loss', patience=15, restore_best_weights=True)\nmodel_checkpoint = ModelCheckpoint('best_model.keras', monitor='val_accuracy', save_best_only=True)\n\n# Train the model with the callbacks\nhistory = model.fit(\n    X_train, y_train,\n    epochs=100,\n    batch_size=32,\n    validation_data=(X_test, y_test),\n    callbacks=[early_stopping, model_checkpoint],\n    verbose=1\n)<\/code><\/pre>\n<\/div>\n<p><strong>Output:<\/strong><br \/>\n<a href=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/06\/img7-deep-learning-training-results.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/06\/img7-deep-learning-training-results.png\" alt=\"\" width=\"1003\" height=\"475\" class=\"aligncenter size-full wp-image-31620\" srcset=\"\/wp-content\/uploads\/2025\/06\/img7-deep-learning-training-results.png 1003w, \/wp-content\/uploads\/2025\/06\/img7-deep-learning-training-results-300x142.png 300w, \/wp-content\/uploads\/2025\/06\/img7-deep-learning-training-results-768x364.png 768w, \/wp-content\/uploads\/2025\/06\/img7-deep-learning-training-results-600x284.png 600w\" sizes=\"(max-width: 1003px) 100vw, 1003px\" \/><\/a><\/p>\n<p>Once the training is complete we load the best model and make predictions on the test. We compare predictions with the actual target label to calculate model performance.<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-python\"># Load the best model\nbest_model = load_model('best_model.keras')\n\ny_pred = best_model.predict(X_test).argmax(axis=-1)\n\nprint(\"Classification Report:n\", classification_report(y_test, y_pred, zero_division = 0))<\/code><\/pre>\n<\/div>\n<p><strong>Output:<\/strong><br \/>\n<a href=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/06\/img8-deep-learning-classification-report.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/06\/img8-deep-learning-classification-report.png\" alt=\"\" width=\"466\" height=\"336\" class=\"aligncenter size-full wp-image-31621\" srcset=\"\/wp-content\/uploads\/2025\/06\/img8-deep-learning-classification-report.png 466w, \/wp-content\/uploads\/2025\/06\/img8-deep-learning-classification-report-300x216.png 300w\" sizes=\"(max-width: 466px) 100vw, 466px\" \/><\/a><\/p>\n<p>The above output shows that we achieve an accuracy of 89% on the test which is much less than what was achieved using the Random Forest algorithm. The reason can be that tree algorithms such as Random Forest are known to perform better on the tabular dataset. Furthermore, the performance of deep learning models is significantly impacted by data imbalance.<\/p>\n<h2>Conclusion<\/h2>\n<p>This article demonstrated a complete workflow for building an IoT intrusion detection system using GridDB for data management and machine learning and deep learning models for classification. We covered how to connect to GridDB, store IoT intrusion data, and use Random Forest and deep neural network models to predict intrusion types. The Random Forest model achieved high accuracy, showing its suitability for tabular datasets, while deep learning models highlighted the challenges of data imbalance.<\/p>\n<p>If you have any questions or need help with GridDB, feel free to reach out on Stack Overflow with the <code>griddb<\/code> tag, and our engineers will be glad to assist.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Intrusions refer to unauthorized activities that exploit vulnerabilities in IoT devices, which can compromise sensitive data or disrupt essential services. Detecting these intrusions is crucial to maintaining the security and integrity of IoT networks. This article demonstrates how to develop a robust intrusion detection system for IoT environments using a machine learning and deep learning [&hellip;]<\/p>\n","protected":false},"author":41,"featured_media":52154,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[121],"tags":[],"class_list":["post-52153","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>IoT Intrusion Detection | GridDB: Open Source Time Series Database for IoT<\/title>\n<meta name=\"description\" content=\"Intrusions refer to unauthorized activities that exploit vulnerabilities in IoT devices, which can compromise sensitive data or disrupt essential\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"IoT Intrusion Detection | GridDB: Open Source Time Series Database for IoT\" \/>\n<meta property=\"og:description\" content=\"Intrusions refer to unauthorized activities that exploit vulnerabilities in IoT devices, which can compromise sensitive data or disrupt essential\" \/>\n<meta property=\"og:url\" content=\"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/\" \/>\n<meta property=\"og:site_name\" content=\"GridDB: Open Source Time Series Database for IoT\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/griddbcommunity\/\" \/>\n<meta property=\"article:published_time\" content=\"2025-06-13T07:00:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/12\/img4-processed-iot-class-labels-bar-plot.png\" \/>\n\t<meta property=\"og:image:width\" content=\"889\" \/>\n\t<meta property=\"og:image:height\" content=\"621\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"griddb-admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@GridDBCommunity\" \/>\n<meta name=\"twitter:site\" content=\"@GridDBCommunity\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"griddb-admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/\"},\"author\":{\"name\":\"griddb-admin\",\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/person\/4fe914ca9576878e82f5e8dd3ba52233\"},\"headline\":\"IoT Intrusion Detection\",\"datePublished\":\"2025-06-13T07:00:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/\"},\"wordCount\":1472,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/#primaryimage\"},\"thumbnailUrl\":\"\/wp-content\/uploads\/2025\/12\/img4-processed-iot-class-labels-bar-plot.png\",\"articleSection\":[\"Blog\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/\",\"url\":\"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/\",\"name\":\"IoT Intrusion Detection | GridDB: Open Source Time Series Database for IoT\",\"isPartOf\":{\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/#primaryimage\"},\"thumbnailUrl\":\"\/wp-content\/uploads\/2025\/12\/img4-processed-iot-class-labels-bar-plot.png\",\"datePublished\":\"2025-06-13T07:00:00+00:00\",\"description\":\"Intrusions refer to unauthorized activities that exploit vulnerabilities in IoT devices, which can compromise sensitive data or disrupt essential\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/#primaryimage\",\"url\":\"\/wp-content\/uploads\/2025\/12\/img4-processed-iot-class-labels-bar-plot.png\",\"contentUrl\":\"\/wp-content\/uploads\/2025\/12\/img4-processed-iot-class-labels-bar-plot.png\",\"width\":889,\"height\":621},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#website\",\"url\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/\",\"name\":\"GridDB: Open Source Time Series Database for IoT\",\"description\":\"GridDB is an open source time-series database with the performance of NoSQL and convenience of SQL\",\"publisher\":{\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#organization\",\"name\":\"Fixstars\",\"url\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/griddb.net\/wp-content\/uploads\/2019\/04\/fixstars_logo_web_tagline.png\",\"contentUrl\":\"https:\/\/griddb.net\/wp-content\/uploads\/2019\/04\/fixstars_logo_web_tagline.png\",\"width\":200,\"height\":83,\"caption\":\"Fixstars\"},\"image\":{\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/griddbcommunity\/\",\"https:\/\/x.com\/GridDBCommunity\",\"https:\/\/www.linkedin.com\/company\/griddb-by-toshiba\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/person\/4fe914ca9576878e82f5e8dd3ba52233\",\"name\":\"griddb-admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/5bceca1cafc06886a7ba873e2f0a28011a1176c4dea59709f735b63ae30d0342?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/5bceca1cafc06886a7ba873e2f0a28011a1176c4dea59709f735b63ae30d0342?s=96&d=mm&r=g\",\"caption\":\"griddb-admin\"},\"url\":\"https:\/\/griddb.net\/en\/author\/griddb-admin\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"IoT Intrusion Detection | GridDB: Open Source Time Series Database for IoT","description":"Intrusions refer to unauthorized activities that exploit vulnerabilities in IoT devices, which can compromise sensitive data or disrupt essential","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/","og_locale":"en_US","og_type":"article","og_title":"IoT Intrusion Detection | GridDB: Open Source Time Series Database for IoT","og_description":"Intrusions refer to unauthorized activities that exploit vulnerabilities in IoT devices, which can compromise sensitive data or disrupt essential","og_url":"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/","og_site_name":"GridDB: Open Source Time Series Database for IoT","article_publisher":"https:\/\/www.facebook.com\/griddbcommunity\/","article_published_time":"2025-06-13T07:00:00+00:00","og_image":[{"width":889,"height":621,"url":"https:\/\/griddb.net\/wp-content\/uploads\/2025\/12\/img4-processed-iot-class-labels-bar-plot.png","type":"image\/png"}],"author":"griddb-admin","twitter_card":"summary_large_image","twitter_creator":"@GridDBCommunity","twitter_site":"@GridDBCommunity","twitter_misc":{"Written by":"griddb-admin","Est. reading time":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/#article","isPartOf":{"@id":"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/"},"author":{"name":"griddb-admin","@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/person\/4fe914ca9576878e82f5e8dd3ba52233"},"headline":"IoT Intrusion Detection","datePublished":"2025-06-13T07:00:00+00:00","mainEntityOfPage":{"@id":"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/"},"wordCount":1472,"commentCount":0,"publisher":{"@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#organization"},"image":{"@id":"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/#primaryimage"},"thumbnailUrl":"\/wp-content\/uploads\/2025\/12\/img4-processed-iot-class-labels-bar-plot.png","articleSection":["Blog"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/","url":"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/","name":"IoT Intrusion Detection | GridDB: Open Source Time Series Database for IoT","isPartOf":{"@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/#primaryimage"},"image":{"@id":"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/#primaryimage"},"thumbnailUrl":"\/wp-content\/uploads\/2025\/12\/img4-processed-iot-class-labels-bar-plot.png","datePublished":"2025-06-13T07:00:00+00:00","description":"Intrusions refer to unauthorized activities that exploit vulnerabilities in IoT devices, which can compromise sensitive data or disrupt essential","inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/griddb.net\/en\/blog\/iot-intrusion-detection\/#primaryimage","url":"\/wp-content\/uploads\/2025\/12\/img4-processed-iot-class-labels-bar-plot.png","contentUrl":"\/wp-content\/uploads\/2025\/12\/img4-processed-iot-class-labels-bar-plot.png","width":889,"height":621},{"@type":"WebSite","@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#website","url":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/","name":"GridDB: Open Source Time Series Database for IoT","description":"GridDB is an open source time-series database with the performance of NoSQL and convenience of SQL","publisher":{"@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#organization","name":"Fixstars","url":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/logo\/image\/","url":"https:\/\/griddb.net\/wp-content\/uploads\/2019\/04\/fixstars_logo_web_tagline.png","contentUrl":"https:\/\/griddb.net\/wp-content\/uploads\/2019\/04\/fixstars_logo_web_tagline.png","width":200,"height":83,"caption":"Fixstars"},"image":{"@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/griddbcommunity\/","https:\/\/x.com\/GridDBCommunity","https:\/\/www.linkedin.com\/company\/griddb-by-toshiba"]},{"@type":"Person","@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/person\/4fe914ca9576878e82f5e8dd3ba52233","name":"griddb-admin","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/5bceca1cafc06886a7ba873e2f0a28011a1176c4dea59709f735b63ae30d0342?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/5bceca1cafc06886a7ba873e2f0a28011a1176c4dea59709f735b63ae30d0342?s=96&d=mm&r=g","caption":"griddb-admin"},"url":"https:\/\/griddb.net\/en\/author\/griddb-admin\/"}]}},"_links":{"self":[{"href":"https:\/\/griddb.net\/en\/wp-json\/wp\/v2\/posts\/52153","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/griddb.net\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/griddb.net\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/griddb.net\/en\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/griddb.net\/en\/wp-json\/wp\/v2\/comments?post=52153"}],"version-history":[{"count":0,"href":"https:\/\/griddb.net\/en\/wp-json\/wp\/v2\/posts\/52153\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/griddb.net\/en\/wp-json\/wp\/v2\/media\/52154"}],"wp:attachment":[{"href":"https:\/\/griddb.net\/en\/wp-json\/wp\/v2\/media?parent=52153"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/griddb.net\/en\/wp-json\/wp\/v2\/categories?post=52153"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/griddb.net\/en\/wp-json\/wp\/v2\/tags?post=52153"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}