{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "%reload_ext autoreload\n", "%autoreload 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# The TrainPhase API" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*This notebook was prepared by Sylvain Gugger - many thanks!*\n", "\n", "Here we show how to use a new API in the fastai library, that allows you all the flexibility you might want while training your model.\n", "\n", "All the examples will run on cifar10, so be sure to change the path to a directory that contains this dataset, with the usual hierarchy (a train and a valid folder, each of them containing ten subdirectories for each class)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from fastai.conv_learner import *\n", "PATH = Path(\"../data/cifar10/\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')\n", "stats = (np.array([ 0.4914 , 0.48216, 0.44653]), np.array([ 0.24703, 0.24349, 0.26159]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This will allow us to grab data for a given image size and batch size." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def get_data(sz,bs):\n", " tfms = tfms_from_stats(stats, sz, aug_tfms=[RandomFlip()], pad=sz//8)\n", " return ImageClassifierData.from_paths(PATH, val_name='test', tfms=tfms, bs=bs)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "size = 32\n", "batch_size = 64" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "data = get_data(size,batch_size)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's create a very simple model that we'll train: a neural net with a hidden layer." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def SimpleNet(layers):\n", " list_layers=[Flatten()]\n", " for i in range(len(layers)-1):\n", " list_layers.append(nn.Linear(layers[i], layers[i + 1]))\n", " if i < len(layers)-2: list_layers.append(nn.ReLU(inplace=True))\n", " else: list_layers.append(nn.LogSoftmax(dim=0))\n", " return nn.Sequential(*list_layers)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "learn = ConvLearner.from_model_data(SimpleNet([32*32*3, 40,10]), data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can use our learner object to give examples of traning.\n", "\n", "With the new API, you don't use a pre-implemented training schedule but you can design your own with object called TrainingPhase. A training phase is just a class that will record all the parameters you want to apply during this part of the training loop, specifically:\n", "- a number of epochs for which these settings will be valid (can be a float)\n", "- an optimizer function (SGD, RMSProp, Adam...)\n", "- a learning rate (or array of lrs) or a range of learning rates (or array of lrs) if you want to change the lr.\n", "- a learning rate decay method (that will explain how you want to change the lr)\n", "- a momentum (which will beta1 if you're using Adam), or a range of momentums if you want to change it\n", "- a momentum decay method (that will explain how you want to change the momentum, if applicable)\n", "- optionally a weight decay (or array of wds)\n", "- optionally a beta parameter (which is the RMSProp alpha or the Adam beta2, if you want another vlaue than default)\n", "\n", "By combining those blocks as you wish, you can implement pretty much any method of training you could think of." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Basic lr decay" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's begin with something basic and say you want to train with SGD and momentum, with a learning rate of 1e-2 for 1 epoch then 1e-3 for two epochs. We'll just create a list of two phases for this." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "phases = [TrainingPhase(epochs=1, opt_fn=optim.SGD, lr = 1e-2), TrainingPhase(epochs=2, opt_fn=optim.SGD, lr = 1e-3)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that we didn't set the momentum parameter because it will default to 0.9. If you don't want any momentum, you'll have to put it to 0.\n", "\n", "Now that we have created this list of phases, we just have to call fit_opt_sched." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "42579b93c5cf45ada3112d3406e991db", "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='Epoch', max=3), HTML(value='')))" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "epoch trn_loss val_loss accuracy \n", " 0 3.406514 4.553418 0.1387 \n", " 1 3.396738 4.559127 0.1383 \n", " 2 3.421531 4.541069 0.138 \n" ] }, { "data": { "text/plain": [ "[array([4.54107]), 0.138]" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "learn.fit_opt_sched(phases)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we want to see what we did, we can use learn.sched.plot_lr()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "