The [Hexabot](https://hexabot.ai/) NLU (Natural Language Understanding) engine is a Python-based project that provides tools for building, training, and evaluating machine learning models for natural language tasks such as intent detection and language recognition. It also includes a REST API for inference, built using FastAPI.
### Directory Structure
* **/run.py:** The CLI tool that provides commands for training, evaluating, and managing models.
* **/models:** Contains the different model definitions and logic for training, testing, and evaluation.
* **/data:** Placeholder for datasets used during training and evaluation.
* **/experiments:** Placeholder for stored models generated during training.
* **/data\_loaders:** Classes that define the way to load datasets to be used by the different models.
* **/main.py:** The FastAPI-based REST API used for inference, exposing endpoints for real-time predictions.
### Setup
**No dependencies needed besides Python 3.11.6, virtualenv, and TensorFlow.** Start developing your new model on top of this workflow by cloning this repository:
```bash
# Set up a virtualenv
pip install virtualenv
python3.11 -m venv venv
source env.sh
pip install -r requirements.txt
```
### Directory structure
*`data`: gitignore'd, place datasets here.
*`experiments`: gitignore'd, trained models written here.
*`data_loaders`: write your data loaders here.
*`models`: write your models here.
### Usage
**Check `models/mlp.py` and `data_loaders/mnist.py` for fully working examples.**
You should run `source env.sh` on each new shell session. This activates the virtualenv and creates a nice alias for `run.py`:
```bash
$ cat env.sh
source env/bin/activate
alias run='python run.py'
```
Most routines involve running a command like this:
```bash
# Usage: run [method] [save_dir] [model] [data_loader] [hparams...]
run fit myexperiment1 mlp mnist --batch_size=32 --learning_rate=0.1
```
Examples :
```bash
# Intent classification
run fit intent-classifier-en-30072024 jisf --intent_num_labels=88 --slot_num_labels=17 --language=en
run predict intent-classifier-fr-30072024 --intent_num_labels=7 --slot_num_labels=2 --language=fr
# Language classification
run fit language-classifier-26082023 tflc
run predict language-classifier-26082023
run evaluate language-classifier-26082023
```
where the `model` and `data_loader` args are the module names (i.e., the file names without the `.py`). The command above would run the Keras model's `fit` method, but it could be any custom as long as it accepts a data loader instance as argument.
**If `save_dir` already has a model**:
* Only the first two arguments are required and the data loader may be changed, but respecifying the model is not allowed-- the existing model will always be used.
* Specified hyperparameter values in the command line WILL override previously used ones (for this run only, not on disk).
#### `tfbp.Model`
Models pretty much follow the same rules as Keras models with very slight differences: the constructor's arguments should not be overriden (since the boilerplate code handles instantiation), and the `save` and `restore` methods don't need any arguments.
```python
import tensorflow as tf
import boilerplate as tfbp
@tfbp.default_export
class MyModel(tfbp.Model):
default_hparams = {
"batch_size": 32,
"hidden_size": 512,
"learning_rate": 0.01,
}
# Don't mess with the args and keyword args, `run.py` handles that.
You can also write your own training loops à la pytorch by overriding the `fit` method or writing a custom method that you can invoke via `run.py` simply by adding the `@tfbp.runnable` decorator. Examples of both are available in `models/mlp.py`.
#### `tfbp.DataLoader`
Since model methods invoked by `run.py` receive a data loader instance, you may name your data loader methods whatever you wish and call them in your model code. A good practice is to make the data loader handle anything that is specific to a particular dataset, which allows the model to be as general as possible.