Optimize ACE hyper-parameters: minimize force time and fitting error.

Setup experiment

Load packages.

using AtomsBase, InteratomicPotentials, PotentialLearning
using Unitful, UnitfulAtomic
using LinearAlgebra, Random, DisplayAs
using DataFrames, Hyperopt

Define paths.

base_path = haskey(ENV, "BASE_PATH") ? ENV["BASE_PATH"] : "../../"
ds_path   = "$base_path/examples/data/a-HfO2/a-HfO2-300K-NVT-6000.extxyz"
res_path  = "$base_path/examples/Opt-ACE-aHfO2/results/";

Load utility functions.

include("$base_path/examples/utils/utils.jl");

Create experiment folder.

run(`mkdir -p $res_path`);

Load datasets

Load atomistic dataset: atomistic configurations (atom positions, geometry, etc.) + DFT data (energies, forces, etc.)

ds = load_data(ds_path, uparse("eV"), uparse("Å"))[1:1000]; # Load first 1K samples.

Split atomistic dataset into training and test

n_train, n_test = 50, 50 # Only 50 samples per dataset are used in this example.
conf_train, conf_test = split(ds, n_train, n_test)
(DataSet{num_configs = 50} 
	 Configuration{S, AtomsBase.FlexibleSystem{3, AtomsBase.Atom, Unitful.Quantity{Float64, 𝐋, Unitful.FreeUnits{(Å,), 𝐋, nothing}}}, Energy, Forces}
	 Configuration{S, AtomsBase.FlexibleSystem{3, AtomsBase.Atom, Unitful.Quantity{Float64, 𝐋, Unitful.FreeUnits{(Å,), 𝐋, nothing}}}, Energy, Forces}
	 ⋮
	 Configuration{S, AtomsBase.FlexibleSystem{3, AtomsBase.Atom, Unitful.Quantity{Float64, 𝐋, Unitful.FreeUnits{(Å,), 𝐋, nothing}}}, Energy, Forces}, DataSet{num_configs = 50} 
	 Configuration{S, AtomsBase.FlexibleSystem{3, AtomsBase.Atom, Unitful.Quantity{Float64, 𝐋, Unitful.FreeUnits{(Å,), 𝐋, nothing}}}, Energy, Forces}
	 Configuration{S, AtomsBase.FlexibleSystem{3, AtomsBase.Atom, Unitful.Quantity{Float64, 𝐋, Unitful.FreeUnits{(Å,), 𝐋, nothing}}}, Energy, Forces}
	 ⋮
	 Configuration{S, AtomsBase.FlexibleSystem{3, AtomsBase.Atom, Unitful.Quantity{Float64, 𝐋, Unitful.FreeUnits{(Å,), 𝐋, nothing}}}, Energy, Forces})

Optimize hyper-parameters

Define a custom loss function. Here, we minimize fitting error and force calculation time. Possible metrics are e_mae, e_rmse, e_rsq, f_mae, f_rmse, f_rsq, and time_us.

function custom_loss(
    metrics::OrderedDict
)
    e_mae     = metrics[:e_mae]
    f_mae     = metrics[:f_mae]
    time_us   = metrics[:time_us]
    e_mae_max = 0.05 # eV/atom
    f_mae_max = 0.05 # eV/Å
    w_e       = e_mae/e_mae_max
    w_f       = f_mae/f_mae_max
    w_t       = 1.0E-3
    loss = w_e * e_mae + w_f * e_mae + w_t * time_us
    return loss
end;

Define model and hyper-parameter value ranges to be optimized.

model = ACE
pars = OrderedDict( :body_order        => [2, 3, 4],
                    :polynomial_degree => [3, 4, 5],
                    :rcutoff           => LinRange(4, 6, 10),
                    :wL                => LinRange(0.5, 1.5, 10),
                    :csp               => LinRange(0.5, 1.5, 10),
                    :r0                => LinRange(0.5, 1.5, 10));

Use latin hypercube sampling to find the optimal hyper-parameters. Alternatively, use random sampling (sampler = RandomSampler()).

sampler = CLHSampler(dims=[Categorical(3), Categorical(3), Continuous(),
                           Continuous(), Continuous(), Continuous()])
iap, res = hyperlearn!(model, pars, conf_train;
                       n_samples = 10, sampler = sampler,
                       loss = custom_loss, ws = [1.0, 1.0], int = true);
E_MAE:0.196 eV/atom, F_MAE:0.303 eV/Å, Time per force per atom:65.657 µs
E_MAE:0.211 eV/atom, F_MAE:0.245 eV/Å, Time per force per atom:97.812 µs
E_MAE:0.162 eV/atom, F_MAE:0.249 eV/Å, Time per force per atom:45.498 µs
E_MAE:0.191 eV/atom, F_MAE:0.298 eV/Å, Time per force per atom:119.695 µs
E_MAE:0.204 eV/atom, F_MAE:0.295 eV/Å, Time per force per atom:120.154 µs
E_MAE:0.087 eV/atom, F_MAE:0.134 eV/Å, Time per force per atom:112.686 µs
E_MAE:0.076 eV/atom, F_MAE:0.113 eV/Å, Time per force per atom:380.355 µs
E_MAE:0.23 eV/atom, F_MAE:0.25 eV/Å, Time per force per atom:51.989 µs
E_MAE:0.078 eV/atom, F_MAE:0.09 eV/Å, Time per force per atom:848.531 µs
E_MAE:0.07 eV/atom, F_MAE:0.114 eV/Å, Time per force per atom:343.02 µs

Post-process results

Save and show results.

@save_var res_path iap.β
@save_var res_path iap.β0
@save_var res_path iap.basis
@save_dataframe res_path res
res
10×13 DataFrame
Rowe_maee_rmsee_rsqf_maef_rmsef_rsqtime_usbody_orderpolynomial_degreercutoffwLcspr0
AnyAnyAnyAnyAnyAnyAnyAnyAnyAnyAnyAnyAny
10.06994760.08870770.8758510.1137890.1465060.941304343.022.03.05.777781.50.9444440.944444
20.07648790.09735170.8504770.1134660.1459090.941782380.3552.05.04.888890.6111111.166670.611111
30.07838180.09264940.8645730.08963410.1149640.963857848.5312.03.05.777781.50.9444440.944444
40.08678530.1104840.8074170.1344110.1744440.916784112.6863.03.04.00.7222221.277781.16667
50.1623230.2059590.3307610.2485120.3218020.71681145.49813.04.05.555560.8333331.388891.27778
60.1912510.2461660.04395520.2978260.3891380.585902119.6953.04.05.555560.8333331.388891.27778
70.19620.255247-0.02788510.3030270.3941340.575265.65672.04.04.444441.277781.055561.5
80.2039270.2501950.01240860.2950620.3846470.595404120.1543.03.04.00.7222221.277781.16667
90.2107650.264187-0.1011430.2445580.313630.73101297.81232.04.04.444441.277781.055561.5
100.2295710.281583-0.2509380.2499510.3240960.7127651.98882.05.04.888890.6111111.166670.611111

Plot error vs time.

err_time = plot_err_time(res)
@save_fig res_path err_time
DisplayAs.PNG(err_time)
Example block output

This page was generated using Literate.jl.