We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Slow loading of image features during training
For VQA tasks, it takes massive amount of time to load the image features for training. The overhead is so much that for the same model it increases the training time by a factor of 50.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `parlai/core/image_featurizers.py`
Content:
```
1 # All rights reserved.
2 # This source code is licensed under the BSD-style license found in the
3 # LICENSE file in the root directory of this source tree. An additional grant
4 # of patent rights can be found in the PATENTS file in the same directory.
5 
6 import parlai.core.build_data as build_data
7 
8 import os
9 import copy
10 import numpy as np
11 from PIL import Image
12 
13 _greyscale = '  .,:;crsA23hHG#98&@'
14 
15 class ImageLoader():
16     """Extract image feature using pretrained CNN network.
17     """
18     def __init__(self, opt):
19         self.opt = copy.deepcopy(opt)
20         self.netCNN = None
21 
22     def init_cnn(self):
23         """Lazy initialization of preprocessor model in case we don't need any image preprocessing."""
24         try:
25             import torch
26         except ModuleNotFoundError:
27             raise ModuleNotFoundError('Need to install Pytorch: go to pytorch.org')
28         from torch.autograd import Variable
29         import torchvision
30         import torchvision.transforms as transforms
31         import torch.nn as nn
32 
33         opt = self.opt
34         self.image_size = opt['image_size']
35         self.crop_size = opt['image_cropsize']
36         self.datatype = opt['datatype']
37         self.image_mode = opt['image_mode']
38 
39         opt['cuda'] = not opt['no_cuda'] and torch.cuda.is_available()
40         self.use_cuda = opt['cuda']
41 
42         if self.use_cuda:
43             print('[ Using CUDA ]')
44             torch.cuda.set_device(opt['gpu'])
45 
46         cnn_type, layer_num = self.image_mode_switcher()
47 
48         # initialize the pretrained CNN using pytorch.
49         CNN = getattr(torchvision.models, cnn_type)
50 
51         # cut off the additional layer.
52         self.netCNN = nn.Sequential(*list(CNN(pretrained=True).children())[:layer_num])
53 
54         # initialize the transform function using torch vision.
55         self.transform = transforms.Compose([
56                             transforms.Scale(self.image_size),
57                             transforms.CenterCrop(self.crop_size),
58                             transforms.ToTensor(),
59                             transforms.Normalize(mean=[0.485, 0.456, 0.406],
60                                     std=[0.229, 0.224, 0.225])
61                             ])
62 
63         # container for single image
64         self.xs = torch.FloatTensor(1, 3, self.crop_size, self.crop_size).fill_(0)
65 
66         if self.use_cuda:
67             self.cuda()
68             self.xs = self.xs.cuda()
69 
70         # make self.xs variable.
71         self.xs = Variable(self.xs)
72 
73     def cuda(self):
74         self.netCNN.cuda()
75 
76     def save(self, feature, path):
77         np.save(path, feature)
78 
79     def image_mode_switcher(self):
80         switcher = {
81             'resnet152': ['resnet152', -1],
82             'resnet101': ['resnet101', -1],
83             'resnet50': ['resnet50', -1],
84             'resnet34': ['resnet34', -1],
85             'resnet18': ['resnet18', -1],
86             'resnet152_spatial': ['resnet152', -2],
87             'resnet101_spatial': ['resnet101', -2],
88             'resnet50_spatial': ['resnet50', -2],
89             'resnet34_spatial': ['resnet34', -2],
90             'resnet18_spatial': ['resnet18', -2],
91         }
92 
93         if self.image_mode not in switcher:
94             raise NotImplementedError('image preprocessing mode' +
95                                       '{} not supported yet'.format(self.image_mode))
96 
97         return switcher.get(self.image_mode)
98 
99     def extract(self, image, path):
100         # check whether initlize CNN network.
101         if not self.netCNN:
102             self.init_cnn()
103 
104         self.xs.data.copy_(self.transform(image))
105         # extract the image feature
106         feature = self.netCNN(self.xs)
107         feature = feature.cpu().data.numpy()
108         # save the feature
109         self.save(feature, path)
110         return feature
111 
112     def img_to_ascii(self, path):
113         im = Image.open(path)
114         im.thumbnail((60, 40), Image.BICUBIC)
115         im = im.convert('L')
116         asc = []
117         for y in range(0, im.size[1]):
118             for x in range(0, im.size[0]):
119                 lum = 255 - im.getpixel((x, y))
120                 asc.append(_greyscale[lum * len(_greyscale) // 256])
121             asc.append('\n')
122         return ''.join(asc)
123 
124     def load(self, path):
125         opt = self.opt
126         mode = opt.get('image_mode', 'raw')
127         if mode is None or mode == 'none':
128             # don't need to load images
129             return None
130         elif mode == 'raw':
131             # raw just returns RGB values
132             return Image.open(path).convert('RGB')
133         elif mode == 'ascii':
134             # convert images to ascii ¯\_(ツ)_/¯
135             return self.img_to_ascii(path)
136         else:
137             # otherwise, looks for preprocessed version under 'mode' directory
138             prepath, imagefn = os.path.split(path)
139 
140             dpath = os.path.join(prepath, mode)
141 
142             if not os.path.exists(dpath):
143                 build_data.make_dir(dpath)
144 
145             imagefn = imagefn.split('.')[0]
146             imagefn = imagefn + '.npy'
147             new_path = os.path.join(prepath, mode, imagefn)
148 
149             if not os.path.isfile(new_path):
150                 return self.extract(Image.open(path).convert('RGB'), new_path)
151             else:
152                 return np.load(new_path)
153
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/parlai/core/image_featurizers.py b/parlai/core/image_featurizers.py
--- a/parlai/core/image_featurizers.py
+++ b/parlai/core/image_featurizers.py
@@ -9,6 +9,7 @@
 import copy
 import numpy as np
 from PIL import Image
+from functools import lru_cache
 
 _greyscale = '  .,:;crsA23hHG#98&@'
 
@@ -121,6 +122,7 @@
             asc.append('\n')
         return ''.join(asc)
 
+    @lru_cache(maxsize=None)
     def load(self, path):
         opt = self.opt
         mode = opt.get('image_mode', 'raw')

{"golden_diff": "diff --git a/parlai/core/image_featurizers.py b/parlai/core/image_featurizers.py\n--- a/parlai/core/image_featurizers.py\n+++ b/parlai/core/image_featurizers.py\n@@ -9,6 +9,7 @@\n import copy\n import numpy as np\n from PIL import Image\n+from functools import lru_cache\n \n _greyscale = '  .,:;crsA23hHG#98&@'\n \n@@ -121,6 +122,7 @@\n             asc.append('\\n')\n         return ''.join(asc)\n \n+    @lru_cache(maxsize=None)\n     def load(self, path):\n         opt = self.opt\n         mode = opt.get('image_mode', 'raw')\n", "issue": "Slow loading of image features during training\nFor VQA tasks, it takes massive amount of time to load the image features for training. The overhead is so much that for the same model it increases the training time by a factor of 50.\n", "before_files": [{"content": "# All rights reserved.\n# This source code is licensed under the BSD-style license found in the\n# LICENSE file in the root directory of this source tree. An additional grant\n# of patent rights can be found in the PATENTS file in the same directory.\n\nimport parlai.core.build_data as build_data\n\nimport os\nimport copy\nimport numpy as np\nfrom PIL import Image\n\n_greyscale = '  .,:;crsA23hHG#98&@'\n\nclass ImageLoader():\n    \"\"\"Extract image feature using pretrained CNN network.\n    \"\"\"\n    def __init__(self, opt):\n        self.opt = copy.deepcopy(opt)\n        self.netCNN = None\n\n    def init_cnn(self):\n        \"\"\"Lazy initialization of preprocessor model in case we don't need any image preprocessing.\"\"\"\n        try:\n            import torch\n        except ModuleNotFoundError:\n            raise ModuleNotFoundError('Need to install Pytorch: go to pytorch.org')\n        from torch.autograd import Variable\n        import torchvision\n        import torchvision.transforms as transforms\n        import torch.nn as nn\n\n        opt = self.opt\n        self.image_size = opt['image_size']\n        self.crop_size = opt['image_cropsize']\n        self.datatype = opt['datatype']\n        self.image_mode = opt['image_mode']\n\n        opt['cuda'] = not opt['no_cuda'] and torch.cuda.is_available()\n        self.use_cuda = opt['cuda']\n\n        if self.use_cuda:\n            print('[ Using CUDA ]')\n            torch.cuda.set_device(opt['gpu'])\n\n        cnn_type, layer_num = self.image_mode_switcher()\n\n        # initialize the pretrained CNN using pytorch.\n        CNN = getattr(torchvision.models, cnn_type)\n\n        # cut off the additional layer.\n        self.netCNN = nn.Sequential(*list(CNN(pretrained=True).children())[:layer_num])\n\n        # initialize the transform function using torch vision.\n        self.transform = transforms.Compose([\n                            transforms.Scale(self.image_size),\n                            transforms.CenterCrop(self.crop_size),\n                            transforms.ToTensor(),\n                            transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                                    std=[0.229, 0.224, 0.225])\n                            ])\n\n        # container for single image\n        self.xs = torch.FloatTensor(1, 3, self.crop_size, self.crop_size).fill_(0)\n\n        if self.use_cuda:\n            self.cuda()\n            self.xs = self.xs.cuda()\n\n        # make self.xs variable.\n        self.xs = Variable(self.xs)\n\n    def cuda(self):\n        self.netCNN.cuda()\n\n    def save(self, feature, path):\n        np.save(path, feature)\n\n    def image_mode_switcher(self):\n        switcher = {\n            'resnet152': ['resnet152', -1],\n            'resnet101': ['resnet101', -1],\n            'resnet50': ['resnet50', -1],\n            'resnet34': ['resnet34', -1],\n            'resnet18': ['resnet18', -1],\n            'resnet152_spatial': ['resnet152', -2],\n            'resnet101_spatial': ['resnet101', -2],\n            'resnet50_spatial': ['resnet50', -2],\n            'resnet34_spatial': ['resnet34', -2],\n            'resnet18_spatial': ['resnet18', -2],\n        }\n\n        if self.image_mode not in switcher:\n            raise NotImplementedError('image preprocessing mode' +\n                                      '{} not supported yet'.format(self.image_mode))\n\n        return switcher.get(self.image_mode)\n\n    def extract(self, image, path):\n        # check whether initlize CNN network.\n        if not self.netCNN:\n            self.init_cnn()\n\n        self.xs.data.copy_(self.transform(image))\n        # extract the image feature\n        feature = self.netCNN(self.xs)\n        feature = feature.cpu().data.numpy()\n        # save the feature\n        self.save(feature, path)\n        return feature\n\n    def img_to_ascii(self, path):\n        im = Image.open(path)\n        im.thumbnail((60, 40), Image.BICUBIC)\n        im = im.convert('L')\n        asc = []\n        for y in range(0, im.size[1]):\n            for x in range(0, im.size[0]):\n                lum = 255 - im.getpixel((x, y))\n                asc.append(_greyscale[lum * len(_greyscale) // 256])\n            asc.append('\\n')\n        return ''.join(asc)\n\n    def load(self, path):\n        opt = self.opt\n        mode = opt.get('image_mode', 'raw')\n        if mode is None or mode == 'none':\n            # don't need to load images\n            return None\n        elif mode == 'raw':\n            # raw just returns RGB values\n            return Image.open(path).convert('RGB')\n        elif mode == 'ascii':\n            # convert images to ascii \u00af\\_(\u30c4)_/\u00af\n            return self.img_to_ascii(path)\n        else:\n            # otherwise, looks for preprocessed version under 'mode' directory\n            prepath, imagefn = os.path.split(path)\n\n            dpath = os.path.join(prepath, mode)\n\n            if not os.path.exists(dpath):\n                build_data.make_dir(dpath)\n\n            imagefn = imagefn.split('.')[0]\n            imagefn = imagefn + '.npy'\n            new_path = os.path.join(prepath, mode, imagefn)\n\n            if not os.path.isfile(new_path):\n                return self.extract(Image.open(path).convert('RGB'), new_path)\n            else:\n                return np.load(new_path)\n", "path": "parlai/core/image_featurizers.py"}], "after_files": [{"content": "# All rights reserved.\n# This source code is licensed under the BSD-style license found in the\n# LICENSE file in the root directory of this source tree. An additional grant\n# of patent rights can be found in the PATENTS file in the same directory.\n\nimport parlai.core.build_data as build_data\n\nimport os\nimport copy\nimport numpy as np\nfrom PIL import Image\nfrom functools import lru_cache\n\n_greyscale = '  .,:;crsA23hHG#98&@'\n\nclass ImageLoader():\n    \"\"\"Extract image feature using pretrained CNN network.\n    \"\"\"\n    def __init__(self, opt):\n        self.opt = copy.deepcopy(opt)\n        self.netCNN = None\n\n    def init_cnn(self):\n        \"\"\"Lazy initialization of preprocessor model in case we don't need any image preprocessing.\"\"\"\n        try:\n            import torch\n        except ModuleNotFoundError:\n            raise ModuleNotFoundError('Need to install Pytorch: go to pytorch.org')\n        from torch.autograd import Variable\n        import torchvision\n        import torchvision.transforms as transforms\n        import torch.nn as nn\n\n        opt = self.opt\n        self.image_size = opt['image_size']\n        self.crop_size = opt['image_cropsize']\n        self.datatype = opt['datatype']\n        self.image_mode = opt['image_mode']\n\n        opt['cuda'] = not opt['no_cuda'] and torch.cuda.is_available()\n        self.use_cuda = opt['cuda']\n\n        if self.use_cuda:\n            print('[ Using CUDA ]')\n            torch.cuda.set_device(opt['gpu'])\n\n        cnn_type, layer_num = self.image_mode_switcher()\n\n        # initialize the pretrained CNN using pytorch.\n        CNN = getattr(torchvision.models, cnn_type)\n\n        # cut off the additional layer.\n        self.netCNN = nn.Sequential(*list(CNN(pretrained=True).children())[:layer_num])\n\n        # initialize the transform function using torch vision.\n        self.transform = transforms.Compose([\n                            transforms.Scale(self.image_size),\n                            transforms.CenterCrop(self.crop_size),\n                            transforms.ToTensor(),\n                            transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                                    std=[0.229, 0.224, 0.225])\n                            ])\n\n        # container for single image\n        self.xs = torch.FloatTensor(1, 3, self.crop_size, self.crop_size).fill_(0)\n\n        if self.use_cuda:\n            self.cuda()\n            self.xs = self.xs.cuda()\n\n        # make self.xs variable.\n        self.xs = Variable(self.xs)\n\n    def cuda(self):\n        self.netCNN.cuda()\n\n    def save(self, feature, path):\n        np.save(path, feature)\n\n    def image_mode_switcher(self):\n        switcher = {\n            'resnet152': ['resnet152', -1],\n            'resnet101': ['resnet101', -1],\n            'resnet50': ['resnet50', -1],\n            'resnet34': ['resnet34', -1],\n            'resnet18': ['resnet18', -1],\n            'resnet152_spatial': ['resnet152', -2],\n            'resnet101_spatial': ['resnet101', -2],\n            'resnet50_spatial': ['resnet50', -2],\n            'resnet34_spatial': ['resnet34', -2],\n            'resnet18_spatial': ['resnet18', -2],\n        }\n\n        if self.image_mode not in switcher:\n            raise NotImplementedError('image preprocessing mode' +\n                                      '{} not supported yet'.format(self.image_mode))\n\n        return switcher.get(self.image_mode)\n\n    def extract(self, image, path):\n        # check whether initlize CNN network.\n        if not self.netCNN:\n            self.init_cnn()\n\n        self.xs.data.copy_(self.transform(image))\n        # extract the image feature\n        feature = self.netCNN(self.xs)\n        feature = feature.cpu().data.numpy()\n        # save the feature\n        self.save(feature, path)\n        return feature\n\n    def img_to_ascii(self, path):\n        im = Image.open(path)\n        im.thumbnail((60, 40), Image.BICUBIC)\n        im = im.convert('L')\n        asc = []\n        for y in range(0, im.size[1]):\n            for x in range(0, im.size[0]):\n                lum = 255 - im.getpixel((x, y))\n                asc.append(_greyscale[lum * len(_greyscale) // 256])\n            asc.append('\\n')\n        return ''.join(asc)\n\n    @lru_cache(maxsize=None)\n    def load(self, path):\n        opt = self.opt\n        mode = opt.get('image_mode', 'raw')\n        if mode is None or mode == 'none':\n            # don't need to load images\n            return None\n        elif mode == 'raw':\n            # raw just returns RGB values\n            return Image.open(path).convert('RGB')\n        elif mode == 'ascii':\n            # convert images to ascii \u00af\\_(\u30c4)_/\u00af\n            return self.img_to_ascii(path)\n        else:\n            # otherwise, looks for preprocessed version under 'mode' directory\n            prepath, imagefn = os.path.split(path)\n\n            dpath = os.path.join(prepath, mode)\n\n            if not os.path.exists(dpath):\n                build_data.make_dir(dpath)\n\n            imagefn = imagefn.split('.')[0]\n            imagefn = imagefn + '.npy'\n            new_path = os.path.join(prepath, mode, imagefn)\n\n            if not os.path.isfile(new_path):\n                return self.extract(Image.open(path).convert('RGB'), new_path)\n            else:\n                return np.load(new_path)\n", "path": "parlai/core/image_featurizers.py"}]}