Files
fastai--fastai/nbs/43_tabular.learner.ipynb
2026-07-13 13:21:43 +08:00

483 lines
17 KiB
Plaintext

{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "097ceec2",
"metadata": {},
"outputs": [],
"source": [
"#| hide\n",
"#| eval: false\n",
"! [ -e /content ] && pip install -Uqq fastai # upgrade fastai on colab"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "154c3eb2",
"metadata": {},
"outputs": [],
"source": [
"#| export\n",
"from fastai.basics import *\n",
"from fastai.tabular.core import *\n",
"from fastai.tabular.model import *\n",
"from fastai.tabular.data import *"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b37ef9b2",
"metadata": {},
"outputs": [],
"source": [
"#| hide\n",
"from nbdev.showdoc import *"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6fbeb1fe",
"metadata": {},
"outputs": [],
"source": [
"#| default_exp tabular.learner"
]
},
{
"cell_type": "markdown",
"id": "c099759e",
"metadata": {},
"source": [
"# Tabular learner\n",
"\n",
"> The function to immediately get a `Learner` ready to train for tabular data"
]
},
{
"cell_type": "markdown",
"id": "07e0e3db",
"metadata": {},
"source": [
"The main function you probably want to use in this module is `tabular_learner`. It will automatically create a `TabularModel` suitable for your data and infer the right loss function. See the [tabular tutorial](http://docs.fast.ai/tutorial.tabular.html) for an example of use in context."
]
},
{
"cell_type": "markdown",
"id": "0f7b0198",
"metadata": {},
"source": [
"## Main functions"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "91c77401",
"metadata": {},
"outputs": [],
"source": [
"#| export\n",
"class TabularLearner(Learner):\n",
" \"`Learner` for tabular data\"\n",
" def predict(self, \n",
" row:pd.Series, # Features to be predicted\n",
" ):\n",
" \"Predict on a single sample\"\n",
" dl = self.dls.test_dl(row.to_frame().T)\n",
" dl.dataset.conts = dl.dataset.conts.astype(np.float32)\n",
" inp,preds,_,dec_preds = self.get_preds(dl=dl, with_input=True, with_decoded=True)\n",
" b = (*tuplify(inp),*tuplify(dec_preds))\n",
" full_dec = self.dls.decode(b)\n",
" return full_dec,dec_preds[0],preds[0]"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3992f2ea",
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"---\n",
"\n",
"[source](https://github.com/fastai/fastai/blob/main/fastai/tabular/learner.py#L16){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
"\n",
"### TabularLearner\n",
"\n",
"> TabularLearner (dls:fastai.data.core.DataLoaders, model:Callable,\n",
"> loss_func:Optional[Callable]=None, opt_func:fastai.optimi\n",
"> zer.Optimizer|fastai.optimizer.OptimWrapper=<function\n",
"> Adam>, lr:float|slice=0.001, splitter:Callable=<function\n",
"> trainable_params>, cbs:fastai.callback.core.Callback|coll\n",
"> ections.abc.MutableSequence|None=None, metrics:Union[Call\n",
"> able,collections.abc.MutableSequence,NoneType]=None,\n",
"> path:str|pathlib.Path|None=None,\n",
"> model_dir:str|pathlib.Path='models',\n",
"> wd:float|int|None=None, wd_bn_bias:bool=False,\n",
"> train_bn:bool=True, moms:tuple=(0.95, 0.85, 0.95),\n",
"> default_cbs:bool=True)\n",
"\n",
"*`Learner` for tabular data*\n",
"\n",
"| | **Type** | **Default** | **Details** |\n",
"| -- | -------- | ----------- | ----------- |\n",
"| dls | DataLoaders | | `DataLoaders` containing fastai or PyTorch `DataLoader`s |\n",
"| model | Callable | | PyTorch model for training or inference |\n",
"| loss_func | Optional | None | Loss function. Defaults to `dls` loss |\n",
"| opt_func | fastai.optimizer.Optimizer \\| fastai.optimizer.OptimWrapper | Adam | Optimization function for training |\n",
"| lr | float \\| slice | 0.001 | Default learning rate |\n",
"| splitter | Callable | trainable_params | Split model into parameter groups. Defaults to one parameter group |\n",
"| cbs | fastai.callback.core.Callback \\| collections.abc.MutableSequence \\| None | None | `Callback`s to add to `Learner` |\n",
"| metrics | Union | None | `Metric`s to calculate on validation set |\n",
"| path | str \\| pathlib.Path \\| None | None | Parent directory to save, load, and export models. Defaults to `dls` `path` |\n",
"| model_dir | str \\| pathlib.Path | models | Subdirectory to save and load models |\n",
"| wd | float \\| int \\| None | None | Default weight decay |\n",
"| wd_bn_bias | bool | False | Apply weight decay to normalization and bias parameters |\n",
"| train_bn | bool | True | Train frozen normalization layers |\n",
"| moms | tuple | (0.95, 0.85, 0.95) | Default momentum for schedulers |\n",
"| default_cbs | bool | True | Include default `Callback`s |"
],
"text/plain": [
"---\n",
"\n",
"[source](https://github.com/fastai/fastai/blob/main/fastai/tabular/learner.py#L16){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
"\n",
"### TabularLearner\n",
"\n",
"> TabularLearner (dls:fastai.data.core.DataLoaders, model:Callable,\n",
"> loss_func:Optional[Callable]=None, opt_func:fastai.optimi\n",
"> zer.Optimizer|fastai.optimizer.OptimWrapper=<function\n",
"> Adam>, lr:float|slice=0.001, splitter:Callable=<function\n",
"> trainable_params>, cbs:fastai.callback.core.Callback|coll\n",
"> ections.abc.MutableSequence|None=None, metrics:Union[Call\n",
"> able,collections.abc.MutableSequence,NoneType]=None,\n",
"> path:str|pathlib.Path|None=None,\n",
"> model_dir:str|pathlib.Path='models',\n",
"> wd:float|int|None=None, wd_bn_bias:bool=False,\n",
"> train_bn:bool=True, moms:tuple=(0.95, 0.85, 0.95),\n",
"> default_cbs:bool=True)\n",
"\n",
"*`Learner` for tabular data*\n",
"\n",
"| | **Type** | **Default** | **Details** |\n",
"| -- | -------- | ----------- | ----------- |\n",
"| dls | DataLoaders | | `DataLoaders` containing fastai or PyTorch `DataLoader`s |\n",
"| model | Callable | | PyTorch model for training or inference |\n",
"| loss_func | Optional | None | Loss function. Defaults to `dls` loss |\n",
"| opt_func | fastai.optimizer.Optimizer \\| fastai.optimizer.OptimWrapper | Adam | Optimization function for training |\n",
"| lr | float \\| slice | 0.001 | Default learning rate |\n",
"| splitter | Callable | trainable_params | Split model into parameter groups. Defaults to one parameter group |\n",
"| cbs | fastai.callback.core.Callback \\| collections.abc.MutableSequence \\| None | None | `Callback`s to add to `Learner` |\n",
"| metrics | Union | None | `Metric`s to calculate on validation set |\n",
"| path | str \\| pathlib.Path \\| None | None | Parent directory to save, load, and export models. Defaults to `dls` `path` |\n",
"| model_dir | str \\| pathlib.Path | models | Subdirectory to save and load models |\n",
"| wd | float \\| int \\| None | None | Default weight decay |\n",
"| wd_bn_bias | bool | False | Apply weight decay to normalization and bias parameters |\n",
"| train_bn | bool | True | Train frozen normalization layers |\n",
"| moms | tuple | (0.95, 0.85, 0.95) | Default momentum for schedulers |\n",
"| default_cbs | bool | True | Include default `Callback`s |"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"show_doc(TabularLearner, title_level=3)"
]
},
{
"cell_type": "markdown",
"id": "453f997c",
"metadata": {},
"source": [
"It works exactly as a normal `Learner`, the only difference is that it implements a `predict` method specific to work on a row of data."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "087411eb",
"metadata": {},
"outputs": [],
"source": [
"#| export\n",
"@delegates(Learner.__init__)\n",
"def tabular_learner(\n",
" dls:TabularDataLoaders,\n",
" layers:list=None, # Size of the layers generated by `LinBnDrop`\n",
" emb_szs:list=None, # Tuples of `n_unique, embedding_size` for all categorical features\n",
" config:dict=None, # Config params for TabularModel from `tabular_config`\n",
" n_out:int=None, # Final output size of the model\n",
" y_range:Tuple=None, # Low and high for the final sigmoid function\n",
" **kwargs\n",
"):\n",
" \"Get a `Learner` using `dls`, with `metrics`, including a `TabularModel` created using the remaining params.\"\n",
" if config is None: config = tabular_config()\n",
" if layers is None: layers = [200,100]\n",
" to = dls.train_ds\n",
" emb_szs = get_emb_sz(dls.train_ds, {} if emb_szs is None else emb_szs)\n",
" if n_out is None: n_out = get_c(dls)\n",
" assert n_out, \"`n_out` is not defined, and could not be inferred from data, set `dls.c` or pass `n_out`\"\n",
" if y_range is None and 'y_range' in config: y_range = config.pop('y_range')\n",
" model = TabularModel(emb_szs, len(dls.cont_names), n_out, layers, y_range=y_range, **config)\n",
" return TabularLearner(dls, model, **kwargs)"
]
},
{
"cell_type": "markdown",
"id": "fa6a78ec",
"metadata": {},
"source": [
"If your data was built with fastai, you probably won't need to pass anything to `emb_szs` unless you want to change the default of the library (produced by `get_emb_sz`), same for `n_out` which should be automatically inferred. `layers` will default to `[200,100]` and is passed to `TabularModel` along with the `config`.\n",
"\n",
"Use `tabular_config` to create a `config` and customize the model used. There is just easy access to `y_range` because this argument is often used.\n",
"\n",
"All the other arguments are passed to `Learner`."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9d695c38",
"metadata": {},
"outputs": [],
"source": [
"path = untar_data(URLs.ADULT_SAMPLE)\n",
"df = pd.read_csv(path/'adult.csv')\n",
"cat_names = ['workclass', 'education', 'marital-status', 'occupation', 'relationship', 'race']\n",
"cont_names = ['age', 'fnlwgt', 'education-num']\n",
"procs = [Categorify, FillMissing, Normalize]\n",
"dls = TabularDataLoaders.from_df(df, path, procs=procs, cat_names=cat_names, cont_names=cont_names, \n",
" y_names=\"salary\", valid_idx=list(range(800,1000)), bs=64)\n",
"learn = tabular_learner(dls)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1c2badc9",
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"---\n",
"\n",
"[source](https://github.com/fastai/fastai/blob/main/fastai/tabular/learner.py#L18){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
"\n",
"### TabularLearner.predict\n",
"\n",
"> TabularLearner.predict (row:pandas.core.series.Series)\n",
"\n",
"*Predict on a single sample*\n",
"\n",
"| | **Type** | **Details** |\n",
"| -- | -------- | ----------- |\n",
"| row | Series | Features to be predicted |"
],
"text/plain": [
"---\n",
"\n",
"[source](https://github.com/fastai/fastai/blob/main/fastai/tabular/learner.py#L18){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
"\n",
"### TabularLearner.predict\n",
"\n",
"> TabularLearner.predict (row:pandas.core.series.Series)\n",
"\n",
"*Predict on a single sample*\n",
"\n",
"| | **Type** | **Details** |\n",
"| -- | -------- | ----------- |\n",
"| row | Series | Features to be predicted |"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"show_doc(TabularLearner.predict)"
]
},
{
"cell_type": "markdown",
"id": "d659d466",
"metadata": {},
"source": [
"We can pass in an individual row of data into our `TabularLearner`'s `predict` method. It's output is slightly different from the other `predict` methods, as this one will always return the input as well:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e1065434",
"metadata": {},
"outputs": [],
"source": [
"row, clas, probs = learn.predict(df.iloc[0])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e7bbe320",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>workclass</th>\n",
" <th>education</th>\n",
" <th>marital-status</th>\n",
" <th>occupation</th>\n",
" <th>relationship</th>\n",
" <th>race</th>\n",
" <th>education-num_na</th>\n",
" <th>age</th>\n",
" <th>fnlwgt</th>\n",
" <th>education-num</th>\n",
" <th>salary</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>Private</td>\n",
" <td>Assoc-acdm</td>\n",
" <td>Married-civ-spouse</td>\n",
" <td>#na#</td>\n",
" <td>Wife</td>\n",
" <td>White</td>\n",
" <td>False</td>\n",
" <td>49.0</td>\n",
" <td>101320.001685</td>\n",
" <td>12.0</td>\n",
" <td>&lt;50k</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"row.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a64dc76d",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(tensor(0), tensor([0.5264, 0.4736]))"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"clas, probs"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2a67008e",
"metadata": {},
"outputs": [],
"source": [
"#| hide\n",
"#test y_range is passed\n",
"learn = tabular_learner(dls, y_range=(0,32))\n",
"assert isinstance(learn.model.layers[-1], SigmoidRange)\n",
"test_eq(learn.model.layers[-1].low, 0)\n",
"test_eq(learn.model.layers[-1].high, 32)\n",
"\n",
"learn = tabular_learner(dls, config = tabular_config(y_range=(0,32)))\n",
"assert isinstance(learn.model.layers[-1], SigmoidRange)\n",
"test_eq(learn.model.layers[-1].low, 0)\n",
"test_eq(learn.model.layers[-1].high, 32)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e853caa0",
"metadata": {},
"outputs": [],
"source": [
"#| export\n",
"@dispatch\n",
"def show_results(x:Tabular, y:Tabular, samples, outs, ctxs=None, max_n=10, **kwargs):\n",
" df = x.all_cols[:max_n]\n",
" for n in x.y_names: df[n+'_pred'] = y[n][:max_n].values\n",
" display_df(df)"
]
},
{
"cell_type": "markdown",
"id": "0133b6cc",
"metadata": {},
"source": [
"## Export -"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "82fb9285",
"metadata": {},
"outputs": [],
"source": [
"#| hide\n",
"from nbdev import nbdev_export\n",
"nbdev_export()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6be5f39b",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"jupytext": {
"split_at_heading": true
},
"kernelspec": {
"display_name": "python3",
"language": "python",
"name": "python3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}