{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "expmkveO04pw" }, "source": [ "## Learning Rate Calibration of Gradient Descent in PyBOP\n", "\n", "In this notebook, we calibrate the learning rate for the gradient descent optimiser on a parameter identification problem. The gradient descent learning rate is taken as the `sigma0` value passed to the `pybop.Optimisation` class, or via `problem.sigma0` or `cost.sigma0` if it is passed earlier in the workflow.\n", "\n", "### Setting up the Environment\n", "\n", "Before we begin, we need to ensure that we have all the necessary tools. We will install PyBOP from its development branch and upgrade some dependencies:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "execution": { "iopub.execute_input": "2024-04-14T18:57:35.622071Z", "iopub.status.busy": "2024-04-14T18:57:35.621492Z", "iopub.status.idle": "2024-04-14T18:57:40.830883Z", "shell.execute_reply": "2024-04-14T18:57:40.830233Z" }, "id": "X87NUGPW04py", "outputId": "0d785b07-7cff-4aeb-e60a-4ff5a669afbf" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Requirement already satisfied: pip in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (24.0)\n", "Requirement already satisfied: ipywidgets in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (8.1.2)\n", "Requirement already satisfied: comm>=0.1.3 in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from ipywidgets) (0.2.1)\n", "Requirement already satisfied: ipython>=6.1.0 in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from ipywidgets) (8.22.1)\n", "Requirement already satisfied: traitlets>=4.3.1 in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from ipywidgets) (5.14.1)\n", "Requirement already satisfied: widgetsnbextension~=4.0.10 in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from ipywidgets) (4.0.10)\n", "Requirement already satisfied: jupyterlab-widgets~=3.0.10 in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from ipywidgets) (3.0.10)\n", "Requirement already satisfied: decorator in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from ipython>=6.1.0->ipywidgets) (5.1.1)\n", "Requirement already satisfied: jedi>=0.16 in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from ipython>=6.1.0->ipywidgets) (0.19.1)\n", "Requirement already satisfied: matplotlib-inline in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from ipython>=6.1.0->ipywidgets) (0.1.6)\n", "Requirement already satisfied: prompt-toolkit<3.1.0,>=3.0.41 in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from ipython>=6.1.0->ipywidgets) (3.0.43)\n", "Requirement already satisfied: pygments>=2.4.0 in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from ipython>=6.1.0->ipywidgets) (2.17.2)\n", "Requirement already satisfied: stack-data in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from ipython>=6.1.0->ipywidgets) (0.6.3)\n", "Requirement already satisfied: pexpect>4.3 in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from ipython>=6.1.0->ipywidgets) (4.9.0)\n", "Requirement already satisfied: parso<0.9.0,>=0.8.3 in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from jedi>=0.16->ipython>=6.1.0->ipywidgets) (0.8.3)\n", "Requirement already satisfied: ptyprocess>=0.5 in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from pexpect>4.3->ipython>=6.1.0->ipywidgets) (0.7.0)\n", "Requirement already satisfied: wcwidth in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from prompt-toolkit<3.1.0,>=3.0.41->ipython>=6.1.0->ipywidgets) (0.2.13)\n", "Requirement already satisfied: executing>=1.2.0 in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from stack-data->ipython>=6.1.0->ipywidgets) (2.0.1)\n", "Requirement already satisfied: asttokens>=2.1.0 in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from stack-data->ipython>=6.1.0->ipywidgets) (2.4.1)\n", "Requirement already satisfied: pure-eval in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from stack-data->ipython>=6.1.0->ipywidgets) (0.2.2)\n", "Requirement already satisfied: six>=1.12.0 in /Users/engs2510/.pyenv/versions/3.11.7/envs/pybop/lib/python3.11/site-packages (from asttokens>=2.1.0->stack-data->ipython>=6.1.0->ipywidgets) (1.16.0)\n", "Note: you may need to restart the kernel to use updated packages.\n", "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "%pip install --upgrade pip ipywidgets\n", "%pip install pybop -q" ] }, { "cell_type": "markdown", "metadata": { "id": "jAvD5fk104p0" }, "source": [ "### Importing Libraries\n", "\n", "With the environment set up, we can now import PyBOP alongside other libraries we will need:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2024-04-14T18:57:40.833454Z", "iopub.status.busy": "2024-04-14T18:57:40.833211Z", "iopub.status.idle": "2024-04-14T18:57:46.230918Z", "shell.execute_reply": "2024-04-14T18:57:46.230420Z" }, "id": "SQdt4brD04p1" }, "outputs": [], "source": [ "import numpy as np\n", "\n", "import pybop" ] }, { "cell_type": "markdown", "metadata": { "id": "5XU-dMtU04p2" }, "source": [ "## Generating Synthetic Data\n", "\n", "To demonstrate parameter estimation, we first need some data. We will generate synthetic data using a forward model, which requires defining a parameter set and the model itself.\n", "\n", "### Defining Parameters and Model\n", "\n", "We start by creating an example parameter set, constructing the single-particle model (SPM) and generating the synthetic data." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2024-04-14T18:57:46.247936Z", "iopub.status.busy": "2024-04-14T18:57:46.247644Z", "iopub.status.idle": "2024-04-14T18:57:46.446493Z", "shell.execute_reply": "2024-04-14T18:57:46.446198Z" } }, "outputs": [], "source": [ "parameter_set = pybop.ParameterSet.pybamm(\"Chen2020\")\n", "parameter_set.update(\n", " {\n", " \"Negative electrode active material volume fraction\": 0.65,\n", " \"Positive electrode active material volume fraction\": 0.51,\n", " }\n", ")\n", "model = pybop.lithium_ion.SPM(parameter_set=parameter_set)\n", "init_soc = 0.4\n", "experiment = pybop.Experiment(\n", " [\n", " (\n", " \"Discharge at 0.5C for 6 minutes (4 second period)\",\n", " \"Charge at 0.5C for 6 minutes (4 second period)\",\n", " ),\n", " ]\n", " * 2\n", ")\n", "values = model.predict(init_soc=init_soc, experiment=experiment)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Adding Noise to Voltage Data\n", "\n", "To make the parameter estimation more realistic, we add Gaussian noise to the data." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2024-04-14T18:57:46.448424Z", "iopub.status.busy": "2024-04-14T18:57:46.448310Z", "iopub.status.idle": "2024-04-14T18:57:46.463231Z", "shell.execute_reply": "2024-04-14T18:57:46.462472Z" } }, "outputs": [], "source": [ "sigma = 0.002\n", "corrupt_values = values[\"Voltage [V]\"].data + np.random.normal(\n", " 0, sigma, len(values[\"Voltage [V]\"].data)\n", ")" ] }, { "cell_type": "markdown", "metadata": { "id": "X8-tubYY04p_" }, "source": [ "## Identifying the Parameters" ] }, { "cell_type": "markdown", "metadata": { "id": "PQqhvSZN04p_" }, "source": [ "We will now set up the parameter estimation process by defining the datasets for optimisation and selecting the model parameters we wish to estimate." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Creating a Dataset\n", "\n", "The dataset for optimisation is composed of time, current, and the noisy voltage data:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2024-04-14T18:57:46.466648Z", "iopub.status.busy": "2024-04-14T18:57:46.466165Z", "iopub.status.idle": "2024-04-14T18:57:46.479234Z", "shell.execute_reply": "2024-04-14T18:57:46.478887Z" }, "id": "zuvGHWID04p_" }, "outputs": [], "source": [ "dataset = pybop.Dataset(\n", " {\n", " \"Time [s]\": values[\"Time [s]\"].data,\n", " \"Current function [A]\": values[\"Current [A]\"].data,\n", " \"Voltage [V]\": corrupt_values,\n", " }\n", ")" ] }, { "cell_type": "markdown", "metadata": { "id": "ffS3CF_704qA" }, "source": [ "### Defining Parameters to Estimate\n", "\n", "We select the parameters for estimation and set up their prior distributions and bounds:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2024-04-14T18:57:46.480892Z", "iopub.status.busy": "2024-04-14T18:57:46.480800Z", "iopub.status.idle": "2024-04-14T18:57:46.482933Z", "shell.execute_reply": "2024-04-14T18:57:46.482614Z" }, "id": "WPCybXIJ04qA" }, "outputs": [], "source": [ "parameters = pybop.Parameters(\n", " pybop.Parameter(\n", " \"Negative electrode active material volume fraction\",\n", " prior=pybop.Uniform(0.45, 0.7),\n", " bounds=[0.4, 0.8],\n", " true_value=0.65,\n", " ),\n", " pybop.Parameter(\n", " \"Positive electrode active material volume fraction\",\n", " prior=pybop.Uniform(0.45, 0.7),\n", " bounds=[0.4, 0.8],\n", " true_value=0.51,\n", " ),\n", ")" ] }, { "cell_type": "markdown", "metadata": { "id": "n4OHa-aF04qA" }, "source": [ "### Setting up the Optimisation Problem with incorrect sigma value\n", "\n", "With the datasets and parameters defined, we can set up the optimisation problem, its cost function, and the optimiser. For gradient descent, the `sigma0` value corresponds to the learning rate. Let's set this hyperparmeter incorrectly to view how we calibrate it. In this example, let's start with `sigma0=0.2`." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2024-04-14T18:57:46.484362Z", "iopub.status.busy": "2024-04-14T18:57:46.484269Z", "iopub.status.idle": "2024-04-14T18:57:46.549050Z", "shell.execute_reply": "2024-04-14T18:57:46.548811Z" }, "id": "etMzRtx404qA" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "NOTE: Boundaries ignored by Gradient Descent\n" ] } ], "source": [ "problem = pybop.FittingProblem(model, parameters, dataset, init_soc=init_soc)\n", "cost = pybop.SumSquaredError(problem)\n", "optim = pybop.GradientDescent(cost, sigma0=0.2, max_iterations=100)" ] }, { "cell_type": "markdown", "metadata": { "id": "caprp-bV04qB" }, "source": [ "### Running the Optimisation\n", "\n", "We proceed to run the optimisation algorithm to estimate the parameters with the updated learning rate (`sigma0`)." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2024-04-14T18:57:46.550494Z", "iopub.status.busy": "2024-04-14T18:57:46.550406Z", "iopub.status.idle": "2024-04-14T18:57:47.825440Z", "shell.execute_reply": "2024-04-14T18:57:47.824752Z" }, "id": "-9OVt0EQ04qB" }, "outputs": [], "source": [ "x, final_cost = optim.run()" ] }, { "cell_type": "markdown", "metadata": { "id": "-4pZsDmS04qC" }, "source": [ "### Viewing the Estimated Parameters\n", "\n", "After the optimisation, we can examine the estimated parameter values. In this case, the optimiser misses the optimal solution by a large amount." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "execution": { "iopub.execute_input": "2024-04-14T18:57:47.828249Z", "iopub.status.busy": "2024-04-14T18:57:47.827941Z", "iopub.status.idle": "2024-04-14T18:57:47.839261Z", "shell.execute_reply": "2024-04-14T18:57:47.838577Z" }, "id": "Hgz8SV4i04qC", "outputId": "e1e42ae7-5075-4c47-dd68-1b22ecc170f6" }, "outputs": [ { "data": { "text/plain": [ "array([0.64609807, 0.51472958])" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x # This will output the estimated parameters" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's plot the time-series prediction for the given solution. As we suspected, the optimiser found a very poor solution. " ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2024-04-14T18:57:47.841607Z", "iopub.status.busy": "2024-04-14T18:57:47.841388Z", "iopub.status.idle": "2024-04-14T18:57:50.112707Z", "shell.execute_reply": "2024-04-14T18:57:50.111902Z" } }, "outputs": [ { "data": { "image/svg+xml": [ "0500100015003.53.553.63.653.7ReferenceModelOptimised ComparisonTime / sVoltage / V" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "pybop.quick_plot(problem, problem_inputs=x, title=\"Optimised Comparison\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Calibrating the Learning Rate \n", "\n", "Now that we've seen how poor an incorrect `sigma0` value is for this optimisation problem, let's calibrate this value to find the optimal solution in the lowest number of iterations." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2024-04-14T18:57:50.118807Z", "iopub.status.busy": "2024-04-14T18:57:50.118279Z", "iopub.status.idle": "2024-04-14T18:59:24.213183Z", "shell.execute_reply": "2024-04-14T18:59:24.212776Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.001\n", "NOTE: Boundaries ignored by Gradient Descent\n", "0.012285714285714285\n", "NOTE: Boundaries ignored by Gradient Descent\n", "0.023571428571428573\n", "NOTE: Boundaries ignored by Gradient Descent\n", "0.03485714285714286\n", "NOTE: Boundaries ignored by Gradient Descent\n", "0.046142857142857145\n", "NOTE: Boundaries ignored by Gradient Descent\n", "0.05742857142857143\n", "NOTE: Boundaries ignored by Gradient Descent\n", "0.06871428571428571\n", "NOTE: Boundaries ignored by Gradient Descent\n", "0.08\n", "NOTE: Boundaries ignored by Gradient Descent\n" ] } ], "source": [ "sigmas = np.linspace(0.001, 0.08, 8) # Change this to a smaller range for a quicker run\n", "xs = []\n", "optims = []\n", "for sigma in sigmas:\n", " print(sigma)\n", " problem = pybop.FittingProblem(model, parameters, dataset, init_soc=init_soc)\n", " cost = pybop.SumSquaredError(problem)\n", " optim = pybop.GradientDescent(cost, sigma0=sigma, max_iterations=100)\n", " x, final_cost = optim.run()\n", " optims.append(optim)\n", " xs.append(x)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2024-04-14T18:59:24.215261Z", "iopub.status.busy": "2024-04-14T18:59:24.215124Z", "iopub.status.idle": "2024-04-14T18:59:24.217691Z", "shell.execute_reply": "2024-04-14T18:59:24.217462Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "| Sigma: 0.001 | Num Iterations: 100 | Best Cost: 0.008590687346571011 | Results: [0.58273999 0.64430015] |\n", "| Sigma: 0.012285714285714285 | Num Iterations: 100 | Best Cost: 0.0017482878947612424 | Results: [0.62229759 0.5406604 ] |\n", "| Sigma: 0.023571428571428573 | Num Iterations: 100 | Best Cost: 0.0013871420979637958 | Results: [0.63941964 0.52140605] |\n", "| Sigma: 0.03485714285714286 | Num Iterations: 100 | Best Cost: 0.001571369568098984 | Results: [0.62907481 0.53267599] |\n", "| Sigma: 0.046142857142857145 | Num Iterations: 28 | Best Cost: 0.0013533853388748253 | Results: [0.64673791 0.51409832] |\n", "| Sigma: 0.05742857142857143 | Num Iterations: 25 | Best Cost: 0.0013584031053821507 | Results: [0.64390064 0.51673076] |\n", "| Sigma: 0.06871428571428571 | Num Iterations: 74 | Best Cost: 0.0013568172573032275 | Results: [0.64444354 0.51631924] |\n", "| Sigma: 0.08 | Num Iterations: 73 | Best Cost: 0.0013551215844470215 | Results: [0.64505654 0.51551585] |\n" ] } ], "source": [ "for optim, sigma in zip(optims, sigmas):\n", " print(\n", " f\"| Sigma: {sigma} | Num Iterations: {optim._iterations} | Best Cost: {optim.pints_optimiser.f_best()} | Results: {optim.pints_optimiser.x_best()} |\"\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Perhaps a better way to view this information is to plot the optimiser convergences," ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2024-04-14T18:59:24.219013Z", "iopub.status.busy": "2024-04-14T18:59:24.218873Z", "iopub.status.idle": "2024-04-14T18:59:34.711291Z", "shell.execute_reply": "2024-04-14T18:59:34.710975Z" } }, "outputs": [ { "data": { "image/svg+xml": [ "204060801000.00850.0090.00950.010.01050.0110.01150.012Sigma: 0.001IterationCost" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "0204060800.5840.5860.5880.590.5920.5940204060800.6440.6460.6480.650.6520.6540.6560.658Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "2040608010000.0050.010.0150.020.0250.030.035Sigma: 0.012285714285714285IterationCost" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "0204060800.540.560.580.60.620204060800.520.5250.530.5350.540.5450.550.555Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "2040608010000.020.040.060.080.1Sigma: 0.023571428571428573IterationCost" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "0204060800.50.520.540.560.580.60.620.640204060800.450.460.470.480.490.50.510.520.530.54Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "204060801000.0050.010.0150.02Sigma: 0.03485714285714286IterationCost" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "0204060800.570.580.590.60.610.620.630204060800.540.560.580.60.620.640.660.680.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "5101520250.0020.0040.0060.0080.010.0120.014Sigma: 0.046142857142857145IterationCost" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "05101520250.650.660.670.680.6905101520250.520.530.540.550.56Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "5101520250.001350.00140.001450.00150.001550.00160.00165Sigma: 0.05742857142857143IterationCost" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "051015200.6340.6360.6380.640.6420.644051015200.5150.51550.5160.51650.5170.51750.5180.51850.5190.5195Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "1020304050607000.0050.010.0150.020.0250.030.0350.04Sigma: 0.06871428571428571IterationCost" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "02040600.610.620.630.640.650.660.670.680.6902040600.520.540.560.580.60.620.64Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "102030405060700.0020.0040.0060.0080.01Sigma: 0.08IterationCost" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "02040600.560.570.580.590.60.610.620.630.640.6502040600.520.530.540.550.560.57Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "for optim, sigma in zip(optims, sigmas):\n", " pybop.plot_convergence(optim, title=f\"Sigma: {sigma}\")\n", " pybop.plot_parameters(optim)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Cost Landscapes\n", "\n", "Finally, we can visualise the cost landscape and the path taken by the optimiser:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2024-04-14T18:59:34.712645Z", "iopub.status.busy": "2024-04-14T18:59:34.712544Z", "iopub.status.idle": "2024-04-14T18:59:54.695647Z", "shell.execute_reply": "2024-04-14T18:59:54.691926Z" } }, "outputs": [ { "data": { "image/svg+xml": [ "0.40.50.60.70.80.40.450.50.550.60.650.70.750.80.10.20.30.4Sigma: 0.001Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "0.40.50.60.70.80.40.450.50.550.60.650.70.750.80.10.20.30.4Sigma: 0.012285714285714285Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "0.40.50.60.70.80.40.450.50.550.60.650.70.750.80.10.20.30.4Sigma: 0.023571428571428573Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "0.40.50.60.70.80.40.450.50.550.60.650.70.750.80.10.20.30.4Sigma: 0.03485714285714286Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "0.40.50.60.70.80.40.450.50.550.60.650.70.750.80.10.20.30.4Sigma: 0.046142857142857145Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "0.40.50.60.70.80.40.450.50.550.60.650.70.750.80.10.20.30.4Sigma: 0.05742857142857143Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "0.40.50.60.70.80.40.450.50.550.60.650.70.750.80.10.20.30.4Sigma: 0.06871428571428571Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/svg+xml": [ "0.40.50.60.70.80.40.450.50.550.60.650.70.750.80.10.20.30.4Sigma: 0.08Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Plot the cost landscape with optimisation path and updated bounds\n", "bounds = np.array([[0.4, 0.8], [0.4, 0.8]])\n", "for optim, sigma in zip(optims, sigmas):\n", " pybop.plot2d(optim, bounds=bounds, steps=10, title=f\"Sigma: {sigma}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Updating the Learning Rate\n", "\n", "Let's take `sigma0 = 0.08` as the best learning rate for this problem and look at the time-series trajectories." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "execution": { "iopub.execute_input": "2024-04-14T18:59:54.698068Z", "iopub.status.busy": "2024-04-14T18:59:54.697929Z", "iopub.status.idle": "2024-04-14T19:00:24.667004Z", "shell.execute_reply": "2024-04-14T19:00:24.666609Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "NOTE: Boundaries ignored by Gradient Descent\n" ] }, { "data": { "image/svg+xml": [ "0500100015003.53.553.63.653.7ReferenceModelOptimised ComparisonTime / sVoltage / V" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "optim = pybop.Optimisation(cost, optimiser=pybop.GradientDescent, sigma0=0.08)\n", "x, final_cost = optim.run()\n", "pybop.quick_plot(problem, problem_inputs=x, title=\"Optimised Comparison\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Conclusion\n", "\n", "This notebook covers how to calibrate the learning rate for the gradient descent optimiser. This provides an introduction into hyper-parameter tuning that will be discussed in further notebooks." ] } ], "metadata": { "colab": { "provenance": [] }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.7" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "06f2374f91c8455bb63252092512f2ed": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "2.0.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "2.0.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border_bottom": null, "border_left": null, "border_right": null, "border_top": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "423bffea3a1c42b49a9ad71218e5811b": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "2.0.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "2.0.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border_bottom": null, "border_left": null, "border_right": null, "border_top": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "56ff19291e464d63b23e63b8e2ac9ea3": { "model_module": "@jupyter-widgets/controls", "model_module_version": "2.0.0", "model_name": "SliderStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "2.0.0", "_model_name": "SliderStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "2.0.0", "_view_name": "StyleView", "description_width": "", "handle_color": null } }, "646a8670cb204a31bb56bc2380898093": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "2.0.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "2.0.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border_bottom": null, "border_left": null, "border_right": null, "border_top": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null } }, "7d46516469314b88be3500e2afcafcf6": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/output", "_model_module_version": "1.0.0", "_model_name": "OutputModel", "_view_count": null, "_view_module": "@jupyter-widgets/output", "_view_module_version": "1.0.0", "_view_name": "OutputView", "layout": "IPY_MODEL_646a8670cb204a31bb56bc2380898093", "msg_id": "", "outputs": [], "tabbable": null, "tooltip": null } }, "8d003c14da5f4fa68284b28c15cee6e6": { "model_module": "@jupyter-widgets/controls", "model_module_version": "2.0.0", "model_name": "VBoxModel", "state": { "_dom_classes": [ "widget-interact" ], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "2.0.0", "_model_name": "VBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "2.0.0", "_view_name": "VBoxView", "box_style": "", "children": [ "IPY_MODEL_aef2fa7adcc14ad0854b73d5910ae3b4", "IPY_MODEL_7d46516469314b88be3500e2afcafcf6" ], "layout": "IPY_MODEL_423bffea3a1c42b49a9ad71218e5811b", "tabbable": null, "tooltip": null } }, "aef2fa7adcc14ad0854b73d5910ae3b4": { "model_module": "@jupyter-widgets/controls", "model_module_version": "2.0.0", "model_name": "FloatSliderModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "2.0.0", "_model_name": "FloatSliderModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "2.0.0", "_view_name": "FloatSliderView", "behavior": "drag-tap", "continuous_update": true, "description": "t", "description_allow_html": false, "disabled": false, "layout": "IPY_MODEL_06f2374f91c8455bb63252092512f2ed", "max": 1.1333333333333333, "min": 0, "orientation": "horizontal", "readout": true, "readout_format": ".2f", "step": 0.011333333333333332, "style": "IPY_MODEL_56ff19291e464d63b23e63b8e2ac9ea3", "tabbable": null, "tooltip": null, "value": 0 } } } } }, "nbformat": 4, "nbformat_minor": 4 }