|
| 1 | +{ |
| 2 | + "cells": [ |
| 3 | + { |
| 4 | + "cell_type": "markdown", |
| 5 | + "metadata": { |
| 6 | + "collapsed": true |
| 7 | + }, |
| 8 | + "source": [ |
| 9 | + "# Image iterators using the multiprocessing module\n", |
| 10 | + "*Jonas Teuwen*" |
| 11 | + ] |
| 12 | + }, |
| 13 | + { |
| 14 | + "cell_type": "markdown", |
| 15 | + "metadata": {}, |
| 16 | + "source": [ |
| 17 | + "In this notebook we show how to combine the multiprocessing module in Python with iterators.\n", |
| 18 | + "\n", |
| 19 | + "This can be useful in deep learning scripts when you, for instance, want to write an iterator which extracts and augments patches from your image on-the-fly to feed to your convolutional neural network (CNN).\n", |
| 20 | + "\n", |
| 21 | + "We simulate the following problem:\n", |
| 22 | + "- You have a list of images, in this case represented by filenames in `self.images`.\n", |
| 23 | + "- We continuously load an image, and put these into a queue, ready to read.\n", |
| 24 | + "- Next should give your next image, in this case we only output the filename.\n", |
| 25 | + "\n", |
| 26 | + "Each process will load one of the images, but as the processes are now separated we need some way to track which images have already been passed and which ones have not. To implement such a counter we can use shared memory in python. The multiprocessing `RawValue` implements such a `ctype` which allows multiple processes to read from the same variable. There is one problem: race conditions. It might happen that the counter value has not been updated yet and then the next process will output the same image. To handle this we use `Lock` to lock the counter when we either read or write to it to prevent such conditions.\n", |
| 27 | + "\n", |
| 28 | + "Note that your output will not necessarily be in the same order as your input as some of the processes processes might complete faster. For CNNs with stochastic gradient decent this is definitely no problem as shuffling improves the result. Check out the Stochastic Gradient Descent Tricks (Sec. 4) at http://cilvr.cs.nyu.edu/diglib/lsml/bottou-sgd-tricks-2012.pdf. \n", |
| 29 | + "\n", |
| 30 | + "For more information check https://docs.python.org/2/library/multiprocessing.html." |
| 31 | + ] |
| 32 | + }, |
| 33 | + { |
| 34 | + "cell_type": "code", |
| 35 | + "execution_count": 1, |
| 36 | + "metadata": { |
| 37 | + "collapsed": true |
| 38 | + }, |
| 39 | + "outputs": [], |
| 40 | + "source": [ |
| 41 | + "import numpy as np\n", |
| 42 | + "from multiprocessing import Process, RawValue, Lock, Queue" |
| 43 | + ] |
| 44 | + }, |
| 45 | + { |
| 46 | + "cell_type": "code", |
| 47 | + "execution_count": 2, |
| 48 | + "metadata": { |
| 49 | + "collapsed": true |
| 50 | + }, |
| 51 | + "outputs": [], |
| 52 | + "source": [ |
| 53 | + "class CounterMulti(object):\n", |
| 54 | + " def __init__(self, val=0):\n", |
| 55 | + " self.val = RawValue('i', val)\n", |
| 56 | + " self.lock = Lock()\n", |
| 57 | + "\n", |
| 58 | + " def incr(self):\n", |
| 59 | + " with self.lock:\n", |
| 60 | + " self.val.value += 1\n", |
| 61 | + "\n", |
| 62 | + " def value(self):\n", |
| 63 | + " with self.lock:\n", |
| 64 | + " return self.val.value\n", |
| 65 | + "\n", |
| 66 | + "class MultiImageIter(object):\n", |
| 67 | + " def __init__(self):\n", |
| 68 | + " self.images = ['image_{}.jpg'.format(i) for i in range(10)]\n", |
| 69 | + " self.n_images = len(self.images)\n", |
| 70 | + " \n", |
| 71 | + " # We define a cursor to track if we have gone through the whole list already\n", |
| 72 | + " self.cursor = 0\n", |
| 73 | + " self.q = Queue(maxsize=2)\n", |
| 74 | + " \n", |
| 75 | + " self.counter = CounterMulti(0)\n", |
| 76 | + " self.procs = [Process(target=self.writer, args=(self.counter,)) for i in range(4)]\n", |
| 77 | + "\n", |
| 78 | + " for p in self.procs: \n", |
| 79 | + " p.deamon = True\n", |
| 80 | + " p.start()\n", |
| 81 | + " \n", |
| 82 | + " def writer(self, counter):\n", |
| 83 | + " while True:\n", |
| 84 | + " if counter.value() < self.n_images:\n", |
| 85 | + " next_value = self.images[counter.value()]\n", |
| 86 | + " counter.incr()\n", |
| 87 | + " self.q.put(next_value)\n", |
| 88 | + " \n", |
| 89 | + " def next(self):\n", |
| 90 | + " if self.cursor < self.n_images:\n", |
| 91 | + " self.cursor += 1\n", |
| 92 | + " image = self.q.get()\n", |
| 93 | + " # Here you can do stuff with your image\n", |
| 94 | + " return image\n", |
| 95 | + " else:\n", |
| 96 | + " raise StopIteration()\n", |
| 97 | + " " |
| 98 | + ] |
| 99 | + }, |
| 100 | + { |
| 101 | + "cell_type": "code", |
| 102 | + "execution_count": 3, |
| 103 | + "metadata": {}, |
| 104 | + "outputs": [], |
| 105 | + "source": [ |
| 106 | + "s = MultiImageIter()\n", |
| 107 | + "vals = []\n", |
| 108 | + "for i in range(10):\n", |
| 109 | + " vals.append(s.next())" |
| 110 | + ] |
| 111 | + }, |
| 112 | + { |
| 113 | + "cell_type": "code", |
| 114 | + "execution_count": 4, |
| 115 | + "metadata": {}, |
| 116 | + "outputs": [ |
| 117 | + { |
| 118 | + "data": { |
| 119 | + "text/plain": [ |
| 120 | + "['image_1.jpg',\n", |
| 121 | + " 'image_2.jpg',\n", |
| 122 | + " 'image_3.jpg',\n", |
| 123 | + " 'image_0.jpg',\n", |
| 124 | + " 'image_5.jpg',\n", |
| 125 | + " 'image_6.jpg',\n", |
| 126 | + " 'image_4.jpg',\n", |
| 127 | + " 'image_7.jpg',\n", |
| 128 | + " 'image_9.jpg',\n", |
| 129 | + " 'image_8.jpg']" |
| 130 | + ] |
| 131 | + }, |
| 132 | + "execution_count": 4, |
| 133 | + "metadata": {}, |
| 134 | + "output_type": "execute_result" |
| 135 | + } |
| 136 | + ], |
| 137 | + "source": [ |
| 138 | + "vals" |
| 139 | + ] |
| 140 | + }, |
| 141 | + { |
| 142 | + "cell_type": "code", |
| 143 | + "execution_count": null, |
| 144 | + "metadata": { |
| 145 | + "collapsed": true |
| 146 | + }, |
| 147 | + "outputs": [], |
| 148 | + "source": [] |
| 149 | + } |
| 150 | + ], |
| 151 | + "metadata": { |
| 152 | + "kernelspec": { |
| 153 | + "display_name": "Python 2", |
| 154 | + "language": "python", |
| 155 | + "name": "python2" |
| 156 | + }, |
| 157 | + "language_info": { |
| 158 | + "codemirror_mode": { |
| 159 | + "name": "ipython", |
| 160 | + "version": 2 |
| 161 | + }, |
| 162 | + "file_extension": ".py", |
| 163 | + "mimetype": "text/x-python", |
| 164 | + "name": "python", |
| 165 | + "nbconvert_exporter": "python", |
| 166 | + "pygments_lexer": "ipython2", |
| 167 | + "version": "2.7.12" |
| 168 | + } |
| 169 | + }, |
| 170 | + "nbformat": 4, |
| 171 | + "nbformat_minor": 2 |
| 172 | +} |
0 commit comments