{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Recreating Ling _IMMI_ (2017)\n", "In this notebook, we will recreate some key results from [Ling et al. _IMMI_ (2017)](https://link.springer.com/article/10.1007/s40192-017-0098-z), which studied the application of random-forest-based uncertainties to materials design. We will show that the errors produced from the Random Forest implemented in lolo (the code used by Ling et al.) are well-calibrated and that the uncertainties can be used with Sequential Learning to quickly find optimal materials within a search space.\n", "\n", "Note: This notebook will require you to install [lolopy](https://pypi.org/project/lolopy/) and establish an account with Citrination to get an an API key (see [Quickstart](https://citrineinformatics.github.io/api-documentation/quickstart/index.html)), and set it as an environment variable named CITRINE_KEY. Also, the uncertainity calculations do not currently function on Windows. \n", "\n", "Last used with matminer version 0.4.5." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "from matplotlib import pyplot as plt\n", "from matminer.data_retrieval.retrieve_Citrine import CitrineDataRetrieval\n", "from matminer.featurizers.conversions import StrToComposition\n", "from matminer.featurizers.base import MultipleFeaturizer\n", "from matminer.featurizers import composition as cf\n", "from lolopy.learners import RandomForestRegressor\n", "from sklearn.model_selection import KFold\n", "from pymatgen import Composition\n", "from scipy.stats import norm\n", "import pandas as pd\n", "import numpy as np\n", "import os" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Set the random seed" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "np.random.seed(8)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Get the Datasets\n", "The Ling Paper used 4 different datasets to test the uncertainty estimates" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "cdr = CitrineDataRetrieval()" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 165/165 [00:03<00:00, 46.07it/s]\n" ] } ], "source": [ "data = cdr.get_dataframe(criteria={'data_set_id': 150888}, print_properties_options=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Convert the composition and class variable from strings" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "f898fcd82d484dd78c460573a779f103", "version_major": 2, "version_minor": 0 }, "text/html": [ "
Failed to display Jupyter Widget of type HBox
.
\n", " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n", " that the widgets JavaScript is still loading. If this message persists, it\n", " likely means that the widgets JavaScript library is either not installed or\n", " not enabled. See the Jupyter\n", " Widgets Documentation for setup instructions.\n", "
\n", "\n", " If you're reading this message in another frontend (for example, a static\n", " rendering on GitHub or NBViewer),\n", " it may mean that your frontend doesn't currently support widgets.\n", "
\n" ], "text/plain": [ "HBox(children=(IntProgress(value=0, description='StrToComposition', max=165), HTML(value='')))" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "data = StrToComposition(target_col_id='composition').featurize_dataframe(data, \"chemicalFormula\")" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "data['ZT'] = pd.to_numeric(data['ZT'], errors='coerce')" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "data.reset_index(drop=True, inplace=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Compute Features\n", "Every dataset except the steel fatigue dataset uses the composition-based features of [Ward et al.](https://www.nature.com/articles/npjcompumats201628)." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "f = MultipleFeaturizer([cf.Stoichiometry(), cf.ElementProperty.from_preset(\"magpie\"),\n", " cf.ValenceOrbital(props=['avg']), cf.IonProperty(fast=True)])" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "e7cbc101a00949beb07d4537a8fd8c20", "version_major": 2, "version_minor": 0 }, "text/html": [ "Failed to display Jupyter Widget of type HBox
.
\n", " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n", " that the widgets JavaScript is still loading. If this message persists, it\n", " likely means that the widgets JavaScript library is either not installed or\n", " not enabled. See the Jupyter\n", " Widgets Documentation for setup instructions.\n", "
\n", "\n", " If you're reading this message in another frontend (for example, a static\n", " rendering on GitHub or NBViewer),\n", " it may mean that your frontend doesn't currently support widgets.\n", "
\n" ], "text/plain": [ "HBox(children=(IntProgress(value=0, description='MultipleFeaturizer', max=165), HTML(value='')))" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "X = np.array(f.featurize_many(data['composition']))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Get the Residuals and RF Uncertainty\n", "As described in the Ling paper, ideally-calibrated uncertainty estimaes should have a particular relationship with the errors of a machine learning model. Specifically, the distribution of $r(x)/\\sigma(x)$ where $r(x)$ is the residual of the prediction and $\\sigma(x)$ is the uncertainty of the prediction for x should have a Gaussian distribution with zero mean and unit standard deviation." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "model = RandomForestRegressor()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Get the errors from 8-fold cross-validation" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "y = data['ZT'].values" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "y_resid = []\n", "y_uncer = []\n", "for train_id, test_id in KFold(8, shuffle=True).split(X):\n", " model.fit(X[train_id], y[train_id])\n", " yf_pred, yf_std = model.predict(X[test_id], return_std=True)\n", " y_resid.extend(yf_pred - y[test_id])\n", " y_uncer.extend(yf_std)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Plot the normalized residuals ($r(x)/\\sigma(x)$) against the normal distribution" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "image/png": "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p06dkJSzBgkai2wKxluBjyS9CYjwrDGbJk1+Csw0s1kAkp4GTiC0tVrlBKBP/PwccJ8k2Xr0s/Lqq69y2rUPANByr6Ppf8JWjBw5Egh9EE6bNo033ngDgOfL9mTNwpmsnjeVbvt35KqrrmLcuHG88847APTu3Zs333yT999/H4Brr72W1157jarfe/Xp04fhw4czadIkAG6++WaGDBnC1KlTAXih+BesmvovypbM47f7daRfv3489NBDTJ8+nSXvzaXNId1ZMXE4G+9xL2aVbHpUT74aO4iyL/8HFeVsdkofWnY6lPKvFtKktCWbdb2Om3ddyUknncRee+3Fzje+QYvtf1rbXVVw61YNNaF88+MZ894M7p45kjsO3oj+/ftjZtxxxx1MnjyZYcOG0aJFC2666SYmTZrEqFGjKCoqonfv3owfP54xY8Ygid69ezN27FjGjRsHwJ///Gdef/11JkyYAMB1113Hyy+/vPYnJDfeeCNDhw5lypQpANxyyy0MHjyYadOmAdC3b18GDBjA9OnTAbjjjjt48MEH+fTTT4HQB9/dd9/NnDlzkMS9995L3759mT9/Pk2bNuWuu+7illtuYeHChZSWltKvX7987N46NXXqVPr37w/Asccey+abb86jjz4KwIknnkirVq0YOHAgACeffDLNmjXjySfD05fTTjsNSTz99NMAnHXWWaxevZrnnnsOgG7durF8+XKGDw9vLZ933nksXryYl156CYDu3bszb948Xn31VSA0WDFjxoy1+bpnz558/PHHjBo1CoArr7yS8ePH88Ybb1BRUcF1113HqFGj6NnvH1SWrabN4b/n4q0X8dprr1FUVMTgwYNZvnw5BxxwAH/84x/Z+cZwfuCtt3K5S3OqSUlzmrXfEYAnb3+SiRMnUlZWxkcffUSvXr2oqKige/fubLXVVtx22220aNGC888/n0022YRHHnmEpk2bcuaZZ1JSUsITTzwBhO+tsrKSIUNC+y6//e1vWbVq1dpOA84991yWLl3KCy+EHnYuvPBCPv/8c15+OeTzHj16MHv2bEaMGAHAxRdfzPTp09d+bz179mTy5Mm8Fff7FVdcwfvvv8/bb78NQK9evRgzZgzvvvsuANdcc806+fr666+nXbt2dbL/Mv7AX+FyfSugnPCcUcD7ZlZtx3uSTgaONLML4vBvgH3N7JKEaabG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\n", "text/plain": [ "