├── LICENSE ├── README.md ├── models.py ├── omniglot.py ├── plots ├── omniglot_train.png └── omniglot_val.png ├── reptile_sine.ipynb ├── train_omniglot.py └── utils.py /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2018, Gabriel Huang 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reptile 2 | 3 | PyTorch implementation of OpenAI's Reptile algorithm for supervised learning. 4 | 5 | Currently, it runs on Omniglot but not yet on MiniImagenet. 6 | 7 | The code has not been tested extensively. Contributions and feedback are more than welcome! 8 | 9 | ## Omniglot meta-learning dataset 10 | 11 | There is already an Omniglot dataset class in torchvision, however it seems to be more adapted for supervised-learning 12 | than few-shot learning. 13 | 14 | The `omniglot.py` provides a way to sample K-shot N-way base-tasks from Omniglot, 15 | and various utilities to split meta-training sets as well as base-tasks. 16 | 17 | ## Features 18 | 19 | - [x] Monitor training with TensorboardX. 20 | - [x] Interrupt and resume training. 21 | - [x] Train and evaluate on Omniglot. 22 | - [ ] Meta-batch size > 1. 23 | - [ ] Train and evaluate on Mini-Imagenet. 24 | - [ ] Clarify Transductive vs. Non-transductive setting. 25 | - [ ] Add training curves in README. 26 | - [ ] Reproduce all settings from OpenAI's code. 27 | - [ ] Shell script to download datasets 28 | 29 | ## How to train on Omniglot 30 | 31 | Download the two parts of the Omniglot dataset: 32 | - https://github.com/brendenlake/omniglot/raw/master/python/images_background.zip 33 | - https://github.com/brendenlake/omniglot/blob/master/python/images_evaluation.zip 34 | 35 | Create a `omniglot/` folder in the repo, unzip and merge the two files to have the following folder structure: 36 | ``` 37 | ./train_omniglot.py 38 | ... 39 | ./omniglot/Alphabet_of_the_Magi/ 40 | ./omniglot/Angelic/ 41 | ./omniglot/Anglo-Saxon_Futhorc/ 42 | ... 43 | ./omniglot/ULOG/ 44 | ``` 45 | 46 | Now start training with 47 | ``` 48 | python train_omniglot.py log --cuda 0 $HYPERPARAMETERS # with CPU 49 | python train_omniglot.py log $HYPERPARAMETERS # with CUDA 50 | ``` 51 | where $HYPERPARAMETERS depends on your task and hyperparameters. 52 | 53 | Behavior: 54 | - If no checkpoints are found in `log/`, this will create a `log/` folder to store tensorboard information and checkpoints. 55 | - If checkpoints are found in `log/`, this will resume from the last checkpoint. 56 | 57 | Training can be interrupted at any time with `^C`, and resumed from the last checkpoint by re-running the same command. 58 | 59 | ## Omniglot Hyperparameters 60 | 61 | The following set of hyperparameters work decently. 62 | They are taken from the OpenAI implementation but are adapted slightly 63 | for `meta-batch=1`. 64 | 65 | 66 | 67 | 68 | For 5-way 5-shot (red curve): 69 | 70 | ```bash 71 | python train_omniglot.py log/o55 --classes 5 --shots 5 --train-shots 10 --meta-iterations 100000 --iterations 5 --test-iterations 50 --batch 10 --meta-lr 0.2 --lr 0.001 72 | ``` 73 | 74 | For 5-way 1-shot (blue curve): 75 | 76 | ```bash 77 | python train_omniglot.py log/o51 --classes 5 --shots 1 --train-shots 12 --meta-iterations 200000 --iterations 12 --test-iterations 86 --batch 10 --meta-lr 0.33 --lr 0.00044 78 | ``` 79 | 80 | 81 | 82 | 83 | ## References 84 | 85 | - [Original Paper](https://arxiv.org/abs/1803.02999): Alex Nichol, Joshua Achiam, John Schulman. "On First-Order Meta-Learning Algorithms". 86 | - [OpenAI blog post](https://blog.openai.com/reptile/). 87 | Check it out, they have an online demo running entirely in Javascript! 88 | - Original code in Tensorflow: https://github.com/openai/supervised-reptile 89 | -------------------------------------------------------------------------------- /models.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch import nn 3 | from torch.autograd import Variable 4 | 5 | 6 | class ReptileModel(nn.Module): 7 | 8 | def __init__(self): 9 | nn.Module.__init__(self) 10 | 11 | def point_grad_to(self, target): 12 | ''' 13 | Set .grad attribute of each parameter to be proportional 14 | to the difference between self and target 15 | ''' 16 | for p, target_p in zip(self.parameters(), target.parameters()): 17 | if p.grad is None: 18 | if self.is_cuda(): 19 | p.grad = Variable(torch.zeros(p.size())).cuda() 20 | else: 21 | p.grad = Variable(torch.zeros(p.size())) 22 | p.grad.data.zero_() # not sure this is required 23 | p.grad.data.add_(p.data - target_p.data) 24 | 25 | def is_cuda(self): 26 | return next(self.parameters()).is_cuda 27 | 28 | 29 | class OmniglotModel(ReptileModel): 30 | """ 31 | A model for Omniglot classification. 32 | """ 33 | def __init__(self, num_classes): 34 | ReptileModel.__init__(self) 35 | 36 | self.num_classes = num_classes 37 | 38 | self.conv = nn.Sequential( 39 | # 28 x 28 - 1 40 | nn.Conv2d(1, 64, 3, 2, 1), 41 | nn.BatchNorm2d(64), 42 | nn.ReLU(True), 43 | 44 | # 14 x 14 - 64 45 | nn.Conv2d(64, 64, 3, 2, 1), 46 | nn.BatchNorm2d(64), 47 | nn.ReLU(True), 48 | 49 | # 7 x 7 - 64 50 | nn.Conv2d(64, 64, 3, 2, 1), 51 | nn.BatchNorm2d(64), 52 | nn.ReLU(True), 53 | 54 | # 4 x 4 - 64 55 | nn.Conv2d(64, 64, 3, 2, 1), 56 | nn.BatchNorm2d(64), 57 | nn.ReLU(True), 58 | 59 | # 2 x 2 - 64 60 | ) 61 | 62 | self.classifier = nn.Sequential( 63 | # 2 x 2 x 64 = 256 64 | nn.Linear(256, num_classes), 65 | nn.LogSoftmax(1) 66 | ) 67 | 68 | def forward(self, x): 69 | out = x.view(-1, 1, 28, 28) 70 | out = self.conv(out) 71 | out = out.view(len(out), -1) 72 | out = self.classifier(out) 73 | return out 74 | 75 | def predict(self, prob): 76 | __, argmax = prob.max(1) 77 | return argmax 78 | 79 | def clone(self): 80 | clone = OmniglotModel(self.num_classes) 81 | clone.load_state_dict(self.state_dict()) 82 | if self.is_cuda(): 83 | clone.cuda() 84 | return clone 85 | 86 | 87 | if __name__ == '__main__': 88 | model = OmniglotModel(20) 89 | x = Variable(torch.zeros(5, 28*28)) 90 | y = model(x) 91 | print 'x', x.size() 92 | print 'y', y.size() 93 | 94 | -------------------------------------------------------------------------------- /omniglot.py: -------------------------------------------------------------------------------- 1 | from torch.utils import data 2 | import os 3 | import numpy as np 4 | from PIL import Image 5 | from torchvision import transforms 6 | 7 | from utils import list_files, list_dir 8 | 9 | # Might need to manually download, extract, and merge 10 | # https://github.com/brendenlake/omniglot/blob/master/python/images_background.zip 11 | # https://github.com/brendenlake/omniglot/blob/master/python/images_evaluation.zip 12 | 13 | 14 | def read_image(path, size=None): 15 | img = Image.open(path, mode='r').convert('L') 16 | if size is not None: 17 | img = img.resize(size) 18 | return img 19 | 20 | 21 | class ImageCache(object): 22 | def __init__(self): 23 | self.cache = {} 24 | 25 | def read_image(self, path, size=None): 26 | key = (path, size) 27 | if key not in self.cache: 28 | self.cache[key] = read_image(path, size) 29 | else: 30 | pass #print 'reusing cache', key 31 | return self.cache[key] 32 | 33 | 34 | class FewShot(data.Dataset): 35 | ''' 36 | Dataset for K-shot N-way classification 37 | ''' 38 | def __init__(self, paths, meta=None, parent=None): 39 | self.paths = paths 40 | self.meta = {} if meta is None else meta 41 | self.parent = parent 42 | 43 | def __len__(self): 44 | return len(self.paths) 45 | 46 | def __getitem__(self, idx): 47 | path = self.paths[idx]['path'] 48 | if self.parent.cache is None: 49 | image = read_image(path, self.parent.size) 50 | else: 51 | image = self.parent.cache.read_image(path, self.parent.size) 52 | if self.parent.transform_image is not None: 53 | image = self.parent.transform_image(image) 54 | label = self.paths[idx] 55 | if self.parent.transform_label is not None: 56 | label = self.parent.transform_label(label) 57 | return image, label 58 | 59 | 60 | class AbstractMetaOmniglot(object): 61 | 62 | def __init__(self, characters_list, cache=None, size=(28, 28), 63 | transform_image=None, transform_label=None): 64 | self.characters_list = characters_list 65 | self.cache = cache 66 | self.size = size 67 | self.transform_image = transform_image 68 | self.transform_label = transform_label 69 | 70 | def __len__(self): 71 | return len(self.characters_list) 72 | 73 | def __getitem__(self, idx): 74 | return self.characters_list[idx] 75 | 76 | def get_random_task(self, N=5, K=1): 77 | train_task, __ = self.get_random_task_split(N, train_K=K, test_K=0) 78 | return train_task 79 | 80 | def get_random_task_split(self, N=5, train_K=1, test_K=1): 81 | train_samples = [] 82 | test_samples = [] 83 | character_indices = np.random.choice(len(self), N, replace=False) 84 | for base_idx, idx in enumerate(character_indices): 85 | character, paths = self.characters_list[idx] 86 | for i, path in enumerate(np.random.choice(paths, train_K + test_K, replace=False)): 87 | new_path = {} 88 | new_path.update(path) 89 | new_path['base_idx'] = base_idx 90 | if i < train_K: 91 | train_samples.append(new_path) 92 | else: 93 | test_samples.append(new_path) 94 | train_task = FewShot(train_samples, 95 | meta={'characters': character_indices, 'split': 'train'}, 96 | parent=self 97 | ) 98 | test_task = FewShot(test_samples, 99 | meta={'characters': character_indices, 'split': 'test'}, 100 | parent=self 101 | ) 102 | return train_task, test_task 103 | 104 | 105 | class MetaOmniglotFolder(AbstractMetaOmniglot): 106 | 107 | def __init__(self, root='omniglot', *args, **kwargs): 108 | ''' 109 | :param root: folder containing alphabets for background and evaluation set 110 | ''' 111 | self.root = root 112 | self.alphabets = list_dir(root) 113 | self._characters = {} 114 | for alphabet in self.alphabets: 115 | for character in list_dir(os.path.join(root, alphabet)): 116 | full_character = os.path.join(root, alphabet, character) 117 | character_idx = len(self._characters) 118 | self._characters[full_character] = [] 119 | for filename in list_files(full_character, '.png'): 120 | self._characters[full_character].append({ 121 | 'path': os.path.join(root, alphabet, character, filename), 122 | 'character_idx': character_idx 123 | }) 124 | characters_list = np.asarray(self._characters.items()) 125 | AbstractMetaOmniglot.__init__(self, characters_list, *args, **kwargs) 126 | 127 | 128 | class MetaOmniglotSplit(AbstractMetaOmniglot): 129 | 130 | pass 131 | 132 | 133 | def split_omniglot(meta_omniglot, validation=0.1): 134 | ''' 135 | Split meta-omniglot into two meta-datasets of tasks (disjoint characters) 136 | ''' 137 | n_val = int(validation * len(meta_omniglot)) 138 | indices = np.arange(len(meta_omniglot)) 139 | np.random.shuffle(indices) 140 | train_characters = meta_omniglot[indices[:-n_val]] 141 | test_characters = meta_omniglot[indices[-n_val:]] 142 | train = MetaOmniglotSplit(train_characters, cache=meta_omniglot.cache, size=meta_omniglot.size, 143 | transform_image=meta_omniglot.transform_image, transform_label=meta_omniglot.transform_label) 144 | test = MetaOmniglotSplit(test_characters, cache=meta_omniglot.cache, size=meta_omniglot.size, 145 | transform_image=meta_omniglot.transform_image, transform_label=meta_omniglot.transform_label) 146 | return train, test 147 | 148 | 149 | # Default transforms 150 | transform_image = transforms.Compose([ 151 | transforms.ToTensor() 152 | ]) 153 | 154 | def transform_label(paths): 155 | return paths['base_idx'] 156 | 157 | 158 | if __name__ == '__main__': 159 | meta_omniglot = MetaOmniglotFolder('omniglot', 160 | size=(64, 64), 161 | cache=ImageCache(), 162 | transform_image=transform_image) 163 | 164 | train, test = split_omniglot(meta_omniglot) 165 | print 'all', len(meta_omniglot) 166 | print 'train', len(train) 167 | print 'test', len(test) 168 | 169 | base_task = train.get_random_task() 170 | print 'base_task', len(base_task) 171 | print 'ask once', base_task[0] 172 | print 'ask twice', base_task[0] 173 | 174 | -------------------------------------------------------------------------------- /plots/omniglot_train.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielhuang/reptile-pytorch/4c85a5da189498994b414de7fe7a401125345a62/plots/omniglot_train.png -------------------------------------------------------------------------------- /plots/omniglot_val.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielhuang/reptile-pytorch/4c85a5da189498994b414de7fe7a401125345a62/plots/omniglot_val.png -------------------------------------------------------------------------------- /reptile_sine.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 108, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "data": { 10 | "image/png": "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\nsWtEDR8f/pgg5yBu6XuLseWYDdZKBSOvFGs8vQfqS1FGT+XxhMfJrc5l2cllxhFqySiUEHmTFI5p\nrmN7VgktapExnQjDfHn0S1rULTzW/zE9CrU8xkT5kltWz8ni2otfaKqRJl1FTubhfrNRCko+S/7M\nOCK7gNk79rWn1nK84jiP9X+s23ew6yxXjDWmrwAre+gzhpFBI+nn3Y8vkr+gQdVgHKGWTORkaKmH\nk5vZlF6Ep6MN/TvYe/109WmWHF/CzX1vpqdLTz0LtSzGnE8lvaye4/gGKTwWNRlfR19mRs5kTc4a\n7Qa/GxGzduwt6hY+PfIpkR6RjAsZZ2w5ZkebsUaNRhoD1mc02DgiCAJzB8ylpKGEnzN+Np5YSyV4\nGDh4ok5bwfbMYm6I9EHZwd7rnx75FBuljZwFpgV+rnbEBbpeHorMWCmFx4IGA/DPmH/iZOPEx4c/\nNoJK7TFrx96a4jUnYY7cOkAL2ow1nj0AtYVSP5PzDPAdwPDA4XyX8h1VTZcX1Mh0AaUVRNyImLWe\n5qb6v9rLtkd6WTrrc9dzV+RdchaYloyJ9CU5r5Li6kbpC831UlgsYpIUJkMa/D4rdpZ0jleYZES1\nnUMn3lAQhPGCIGQJgnBSEITndWGzPepb6llwbAED/QYyNGCoIZa0SC6LNaavAKUN9Bl70XVzEuZQ\n21LLt6nfGkGlhRM5BStVHaOs07i2d8ec9CeHP8HV1pX7Yu7TszjLZcz5WoHNGecL9U5ulsJil1Ra\n3xFxBz72Pnx8+GOzSf3tsmMXBEEJfAZMAKKAOwRBiOqq3fb4Mf1HyhvLmZswV07x6gIXxRpFUQrD\nhN0Adi4XXdfXvS+TQifxS8YvbQ8kkNEasdd1VOPI3a5Hsbdpv7joQMEBdp/bzazYWTjbOBtAoWUS\n7utMTw+Hv0OR6SvA3kMKj12AnZUdD/d7mOSSZHac3WEEpZ1HFzv2QcBJURRzRFFsBhYBem0uUtFY\nwcK0hdzQ8wbivM2vQY8pcVGs8dxhqMqTCmfaYHa/2ahFNV8e+9LAKi2btKJGNqkTSGzaB6rmq14r\niiLzDs/D18GX2yNuN5BCy0QQBEZH+rI7u4y6ujrp4DTiRik8dglTe08lxCWEjw9/3PaUMRNDF469\nB5B3wX+fPf81vfF1ytc0qBp4vP/j+lym29Aaa6xL/gMUVhA+oc3rAp0DmdF3BstOLCO3KtewIi2Y\n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\n", 11 | "text/plain": [ 12 | "" 13 | ] 14 | }, 15 | "metadata": {}, 16 | "output_type": "display_data" 17 | } 18 | ], 19 | "source": [ 20 | "import numpy as np\n", 21 | "%matplotlib inline\n", 22 | "import matplotlib.pyplot as plt\n", 23 | "import torch\n", 24 | "\n", 25 | "class SineWaveTask:\n", 26 | " def __init__(self):\n", 27 | " self.a = np.random.uniform(0.1, 5.0)\n", 28 | " self.b = np.random.uniform(0, 2*np.pi)\n", 29 | " self.train_x = None\n", 30 | " \n", 31 | " def f(self, x):\n", 32 | " return self.a * np.sin(x + self.b)\n", 33 | " \n", 34 | " def training_set(self, size=10, force_new=False):\n", 35 | " if self.train_x is None and not force_new:\n", 36 | " self.train_x = np.random.uniform(-5, 5, size)\n", 37 | " x = self.train_x\n", 38 | " elif not force_new:\n", 39 | " x = self.train_x\n", 40 | " else:\n", 41 | " x = np.random.uniform(-5, 5, size)\n", 42 | " y = self.f(x)\n", 43 | " return torch.Tensor(x), torch.Tensor(y)\n", 44 | " \n", 45 | " def test_set(self, size=50):\n", 46 | " x = np.linspace(-5, 5, size)\n", 47 | " y = self.f(x)\n", 48 | " return torch.Tensor(x), torch.Tensor(y)\n", 49 | " \n", 50 | " def plot(self, *args, **kwargs):\n", 51 | " x, y = self.test_set(size=100)\n", 52 | " return plt.plot(x.numpy(), y.numpy(), *args, **kwargs)\n", 53 | " \n", 54 | " def plot_model(self, new_model, *args, **kwargs):\n", 55 | " x, y_true = self.test_set(size=100)\n", 56 | " x = Variable(x[:, None])\n", 57 | " y_true = Variable(y_true[:, None]) \n", 58 | "\n", 59 | " y_pred = new_model(x)\n", 60 | "\n", 61 | " plt.plot(x.data.numpy().flatten(),\n", 62 | " y_pred.data.numpy().flatten(),\n", 63 | " *args, **kwargs)\n", 64 | "\n", 65 | "TRAIN_SIZE = 10000\n", 66 | "TEST_SIZE = 1000\n", 67 | "SINE_TRAIN = [SineWaveTask() for _ in range(TRAIN_SIZE)]\n", 68 | "SINE_TEST = [SineWaveTask() for _ in range(TEST_SIZE)]\n", 69 | " \n", 70 | "SineWaveTask().plot()\n", 71 | "SineWaveTask().plot()\n", 72 | "SineWaveTask().plot()\n", 73 | "plt.show()" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": 109, 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "from torch import nn\n", 83 | "from torch.autograd import Variable\n", 84 | "\n", 85 | "class SineModel(nn.Module):\n", 86 | " \n", 87 | " def __init__(self):\n", 88 | " nn.Module.__init__(self)\n", 89 | " \n", 90 | " self.main = nn.Sequential(\n", 91 | " nn.Linear(1, 40),\n", 92 | " nn.ReLU(), \n", 93 | " nn.Linear(40, 40),\n", 94 | " nn.ReLU(),\n", 95 | " nn.Linear(40, 1),\n", 96 | " )\n", 97 | " \n", 98 | " def forward(self, x):\n", 99 | " return self.main(x)" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": 141, 105 | "metadata": { 106 | "scrolled": false 107 | }, 108 | "outputs": [ 109 | { 110 | "name": "stdout", 111 | "output_type": "stream", 112 | "text": [ 113 | "Iteration 0\n", 114 | "AvgTrainML 8.09946346282959\n", 115 | "AvgTestML 3.97149658203125\n", 116 | "Iteration 1000\n", 117 | "AvgTrainML 4.275741208190485\n", 118 | "AvgTestML 4.162097644520115\n", 119 | "Iteration 2000\n", 120 | "AvgTrainML 4.067162369284418\n", 121 | "AvgTestML 3.966795645905824\n", 122 | "Iteration 3000\n", 123 | "AvgTrainML 3.914164396926957\n", 124 | "AvgTestML 3.768065965763322\n", 125 | "Iteration 4000\n", 126 | "AvgTrainML 3.6973918626264797\n", 127 | "AvgTestML 3.555037132582452\n", 128 | "Iteration 5000\n", 129 | "AvgTrainML 3.4448335885501242\n", 130 | "AvgTestML 3.3262997974719233\n", 131 | "Iteration 6000\n", 132 | "AvgTrainML 3.223463734110765\n", 133 | "AvgTestML 3.1104834275688567\n", 134 | "Iteration 7000\n", 135 | "AvgTrainML 3.0278885320190723\n", 136 | "AvgTestML 2.932813110623911\n", 137 | "Iteration 8000\n", 138 | "AvgTrainML 2.871648093244247\n", 139 | "AvgTestML 2.767195289246812\n", 140 | "Iteration 9000\n", 141 | "AvgTrainML 2.748475796616382\n", 142 | "AvgTestML 2.6258081869034195\n", 143 | "Iteration 10000\n", 144 | "AvgTrainML 2.6343498863201713\n", 145 | "AvgTestML 2.507547277265286\n", 146 | "Iteration 11000\n", 147 | "AvgTrainML 2.515370351330847\n", 148 | "AvgTestML 2.4022650124607745\n", 149 | "Iteration 12000\n", 150 | "AvgTrainML 2.4225316852415535\n", 151 | "AvgTestML 2.3065175412518473\n", 152 | "Iteration 13000\n", 153 | "AvgTrainML 2.3360310473273507\n", 154 | "AvgTestML 2.2238425549675185\n", 155 | "Iteration 14000\n", 156 | "AvgTrainML 2.254890416177754\n", 157 | "AvgTestML 2.148761134525475\n", 158 | "Iteration 15000\n", 159 | "AvgTrainML 2.181187565414477\n", 160 | "AvgTestML 2.0845814299774497\n", 161 | "Iteration 16000\n", 162 | "AvgTrainML 2.1165671503353662\n", 163 | "AvgTestML 2.022494854117507\n", 164 | "Iteration 17000\n", 165 | "AvgTrainML 2.052629884563503\n", 166 | "AvgTestML 1.9637563328990792\n", 167 | "Iteration 18000\n", 168 | "AvgTrainML 1.999231543742974\n", 169 | "AvgTestML 1.9085031812136795\n", 170 | "Iteration 19000\n", 171 | "AvgTrainML 1.949151531205856\n", 172 | "AvgTestML 1.8627918747326906\n", 173 | "Iteration 20000\n", 174 | "AvgTrainML 1.905283202963685\n", 175 | "AvgTestML 1.8186855308525705\n", 176 | "Iteration 21000\n", 177 | "AvgTrainML 1.8644140797286568\n", 178 | "AvgTestML 1.7788959518099656\n", 179 | "Iteration 22000\n", 180 | "AvgTrainML 1.824894955347479\n", 181 | "AvgTestML 1.7444134221913197\n", 182 | "Iteration 23000\n", 183 | "AvgTrainML 1.7872425281312636\n", 184 | "AvgTestML 1.7112886088658468\n", 185 | "Iteration 24000\n", 186 | "AvgTrainML 1.754621096038814\n", 187 | "AvgTestML 1.680986940941804\n", 188 | "Iteration 25000\n", 189 | "AvgTrainML 1.722364959180905\n", 190 | "AvgTestML 1.6502941590888467\n", 191 | "Iteration 26000\n", 192 | "AvgTrainML 1.6928258662461138\n", 193 | "AvgTestML 1.622442476100378\n", 194 | "Iteration 27000\n", 195 | "AvgTrainML 1.664365837051514\n", 196 | "AvgTestML 1.596648142586849\n", 197 | "Iteration 28000\n", 198 | "AvgTrainML 1.6382422264790666\n", 199 | "AvgTestML 1.5717854193412275\n", 200 | "Iteration 29000\n", 201 | "AvgTrainML 1.6123231863545633\n", 202 | "AvgTestML 1.5450866501761706\n", 203 | "Iteration 30000\n", 204 | "AvgTrainML 1.5880183347655479\n", 205 | "AvgTestML 1.5217353592075993\n", 206 | "Iteration 31000\n", 207 | "AvgTrainML 1.5665537720796907\n", 208 | "AvgTestML 1.4989666088842453\n", 209 | "Iteration 32000\n", 210 | "AvgTrainML 1.5453014466392176\n", 211 | "AvgTestML 1.4787024501601302\n", 212 | "Iteration 33000\n", 213 | "AvgTrainML 1.5238125799494526\n", 214 | "AvgTestML 1.4601477508545575\n", 215 | "Iteration 34000\n", 216 | "AvgTrainML 1.5046891563971987\n", 217 | "AvgTestML 1.442912513704855\n", 218 | "Iteration 35000\n", 219 | "AvgTrainML 1.487499570551795\n", 220 | "AvgTestML 1.4260035342264656\n", 221 | "Iteration 36000\n", 222 | "AvgTrainML 1.4703936512488494\n", 223 | "AvgTestML 1.4097775442926481\n", 224 | "Iteration 37000\n", 225 | "AvgTrainML 1.4551769582552427\n", 226 | "AvgTestML 1.3939758584016346\n", 227 | "Iteration 38000\n", 228 | "AvgTrainML 1.4392615246315916\n", 229 | "AvgTestML 1.3793357098103898\n", 230 | "Iteration 39000\n", 231 | "AvgTrainML 1.4246990968720343\n", 232 | "AvgTestML 1.36484368721826\n", 233 | "Iteration 40000\n", 234 | "AvgTrainML 1.409577451415159\n", 235 | "AvgTestML 1.3495203795850912\n", 236 | "Iteration 41000\n", 237 | "AvgTrainML 1.3966248741224485\n", 238 | "AvgTestML 1.3360226454538688\n", 239 | "Iteration 42000\n", 240 | "AvgTrainML 1.3824323878240032\n", 241 | "AvgTestML 1.3227006480120882\n", 242 | "Iteration 43000\n", 243 | "AvgTrainML 1.370676159182062\n", 244 | "AvgTestML 1.3107460594252072\n", 245 | "Iteration 44000\n", 246 | "AvgTrainML 1.3576538518520194\n", 247 | "AvgTestML 1.299080725839119\n", 248 | "Iteration 45000\n", 249 | "AvgTrainML 1.3463234795639236\n", 250 | "AvgTestML 1.2880362176160989\n", 251 | "Iteration 46000\n", 252 | "AvgTrainML 1.3363556402723098\n", 253 | "AvgTestML 1.2774769603790042\n", 254 | "Iteration 47000\n", 255 | "AvgTrainML 1.3254309813797176\n", 256 | "AvgTestML 1.267535943921412\n", 257 | "Iteration 48000\n", 258 | "AvgTrainML 1.3157809638687759\n", 259 | "AvgTestML 1.2568905930802456\n", 260 | "Iteration 49000\n", 261 | "AvgTrainML 1.3054583019897188\n", 262 | "AvgTestML 1.2477075491783876\n", 263 | "Iteration 50000\n", 264 | "AvgTrainML 1.2953340137758034\n", 265 | "AvgTestML 1.2381416758709067\n", 266 | "Iteration 51000\n", 267 | "AvgTrainML 1.287240299058473\n", 268 | "AvgTestML 1.2324038234482713\n", 269 | "Iteration 52000\n", 270 | "AvgTrainML 1.27823693830054\n", 271 | "AvgTestML 1.2234313201724254\n", 272 | "Iteration 53000\n", 273 | "AvgTrainML 1.2699619884828497\n", 274 | "AvgTestML 1.215070754264943\n", 275 | "Iteration 54000\n", 276 | "AvgTrainML 1.2609956086706515\n", 277 | "AvgTestML 1.2076349881787027\n", 278 | "Iteration 55000\n", 279 | "AvgTrainML 1.25268128190052\n", 280 | "AvgTestML 1.1996545617699992\n", 281 | "Iteration 56000\n", 282 | "AvgTrainML 1.2447443802043985\n", 283 | "AvgTestML 1.192261443670586\n", 284 | "Iteration 57000\n", 285 | "AvgTrainML 1.2366237305931838\n", 286 | "AvgTestML 1.1845510822369265\n", 287 | "Iteration 58000\n", 288 | "AvgTrainML 1.2286036201487325\n", 289 | "AvgTestML 1.1775928458142593\n", 290 | "Iteration 59000\n", 291 | "AvgTrainML 1.221877221575317\n", 292 | "AvgTestML 1.171457689726962\n", 293 | "Iteration 60000\n", 294 | "AvgTrainML 1.2151400877248852\n", 295 | "AvgTestML 1.1653833777881308\n", 296 | "Iteration 61000\n", 297 | "AvgTrainML 1.2082252498881216\n", 298 | "AvgTestML 1.1590398807305762\n", 299 | "Iteration 62000\n", 300 | "AvgTrainML 1.2018387612477521\n", 301 | "AvgTestML 1.1527474918426015\n", 302 | "Iteration 63000\n", 303 | "AvgTrainML 1.1959332033608174\n", 304 | "AvgTestML 1.1465407875446132\n", 305 | "Iteration 64000\n", 306 | "AvgTrainML 1.1898906770902398\n", 307 | "AvgTestML 1.140969198257423\n", 308 | "Iteration 65000\n", 309 | "AvgTrainML 1.1834008668635354\n", 310 | "AvgTestML 1.134500769782299\n", 311 | "Iteration 66000\n", 312 | "AvgTrainML 1.1777513144403706\n", 313 | "AvgTestML 1.1285425194413168\n", 314 | "Iteration 67000\n", 315 | "AvgTrainML 1.1710376822224091\n", 316 | "AvgTestML 1.1229148429464058\n", 317 | "Iteration 68000\n", 318 | "AvgTrainML 1.165620701461211\n", 319 | "AvgTestML 1.1166284247022829\n", 320 | "Iteration 69000\n", 321 | "AvgTrainML 1.159428336910812\n", 322 | "AvgTestML 1.1109939229197283\n", 323 | "Iteration 70000\n", 324 | "AvgTrainML 1.15296453105285\n", 325 | "AvgTestML 1.1056918554481414\n", 326 | "Iteration 71000\n", 327 | "AvgTrainML 1.1470487625023067\n", 328 | "AvgTestML 1.1000350656582463\n", 329 | "Iteration 72000\n", 330 | "AvgTrainML 1.1414506405656923\n", 331 | "AvgTestML 1.0948934696927368\n", 332 | "Iteration 73000\n", 333 | "AvgTrainML 1.136708745447693\n", 334 | "AvgTestML 1.0893504739445092\n", 335 | "Iteration 74000\n", 336 | "AvgTrainML 1.1314485706046504\n", 337 | "AvgTestML 1.0840004579830569\n", 338 | "Iteration 75000\n", 339 | "AvgTrainML 1.1264804285195964\n", 340 | "AvgTestML 1.0784695498973627\n", 341 | "Iteration 76000\n", 342 | "AvgTrainML 1.1217636550523786\n", 343 | "AvgTestML 1.0743741445032897\n", 344 | "Iteration 77000\n", 345 | "AvgTrainML 1.117040051110304\n", 346 | "AvgTestML 1.0692733132126584\n", 347 | "Iteration 78000\n", 348 | "AvgTrainML 1.1124799562509227\n", 349 | "AvgTestML 1.0646136807462416\n", 350 | "Iteration 79000\n", 351 | "AvgTrainML 1.1073753290056603\n", 352 | "AvgTestML 1.0599222182567973\n", 353 | "Iteration 80000\n", 354 | "AvgTrainML 1.1022184757623896\n", 355 | "AvgTestML 1.0551786034901047\n", 356 | "Iteration 81000\n", 357 | "AvgTrainML 1.0969906714424253\n", 358 | "AvgTestML 1.0500988569407717\n", 359 | "Iteration 82000\n", 360 | "AvgTrainML 1.092047402420893\n", 361 | "AvgTestML 1.0459269436122103\n", 362 | "Iteration 83000\n", 363 | "AvgTrainML 1.0868086380406345\n", 364 | "AvgTestML 1.0409145374950528\n", 365 | "Iteration 84000\n", 366 | "AvgTrainML 1.082437060328239\n", 367 | "AvgTestML 1.0366477695940428\n", 368 | "Iteration 85000\n", 369 | "AvgTrainML 1.078031000288427\n", 370 | "AvgTestML 1.0325273986523726\n", 371 | "Iteration 86000\n", 372 | "AvgTrainML 1.0736007824719724\n", 373 | "AvgTestML 1.0282778255681975\n", 374 | "Iteration 87000\n", 375 | "AvgTrainML 1.0692725150070874\n", 376 | "AvgTestML 1.0241360956060668\n", 377 | "Iteration 88000\n", 378 | "AvgTrainML 1.0651171631630272\n", 379 | "AvgTestML 1.0201308815812133\n", 380 | "Iteration 89000\n", 381 | "AvgTrainML 1.0608685571386738\n", 382 | "AvgTestML 1.0167469457556821\n", 383 | "Iteration 90000\n", 384 | "AvgTrainML 1.0572750215054794\n", 385 | "AvgTestML 1.0134486376664849\n", 386 | "Iteration 91000\n", 387 | "AvgTrainML 1.0529811108600757\n", 388 | "AvgTestML 1.0093619233152118\n", 389 | "Iteration 92000\n", 390 | "AvgTrainML 1.0487586379124114\n", 391 | "AvgTestML 1.0054205112951529\n", 392 | "Iteration 93000\n", 393 | "AvgTrainML 1.0444496177844536\n", 394 | "AvgTestML 1.0019860401893086\n", 395 | "Iteration 94000\n", 396 | "AvgTrainML 1.0409572717386153\n", 397 | "AvgTestML 0.9982270407251003\n", 398 | "Iteration 95000\n", 399 | "AvgTrainML 1.0376474304120489\n", 400 | "AvgTestML 0.9950220611664956\n", 401 | "Iteration 96000\n", 402 | "AvgTrainML 1.033840559505032\n", 403 | "AvgTestML 0.9916403281831094\n", 404 | "Iteration 97000\n", 405 | "AvgTrainML 1.0306124323269001\n", 406 | "AvgTestML 0.9883483971625332\n", 407 | "Iteration 98000\n", 408 | "AvgTrainML 1.0265816421405343\n", 409 | "AvgTestML 0.9852151285502534\n", 410 | "Iteration 99000\n", 411 | "AvgTrainML 1.022504568817109\n", 412 | "AvgTestML 0.9817072960036415\n" 413 | ] 414 | } 415 | ], 416 | "source": [ 417 | "def do_base_learning(model, wave, lr_inner, n_inner):\n", 418 | " new_model = SineModel()\n", 419 | " new_model.load_state_dict(model.state_dict()) # copy? looks okay\n", 420 | " inner_optimizer = torch.optim.SGD(new_model.parameters(), lr=lr_inner)\n", 421 | " # K steps of gradient descent\n", 422 | " for i in range(n_inner):\n", 423 | "\n", 424 | " x, y_true = wave.training_set()\n", 425 | " x = Variable(x[:, None])\n", 426 | " y_true = Variable(y_true[:, None])\n", 427 | "\n", 428 | " y_pred = new_model(x)\n", 429 | "\n", 430 | " loss = ((y_pred - y_true)**2).mean()\n", 431 | "\n", 432 | " inner_optimizer.zero_grad()\n", 433 | " loss.backward()\n", 434 | " inner_optimizer.step()\n", 435 | " return new_model\n", 436 | "\n", 437 | "\n", 438 | "def do_base_learning_adam(model, wave, lr_inner, n_inner, state=None):\n", 439 | " new_model = SineModel()\n", 440 | " new_model.load_state_dict(model.state_dict()) # copy? looks okay\n", 441 | " inner_optimizer = torch.optim.SGD(new_model.parameters(), lr=lr_inner)\n", 442 | " if state is not None:\n", 443 | " inner_optimizer.load_state_dict(state)\n", 444 | " # K steps of gradient descent\n", 445 | " for i in range(n_inner):\n", 446 | "\n", 447 | " x, y_true = wave.training_set()\n", 448 | " x = Variable(x[:, None])\n", 449 | " y_true = Variable(y_true[:, None])\n", 450 | "\n", 451 | " y_pred = new_model(x)\n", 452 | "\n", 453 | " loss = ((y_pred - y_true)**2).mean()\n", 454 | "\n", 455 | " inner_optimizer.zero_grad()\n", 456 | " loss.backward()\n", 457 | " inner_optimizer.step()\n", 458 | " return new_model, inner_optimizer.state_dict()\n", 459 | "\n", 460 | "\n", 461 | "def do_base_eval(new_model, wave):\n", 462 | " x, y_true = wave.test_set()\n", 463 | " x = Variable(x[:, None])\n", 464 | " y_true = Variable(y_true[:, None]) \n", 465 | " \n", 466 | " y_pred = new_model(x)\n", 467 | " \n", 468 | " loss = ((y_pred - y_true)**2).mean()\n", 469 | " return loss.data[0]\n", 470 | " \n", 471 | " \n", 472 | "def reptile_sine(model, iterations, lr_inner=0.01, \n", 473 | " lr_outer=0.001, n_inner=3):\n", 474 | " optimizer = torch.optim.Adam(model.parameters(), lr=lr_outer)\n", 475 | "\n", 476 | " train_metalosses =[]\n", 477 | " test_metalosses = []\n", 478 | " \n", 479 | " inner_optimizer_state = None\n", 480 | " \n", 481 | " # Sample an epoch by shuffling all training tasks\n", 482 | " for t in xrange(iterations):\n", 483 | " \n", 484 | " # Sample task\n", 485 | " wave = random.sample(SINE_TRAIN, 1)[0]\n", 486 | "\n", 487 | " # Take k gradient steps\n", 488 | " new_model = do_base_learning(model, wave, lr_inner, n_inner)\n", 489 | " #new_model, inner_optimizer_state = do_base_learning_adam(\n", 490 | " # model, wave, lr_inner, n_inner, inner_optimizer_state)\n", 491 | "\n", 492 | " # Eval\n", 493 | " train_metaloss = do_base_eval(new_model, wave) \n", 494 | " \n", 495 | " # Inject updates into each .grad\n", 496 | " for p, new_p in zip(model.parameters(), new_model.parameters()):\n", 497 | " if p.grad is None:\n", 498 | " p.grad = Variable(torch.zeros(p.size()))\n", 499 | " p.grad.data.add_(p.data - new_p.data)\n", 500 | "\n", 501 | " # Update meta-parameters\n", 502 | " optimizer.step()\n", 503 | " optimizer.zero_grad()\n", 504 | " \n", 505 | " \n", 506 | " ############# Validation\n", 507 | " wave = random.sample(SINE_TEST, 1)[0]\n", 508 | " new_model = do_base_learning(model, wave, lr_inner, n_inner)\n", 509 | " test_metaloss = do_base_eval(new_model, wave)\n", 510 | " \n", 511 | " ############# Log\n", 512 | " train_metalosses.append(train_metaloss)\n", 513 | " test_metalosses.append(test_metaloss)\n", 514 | " \n", 515 | " if t % 1000 == 0:\n", 516 | " print 'Iteration', t\n", 517 | " print 'AvgTrainML', np.mean(train_metalosses)\n", 518 | " print 'AvgTestML ', np.mean(test_metalosses)\n", 519 | " \n", 520 | " \n", 521 | "model = SineModel()\n", 522 | "reptile_sine(model, iterations=100000)" 523 | ] 524 | }, 525 | { 526 | "cell_type": "code", 527 | "execution_count": 132, 528 | "metadata": {}, 529 | "outputs": [ 530 | { 531 | "data": { 532 | "text/plain": [ 533 | "" 534 | ] 535 | }, 536 | "execution_count": 132, 537 | "metadata": {}, 538 | "output_type": "execute_result" 539 | }, 540 | { 541 | "data": { 542 | "image/png": "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\n", 543 | "text/plain": [ 544 | "" 545 | ] 546 | }, 547 | "metadata": {}, 548 | "output_type": "display_data" 549 | } 550 | ], 551 | "source": [ 552 | "wave = SineWaveTask()\n", 553 | "wave.plot(label='ground truth')\n", 554 | "\n", 555 | "for n_inner_ in xrange(4):\n", 556 | " new_model = do_base_learning(model, wave, \n", 557 | " lr_inner=0.01, n_inner=n_inner_)\n", 558 | " wave.plot_model(new_model, label='{} gradient steps'.format(n_inner_))\n", 559 | " \n", 560 | "plt.legend()" 561 | ] 562 | } 563 | ], 564 | "metadata": { 565 | "kernelspec": { 566 | "display_name": "Python 2", 567 | "language": "python", 568 | "name": "python2" 569 | }, 570 | "language_info": { 571 | "codemirror_mode": { 572 | "name": "ipython", 573 | "version": 2 574 | }, 575 | "file_extension": ".py", 576 | "mimetype": "text/x-python", 577 | "name": "python", 578 | "nbconvert_exporter": "python", 579 | "pygments_lexer": "ipython2", 580 | "version": "2.7.14" 581 | } 582 | }, 583 | "nbformat": 4, 584 | "nbformat_minor": 2 585 | } 586 | -------------------------------------------------------------------------------- /train_omniglot.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | import tqdm 4 | import json 5 | import re 6 | import torch 7 | from torch import nn 8 | from torch.autograd import Variable 9 | from torch.utils.data import DataLoader 10 | from torchvision import transforms 11 | import numpy as np 12 | from tensorboardX import SummaryWriter 13 | 14 | from models import OmniglotModel 15 | from omniglot import MetaOmniglotFolder, split_omniglot, ImageCache, transform_image, transform_label 16 | from utils import find_latest_file 17 | 18 | 19 | def make_infinite(dataloader): 20 | while True: 21 | for x in dataloader: 22 | yield x 23 | 24 | def Variable_(tensor, *args_, **kwargs): 25 | ''' 26 | Make variable cuda depending on the arguments 27 | ''' 28 | # Unroll list or tuple 29 | if type(tensor) in (list, tuple): 30 | return [Variable_(t, *args_, **kwargs) for t in tensor] 31 | # Unroll dictionary 32 | if isinstance(tensor, dict): 33 | return {key: Variable_(v, *args_, **kwargs) for key, v in tensor.items()} 34 | # Normal tensor 35 | variable = Variable(tensor, *args_, **kwargs) 36 | if args.cuda: 37 | variable = variable.cuda() 38 | return variable 39 | 40 | # Parsing 41 | parser = argparse.ArgumentParser('Train reptile on omniglot') 42 | 43 | # Mode 44 | parser.add_argument('logdir', help='Folder to store everything/load') 45 | 46 | # - Training params 47 | parser.add_argument('--classes', default=5, type=int, help='classes in base-task (N-way)') 48 | parser.add_argument('--shots', default=5, type=int, help='shots per class (K-shot)') 49 | parser.add_argument('--train-shots', default=10, type=int, help='train shots') 50 | parser.add_argument('--meta-iterations', default=100000, type=int, help='number of meta iterations') 51 | parser.add_argument('--start-meta-iteration', default=0, type=int, help='start iteration') 52 | parser.add_argument('--iterations', default=5, type=int, help='number of base iterations') 53 | parser.add_argument('--test-iterations', default=50, type=int, help='number of base iterations') 54 | parser.add_argument('--batch', default=10, type=int, help='minibatch size in base task') 55 | parser.add_argument('--meta-lr', default=1., type=float, help='meta learning rate') 56 | parser.add_argument('--lr', default=1e-3, type=float, help='base learning rate') 57 | 58 | # - General params 59 | parser.add_argument('--validation', default=0.1, type=float, help='Percentage of validation') 60 | parser.add_argument('--validate-every', default=100, type=int, help='Meta-evaluation every ... base-tasks') 61 | parser.add_argument('--input', default='omniglot', help='Path to omniglot dataset') 62 | parser.add_argument('--cuda', default=1, type=int, help='Use cuda') 63 | parser.add_argument('--check-every', default=10000, type=int, help='Checkpoint every') 64 | parser.add_argument('--checkpoint', default='', help='Path to checkpoint. This works only if starting fresh (i.e., no checkpoints in logdir)') 65 | 66 | # Do some processing 67 | args = parser.parse_args() 68 | print args 69 | args_filename = os.path.join(args.logdir, 'args.json') 70 | run_dir = args.logdir 71 | check_dir = os.path.join(run_dir, 'checkpoint') 72 | 73 | # By default, continue training 74 | # Check if args.json exists 75 | if os.path.exists(args_filename): 76 | print 'Attempting to resume training. (Delete {} to start over)'.format(args.logdir) 77 | # Resuming training is incompatible with other checkpoint 78 | # than the last one in logdir 79 | assert args.checkpoint == '', 'Cannot load other checkpoint when resuming training.' 80 | # Attempt to find checkpoint in logdir 81 | args.checkpoint = args.logdir 82 | else: 83 | print 'No previous training found. Starting fresh.' 84 | # Otherwise, initialize folders 85 | if not os.path.exists(run_dir): 86 | os.makedirs(run_dir) 87 | if not os.path.exists(check_dir): 88 | os.makedirs(check_dir) 89 | # Write args to args.json 90 | with open(args_filename, 'wb') as fp: 91 | json.dump(vars(args), fp, indent=4) 92 | 93 | 94 | # Create tensorboard logger 95 | logger = SummaryWriter(run_dir) 96 | 97 | # Load data 98 | # Resize is done by the MetaDataset because the result can be easily cached 99 | omniglot = MetaOmniglotFolder(args.input, size=(28, 28), cache=ImageCache(), 100 | transform_image=transform_image, 101 | transform_label=transform_label) 102 | meta_train, meta_test = split_omniglot(omniglot, args.validation) 103 | 104 | print 'Meta-Train characters', len(meta_train) 105 | print 'Meta-Test characters', len(meta_test) 106 | 107 | 108 | # Loss 109 | cross_entropy = nn.NLLLoss() 110 | def get_loss(prediction, labels): 111 | return cross_entropy(prediction, labels) 112 | 113 | 114 | def do_learning(net, optimizer, train_iter, iterations): 115 | 116 | net.train() 117 | for iteration in xrange(iterations): 118 | # Sample minibatch 119 | data, labels = Variable_(train_iter.next()) 120 | 121 | # Forward pass 122 | prediction = net(data) 123 | 124 | # Get loss 125 | loss = get_loss(prediction, labels) 126 | 127 | # Backward pass - Update fast net 128 | optimizer.zero_grad() 129 | loss.backward() 130 | optimizer.step() 131 | 132 | return loss.data[0] 133 | 134 | 135 | def do_evaluation(net, test_iter, iterations): 136 | 137 | losses = [] 138 | accuracies = [] 139 | net.eval() 140 | for iteration in xrange(iterations): 141 | # Sample minibatch 142 | data, labels = Variable_(test_iter.next()) 143 | 144 | # Forward pass 145 | prediction = net(data) 146 | 147 | # Get loss 148 | loss = get_loss(prediction, labels) 149 | 150 | # Get accuracy 151 | argmax = net.predict(prediction) 152 | accuracy = (argmax == labels).float().mean() 153 | 154 | losses.append(loss.data[0]) 155 | accuracies.append(accuracy.data[0]) 156 | 157 | return np.mean(losses), np.mean(accuracies) 158 | 159 | 160 | def get_optimizer(net, state=None): 161 | optimizer = torch.optim.Adam(net.parameters(), lr=args.lr, betas=(0, 0.999)) 162 | if state is not None: 163 | optimizer.load_state_dict(state) 164 | return optimizer 165 | 166 | 167 | def set_learning_rate(optimizer, lr): 168 | for param_group in optimizer.param_groups: 169 | param_group['lr'] = lr 170 | 171 | 172 | # Build model, optimizer, and set states 173 | meta_net = OmniglotModel(args.classes) 174 | if args.cuda: 175 | meta_net.cuda() 176 | meta_optimizer = torch.optim.SGD(meta_net.parameters(), lr=args.meta_lr) 177 | info = {} 178 | state = None 179 | 180 | 181 | # checkpoint is directory -> Find last model or '' if does not exist 182 | if os.path.isdir(args.checkpoint): 183 | latest_checkpoint = find_latest_file(check_dir) 184 | if latest_checkpoint: 185 | print 'Latest checkpoint found:', latest_checkpoint 186 | args.checkpoint = os.path.join(check_dir, latest_checkpoint) 187 | else: 188 | args.checkpoint = '' 189 | 190 | # Start fresh 191 | if args.checkpoint == '': 192 | print 'No checkpoint. Starting fresh' 193 | 194 | # Load file 195 | elif os.path.isfile(args.checkpoint): 196 | print 'Attempting to load checkpoint', args.checkpoint 197 | checkpoint = torch.load(args.checkpoint) 198 | meta_net.load_state_dict(checkpoint['meta_net']) 199 | meta_optimizer.load_state_dict(checkpoint['meta_optimizer']) 200 | state = checkpoint['optimizer'] 201 | args.start_meta_iteration = checkpoint['meta_iteration'] 202 | info = checkpoint['info'] 203 | else: 204 | raise ArgumentError('Bad checkpoint. Delete logdir folder to start over.') 205 | 206 | # Main loop 207 | for meta_iteration in tqdm.trange(args.start_meta_iteration, args.meta_iterations): 208 | 209 | # Update learning rate 210 | meta_lr = args.meta_lr * (1. - meta_iteration/float(args.meta_iterations)) 211 | set_learning_rate(meta_optimizer, meta_lr) 212 | 213 | # Clone model 214 | net = meta_net.clone() 215 | optimizer = get_optimizer(net, state) 216 | # load state of base optimizer? 217 | 218 | # Sample base task from Meta-Train 219 | train = meta_train.get_random_task(args.classes, args.train_shots or args.shots) 220 | train_iter = make_infinite(DataLoader(train, args.batch, shuffle=True)) 221 | 222 | # Update fast net 223 | loss = do_learning(net, optimizer, train_iter, args.iterations) 224 | state = optimizer.state_dict() # save optimizer state 225 | 226 | # Update slow net 227 | meta_net.point_grad_to(net) 228 | meta_optimizer.step() 229 | 230 | # Meta-Evaluation 231 | if meta_iteration % args.validate_every == 0: 232 | print '\n\nMeta-iteration', meta_iteration 233 | print '(started at {})'.format(args.start_meta_iteration) 234 | print 'Meta LR', meta_lr 235 | 236 | for (meta_dataset, mode) in [(meta_train, 'train'), (meta_test, 'val')]: 237 | 238 | train, test = meta_dataset.get_random_task_split(args.classes, train_K=args.shots, test_K=5) # is that 5 ok? 239 | train_iter = make_infinite(DataLoader(train, args.batch, shuffle=True)) 240 | test_iter = make_infinite(DataLoader(test, args.batch, shuffle=True)) 241 | 242 | # Base-train 243 | net = meta_net.clone() 244 | optimizer = get_optimizer(net, state) # do not save state of optimizer 245 | loss = do_learning(net, optimizer, train_iter, args.test_iterations) 246 | 247 | # Base-test: compute meta-loss, which is base-validation error 248 | meta_loss, meta_accuracy = do_evaluation(net, test_iter, 1) # only one iteration for eval 249 | 250 | # (Logging) 251 | loss_ = '{}_loss'.format(mode) 252 | accuracy_ = '{}_accuracy'.format(mode) 253 | meta_lr_ = 'meta_lr' 254 | info.setdefault(loss_, {}) 255 | info.setdefault(accuracy_, {}) 256 | info.setdefault(meta_lr_, {}) 257 | info[loss_][meta_iteration] = meta_loss 258 | info[accuracy_][meta_iteration] = meta_accuracy 259 | info[meta_lr_][meta_iteration] = meta_lr 260 | print '\nMeta-{}'.format(mode) 261 | print 'average metaloss', np.mean(info[loss_].values()) 262 | print 'average accuracy', np.mean(info[accuracy_].values()) 263 | logger.add_scalar(loss_, meta_loss, meta_iteration) 264 | logger.add_scalar(accuracy_, meta_accuracy, meta_iteration) 265 | logger.add_scalar(meta_lr_, meta_lr, meta_iteration) 266 | 267 | if meta_iteration % args.check_every == 0 and not (args.checkpoint and meta_iteration == args.start_meta_iteration): 268 | # Make a checkpoint 269 | checkpoint = { 270 | 'meta_net': meta_net.state_dict(), 271 | 'meta_optimizer': meta_optimizer.state_dict(), 272 | 'optimizer': state, 273 | 'meta_iteration': meta_iteration, 274 | 'info': info 275 | } 276 | checkpoint_path = os.path.join(check_dir, 'check-{}.pth'.format(meta_iteration)) 277 | torch.save(checkpoint, checkpoint_path) 278 | print 'Saved checkpoint to', checkpoint_path 279 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | 5 | # Those two functions are taken from torchvision code because they are not available on pip as of 0.2.0 6 | def list_dir(root, prefix=False): 7 | """List all directories at a given root 8 | Args: 9 | root (str): Path to directory whose folders need to be listed 10 | prefix (bool, optional): If true, prepends the path to each result, otherwise 11 | only returns the name of the directories found 12 | """ 13 | root = os.path.expanduser(root) 14 | directories = list( 15 | filter( 16 | lambda p: os.path.isdir(os.path.join(root, p)), 17 | os.listdir(root) 18 | ) 19 | ) 20 | 21 | if prefix is True: 22 | directories = [os.path.join(root, d) for d in directories] 23 | 24 | return directories 25 | 26 | 27 | def list_files(root, suffix, prefix=False): 28 | """List all files ending with a suffix at a given root 29 | Args: 30 | root (str): Path to directory whose folders need to be listed 31 | suffix (str or tuple): Suffix of the files to match, e.g. '.png' or ('.jpg', '.png'). 32 | It uses the Python "str.endswith" method and is passed directly 33 | prefix (bool, optional): If true, prepends the path to each result, otherwise 34 | only returns the name of the files found 35 | """ 36 | root = os.path.expanduser(root) 37 | files = list( 38 | filter( 39 | lambda p: os.path.isfile(os.path.join(root, p)) and p.endswith(suffix), 40 | os.listdir(root) 41 | ) 42 | ) 43 | 44 | if prefix is True: 45 | files = [os.path.join(root, d) for d in files] 46 | 47 | return files 48 | 49 | 50 | def find_latest_file(folder): 51 | files = [] 52 | for fname in os.listdir(folder): 53 | s = re.findall(r'\d+', fname) 54 | if len(s) == 1: 55 | files.append((int(s[0]), fname)) 56 | if files: 57 | return max(files)[1] 58 | else: 59 | return None 60 | 61 | pass --------------------------------------------------------------------------------