> For the complete documentation index, see [llms.txt](https://docs.apolo.us/index/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apolo.us/index/getting-started/first-steps/training-your-first-model.md).

# Training Your First Model

## Introduction

In this tutorial, we describe the recommended way to train a simple machine learning model on the Apolo platform. As our ML engineers prefer PyTorch over other ML frameworks, we show the training and evaluation of one of the basic PyTorch examples.

We assume that you have already signed up to the platform, installed the Apolo CLI, and logged in to the platform (see [Getting Started](/index/getting-started/first-steps/getting-started.md)).

We base our example on the [Classifying Names with a Character-Level RNN](https://pytorch.org/tutorials/intermediate/char_rnn_classification_tutorial.html) tutorial.

## Initializing a new flow

To simplify working with Apolo Platform and to help establish the best practices in the ML environment, we provide a [flow template](https://github.com/neuro-inc/flow-template). This template consists of the recommended directories and files. It's designed to operate smoothly with our [base environment.](/index/getting-started/apolo-base-docker-image.md)

To use it, install the [**cookiecutter**](https://github.com/cookiecutter/cookiecutter) package and initialize **cookiecutter-neuro-project**:

```
pipx install cookiecutter
cookiecutter gh:neuro-inc/cookiecutter-neuro-project --checkout release
```

You will then need to provide some information about the new flow:

```
[1/6] flow_name (My flow): Apolo Tutorial
[2/6] flow_description ():
[3/6] flow_dir (apolo tutorial): apolo-tutorial
[4/6] flow_id (apolo_tutorial):
[5/6] code_directory (modules): rnn
[6/6] preserve Apolo Flow template hints (yes):
```

## Flow configuration structure

After you execute the command mentioned above, you get the following structure:

```
apolo-tutorial
├── .github/            <- Github workflows and a dependabot.yml file
├── .apolo/             <- apolo and apolo-flow CLI configuration files (live.yml, project.yml)
├── config/             <- configuration files for various integrations
├── data/               <- training and testing datasets (we don't keep it under source control)
├── notebooks/          <- Jupyter notebooks
├── rnn/                <- models' source code
├── results/            <- training artifacts
├── .gitignore          <- default .gitignore file for a Python ML project
├── .apolo.toml         <- autogenerated config file
├── .apoloignore        <- a file telling apolo to ignore the results/ folder
├── HELP.md             <- autogenerated template reference
├── README.md           <- autogenerated informational file
├── Dockerfile          <- description of the base image used for your project
├── apt.txt             <- list of system packages to be installed in the training environment
├── requirements.txt    <- list of Python dependencies to be installed in the training environment
├── setup.cfg           <- linter settings (Python code quality checking)
└── update_actions.py   <- instructions on update actions
```

When you run a job (for example, via `apolo-flow run jupyter`), the directories are mounted to the job as follows:

| Mount Point           | Description              | Storage URI                         |
| --------------------- | ------------------------ | ----------------------------------- |
| `/project/data/`      | Training / testing data  | `storage:apolo_tutorial/data/`      |
| `/project/rnn/`       | User's Python code       | `storage:apolo_tutorial/rnn/`       |
| `/project/notebooks/` | User's Jupyter notebooks | `storage:apolo_tutorial/notebooks/` |
| `/project/results/`   | Logs and results         | `storage:apolo_tutorial/results/`   |

## Filling the flow

Now we need to fill newly created flow with the content:

* Change working directory:

```
cd apolo-tutorial
```

* Copy the [model source](https://github.com/pytorch/tutorials/blob/master/intermediate_source/char_rnn_classification_tutorial.py) to your `rnn` folder:

```
curl https://raw.githubusercontent.com/pytorch/tutorials/master/intermediate_source/char_rnn_classification_tutorial.py -o rnn/char_rnn_classification_tutorial.py
```

* Download data from [here](https://download.pytorch.org/tutorial/data.zip), extract the ZIP’s content and put it in your `data` folder:

```
curl https://download.pytorch.org/tutorial/data.zip -o data/data.zip && unzip data/data.zip && rm data/data.zip
```

## Training and evaluating the model

When you start working with a flow on the Apolo platform, the basic flow looks as follows: you set up the remote environment, upload data and code to your storage, run training, and evaluate the results.

To set up the remote environment, run

```
apolo-flow build train
```

This command uploads the build context (your `Dockerfile`, `apt.txt`, `requirements.txt`, and the rest of the flow directory) to the platform storage, runs a [kaniko](https://github.com/GoogleContainerTools/kaniko) build job on the cluster which installs the system packages from `apt.txt` and the pip dependencies from `requirements.txt` on top of the base image, and pushes the resulting image to the platform registry as `image:apolo_tutorial:v1`.

To upload data and code to your storage, run

```
apolo-flow upload ALL
```

To run training job, you need to specify the training script in `.apolo/live.yml`, and then run `apolo-flow run train`:

* open `.apolo/live.yml` in an editor,
* find the following lines (make sure you're looking at the `train` job, not `multitrain` which has a very similar section):

```
    bash: |
        cd $[[ volumes.project.mount ]]
        python -u $[[ volumes.code.mount ]]/train.py --data $[[ volumes.data.mount ]]
```

* and replace it with the following lines (in the base image, PyTorch lives in the dedicated `torch` conda environment, so the script must be started via `conda run`):

```
    bash: |
        cd $[[ volumes.project.mount ]]
        conda run --no-capture-output -n torch python -u $[[ volumes.code.mount ]]/char_rnn_classification_tutorial.py
```

* also set a resource preset for the `train` job (uncomment the `preset` attribute in the same section and pick a preset from `apolo config show`). Without it, the job runs on the cluster's default preset, which may be too small for PyTorch:

```
    preset: cpu-large
```

Now, you can run

```
apolo-flow run train
```

and observe the output. The script prints the device it runs on and some dataset and model information first, and then the model is being trained (the exact numbers will differ between runs):

```
Using device = cpu
loaded 20074 items of data
train examples = 17063, validation examples = 3011
RNN(
  (rnn): RNN(57, 128)
  (h2o): Linear(in_features=128, out_features=18, bias=True)
  (softmax): LogSoftmax(dim=1)
)
tensor([[-2.9647, -2.9189, -3.0335, -2.8443, -2.8071, -2.8551, -2.9571, -2.8949,
         -2.8900, -2.8788, -2.9183, -2.7953, -2.8001, -3.0129, -2.8590, -2.8371,
         -2.8665, -2.9329]], grad_fn=<LogSoftmaxBackward0>)
('Vietnamese', 11)
training on data set with n = 17063
5 (19%):         average batch loss = 0.897408483127006
10 (37%):        average batch loss = 0.6953497114176003
15 (56%):        average batch loss = 0.5699283249723628
20 (74%):        average batch loss = 0.4871584071976522
25 (93%):        average batch loss = 0.42846454157089486
training took 610.8117492198944s
```

On a `cpu-large` preset, the training takes around 10 minutes.
