BFFT Techbog Dezember: H20, when should I go to work?
01.06.2017

BFFT Techblog: H2O, when should I go to work?

Thema: Wie man mit einfachen Mitteln einen Prototypen für eine Machine-Learning-Applikation mit H2O entwickeln kann.

 

Motivation
Jeden Tag, wenn ich in die Arbeit fahre, bilde ich mir ein, dass der Verkehr wieder dichter geworden ist. Kommt mir das nur so vor oder ist das wirklich der Fall? Lässt sich gar eine optimale Zeit finden, wann man morgens früh zur Arbeit fahren sollte?

Als Data-Science-Freak sollte das doch eine relativ einfache Aufgabe sein? Danke Digitialisierung, diese lässt sich mittlerweile fast überall finden, unter anderem in Form von Webcams auf Autobahnen, die nur darauf warten, eingehender ausgewertet zu werden.

Vorverarbeitung
Für diese Aufgabe kann die BayernInfo-Webseite zur Datensammlung in Betracht gezogen werden1. Die Bilder der Webcam werden etwa alle 60 Sekunden aktualisiert. Dafür habe ich einen Cronjob für die Dauer von zwei Wochen eingerichtet. Dieser Job speichert morgens zwischen 7 und 10 Uhr alle 5 Minuten das aktuelle Bild auf die Festplatte.

Die weitere Verarbeitung und Analysen erfolgen unter Verwendung von R2. Mit dem imager package3 können wir Bilder problemlos verarbeiten und anzeigen. Ein Beispielbild sieht wie folgt aus:

unnamed-chunk-1-1
Außerdem können wir nur die linke Seite des Bildes nehmen, die den Weg nach Ingolstadt darstellt.

library(imager)
img <- imsub(grayscale(img), y > 32, x <= 320)
plot(img)

unnamed-chunk-2-1

Hierbei ist es ein sehr gängiges Verfahren, sliding windows genannt, das betreffende Bild in Teilbilder4 5 6 zu zerlegen.

So teilen wir jedes Bild in 16 Teilbilder auf. Im Folgenden sehen wir auf der linken Seite ein Teilbild ohne Auto und auf der rechten Seite ein Teilbild mit einem Auto darauf.

imgTile1 <- imsub(img, y > 224 & y <= 336, x > 0 & x <= 80)
imgTile2 <- imsub(img, y > 224 & y <= 336, x > 80 & x <= 160)

layout(t(1:2))
plot(imgTile1)
plot(imgTile2)

unnamed-chunk-3-1

Im Folgenden werden wir allen Bildern die Kategorien 0, 1 und 2 zuordnen. Hier bedeutet 0, dass kein Fahrzeug auf dem Bild zu erkennen ist. Kategorie 1 bedeutet, dass ein Fahrzeug erkennbar ist und Kategorie 2, dass zwei oder mehr Fahrzeuge auf dem Bild identifiziert werden können.

Dadurch kann die Anzahl der Fahrzeuge für jedes Teilbild bestimmt werden. Durch das Zusammenzählen der Fahrzeuge auf den 16 Teilbildern können wir die ungefähre Anzahl der Fahrzeuge auf dem Gesamtbild abschätzen. An dieser Stelle ist anzumerken, dass die Perspektive im Bild verzerrt ist. Es ist möglich, dass rechts oben mehr Fahrzeuge auf einem Teilbild zu finden sind, als zum Beispiel links unten, da die Fahrzeuge kleiner erscheinen. Es wäre also besser, wenn der Bereich oben rechts noch feiner eingeteilt wäre. Die hier vorgestellte Aufteilung sollte aber für eine erste Schätzung ausreichend sein.

Dieses Verfahren ermöglicht es, die Aufgabe in mehrere Teilaufgaben zu unterteilen. Der Hauptvorteil besteht darin, dass die Verarbeitung der Teilbilder für die Prognose parallel erfolgen kann. Auf alle Bilder wird eine Grauskalierung vorgenommen, da die Farbe der Fahrzeuge für deren Erkennung nicht von Bedeutung ist.

Trainingsphase
Für das Training werden die Bilder eines Tages aus dem zweiwöchigen Beobachtungszeitraum genommen. Diese Bilder werden manuell gelabelt.

Die gelabelten Teilbilder legen wir in den Ordnern “/labeled/0”, “labeled/1” und “labeled/2” ab. So können wir zum Beispiel die gelabelten Teilbilder mit einem Auto darauf wie folgt zum Trainingsdatensatz hinzufügen:

filelist1 <- list.files(paste0(getwd(),"/data/labeled/1"), full.names = TRUE)
train <- NULL
for (img in filelist1) {
  im.tmp <- load.image(img)
  train <- rbind(
    train, 
    data.frame(label = "1", feature = matrix(im.tmp[,,1,1], nrow = 1)))

Wenn die Bilder mit der load.image Funktion geladen werden, werden sie in R als Array mit vier Dimensionen (x, y, z, c) gespeichert. Hier repräsentieren x und y die räumliche Dimension, z die Tiefendimension (entspricht der Zeit in einem Film), und c die Farbdimension7. Da wir ein Graustufenbild vorliegen haben, folgt z = 1 und c = 1. Für die weitere Verarbeitung konvertieren wir die x×y Matrix in eine 1×(xyMatrix.

Im Anschluss lässt sich ein Modell mit H2O8 trainieren. H2O ist eine Open-Source-Software, die Algorithmen für Statistik, Data Mining und maschinelles Lernen im Kontext von Big Data bietet. H2O wurde in Java implementiert. Darüber hinaus bietet H2O Rest APIs, daher kann man H2O auch mit JavaScript, R, Python, Excel/Tableau und Flow, was einer Notebook-Benutzeroberfläche entspricht, aufrufen,

download

Quelle:http://docs.h2o.ai/h2o/latest-stable/h2o-docs/architecture.html#h2o-software-stack

Zudem kann H2O über Spark- oder Hadoop-Interfaces aufgerufen werden. H2O bietet viele verschiedene Modelle für maschinelles Lernen wie Generalized Linear Model (GLM), Gradient Boosting Machine (GBM) oder Random Forest (RF). Auch wenn im Normalfall häufig ein Deep-Learning-Ansatz für die Bilderkennung verwendet wird, liefert ein Random-Forest-Ansatz oft gute Ergebnisse, weshalb wir es in diesem Fall ausprobieren sollten:

library(h2o)

## use more threads one a multicore server
h2o.init(nthreads = 1) 

## import train data set to H2O
train.hex <- as.h2o(train)

## train a random forest model while using a 10-fold cross-validation 
## to avoid overfitting the first column is the target, the remaining 
## columns are the features
model.rf <- h2o.randomForest(y = 1, x = 2:ncol(train.hex), 
                             training_frame = train.hex, 
                             ntrees = 200, nfolds = 10)

Sehen wir uns die Modell-Performance an, die uns H2O unmittelbar liefert:

library(caret)
confusionMatrix(as.table(as.matrix(
  model.rf@model$cross_validation_metrics@metrics$cm$table[1:3, 1:3])))

## Output: Confusion Matrix and Statistics
## 
##     0   1   2
## 0 440   2   3
## 1  46  24   8
## 2   5  10  54
## 
## Overall Statistics
##                                           
##                Accuracy : 0.875           
##                  95% CI : (0.8456, 0.9006)
##     No Information Rate : 0.8294          
##     P-Value [Acc > NIR] : 0.001362        
##                                           
##                   Kappa : 0.6486          
##  Mcnemar's Test P-Value : 6.364e-09       
## 
## Statistics by Class:
## 
##                      Class: 0 Class: 1 Class: 2
## Sensitivity            0.8961  0.66667  0.83077
## Specificity            0.9505  0.90288  0.97154
## Pos Pred Value         0.9888  0.30769  0.78261
## Neg Pred Value         0.6531  0.97665  0.97897
## Prevalence             0.8294  0.06081  0.10980
## Detection Rate         0.7432  0.04054  0.09122
## Detection Prevalence   0.7517  0.13176  0.11655
## Balanced Accuracy      0.9233  0.78477  0.90115

In der Konfusionsmatrix erhalten wir horizontal die vorhergesagten und vertikal die Istwerte für die Kategorien 0 bis 2. Das bedeutet, dass in 440 Fällen Kategorie 0 vorhergesagt wurde, was korrekt war, aber in 46 Fällen wäre Kategorie 1 und in 5 Fällen Kategorie 2 die richtige Kategorie gewesen. Das Modell lag hier falsch. Die berechnete Genauigkeit, welche der Summe der Werte auf der Diagonalen, dividiert durch die Anzahl aller Fälle entspricht, beträgt 87,5 Prozent, was nicht schlecht erscheint..

Prognosephase
Nun sind wir bereit, unser trainiertes Modell auf die Daten der verbleibenden Tage anzuwenden:

pred <- h2o.predict(object = model.rf, newdata = test.hex)

## shutdown the java process 
h2o.shutdown(prompt = FALSE)

Für jeden Tag innerhalb von zwei Wochen erhalten wir die geschätzte Anzahl von Fahrzeugen zwischen 07:00 und 10:00 Uhr in 5 Minuten Schritten. Diese Daten sehen so aus:

##                      t ncars  wday
## 1: 2016-09-29 07:00:02     4 Thurs
## 2: 2016-09-29 07:05:02    10 Thurs
## 3: 2016-09-29 07:10:02     7 Thurs
## 4: 2016-09-29 07:15:02     8 Thurs
## 5: 2016-09-29 07:20:01     9 Thurs
## 6: 2016-09-29 07:25:01     3 Thurs

Sehen wir uns die Anzahl der geschätzten Fahrzeuge unabhängig vom Wochentag an:

library(ggplot2)
p <- ggplot(data = data.cars, aes(t, ncars)) + 
  geom_point() + geom_smooth() + theme_light()
p

unnamed-chunk-12-1

Zwischen 8 und 9 Uhr morgens scheinen die wenigsten Fahrzeuge unterwegs zu sein. Doch gibt es Unterschiede zwischen den einzelnen Wochentagen?

p + facet_wrap(~wday)

unnamed-chunk-13-1

Montag und Dienstag scheint es ratsam zu sein, so spät wie möglich gegen 9 Uhr zur Arbeit zu fahren. An den übrigen Wochentagen sollte man lieber eher aufstehen und zwischen 8 und 9 Uhr fahren.

Zusammenfasssung

Es ist klar, dass das vorliegende Beispiel eines Beobachtungszeitraums von zwei Wochen nicht für das morgendliche Verkehrsvolumen eines bestimmten Autobahnabschnitts repräsentativ sein kann. Ziel war es, zu zeigen, wie ein Prototyp einer Anwendung für maschinelles Lernen mit H2O mit relativ geringem Aufwand entwickelt werden kann.

H2O wird als Framework für maschinelles Lernen verwendet, da es Wrapper für viele Sprachen besitzt, wie R, Python oder Scala. Intern ist H2jedoch in Java implementiert. Wenn ein H2O-Cluster eingerichtet ist, kann man über eine REST API auf ihn zugreifen. Außerdem kann H2O auf Apache Spark zugreifen. Daher scheint es sehr geeignet zu sein, H2O in eine Big-Data-Anwendung zu integrieren.

1. http://www.bayerninfo.de/webcams/webcams-all-muenchen-nuernberg

2. https://www.r-project.org/

3. https://cran.r-project.org/web/packages/imager/index.html

4. https://www.coursera.org/learn/machine-learning/lecture/bQhq3/sliding-windows

5. http://cs229.stanford.edu/proj2012/LiZahr-LearningToRecognizeObjectsInImages.pdf

6. http://datalab.lu/blog/2012/04/22/machine-learning-for-identification-of-cars/

7. https://cran.r-project.org/web/packages/imager/imager.pdf

8. http://www.h2o.ai/

 


Autor: Andreas W. (Entwicklung Konzepte & Tooling)
Kontakt: techblog@bfft.de 
Bildquellen: http://docs.h2o.ai/h2o/latest-stable/h2o-docs/architecture.html#h2o-software-stack; Andreas W.

Themenvorschau Januar 2017: „Node how to Express yourself with Angular“ – Wie man mit Node.js, AngularJS und Socket.IO eine Anwendung zur Visualisierung von Echtzeitdaten entwickelt.


Jobs bei BFFT: