Kaggle DSTL Satellite Imagery Feature Detection

Overview

DSTL

https://www.kaggle.com/c/dstl-satellite-imagery-feature-detection/

Note: this repo is not supported. License is MIT.

Object types

Note that labels here are 1 less than in submission file:

  • 0: Buildings - large building, residential, non-residential, fuel storage facility, fortified building
  • 1: Misc. Manmade structures
  • 2: Road
  • 3: Track - poor/dirt/cart track, footpath/trail
  • 4: Trees - woodland, hedgerows, groups of trees, standalone trees
  • 5: Crops - contour ploughing/cropland, grain (wheat) crops, row (potatoes, turnips) crops
  • 6: Waterway
  • 7: Standing water
  • 8: Vehicle Large - large vehicle (e.g. lorry, truck, bus), logistics vehicle
  • 9: Vehicle Small - small vehicle (car, van), motorbike

General approach

UNet network with batch-normalization added, training with Adam optimizer with a loss that is a sum of 0.1 cross-entropy and 0.9 dice loss. Input for UNet was a 116 by 116 pixel patch, output was 64 by 64 pixels, so there were 16 additional pixels on each side that just provided context for the prediction. Batch size was 128, learning rate was set to 0.0001 (but loss was multiplied by the batch size). Learning rate was divided by 5 on the 25-th epoch and then again by 5 on the 50-th epoch, most models were trained for 70-100 epochs. Patches that formed a batch were selected completely randomly across all images. During one epoch, network saw patches that covered about one half of the whole training set area. Best results for individual classes were achieved when training on related classes, for example buildings and structures, roads and tracks, two kinds of vehicles.

Augmentations included small rotations for some classes (±10-25 degrees for houses, structures and both vehicle classes), full rotations and vertical/horizontal flips for other classes. Small amount of dropout (0.1) was used in some cases. Alignment between channels was fixed with the help of cv2.findTransformECC, and lower-resolution layers were upscaled to match RGB size. In most cases, 12 channels were used (RGB, P, M), while in some cases just RGB and P or all 20 channels made results slightly better.

Validation

Validation was very hard, especially for both water and both vehicle classes. In most cases, validation was performed on 5 images (6140_3_1, 6110_1_2, 6160_2_1, 6170_0_4, 6100_2_2), while other 20 were used for training. Re-training the model with the same parameters on all 25 images improved LB score.

Some details

  • This setup provides good results for small-scale classes (houses, structures, small vehicles), reasonable results for most other classes and overfits quite badly on waterway.
  • Man-made structures performed significantly better if training polygons were made bigger by 0.5 pixel before producing training masks.
  • For some classes (e.g. vehicles), it helped a bit to make the first downscaling in UNet 4x instead of default 2x, and also made training 1.5x faster.
  • Averaging of predictions (of one model) with small shifts (1/3 of the 64 pixel step) were used for some classes.
  • Predictions on the edges of the input image (closer than 16 pixels to the border) were bad for some classes and were left empty in this case.
  • All models were implemented in pytorch, training for 70 epochs took about 5 hours, submission generation took about 30 minutes without averaging, or about 5 hours with averaging.

Other things tried

A lot of things that either did not bring noticeable improvements, or made things worse:

  • Losses: jaccard instead of dice, trying to predict distance to the border of the objects.
  • Color augmentations.
  • Oversampling of rare classes.
  • Passing lower-resolution channels directly to lower-resolution layers in UNet.
  • Varying UNet filter sizes, activations, number of layers and upscale/downscale steps, using deconvolutions instead of upsampling.
  • Learning rate decay.
  • Models: VGG-like modules for UNet, SegNet, DenseNet

Object types stats

Area by classs:

im_id 0 1 2 3 4 5 6 7 8 9
6010_1_2 0.0% 0.0653% 0.0% 1.3345% 4.5634% 0.0% 0.0% 0.0% 0.0% 0.0%
6010_4_2 0.0% 0.0% 0.0% 1.9498% 12.3410% 0.0% 0.0% 0.0% 0.0% 0.0%
6010_4_4 0.0% 0.0% 0.0% 0.0% 22.8556% 0.0% 0.0% 0.0% 0.0% 0.0%
6040_1_0 0.0% 0.0% 0.0% 1.4446% 8.0062% 0.0% 0.0% 0.0% 0.0% 0.0%
6040_1_3 0.0% 0.0% 0.0% 0.2019% 18.7376% 3.6610% 0.0% 0.0% 0.0% 0.0%
6040_2_2 0.0% 0.0% 0.0% 0.9581% 18.7348% 0.0% 0.0% 0.0% 0.0% 0.0%
6040_4_4 0.0% 0.0% 0.0% 1.8893% 2.9152% 0.0% 0.0% 0.0% 0.0% 0.0%
6060_2_3 0.1389% 0.3037% 0.0% 3.0302% 8.4519% 93.5617% 0.0% 0.0% 0.0% 0.0003%
6070_2_3 1.5524% 0.3077% 0.8135% 0.0% 16.0439% 0.0% 10.6325% 0.0543% 0.0% 0.0058%
6090_2_0 0.0% 0.0343% 0.0% 0.4072% 10.1105% 28.2399% 0.0% 0.3130% 0.0% 0.0008%
6100_1_3 8.7666% 2.7289% 2.2145% 12.2506% 6.2015% 2.6901% 0.0% 0.6839% 0.0110% 0.0459%
6100_2_2 3.1801% 0.8188% 1.1903% 3.7222% 7.6089% 44.3148% 1.8823% 0.0512% 0.0100% 0.0242%
6100_2_3 8.2184% 1.4110% 1.2099% 9.5948% 7.5323% 0.0% 0.0% 0.0603% 0.0148% 0.0661%
6110_1_2 13.1314% 2.8616% 0.4192% 4.1817% 3.3154% 49.7792% 0.0% 0.1527% 0.0% 0.0065%
6110_3_1 4.5495% 1.2561% 3.6302% 2.8221% 5.4133% 57.6089% 0.0% 0.5531% 0.0181% 0.0253%
6110_4_0 2.4051% 0.5732% 1.8409% 2.8067% 5.7379% 80.7666% 0.0% 1.4210% 0.0136% 0.0017%
6120_2_0 1.7980% 0.7257% 0.8505% 4.4026% 5.6352% 79.5910% 0.0% 0.0% 0.0138% 0.0041%
6120_2_2 20.6570% 2.0389% 4.2547% 8.6533% 4.4347% 10.2929% 0.0% 0.2859% 0.0076% 0.1560%
6140_1_2 12.9211% 2.4488% 0.3538% 4.1461% 3.1027% 49.5910% 0.0% 0.1415% 0.0% 0.0086%
6140_3_1 5.2015% 1.4349% 3.4252% 2.5189% 5.8852% 57.3959% 0.0% 0.4664% 0.0042% 0.0358%
6150_2_3 0.0% 0.6055% 0.0% 3.0197% 13.5187% 80.6649% 0.0% 0.0% 0.0% 0.0%
6160_2_1 0.0% 0.0% 0.0% 2.7986% 10.2713% 0.0% 0.0% 0.0% 0.0% 0.0%
6170_0_4 0.0% 0.0016% 0.0% 0.1994% 24.8913% 0.0% 0.0% 0.0152% 0.0% 0.0%
6170_2_4 0.0% 0.0011% 0.0% 2.5070% 7.7844% 49.5326% 0.0% 0.0089% 0.0% 0.0%
6170_4_1 0.0% 0.0% 0.0% 0.1349% 20.2214% 0.0% 0.0% 0.0% 0.0% 0.0%

Making a submission

Train a CNN (choose number of epochs and other hyper-params running without --all):

$ ./train.py checkpoint-folder --all --hps dice_loss=10,n_epochs=70

Make submission file (check hyperparameters doing a submission for the model trained with validation by running with --validation *value* and optionally --valid-polygons):

$ ./make_submission.py checkpoint-folder submission.csv.gz

Finally, use ./merge_submission.py to produce the final submission.

This just gives a general idea, real submissions were generated with different hyperparameters for different classes, and all above commands have more options that are documented in the commands themselves (use --help, check the code if in doubt).

Comments
  • A sincere apology

    A sincere apology

    I'm really really sorry. Because of my poor English skills, I didn't think "excuse me" meant contempt.I just wanted to ask one question.I am really sorry that I have brought you a bad mood。And I did have a problem when I run your source code that like "the surviving open question". Can you give a general analysis of the reasons? Thank you a thousand times, you're an angel.

    opened by HalfLemon 9
  • Missing file on run train_wkt_v4.csv

    Missing file on run train_wkt_v4.csv

    Hey, first of, thanks for sharing, it is awesome!

    I would just like to say that I tried to replicate your results and even though I had a lot of trouble with the attr package (because you were using almost the dev version, right?) I got around it.

    This however seems like a problem I cant fix on my own.

    Upon this command: ./train.py checkpoint-folder --all --hps dice_loss=10,n_epochs=70 as given in the README, I get this error:

    Traceback (most recent call last):
      File "./train.py", line 704, in <module>
        main()
      File "./train.py", line 640, in main
        all_im_ids = list(utils.get_wkt_data())
      File "/root/sharedfolder/kaggle-dstl/utils.py", line 41, in get_wkt_data
        with open('./train_wkt_v4.csv') as f:
    FileNotFoundError: [Errno 2] No such file or directory: './train_wkt_v4.csv'
    

    However it first gives me some long parameter filled dict, if that helps. Any chance you might share this file? Even if it is private?

    opened by jakubLangr 7
  • Running error

    Running error

    Position: train.py->main() arg('--model-path',type=Path) Question: Could you tell me what to fill in here? ( train_geojson_v3 ?) if is 'train_geojson_v3', it‘s error :No such file or directory: 'align_cache\6010_1_2_p.alignment'

    opened by HalfLemon 6
  • I found 2 problem that lead to the source code can not work

    I found 2 problem that lead to the source code can not work

    first, i want very thanks the autor ,it`s a outstading project,howener ,there are some problem in it. it lead to the source can not work well.

    i find two very serious problems.

    A:the list is to explain the problem.

    in train.py
    ` inputs.append(patch[:, m: -m, m: -m].astype(np.float32))
    outputs.append(mask[:, m: -m, m: -m].astype(np.float32))
    
           if self.hps.needs_dist:
               dist_outputs.append(dist_mask[:, m: -m, m: -m].astype(np.float32))`
       return (torch.from_numpy(np.array(inputs)),
               torch.from_numpy(np.array(outputs)),
               torch.from_numpy(np.array(dist_outputs)))
    
    the return 3th args, (dist_outputs) is null . so it is not transform to torch.
    the error message is: RuntimeError: the given numpy array has zero-sized dimensions. Zero-sized dimensions are not supported in PyTorch
    
    in models.py
    @property def needs_dist(self): return (self.dist_loss != 0 or self.dist_dice_loss != 0 or self.dist_jaccard_loss != 0)
    you can find "title 1" `s if-else decide by this function
    
    however , the source code default this 3 value is 0
    in models.py
    dist_loss = attr.ib(default=0.0) dist_dice_loss = attr.ib(default=0.0) dist_jaccard_loss = attr.ib(default=0.0)
    
    the web course is : $ ./train.py checkpoint-floder --all --hps dice_loss=10,n_epochs=70
    so come up the "title 1" problem.
    
    1. I don`t kown that how to moditfy the parameter in train.py (the main function)

    B . the second problem in train.py _train_on_feeds()

    ` def _train_on_feeds(self, gen_batch, n_batches: int, no_mp: bool): losses = [[] for _ in range(self.hps.n_classes)] #self.hp.n_classes=10 jaccard_stats = self._jaccard_stats()

    def log():
        logger.info(
            'Train loss: {loss:.3f}, Jaccard: {jaccard}, '
            'speed: {speed:,} patches/s'.format(
                loss=np.array(losses)[:, -log_step:].mean(),
                speed=int(len(losses[0]) * self.hps.batch_size / (t1 - t00)),
                jaccard=self._format_jaccard(jaccard_stats),
            ))
    
    t0 = t00 = time.time()
    log_step = 50
    im_log_step = n_batches // log_step * log_step
    map_ = (map if no_mp else
            partial(utils.imap_fixed_output_buffer, threads=4))
    for i, (x, y, dist_y) in enumerate(map_(gen_batch, range(n_batches))):
        if losses[0] and i % log_step == 0:
            for cls, ls in zip(self.hps.classes, losses):
                self._log_value(
                    'loss/cls-{}'.format(cls), np.mean(ls[-log_step:]))
            if self.hps.has_all_classes:
                self._log_value(
                    'loss/cls-mean', np.mean([
                        l for ls in losses for l in ls[-log_step:]]))
            pred_y = self.net(self._var(x)).data.cpu()                         
            self._update_jaccard(jaccard_stats, y.numpy(), pred_y.numpy())
            self._log_jaccard(jaccard_stats)
            if i == im_log_step:
                self._log_im(
                    x.numpy(), y.numpy(), dist_y.numpy(), pred_y.numpy())
        step_losses = self.train_step(x, y, dist_y)        
                                                            
        for ls, l in zip(losses, step_losses):
            ls.append(l)
        t1 = time.time()
        dt = t1 - t0
        if dt > 10:
            log()
            jaccard_stats = self._jaccard_stats()
            t0 = t1
    if losses:
        log()`
    

    the code : if losses[0] and i % log_step == 0: this if will never run.

    opened by HalfLemon 1
  • A little question

    A little question

    in train.py arg('logdir', help='Path to log directory') arg('--hps', help='Change hyperparameters in k1=v1,k2=v2 format') arg('--all', action='store_true', help='Train on all images without validation') arg('--validation', choices=['random', 'stratified', 'square', 'custom'], default='custom', help='validation strategy') arg('--valid-only', action='store_true') arg('--only', help='Train on this image ids only (comma-separated) without validation') arg('--clean', action='store_true', help='Clean logdir') arg('--no-mp', action='store_true', help='Disable multiprocessing') arg('--model-path', type=Path)

    excuse me, the last 'model-path' stands for what?

    opened by HalfLemon 1
  • error

    error

    if you run the original source code ,you will find a very serious problem.

    the list is to explain the problem.

    1. in train.py ` inputs.append(patch[:, m: -m, m: -m].astype(np.float32)) outputs.append(mask[:, m: -m, m: -m].astype(np.float32))

             if self.hps.needs_dist:
                 dist_outputs.append(dist_mask[:, m: -m, m: -m].astype(np.float32))`
         return (torch.from_numpy(np.array(inputs)),
                 torch.from_numpy(np.array(outputs)),
                 torch.from_numpy(np.array(dist_outputs)))
      

      the return 3th args, (dist_outputs) is null . so it is not transform to torch. the error message is: RuntimeError: the given numpy array has zero-sized dimensions. Zero-sized dimensions are not supported in PyTorch

    2. in models.py @property def needs_dist(self): return (self.dist_loss != 0 or self.dist_dice_loss != 0 or self.dist_jaccard_loss != 0) you can find "title 1" `s if-else decide by this function

    3. however , the source code default this 3 value is 0 in models.py dist_loss = attr.ib(default=0.0) dist_dice_loss = attr.ib(default=0.0) dist_jaccard_loss = attr.ib(default=0.0)

    4. the web course is : $ ./train.py checkpoint-floder --all --hps dice_loss=10,n_epochs=70 so come up the "title 1" problem.

    5. I don`t kown that how to solve this problem.



    B . the second problem in train.py _train_on_feeds()

      `    def _train_on_feeds(self, gen_batch, n_batches: int, no_mp: bool):
        losses = [[] for _ in range(self.hps.n_classes)]      #self.hp.n_classes=10
        jaccard_stats = self._jaccard_stats()
    
        def log():
            logger.info(
                'Train loss: {loss:.3f}, Jaccard: {jaccard}, '
                'speed: {speed:,} patches/s'.format(
                    loss=np.array(losses)[:, -log_step:].mean(),
                    speed=int(len(losses[0]) * self.hps.batch_size / (t1 - t00)),
                    jaccard=self._format_jaccard(jaccard_stats),
                ))
    
        t0 = t00 = time.time()
        log_step = 50
        im_log_step = n_batches // log_step * log_step
        map_ = (map if no_mp else
                partial(utils.imap_fixed_output_buffer, threads=4))
        for i, (x, y, dist_y) in enumerate(map_(gen_batch, range(n_batches))):
            if losses[0] and i % log_step == 0:
                for cls, ls in zip(self.hps.classes, losses):
                    self._log_value(
                        'loss/cls-{}'.format(cls), np.mean(ls[-log_step:]))
                if self.hps.has_all_classes:
                    self._log_value(
                        'loss/cls-mean', np.mean([
                            l for ls in losses for l in ls[-log_step:]]))
                pred_y = self.net(self._var(x)).data.cpu()                         
                self._update_jaccard(jaccard_stats, y.numpy(), pred_y.numpy())
                self._log_jaccard(jaccard_stats)
                if i == im_log_step:
                    self._log_im(
                        x.numpy(), y.numpy(), dist_y.numpy(), pred_y.numpy())
            step_losses = self.train_step(x, y, dist_y)        
                                                                
            for ls, l in zip(losses, step_losses):
                ls.append(l)
            t1 = time.time()
            dt = t1 - t0
            if dt > 10:
                log()
                jaccard_stats = self._jaccard_stats()
                t0 = t1
        if losses:
            log()`
    

    the code : if losses[0] and i % log_step == 0: this if will never run.

    opened by HalfLemon 0
  • Typo on README???

    Typo on README???

    hi,

    should it be $ ./train.py checkpoint-folder --all --hps dist_loss=10,n_epochs=70 instead of $ ./train.py checkpoint-folder --all --hps dice_loss=10,n_epochs=70

    Thank you!

    opened by choyjeff 3
  • Your program doesn't work.

    Your program doesn't work.

    made a mistake when I ran the train.py.

    RuntimeError: the given numpy array has zero-sized dimensions. Zero-sized dimensions are not supported in PyTorch

    opened by GuYunxian 5
Owner
Konstantin Lopuhin
Konstantin Lopuhin
To propose and implement a multi-class classification approach to disaster assessment from the given data set of post-earthquake satellite imagery.

To propose and implement a multi-class classification approach to disaster assessment from the given data set of post-earthquake satellite imagery.

Kunal Wadhwa 2 Jan 5, 2022
Aerial Imagery dataset for fire detection: classification and segmentation (Unmanned Aerial Vehicle (UAV))

Aerial Imagery dataset for fire detection: classification and segmentation using Unmanned Aerial Vehicle (UAV) Title FLAME (Fire Luminosity Airborne-b

null 79 Jan 6, 2023
Change is Everywhere: Single-Temporal Supervised Object Change Detection in Remote Sensing Imagery (ICCV 2021)

Change is Everywhere Single-Temporal Supervised Object Change Detection in Remote Sensing Imagery by Zhuo Zheng, Ailong Ma, Liangpei Zhang and Yanfei

Zhuo Zheng 125 Dec 13, 2022
Kaggle G2Net Gravitational Wave Detection : 2nd place solution

Kaggle G2Net Gravitational Wave Detection : 2nd place solution

Hiroshechka Y 33 Dec 26, 2022
1st place solution to the Satellite Image Change Detection Challenge hosted by SenseTime

1st place solution to the Satellite Image Change Detection Challenge hosted by SenseTime

Lihe Yang 209 Jan 1, 2023
YOLTv5 rapidly detects objects in arbitrarily large aerial or satellite images that far exceed the ~600×600 pixel size typically ingested by deep learning object detection frameworks

YOLTv5 rapidly detects objects in arbitrarily large aerial or satellite images that far exceed the ~600×600 pixel size typically ingested by deep learning object detection frameworks.

Adam Van Etten 145 Jan 1, 2023
moving object detection for satellite videos.

DSFNet: Dynamic and Static Fusion Network for Moving Object Detection in Satellite Videos Algorithm Introduction DSFNet: Dynamic and Static Fusion Net

xiaochao 39 Dec 16, 2022
FactSeg: Foreground Activation Driven Small Object Semantic Segmentation in Large-Scale Remote Sensing Imagery (TGRS)

FactSeg: Foreground Activation Driven Small Object Semantic Segmentation in Large-Scale Remote Sensing Imagery by Ailong Ma, Junjue Wang*, Yanfei Zhon

Kingdrone 43 Jan 5, 2023
Experiments on Flood Segmentation on Sentinel-1 SAR Imagery with Cyclical Pseudo Labeling and Noisy Student Training

Flood Detection Challenge This repository contains code for our submission to the ETCI 2021 Competition on Flood Detection (Winning Solution #2). Acco

Siddha Ganju 108 Dec 28, 2022
A PyTorch implementation of Multi-digit Number Recognition from Street View Imagery using Deep Convolutional Neural Networks

SVHNClassifier-PyTorch A PyTorch implementation of Multi-digit Number Recognition from Street View Imagery using Deep Convolutional Neural Networks If

Potter Hsu 182 Jan 3, 2023
Deep Learning pipeline for motor-imagery classification.

BCI-ToolBox 1. Introduction BCI-ToolBox is deep learning pipeline for motor-imagery classification. This repo contains five models: ShallowConvNet, De

DongHee 18 Oct 31, 2022
Kaggle Lyft Motion Prediction for Autonomous Vehicles 4th place solution

Lyft Motion Prediction for Autonomous Vehicles Code for the 4th place solution of Lyft Motion Prediction for Autonomous Vehicles on Kaggle. Discussion

null 44 Jun 27, 2022
🏅 The Most Comprehensive List of Kaggle Solutions and Ideas 🏅

?? Collection of Kaggle Solutions and Ideas ??

Farid Rashidi 2.3k Jan 8, 2023
Winning solution of the Indoor Location & Navigation Kaggle competition

This repository contains the code to generate the winning solution of the Kaggle competition on indoor location and navigation organized by Microsoft

Tom Van de Wiele 62 Dec 28, 2022
7th place solution of Human Protein Atlas - Single Cell Classification on Kaggle

kaggle-hpa-2021-7th-place-solution Code for 7th place solution of Human Protein Atlas - Single Cell Classification on Kaggle. A description of the met

null 8 Jul 9, 2021
Kaggle | 9th place (part of) solution for the Bristol-Myers Squibb – Molecular Translation challenge

Part of the 9th place solution for the Bristol-Myers Squibb – Molecular Translation challenge translating images containing chemical structures into I

Erdene-Ochir Tuguldur 22 Nov 30, 2022
My 1st place solution at Kaggle Hotel-ID 2021

1st place solution at Kaggle Hotel-ID My 1st place solution at Kaggle Hotel-ID to Combat Human Trafficking 2021. https://www.kaggle.com/c/hotel-id-202

Kohei Ozaki 18 Aug 19, 2022
Kaggle | 9th place single model solution for TGS Salt Identification Challenge

UNet for segmenting salt deposits from seismic images with PyTorch. General We, tugstugi and xuyuan, have participated in the Kaggle competition TGS S

Erdene-Ochir Tuguldur 276 Dec 20, 2022
10th place solution for Google Smartphone Decimeter Challenge at kaggle.

Under refactoring 10th place solution for Google Smartphone Decimeter Challenge at kaggle. Google Smartphone Decimeter Challenge Global Navigation Sat

null 12 Oct 25, 2022