{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install pytorch-lightning\n!pip install -U 'git+https://github.com/cocodataset/cocoapi.git#subdirectory=PythonAPI'\n!pip install git+git://github.com/lgvaz/mantisshrimp.git","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Wheat","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"from mantisshrimp.imports import *\nfrom mantisshrimp import *\nimport pandas as pd\nimport albumentations as A","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Parser","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"The first step is to understand the data. In this task we were given a `.csv` file with annotations, let's take a look at that.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"<div class=\"alert alert-info\">\n    \n**Note:**  \n\nReplace `source` with your own path for the dataset directory.\n    \n</div>","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"source = Path('../input/global-wheat-detection/')\ndf = pd.read_csv(source / \"train.csv\")\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"At first glance, we can make the following assumptions:  \n* Multiple rows with the same object_id, width, height  \n* A different bbox for each row  \n* source doesn't seem relevant right now  ","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"Once we know what our data provides we can create our custom `Parser`.  ","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"When creating a `Parser` we inherit from smaller building blocks that provides the functionallity we want:  \n* `DefaultImageInfoParser`: Will parse standard fields for image information, e.g. `filepath`, `height`, `width`  \n* `FasterRCNNParser`: Since we only need to predict bboxes we will use a `FasterRCNN` model, this will parse all the requirements for using such a model.  ","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"We can also specify exactly what fields we would like to parse, in fact, the parsers we are currently using are just helper classes that groups a collection of individual parsers.  \nWe are going to see how to use individual parsers in a future tutorial.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"<div class=\"alert alert-info\">\n    \n**Note:**\n\nIf you are using an IDE there is a little bit of magic than can happen. Once you created defined your class you can right click on it and select the option _\"implement abstract methods\"_, this will automatically populate your class with all the methods you need to override. \n\nIf you are using a notebook, or your IDE does not support that, check the documentation to know what methods you should override.\n\n</div>","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"\n<div class=\"alert alert-warning\">\n    \n**Important:**  \n    \nBe sure to return the correct type on all overriden methods!\n    \n</div>","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"class WheatParser(DefaultImageInfoParser, FasterRCNNParser):\n    def __init__(self, df, source):\n        self.df = df\n        self.source = source\n        self.imageid_map = IDMap()\n\n    def __iter__(self):\n        yield from self.df.itertuples()\n\n    def __len__(self):\n        return len(self.df)\n\n    def imageid(self, o) -> int:\n        return self.imageid_map[o.image_id]\n\n    def filepath(self, o) -> Union[str, Path]:\n        return self.source / f\"{o.image_id}.jpg\"\n\n    def height(self, o) -> int:\n        return o.height\n\n    def width(self, o) -> int:\n        return o.width\n\n    def label(self, o) -> int:\n        return 1\n\n    def bbox(self, o) -> BBox:\n        return BBox.from_xywh(*np.fromstring(o.bbox[1:-1], sep=\",\"))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Defining the `__init__` is completely up to you, normally we have to pass our data (the `df` in our case) and the folder where our images are contained (`source` in our case).","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"We then override `__iter__`, telling our parser how to iterate over our data. In our case we call `df.itertuples` to iterate over all `df` rows.\n\n`__len__` is not obligatory but will help visualizing the progress when parsing.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"And finally we override all the other methods, they all receive a single argument `o`, which is the object returned by `__iter__`.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"Now we just need to decide how to split our data and `Parser.parse`!","execution_count":null},{"metadata":{"scrolled":false,"trusted":true},"cell_type":"code","source":"data_splitter = RandomSplitter([.8, .2])\nparser = WheatParser(df, source / \"train\")\ntrain_rs, valid_rs = parser.parse(data_splitter)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's take a look at one record.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"show_record(train_rs[0], label=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Transforms and Datasets","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"Mantisshrimp is agnostic to the transform library you want to use. We provide default support for [albumentations](https://github.com/albumentations-team/albumentations) but if you want to use another library you just need to inherit and override all abstract methods of `Transform`.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"For simplicity, let's use a single transform on the train data and no transforms on the validation data.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train_tfm = AlbuTransform([A.Flip()])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"For creating a `Dataset` we just need need to pass the parsed records from the previous step and optionally a transform.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train_ds = Dataset(train_rs, train_tfm)\nvalid_ds = Dataset(valid_rs)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Model","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"Now [pytorch-lightning](https://github.com/PytorchLightning/pytorch-lightning) enters the picture.  ","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"Everything from now is almost pure lightning, the only big difference is that instead of inheriting from `LightningModule` we inherit from the specialized `MantisFasterRCNN`, this will automatically create the model architecture and download the pre-trained model weights.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"If you are not familiar with lightning, be sure to check their excelent [documentation](https://pytorch-lightning.readthedocs.io/en/stable/).","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"class WheatModel(MantisFasterRCNN):\n    def configure_optimizers(self):\n        opt = SGD(self.parameters(), 1e-3, momentum=0.9)\n        return opt","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We create the model passing how many classes we have.  \n\nIn our case we have two: `wheat` and `background`.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"model = WheatModel(2)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## DataLoader","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"Another difference from lightning is that all mantis models have a `dataloader` method that returns a customized `DataLoader` for each model.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train_dl = model.dataloader(train_ds, shuffle=True, batch_size=4, num_workers=2)\nvalid_dl = model.dataloader(valid_ds, batch_size=4, num_workers=2)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Train","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"That's it! Trainer for train! 🚀 🚀 🚀","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"trainer = Trainer(max_epochs=1, gpus=1)\ntrainer.fit(model, train_dl, valid_dl)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}