{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "XXosxKoCxNVG" }, "source": [ "# MACE in Practice II" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[](https://colab.research.google.com/github/ddmms/camml-tutorials/blob/main/notebooks/day-4/T02-MACE-Practice-II.ipynb)" ] }, { "cell_type": "markdown", "metadata": { "id": "XYamp68VxNVI" }, "source": [ "In this tutorial, you will learn how to improve MLIP models by using iterative training and active learning. We illustrate these training workflows on MACE, but they are broadly applicable to all MLIPs. We will also showcase the state-of-the-art [foundational models](https://matbench-discovery.materialsproject.org/) - the latest development in the field of MLIPs. These models are trained on massive training sets of [inorganic](https://doi.org/10.48550/arXiv.2401.00096) and [organic](https://doi.org/10.48550/arXiv.2312.15211) databases and show a great deal of `out-of-the-box` MD stability in an extensive variety of [applications](https://doi.org/10.48550/arXiv.2401.00096). We will discuss [fine-tunning](https://doi.org/10.48550/arXiv.2405.20217) which is an actively-researched technique to tweak these foundational models to new systems (out of training) and/or new levels of reference theory.\n", "\n", "## Learning Objectives for today:\n", "\n", "1. **Iterative Training: improving stability and accuracy**\n", "2. **Active learning: committee models**\n", "3. **Foundational models and fine-tuning**" ] }, { "cell_type": "markdown", "metadata": { "id": "KSAoZccQxNVJ" }, "source": [ "## 1. Iterative Training" ] }, { "cell_type": "markdown", "metadata": { "id": "8ieIGRPOxNVK" }, "source": [ "### 1.1 MD with a smaller MACE model" ] }, { "cell_type": "markdown", "metadata": { "id": "UTK__Hd8xNVK" }, "source": [ "The model we trained in our previous tutorial was already stable in MD and quite accurate with little training. This is both because MACE models are smooth and very accurate but also because the task of simulating a single molecule for a few picoseconds is not all that difficult. In general, in real research applications, achieving MD stability and accuracy is not always straightforward from the get-go. Models can be improved through iterative training and active learning which expands the training data to fix errors on the model's potential energy surface.\n", "\n", "**To illustrate these concepts in practice, let's first train a less accurate MACE by reducing a lot the model size and amount of training data: we will make a model with just 20 training configurations. Note that a larger model would probably work out of the box for this example.**" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "9FRg6XJ0D2yG" }, "outputs": [], "source": [ "from ase.io import read, write\n", "\n", "device = 'cuda'\n", "# device = 'cpu'\n", "\n", "db = read('data/solvent_xtb.xyz', ':')\n", "write('data/solvent_xtb_train_23.xyz', db[:23]) #first 20 configs plus the 3 E0s will be used for training (parameter optimization)\n", "write('data/solvent_xtb_valid_20.xyz', db[23:43]) #next 20 configs will be used for validation (independent, stop condition)" ] }, { "cell_type": "markdown", "metadata": { "id": "VleNMLWZD2yH" }, "source": [ "Let's make an input config." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "Ek3iJ6rFD2yH", "outputId": "138ddcc9-7a15-45ce-fe2a-ca272d612725" }, "outputs": [], "source": [ "%%writefile config/config-03.yml\n", "\n", "model: \"MACE\"\n", "num_channels: 32\n", "max_L: 0\n", "r_max: 4.0\n", "max_ell: 2\n", "name: \"mace02_com1\"\n", "model_dir: \"MACE_models\"\n", "log_dir: \"MACE_models\"\n", "checkpoints_dir: \"MACE_models\"\n", "results_dir: \"MACE_models\"\n", "train_file: \"data/solvent_xtb_train_23.xyz\"\n", "valid_file: \"data/solvent_xtb_valid_20.xyz\"\n", "test_file: \"data/solvent_xtb_test.xyz\"\n", "energy_key: \"energy_xtb\"\n", "forces_key: \"forces_xtb\"\n", "batch_size: 10\n", "max_num_epochs: 200\n", "swa: True\n", "seed: 123\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dev = f'device: {device}'\n", "%store dev >>\"config/config-03.yml\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "QiAR1bg1D2yH", "outputId": "6a4d18b1-2fae-4b62-b1a1-597b434a1f62" }, "outputs": [], "source": [ "import warnings\n", "warnings.filterwarnings(\"ignore\")\n", "from mace.cli.run_train import main as mace_run_train_main\n", "import sys\n", "import logging\n", "\n", "def train_mace(config_file_path):\n", " logging.getLogger().handlers.clear()\n", " sys.argv = [\"program\", \"--config\", config_file_path]\n", " mace_run_train_main()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "Cl9JEPvuD2yI", "outputId": "166104df-10db-47a6-e970-ea361ae55868" }, "outputs": [], "source": [ "train_mace(\"config/config-03.yml\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "gyJtdwaIxNVK" }, "outputs": [], "source": [ "#remove checkpoints since they may cause errors on retraining a model with the same name but a different architecture\n", "import glob\n", "import os\n", "for file in glob.glob(\"MACE_models/*.pt\"):\n", " os.remove(file)" ] }, { "cell_type": "markdown", "metadata": { "id": "30ij5FnzxNVL" }, "source": [ "Notice, we are getting substantially larger errors than before. Now, let's run some dynamics:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "l3Y0XqKCxNVL" }, "outputs": [], "source": [ "from ase.io import read, write\n", "from ase import units\n", "from ase.md.langevin import Langevin\n", "from ase.md.velocitydistribution import Stationary, ZeroRotation, MaxwellBoltzmannDistribution\n", "from aseMolec import extAtoms as ea\n", "\n", "import os\n", "import time\n", "import numpy as np\n", "import pylab as pl\n", "from IPython import display\n", "np.random.seed(701) #just making sure the MD failure is reproducible\n", "\n", "def simpleMD(init_conf, temp, calc, fname, s, T):\n", " init_conf.set_calculator(calc)\n", "\n", " #initialize the temperature\n", "\n", " MaxwellBoltzmannDistribution(init_conf, temperature_K=300) #initialize temperature at 300\n", " Stationary(init_conf)\n", " ZeroRotation(init_conf)\n", "\n", " dyn = Langevin(init_conf, 1.0*units.fs, temperature_K=temp, friction=0.1) #drive system to desired temperature\n", "\n", " %matplotlib inline\n", "\n", " time_fs = []\n", " temperature = []\n", " energies = []\n", "\n", " #remove previously stored trajectory with the same name\n", " os.system('rm -rfv '+fname)\n", "\n", " fig, ax = pl.subplots(2, 1, figsize=(6,6), sharex='all', gridspec_kw={'hspace': 0, 'wspace': 0})\n", "\n", " def write_frame():\n", " dyn.atoms.info['energy_mace'] = dyn.atoms.get_potential_energy()\n", " dyn.atoms.arrays['force_mace'] = dyn.atoms.calc.get_forces()\n", " dyn.atoms.write(fname, append=True)\n", " time_fs.append(dyn.get_time()/units.fs)\n", " temperature.append(dyn.atoms.get_temperature())\n", " energies.append(dyn.atoms.get_potential_energy()/len(dyn.atoms))\n", "\n", " ax[0].plot(np.array(time_fs), np.array(energies), color=\"b\")\n", " ax[0].set_ylabel('E (eV/atom)')\n", "\n", " # plot the temperature of the system as subplots\n", " ax[1].plot(np.array(time_fs), temperature, color=\"r\")\n", " ax[1].set_ylabel('T (K)')\n", " ax[1].set_xlabel('Time (fs)')\n", "\n", " display.clear_output(wait=True)\n", " display.display(pl.gcf())\n", " time.sleep(0.01)\n", "\n", " dyn.attach(write_frame, interval=s)\n", " t0 = time.time()\n", " dyn.run(T)\n", " t1 = time.time()\n", " print(\"MD finished in {0:.2f} minutes!\".format((t1-t0)/60))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#let us start with a single molecule\n", "init_conf = ea.sel_by_info_val(read('data/solvent_molecs.xyz',':'), 'Nmols', 1)[0].copy()\n", "\n", "#we can use MACE as a calculator in ASE!\n", "from mace.calculators import MACECalculator\n", "mace_calc = MACECalculator(model_paths=['MACE_models/mace02_com1_run-123_stagetwo.model'], device=device)\n", "\n", "simpleMD(init_conf, temp=1200, calc=mace_calc, fname='moldyn/mace02_md.xyz', s=10, T=2000)" ] }, { "cell_type": "raw", "metadata": { "id": "O9elyP0FxNVM" }, "source": [ "Depending on the random seed for the MD, you may see different things. At first sight, the energy against time doesn't look too bad. Let's look at the trajectory." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "22uVfg2UxNVM" }, "outputs": [], "source": [ "from ase.io import read, write\n", "import nglview as nv\n", "\n", "traj = read('moldyn/mace02_md.xyz', ':')\n", "view = nv.show_asetraj(traj)\n", "view._set_size(w='100%', h='500px')\n", "view" ] }, { "cell_type": "markdown", "metadata": { "id": "hBbdTtPTxNVN" }, "source": [ "If you go to the end of the trajectory, you should find that the bond angles are actually very strange - it looks unphsyical." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Task for you**\n", "\n", "