Neural Network Solutions for Image and Audio Processing

Giulio Alessandrini, WRI

Join the Conversation #WolframTechConf

ABSTRACT

The Wolfram Language contains a scalable, industrial-strength and easy-to-use neural net framework. In this talk, we are going to demonstrate its capabilities and its integration with the rest of the language. We are going to show how to build and train a net model from scratch, as well as work with a ready-to-use model from the Neural Net Repository. Last, we would like to cover some of our short- to mid-term-future plans.

Outline

A quick recap

Localize features: Landmark Regression

Pixel regression: Image Colorization

Pixel classification: Semantic Segmentation

Convolutional Neural Networks for Audio

Combine tasks: Object Detection

A quick recap

To summarize the last part of the previous talk and hammer the basics.

A quick recap

LeNet-5

lenet=NetChain[
{
ConvolutionLayer[20,5],
ElementwiseLayer[Ramp],
PoolingLayer[2,2],

ConvolutionLayer[50,5],
ElementwiseLayer[Ramp],
PoolingLayer[2,2],

FlattenLayer[],
LinearLayer[500],
ElementwiseLayer[Ramp],
LinearLayer[10],

SoftmaxLayer[]
},
"Input"->NetEncoder[{"Image",{28,28},"Grayscale"}],
"Output"->NetDecoder[{"Class",Range[0,9]}]
];

NetInformation[lenet,"ArraysTotalElementCount"]

A quick recap

LinearLayer

lenet=NetChain[
{
ConvolutionLayer[20,5],
ElementwiseLayer[Ramp],
PoolingLayer[2,2],

ConvolutionLayer[50,5],
ElementwiseLayer[Ramp],
PoolingLayer[2,2],

FlattenLayer[],
LinearLayer[500],
ElementwiseLayer[Ramp],
LinearLayer[10],

SoftmaxLayer[]
},
"Input"->NetEncoder[{"Image",{28,28},"Grayscale"}],
"Output"->NetDecoder[{"Class",Range[0,9]}]
];

linear=LinearLayer[7,"Input"-> {9}];

NetInformation[linear,"ArraysTotalElementCount"]

For simplicity we depict a fully connected linear layer in 1 dimension.

Block[
{h=4,r=0.3,layer0Pts,layer1Pts},
layer0Pts=Thread[{1.5 Range[0,8],0}];
layer1Pts=Thread[{1.5 Range[1,7],h}];
Graphics[
{{Gray,Outer[Line[{##}]&,layer0Pts,layer1Pts,1]},
{FaceForm[Orange],Map[Disk[#,r]&,layer0Pts],Map[Disk[#,r]&,layer1Pts]}
}
]
]

Every neuron of the bottom layer is connected with the top layer resulting in (Subscript[n, bottom]) × (Subscript[n, top]) connections.

Hence, (Subscript[n, bottom]) × (Subscript[n, top]) weights and (Subscript[n, top]) biases have to be learned.

A fully connected layer at the beginning of LeNet-5 would result in 48 millions weights and biases!

NetInformation[LinearLayer[{20,28,28},"Input"-> {3,32,32}],"ArraysTotalElementCount"]

A quick recap

ConvolutionLayer

lenet=NetChain[
{
ConvolutionLayer[20,5],
ElementwiseLayer[Ramp],
PoolingLayer[2,2],

ConvolutionLayer[50,5],
ElementwiseLayer[Ramp],
PoolingLayer[2,2],

FlattenLayer[],
LinearLayer[500],
ElementwiseLayer[Ramp],
LinearLayer[10],

SoftmaxLayer[]
},
"Input"->NetEncoder[{"Image",{28,28},"Grayscale"}],
"Output"->NetDecoder[{"Class",Range[0,9]}]
]

In a convolutional layer only neurons within a given radius r are connected to their respective neuron in the other layer.

Block[
{h=4,r=0.3,layer0Pts,layer1Pts},
layer0Pts=Thread[{1.5 Range[0,8],0}];
layer1Pts=Thread[{1.5 Range[1,7],h}];
Graphics[
{{Gray,MapThread[With[{pt=#1},Map[Line[{pt,#}]&,#2]]&,{layer1Pts,Partition[layer0Pts,3,1]}]},
{FaceForm[Orange],Map[Disk[#,r]&,layer0Pts],Map[Disk[#,r]&,layer1Pts]}
}
]
]

Furthermore, the weights of shifted connections are shared.

Animate[
Block[
{h=4,r=0.3,layer0Pts,layer1Pts},
layer0Pts=Thread[{1.5 Range[0,8],0}];
layer1Pts=Thread[{1.5 Range[1,7],h}];
Graphics[
{{Gray,With[{pt=layer1Pts[[k]]},Map[Line[{pt,#}]&,Partition[layer0Pts,3,1][[k]]]]},
{FaceForm[Orange],Map[Disk[#,r]&,layer0Pts],Map[Disk[#,r]&,layer1Pts]}
}
]
],
{k,1,7,1}
]

Hence, just 2r+1 weights and 1 biases have to be learned.

The first layer at the beginning of LeNet-5 has just 5×5×3×20 weights and 20 biases!

NetInformation[ConvolutionLayer[20,{5,5},"Input"-> {3,32,32}],"ArraysTotalElementCount"]

Summary

Use a convolution layer, if:

the n–dimensional data has a strong local neighborhood correlation,

the machine learning task needs to be translational invariant, and

the number of weights and biases needs stay small.

Note, that with respect to the number of feature channels a convolution layer remains fully-connected. Every pair of incoming and outgoing channel constitutes a convolution in the remaining spatial dimensions.

A quick recap

Rectified Linear Unit (ReLU)

lenet=NetChain[
{
ConvolutionLayer[20,5],
ElementwiseLayer[Ramp],
PoolingLayer[2,2],

ConvolutionLayer[50,5],
ElementwiseLayer[Ramp],
PoolingLayer[2,2],

FlattenLayer[],
LinearLayer[500],
ElementwiseLayer[Ramp],
LinearLayer[10],

SoftmaxLayer[]
},
"Input"->NetEncoder[{"Image",{28,28},"Grayscale"}],
"Output"->NetDecoder[{"Class",Range[0,9]}]
]

A ReLU transmits a positive neural excitation and blocks the negative ones.

Plot[Ramp[x],{x,-3,3}]

A LinearLayer or ConvolutionLayer in combination with a ReLU denotes a simple model of a neuron layer.

(Subscript[y, k])=σ((∑_j)(Subscript[x, j]) (Subscript[w, k,j])-(Subscript[b, j]))

Around zero, a ReLU-layer constitutes a non-linearity, which is essential for building a non-linear classifier.

Without a non-linearity a stack of linear layers is equivalent to a single linear layer.

Summary

Insert a non-linear layer in between linear layers.

ReLU is the simplest non-linear layer.

Other non-linear layers exist, e.g.  LogisticSigmoid, Tanh, ...

A quick recap

Pooling Layer

NetChain[
{
ConvolutionLayer[20,5],
ElementwiseLayer[Ramp],
PoolingLayer[2,2],

ConvolutionLayer[50,5],
ElementwiseLayer[Ramp],
PoolingLayer[2,2],

FlattenLayer[],
LinearLayer[500],
ElementwiseLayer[Ramp],
LinearLayer[10],

SoftmaxLayer[]
},
"Input"->NetEncoder[{"Image",{28,28},"Grayscale"}],
"Output"->NetDecoder[{"Class",Range[0,9]}]
]

To reduce the number of pixels per layer pooling layers are applied.

A pooling layer is equivalent to a Max filter. Only the strongest signal for a given neighborhood of neurons is conveyed.

PoolingLayer

pool=PoolingLayer[2,2,"Input"-> NetEncoder["Image"],"Output"-> NetDecoder["Image"]];

pool[]

Summary

PoolingLayers reduce the number of neurons in subsequent layer.

Spatial information is destroyed.

Invariance with respect to spatial translation is gained.

A pooling layer has no trainable parameters.

Image Classification

SoftmaxLayer

NetChain[
{
ConvolutionLayer[20,5],
ElementwiseLayer[Ramp],
PoolingLayer[2,2],

ConvolutionLayer[50,5],
ElementwiseLayer[Ramp],
PoolingLayer[2,2],

FlattenLayer[],
LinearLayer[500],
ElementwiseLayer[Ramp],
LinearLayer[10],

SoftmaxLayer[]
},
"Input"->NetEncoder[{"Image",{28,28},"Grayscale"}],
"Output"->NetDecoder[{"Class",Range[0,9]}]
]

A softmax layer convert neural activities into class probabilities.

A softmax layer applies Exp the values and normalizes with respect to the sum.

SoftmaxLayer[][{-1.9,0.3,4.5,0.0}]

Normalize[Exp[{-1.9,0.3,4.5,0.0}],Total]

Summary

A softmax layer converts values ∈ ℝ into interval [0,1].

A softmax layer has no trainable parameters.

Beyond Simple Classification

Feature can be used for more than image classification:

Feature detection (landmarks, face detection, bounding boxes, ...)

Numeric regression (pixel colorization, depth map estimation, ...)

Pixel segmentation (image masking, semantic components, ...)

A combination of these

Landmark Regression

Facial Landmark Detection with Tweaked Convolutional Neural Networks

Extracted features are processed to represent landmark coordinates

Landmark Regression

Feature extraction

The image data is processed by a simple convolutional architecture

vanillaCNN=NetModel["Vanilla CNN for Facial Landmark Regression"];
rowPlot@NetTake[vanillaCNN, "ActivationAbs4"]

Landmark Regression

Classification

A linear layer is used to predict the landmark position from the features

rowPlot@NetDrop[vanillaCNN, "ActivationAbs4"]

Landmark Regression

Data Regularization

NetEncoder can do more than just convert images to tensors

This model requires a standardized input

A "MeanImage" and "VarianceImage" can be added to the encoder

NetExtract[vanillaCNN,"Input"]

ImageAdjust/@Lookup[Normal[NetExtract[vanillaCNN,"Input"]],{"MeanImage","VarianceImage"}]//Magnify[#,3]&

Landmark Regression

Post-processing

Landmarks  are often returned in the [0, 1] range so some post-processing is required to display then in the original coordinate system

Define an evaluation function to process the net output

netEvaluation[vanillaCNN][image_]:=Transpose[Transpose[vanillaCNN[image]] ImageDimensions[image]]

face=;

landmarks=netEvaluation[vanillaCNN][face]

HighlightImage[face,{PointSize[.05],landmarks}]

Semantic Segmentation

Like a standard classifier but for pixels!

Semantic Segmentation

Issues with deep architectures

The deeper the better... up to a point:

Extra memory and training time cost

Feature maps gets increasingly small

Diminishing return in accuracy

Vanishing gradients

Semantic Segmentation

Effective depth

Even residual architectures can suffer from vanishing gradients!
This prevents effective end-to-end training

Wider or Deeper: Revisiting the ResNet Model for Visual Recognition [arXiv]

Semantic Segmentation

Feature extraction

Shallow and wide ResNet

Only 17 residual units, all the blocks are trained end-to-end

segmentationNet=NetModel["Ademxapp Model A1 Trained on PASCAL VOC2012 and MS-COCO Data"];
rowPlot@NetTake[segmentationNet,"7a"]

(RowWithSeparator[(Spacer1), , (CopyTag[NetChain[]])])

Semantic Segmentation

Pixel classification

The feature maps are used to compute a probability distribution among 21 classes

A grid of 63×63 distributions is generated

NetDrop[segmentationNet,"7a"]

Semantic Segmentation

Result

testImage=;

segmentation=netEvaluator[segmentationNet][testImage];

Tally[Flatten[segmentation]]

The final segmentation

segmentation//colorizeSegmentation

A comparison with the original image

highlightSegmentation[testImage,segmentation]

Image Colorization

Let there be Color! Joint End-to-end Learning of Global and Local Image Priors for Automatic Image Colorization with Simultaneous Classification

Train low-level features for both classification and colorization

Combine local image features with global priors

Image Colorization

Chromaticity

Recovering an RGB color from a gray value is a problem in three dimensions

dog=;

ChromaticityPlot3D[{"RGB",#},"RGB",FillingStyle->Opacity[.2],BoxRatios->Automatic]&/@{dog,ColorConvert[dog,"Grayscale"]}

By decomposing the image in a luminance part and a chromaticity part, the colorization is now a 2D problem

Given the luminance L, the net have to predict the two chromaticity components  (SuperStar[a]) and (SuperStar[b])

MapThread[Labeled,{ColorSeparate[dog],{"L","(SuperStar[a])","(SuperStar[b])"}}]
ChromaticityPlot3D[{"RGB",#},"LAB",FillingStyle->Opacity[.2],BoxRatios->Automatic]&/@{dog,ColorConvert[dog,"Grayscale"]}

Image Colorization

Low-level features

Low-level features extracted by a standard DNN

6 convolution with a 3×3 kernel

Downsampling is achieved via bigger stride

colorNet=NetModel["ColorNet Image Colorization Trained on ImageNet Competition Data"];

rowPlot@NetTake[NetExtract[colorNet,"1"],12]

Image Colorization

Mid-level features

Low-level features are processed with two extra convolution layers

A channel bottleneck from 512 to 256 is used to compress the information

rowPlot@NetTake[NetExtract[colorNet,"1"],{13,-1}]

Image Colorization

Global features

Low-level features are further processed by four more convolutions

The final features are processed by three linear layers to extract the final prior vector

The 256-dimensional vector representation can be used (and trained) for image classification

rowPlot@NetTake[NetExtract[colorNet,"2"],{14,-2}]

Image Colorization

Colorization

Mid-level and global features are fused in a final feature map

Features are used for predicting the color information via convolution and up-sampling layers

rowPlot@NetExtract[colorNet,"4"]

Image Colorization

Result

Check the result on a grayscale image

gray=ColorConvert[dog,"Grayscale"]

res=Image[Prepend[
	ArrayResample[colorNet[#], Prepend[Reverse@ImageDimensions[#], 2]], 
	ImageData[ColorSeparate[#, "L"]]],
	Interleaving -> False, 
	ColorSpace -> "LAB"
]&@gray

Measure the difference with the original

ColorDistance[dog,res]
%//Median

Image Colorization

Mixing features

Local features and global prior do not need to come from the same image

Modify the network to evaluate the global features on a different image

newColorNet[global_]:=
NetTake[colorNet,{"3","4"}][<|
"1"->NetTake[colorNet,{"1","1"}][#],
"2"->NetTake[colorNet,{"2","2"}][global]
|>]&;

dog2=;

res=Image[Prepend[
	ArrayResample[newColorNet[dog2][#], Prepend[Reverse@ImageDimensions[#], 2]], 
	ImageData[ColorSeparate[#, "L"]]],
	Interleaving -> False, 
	ColorSpace -> "LAB"
]&@ColorConvert[dog,"Grayscale"]

Convolutional Neural Networks for Audio

Introduction

In the last few years the big machine learning expansion had its effect on audio analysis too:

(RowDefault[, , (CopyTag[NetChain[]]), , {"m", "e", "o", "w"}])

Instead of crafting more and more complex features and tools, the paradigm has shifted towards leveraging massive amounts of computational power and annotated data.

Most of the state of the art results in speech analysis, event detection and music information retrieval are now leveraging neural networks/machine learning.

Convolutional Neural Networks for Audio

Feature Extraction

First of all, we need to figure out what an audio object is:

audio=;

In its simplest form it is just a sequence of values:

AudioLength[audio]
AudioData[audio]//Short

AudioPlot[audio]

We are dealing with variable length sequences that can be extremely long. 
What is the best way to feed this kind of data into a neural network?

Convolutional Neural Networks for Audio

Audio NetEncoders

We introduced the audio NetEncoders to make this step easy: they provide feature extraction, as fast as possible.

There are some preprocessing steps that are common to all of the audio encoders:

Down-mixing

Normalization

Part extraction (trimming or padding)

Resampling to a common sample rate

The possible inputs are  Audio objects (both in-core and out-of-core) and File objects.

Convolutional Neural Networks for Audio

The Data

Let’s get started by accessing and inspecting the dataset a bit:

ro=ResourceObject[];

trainingData=ResourceData[ro,"TrainingData"];
testingData=ResourceData[ro,"TestData"];
RandomSample[trainingData,3]//Dataset

Convolutional Neural Networks for Audio

The Data

One important thing we made sure of is that the speakers in the training and testing sets are different. This means that in the testing phase, the trained classifier/network will encounter speakers that it will never have heard before.

Intersection[trainingData[[All,"SpeakerID"]],testingData[[All,"SpeakerID"]]]

The possible output values are the digits from 0 to 9:

classes=Union[trainingData[[All,"Output"]]]

Conveniently, the length of all of the input data is between .5 and 1 seconds, with the majority for the signals being one second long:

Dataset[trainingData][Histogram[#,ScalingFunctions->"Log"]&@*Duration,"Input"]

Convolutional Neural Networks for Audio

Network definition

Let’s remember that we’re playing with a spoken versions of MNIST, so what could be a better starting place than LeNet? 
Given that LeNet expects a fixed size input, we’ll fix the length of the signals to about one second, and we’ll tune the parameters of the NetEncoder so that the input will have the same dimensions of the MNIST images. This is one of the reasons we can confidently use a CNN architecture for this job: we are dealing with 2D matrices (images, in essence—actually, that’s how we usually look at MFCC), and we want the network to infer information from their structure.

Convolutional Neural Networks for Audio

Network definition

Let’s grab LeNet from NetModel:

lenet=NetModel["LeNet Trained on MNIST Data","UninitializedEvaluationNet"]

We will use the “AudioMFCC” encoder, since it produces a very compact representation of the input data:

encoder=NetEncoder[{"AudioMFCC","TargetLength"->28,"SampleRate"->16000,"WindowSize" -> 1024,"Offset"-> 570,"NumberOfCoefficients"->28,"Normalization"->True}]

Since the encoder produces two dimensional data (time, frequency), and the net requires three dimensional inputs (where the first dimensions is the channel dimensions), we can use ReplicateLayer to make them compatible:

lenet=NetPrepend[lenet,ReplicateLayer[1]]

Convolutional Neural Networks for Audio

Network definition

Using NetReplacePart we can attach the “AudioMFCC” NetEncoder to the input and the appropriate NetDecoder to the output:

audioLeNet=NetReplacePart[lenet,
{
"Input"->encoder,
"Output"->NetDecoder[{"Class",classes}]
}
];

Convolutional Neural Networks for Audio

Network definition

To speed up convergence and prevent overfitting, we can use NetReplace to add a BatchNormalizationLayer after every convolution:

audioLeNet=NetReplace[audioLeNet,{x_ConvolutionLayer:>NetChain[{x,BatchNormalizationLayer[]}]}];

rowPlot[audioLeNet]

Convolutional Neural Networks for Audio

Training

Now our net is ready for training! After defining a validation set on 5% of the training data, we can let NetTrain worry about all hyperparameters:

resultObject=NetTrain[
audioLeNet,
trainingData,
All,
ValidationSet->Scaled[.05]
]

(CopyTag[NetTrainResultsObject[({{({{({{(RowDefault["Total training time", ":"]), "1.4 min"}, {(RowDefault["Total rounds", ":"]), 10}, {(RowDefault["Total batches", ":"]), 1490}, {(RowDefault["Batch size", ":"]), 64}, {(RowDefault["Method", ":"]), "ADAM"}, {(RowDefault["Final round loss", ":"]), 0.02813286391187943`}, {(RowDefault["Final validation loss", ":"]), 0.28785972855985165`}, {(RowDefault["Final round error", ":"]), "0.587%"}, {(RowDefault["Final validation error", ":"]), "7.81%"}}), ({{(RowDefault["Loss evolution plot", ":"]), (RowDefault["Error evolution plot", ":"])}, {, }})}})}})]])

(*SetDirectory[NotebookDirectory[]]
Export["audioLeNet_result.mx",resultObject]*)

SetDirectory[NotebookDirectory[]]

resultObject=Import["audioLeNet_result.mx"];

trainedCNN=resultObject["TrainedNet"];

Convolutional Neural Networks for Audio

We can use ClassifierMeasurements on the net to measure the performance:

Testing

cm=ClassifierMeasurements[trainedCNN,#Input->#Output&/@testingData];
cm["Accuracy"]
cm["ConfusionMatrixPlot"]

Object Detection

YOLO: Real-Time Object Detection

Object detection requires a neural net to perform at least two types of task

Predict the location of objects in an image

Predict the bounding box of the object

In addition, classification of the object type is usually required

Classify the detected objects

Object Detection

Feature extraction

Fully convolutional architecture

Shortcut connection between different scales

yolo=NetModel["YOLO V2 Trained on MS-COCO Data"];
NetExtract[yolo,"Conv"]

Object Detection

Classification

Three types of output:

coordinate regression

probability of an object

conditioned probability of a given class

NetExtract[yolo,"Region"]

Object Detection

Bounding box prediction

Predict offsets, not absolute values!

a grid gives a fixed prior on the box position

an anchor box gives a fixed prior on the box  size

The network need only to predict the adjustments

relative translation from the grid cell

scaling factor width and height

NetInformation[NetExtract[yolo,"BoxTransformation"],"SummaryGraphic"]

Object Detection

Net prediction

The model predicts a number of locations with corresponding probabilities:

There are 13×13 location prior and 5 size priors

A total of 845 prediction is generated

testImage=;
res=yolo[testImage];
Dimensions/@res

Object Detection

Net prediction

Visualize all the boxes scaled by their probability:

rectangles=Apply[Rectangle[{#1-#3/2,1-#2-#4/2},{#1+#3/2,1-#2+#4/2}]&,res["Boxes"],1];
Manipulate[
Graphics[
MapThread[{EdgeForm[{Opacity[#1+.01],Thickness[#1/100+.01]}],#2}&,{res["Objectness"]If[class===All,1,Extract[res["ClassProb"],{All,Position[labelsYOLO,class][[1,1]]}]],rectangles}],
BaseStyle->{FaceForm[]}
],
{class,{All,"cat","dog"}}
]

Object Detection

Final result

Only the best results are kept and overlapping boxes are deleted using non-max suppression

detection=netEvaluator[yolo][testImage]

Combine the detection result and the input image:

highlightDetection[testImage,detection]

Object Detection

Live!—hopefully =)

Dynamic[showDetection[ImageReflect[CurrentImage[],Left]]]

Thank you!

Init

General

summary := NetInformation[#, "SummaryGraphic"]&
Magnify[#,1.5]&;(*for summary graph*)

ColorNet evaluation functions

netEvaluator[net:NetModel["ColorNet Image Colorization Trained on ImageNet Competition Data"]][img_Image] := 
Image[Prepend[
	ArrayResample[net[img], Prepend[Reverse@ImageDimensions[img], 2]], 
	ImageData[ColorSeparate[img, "L"]]],
	Interleaving -> False, 
	ColorSpace -> "LAB"
]

Ademxapp evaluation functions

netEvaluator[net: NetModel["Ademxapp Model A1 Trained on PASCAL VOC2012 and MS-COCO Data"]][img_] :=
Block[
  { resized, encData, dec, mean, var, prob},
  resized = ImageResize[img, {504}];
  encData = Normal@NetExtract[net, "Input"];
  dec = NetExtract[net, "Output"];
  {mean, var} = Lookup[encData, {"MeanImage", "VarianceImage"}];
  prob = NetReplacePart[net,
     {"Input" -> 
       NetEncoder[{"Image", ImageDimensions@resized, 
         "MeanImage" -> mean, "VarianceImage" -> var}], 
      "Output" -> Automatic}
     ][resized];
  prob = ArrayResample[prob, 
    Append[Reverse@ImageDimensions@img, 21]];
  dec[prob]
  ]

indexToColor = 
  Prepend[Thread[
    Range[2, 21] -> ColorData["Atoms", "ColorList"][[1 ;; 20]]], 
   1 -> Black];

labelsAdemxapp = {"background", "airplane", "bicycle", "bird", "boat", 
   "bottle", "bus", "car", "cat", "chair", "cow", "table", "dog", 
   "horse", "motorcycle", "person", "plant", "sheep", "sofa", "train",
    "television"};

colorizeSegmentation[mask_] := Colorize[mask, ColorRules -> indexToColor]

highlightSegmentation[img_, mask_] := Block[
  {classes, maskPlot, composition},
  classes = DeleteDuplicates[Flatten@mask];
  maskPlot = colorizeSegmentation[mask];
  composition = ImageCompose[img, {maskPlot, 0.5}];
  Legended[
   composition, 
   SwatchLegend[indexToColor[[classes, 2]], labelsAdemxapp[[classes]]]]
  ]

Yolo evaluation functions

Write an evaluation function to scale the result to the input image size and suppress the least probable detections:

nonMaxSuppression[overlapThreshold_][detection_] := 
Module[{boxes, confidence},
	Fold[
		{list, new} |-> If[NoneTrue[list[[All, 1]], IoU[#, new[[1]]] > overlapThreshold &], Append[list, new], list],
		Sequence @@ TakeDrop[ReverseSortBy[detection, Last], 1]
	]
]
IoU := Area[RegionIntersection[##]]/Area[RegionUnion[##]]&

netEvaluator[net: NetModel["YOLO V2 Trained on MS-COCO Data"]][img_, detectionThreshold_ : .4, overlapThreshold_ : .5] := 
 Module[
  	{w, h, scale, coords, obj, classes, boxes, padding, classProb, bestClass, probable, finals},
  
  	{coords, classes, obj} = Values[net[img]];
  
  	(* transform coordinates into rectangular boxes *)
  	{w, h} = ImageDimensions[img];
  	scale = Max[{w, h}] / 416;
  	padding = 416 (1 - {w, h} / Max[{w, h}]) / 2;
  	boxes = Apply[
    		Rectangle[
      			scale {#1 - #3/2 - padding[[1]], 416 - #2 - #4/2 - padding[[2]]},
      			scale {#1 + #3/2 - padding[[1]], 416 - #2 + #4/2 - padding[[2]]}
      		] &,
    		416 coords,
    	1];
  	
  	(* each class probability is rescaled with the box objectivness *)
  	classProb = classes * obj;
  	(* filter by probability*)
  	(* very small probability are thresholded *)
  	probable = Position[Max /@ classProb , p_ /; p - 10^-2 > detectionThreshold];	
  	If[Length[probable] == 0, Return[{}]];
  	
  	(* gather the boxes of the same class and perform non-max suppression *)
  	bestClass = Last@*Ordering /@ classProb;
  	finals = Join @@ Values @ GroupBy[
      		MapThread[
       			{#1, #2, #3[[#2]]} &,
       			{Extract[boxes, probable], Extract[bestClass, probable], Extract[classProb, probable]}
       		],
      		#[[2]]&, nonMaxSuppression[overlapThreshold]
      	]
  ]

Highlight the detected bounding boxes on the original image:

labelsYOLO = {"person","bicycle","car","motorcycle","airplane","bus","train","truck","boat","traffic light","fire hydrant","stop sign","parking meter","bench","bird","cat","dog","horse","sheep","cow","elephant","bear","zebra","giraffe","backpack","umbrella","handbag","tie","suitcase","frisbee","skis","snowboard","sports ball","kite","baseball bat","baseball glove","skateboard","surfboard","tennis racket","bottle","wine glass","cup","fork","knife","spoon","bowl","banana","apple","sandwich","orange","broccoli","carrot","hot dog","pizza","donut","cake","chair","couch","potted plant","bed","dining table","toilet","tv","laptop","mouse","remote","keyboard","cell phone","microwave","oven","toaster","sink","refrigerator","book","clock","vase","scissors","teddy bear","hair drier","toothbrush"};

highlightDetection[img_, detection_] := HighlightImage[img,
	MapThread[{
	White,
	Inset[Framed[
		Style[labelsYOLO[[#2]],Black,FontSize->Scaled[1/12]],
		Background->GrayLevel[1,.6]],
		Last[#1],
		{Right,Top}],
	#1}&, Transpose[detection]
]]

showDetection[img_] := highlightDetection[img, netEvaluator[NetModel["YOLO V2 Trained on MS-COCO Data"]][img]]

Plotting functions

rowPlot = Row[{convPlot[#, ImageSize -> 120, BaselinePosition -> Scaled[.8]], #}, Spacer[1]]&;

layerColor = {
 ConvolutionLayer -> RGBColor[0.6, 0.7, .9],
 LinearLayer -> RGBColor[1, 0.2, 0.5],
 "Input" -> GrayLevel[.9],
 _ -> GrayLevel[.9]
};

ClearAll[convPlot]
convPlot[net_, p: _?NumericQ : 1/3, s: _?NumericQ : 1.2, opts: OptionsPattern[Graphics3D]] :=
Block[
	{layers, layerTypes, output, input, sizes, good, colors, reducer},
	
	layers = NetInformation[net,"Layers"];
	layerTypes = Prepend[Values[Head /@ layers], "Input"];
	
	output = NetExtract[#, "Output"]& /@ layers;
	input = NetExtract[First[layers], "Input"];
	sizes = Replace[Prepend[Values[output], input], 
		{
			x_/;Length[x] == 1 :> Join[x, {1,1}],
			x_/;Length[x] == 0 :> Prepend[{1,1}, x]
		},
		1
	];
	
	good = Position[sizes, x_/;Length[x] == 3, 1];
	{sizes, layerTypes} = Transpose[
		Extract[Transpose[{sizes, layerTypes}], good]
	];
	
	colors = Replace[layerTypes, layerColor, 1];
	
	If[Length[sizes] == 0, Return[Graphics3D[]]];
	
	reducer := Power[#, p]&;
	Graphics3D[
	{
		LightBlue,
		MapThread[
			Function[{color, ch,w,h,pos},
				{color, Cuboid[{pos,-w/2,-h/2}, {pos+ch,w/2,h/2}]}
			],
			{
				colors, 
				reducer@sizes[[All,1]],
				sizes[[All,2]],
				sizes[[All,3]],
				(Accumulate[Prepend[reducer@sizes[[;;-2,1]],0]]) s
			}
		]},
		opts,
		Lighting -> {{"Ambient",White}},
		Boxed -> False
	]
]