{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Introduction to PyALAF\n", "PyALAF is developed to solve a large range of Active Learning problems by making use of so called acquisition functions. \n", "This enables to choose from a broad range of settings for running the Active Learning. PyALAF enables pool based learning as well as population based learning. It also allows to find several suggested data points in one iteration. \n", "By the choice of the acquisition function one can control whether the model function that approximates the true data is optimized only close to the maximum in order to find that maximum or whether the full model function is optimized. \n", "PyALAF is compatible with most of sklearns regression models and also can use linear regression models." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The usage of PyALAF is very simple. First, we need to import some libraries." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Disabled warnings\n", "Disabled warnings\n", "Disabled warnings\n" ] } ], "source": [ "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "from PyALAF.models import inv_sphere\n", "from PyALAF.multi_optimize import run_continuous_batch_learning_multi\n", "from PyALAF.aggregation_fn import identity_aggregation_fn as identity\n", "\n", "from sklearn.gaussian_process import GaussianProcessRegressor as GPR\n", "from sklearn.gaussian_process.kernels import RBF, WhiteKernel\n", "\n", "random_state=41" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we create some test data by first setting some parameters. For this basic example, we use only 1D data with a grid spacing of 100 data points in any dimension in the intervall [-2,2]. " ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Shape of the test pool: (100, 1)\n" ] } ], "source": [ "#Parameters for grid\n", "grid_size = 100 #Number of data points per dimension\n", "dimensions = 1 #Number of dimensions\n", "lim = [[-2,],[2,]] #Bounds for each dimension. First lower bounds, than upper bounds\n", "\n", "#Create a grid for arbitrary number of dimensions\n", "x = []\n", "[x.append(np.linspace(lim[0][i],lim[1][i], grid_size )) for i in range(dimensions)]\n", "pool = np.meshgrid(*x)\n", "pool = np.array(pool).T\n", "test_pool = pool.reshape(grid_size**dimensions, dimensions)\n", "print('Shape of the test pool: ', test_pool.shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we define a data_model. The data model represents the true underlying data in this case. We sample data from this model, where we can also decide that this data is noisy. Therefore, we set the noise parameter." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "#Data model\n", "data_model = inv_sphere(d=dimensions, random_state=random_state)\n", "noise = np.sqrt(1e-6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then, we define a machine learning model that is used to approximate the true data. Here, we decide for a Gaussian Process Regression model with the standard RBF kernel and an additional WhiteKernel." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "kernel = RBF(length_scale=0.1, length_scale_bounds=[0.05,10])+WhiteKernel()\n", "regression_model = GPR(kernel)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we are equipped with everything to start the actual active learning routine. But before we call this routine, we need to define some parameters. Most parameters have default values that you can look up in the documentation." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "initial_samples=5 #Number of initial data points \n", "max_samples=30 #Number of data points that are maximally acquired \n", "batch_size=5 #Collect data points in batches of five before evaluating the model with a test data set\n", "n_repetitions=2 #Repeat the active learning two times\n", " \n", "initialization_method = 'random' #Algorithm to collect initial data\n", "optimization_method = 'PSO' #Algorithm to search for the maximum of the acquisition function. Choose from 'PSO' or 'lbfgs'AttributeError\n", "n_jobs = 1 #Number of jobs to start for parallel computing\n", "\n", "pso_options = {'c1': 0.5, 'c2': 0.3, 'w': 0.9, 'p':10*dimensions, 'i':50} #Options for particle swarm optimization\n", "\n", "active_learning_steps = int((max_samples-initial_samples)/batch_size) #Number of AL steps is calculated automatically" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this example, we want to compare the performance of 4 different acquisition functions, which we define below. In the dictionary, we save the name of the acquisition function together with a hyperparameter alpha. This is only used for the 'ideal' algorithm here." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "acquisition_functions = {'random':[0], 'ideal': [10], 'std': [0], 'GSx':[0]} " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To save the results, we create a dictionary that contains an empty data frame for each acquisition function. " ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'random_0': Empty DataFrame\n", " Columns: []\n", " Index: [],\n", " 'ideal_10': Empty DataFrame\n", " Columns: []\n", " Index: [],\n", " 'std_0': Empty DataFrame\n", " Columns: []\n", " Index: [],\n", " 'GSx_0': Empty DataFrame\n", " Columns: []\n", " Index: []}" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "score_dict = {}\n", "for acquisition_function, alpha_values in acquisition_functions.items():\n", " for alpha in alpha_values:\n", " key = acquisition_function+'_'+str(alpha)\n", " score_dict[key] = pd.DataFrame()\n", "\n", "score_dict" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "No, we run the Active Learning algorithm. We repeat the AL experiment only two times here. However, you can increase n_repetitions to run several AL experiments with different random seeds. Statistics will get better for running the experiment more often. Thus, for getting meaningful results when comparing different algorithms, it is suggested to run the AL experiment several times. \n", "Note that in the final application the AL experiment is only run once and without testing the models.\n", "\n", "For running the Active Learning it is recommended to use either the ```run_continuous_batch_learning_multi``` or ```run_batch_learning_multi``` for the population-based or pool-based approach, respectively. Both functions allow in principle to combine multiple objectives via aggregation functions. However, since we are here interested only in a single objective, we chose the identiy aggregation function." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2026-01-30 12:13:14,519 - basic_logger - INFO - Setting up Active Learning\n", "2026-01-30 12:13:14,521 - basic_logger - INFO - Noise converted: \n", "2026-01-30 12:13:14,522 - basic_logger - INFO - from 0.001 to [0.001]\n", "2026-01-30 12:13:14,523 - basic_logger - INFO - Test metrics will be calculated.\n", "2026-01-30 12:13:14,526 - basic_logger - INFO - Initialization method: random\n", "2026-01-30 12:13:14,527 - basic_logger - INFO - Initialization finished.\n", "2026-01-30 12:13:14,550 - basic_logger - INFO - Start Active Learning\n", "2026-01-30 12:13:14,553 - basic_logger - INFO - Optimization method: PSO\n", "2026-01-30 12:13:14,554 - basic_logger - INFO - Acquisition function: random\n", "2026-01-30 12:13:14,561 - basic_logger - INFO - Step 1\n", "2026-01-30 12:13:14,659 - basic_logger - INFO - Step 2\n", "2026-01-30 12:13:14,766 - basic_logger - INFO - Step 3\n", "2026-01-30 12:13:14,908 - basic_logger - INFO - Step 4\n", "2026-01-30 12:13:15,008 - basic_logger - INFO - Step 5\n", "2026-01-30 12:13:15,105 - basic_logger - INFO - Finished Active Learning\n", "2026-01-30 12:13:15,108 - basic_logger - INFO - Setting up Active Learning\n", "2026-01-30 12:13:15,110 - basic_logger - INFO - Noise converted: \n", "2026-01-30 12:13:15,111 - basic_logger - INFO - from 0.001 to [0.001]\n", "2026-01-30 12:13:15,111 - basic_logger - INFO - Test metrics will be calculated.\n", "2026-01-30 12:13:15,113 - basic_logger - INFO - Initialization method: random\n", "2026-01-30 12:13:15,114 - basic_logger - INFO - Initialization finished.\n", "2026-01-30 12:13:15,136 - basic_logger - INFO - Start Active Learning\n", "2026-01-30 12:13:15,137 - basic_logger - INFO - Optimization method: PSO\n", "2026-01-30 12:13:15,138 - basic_logger - INFO - Acquisition function: ideal\n", "2026-01-30 12:13:15,139 - basic_logger - INFO - Step 1\n" ] } ], "source": [ "%%capture\n", "\n", "for i in range(n_repetitions):\n", " random_state += 1\n", " \n", " for acquisition_function, alpha_values in acquisition_functions.items():\n", " for alpha in alpha_values:\n", " samples, values, result_dict = run_continuous_batch_learning_multi(\n", " models = [data_model], \n", " aggregation_function = identity,\n", " regression_models = [regression_model],\n", " acquisition_function = acquisition_function,\n", " opt_method = optimization_method,\n", " pool = test_pool, \n", " batch_size=batch_size,\n", " noise=noise,\n", " initial_samples=initial_samples, \n", " active_learning_steps=active_learning_steps,\n", " lim_features=lim,\n", " alpha=alpha,\n", " n_jobs=n_jobs,\n", " random_state=random_state,\n", " calculate_test_metrics=True,\n", " initialization=initialization_method,\n", " pso_options=pso_options\n", " )\n", "\n", " result = result_dict['aggregated']\n", " key = acquisition_function+'_'+str(alpha)\n", " score_dict[key] = pd.concat([score_dict[key], result])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As an output we get the acquired data points in the order they were acquired and also the result data frame which we save. You can see how this data frame looks like below. The mean squared error (MSE) the maximum error (MaxE), the mean absolute error (MAE) and the maximum observed value are stored." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
| \n", " | mean_MSE_train | \n", "mean_MAE_train | \n", "mean_MaxE_train | \n", "mean_MSE_test | \n", "mean_MAE_test | \n", "mean_MaxE_test | \n", "max_observation | \n", "
|---|---|---|---|---|---|---|---|
| m | \n", "\n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " |
| 5.0 | \n", "2.468643 | \n", "1.190516 | \n", "2.867850 | \n", "1.649410 | \n", "1.021930 | \n", "3.051051 | \n", "-0.038660 | \n", "
| 10.0 | \n", "0.000046 | \n", "0.003899 | \n", "0.010574 | \n", "0.012076 | \n", "0.064867 | \n", "0.291011 | \n", "-0.020379 | \n", "
| 15.0 | \n", "0.000002 | \n", "0.001037 | \n", "0.002343 | \n", "0.003411 | \n", "0.022224 | \n", "0.164779 | \n", "-0.020379 | \n", "
| 20.0 | \n", "0.000001 | \n", "0.000910 | \n", "0.001985 | \n", "0.002405 | \n", "0.017744 | \n", "0.147543 | \n", "-0.003877 | \n", "
| 25.0 | \n", "0.000002 | \n", "0.001151 | \n", "0.003629 | \n", "0.000015 | \n", "0.002096 | \n", "0.021392 | \n", "-0.003877 | \n", "
| 30.0 | \n", "0.000002 | \n", "0.001173 | \n", "0.003828 | \n", "0.000012 | \n", "0.001913 | \n", "0.019268 | \n", "-0.003877 | \n", "