Data Science, Machine Learning und KI
Kontakt

In a recent blog post our CEO Sebastian Heinz wrote about Google’s newest stroke of genius – AutoML Vision. A cloud service „that is able to build deep learning models for image recognition completely fully automated and from scratch„. AutoML Vision is part of the current trend towards the automation of machine learning tasks. This trend started with automation of hyperparameter optimization for single models (Including services like SigOpt, Hyperopt, SMAC), went along with automated feature engineering and selection (see my colleague Lukas‘ blog post about our bounceR package) towards full automation of complete data pipelines including automated model stacking (a common model ensembling technique).

One company at the frontier of this development is certainly h2o.ai. They developed both a free Python/R library (H2O AutoML) as well as an enterprise ready software solution called Driverless AI. But H2O is by far not the only player on the field. This blog post will provide you with a short comparison between two freely available Auto ML solutions and compare them by predictive performance as well as general usability.

H2O AutoML

H2O AutoML is an extension to H2O’s popular java based open source machine learning framework with APIs for Python and R. It automatically trains, tunes and cross-validates models (including Generalized Linear Models [GLM], Gradient Boosting Machines [GBM], Random Forest [RF], Extremely Randomized Forest [XRF], and Neural Networks). Hyperparameter optimization is done using a random search over a list of reasonable parameters (both RF and XRF are currently not tuned). In the end, H2O produces a leaderboard of models and builds two types of stacked ensembles from the base models. One including all base models, the other including only the best base model of each family.

Model training can be controlled by either the number of models to be trained, or the total training time. Especially the later makes model training quite transparent. One of the big advantages of H2O is that all models are parallelized out-of-the-box.

auto-sklearn

auto-sklearn is an automated machine learning toolkit based on Python’s Scikit-Learn Library. A detailed explanation of auto-sklearn can be found in Feurer et al. (2015). In H2O AutoML, each model was independently tuned and added to a leaderboard. In auto-sklearn, the authors combine model selection and hyperparameter optimization in what they call „Combined Algorithm Selection and Hyperparameter optimization“ (CASH). This joint optimization problem is than solved using a tree-based Bayesian optimization methods called „Sequential Model-based Algorithm Configuration“ (SMAC) (see Bergstra 2011).

So contrary to H2O AutoML, auto-sklearn optimizes a complete modeling pipeline including various data and feature preprocessing steps as well as the model selection and hyperparameter optimization. Data preprocessing includes one-hot-encoding, scaling, imputation, and balancing. Feature preprocessing includes, among others, feature agglomeration, ICA and PCA. Algorithms included in auto-sklearn are similar to those in H2O AutoML, but in addition also includes more traditional methods like k-Nearest-Neighbors (kNN), Naive Bayes, and Support Vector Machines (SVM).

Similar to H2O AutoML, auto-sklearn includes a final model ensemble step. Whereas H2O AutoML uses simple but efficient model stacking, auto-sklearn uses ensemble selection. A greedy method that adds individual models iteratively to the ensemble if and only if they increase the validation performance. Like H2O, auto-sklearn allows model training to be controlled by the total training time.

Benchmark

In order to compare the predictive performance of H2O’s AutoML with auto-sklearn, one can conduct a small simulation study. My colleague André’s R package Xy offers a straightforward way to simulate regression datasets with linear, non-linear, and noisy relationships. Using multiple (ten in total) simulation runs makes the whole simulation a bit more robust. The following R code was used to simulate the data:

<span class="hljs-attr">library(Xy)</span>
<span class="hljs-attr">library(caret)</span>
<span class="hljs-attr">library(dplyr)</span>
<span class="hljs-attr">library(data.table)</span>
<span class="hljs-comment">
# Number of datasets</span>
<span class="hljs-attr">n_data_set</span> <span class="hljs-string"><- 10</span>

<span class="hljs-attr">for</span> <span class="hljs-string">(i in seq(n_data_set)) {</span>
<span class="hljs-comment">
# Sim settings</span>
<span class="hljs-attr">n</span> <span class="hljs-string"><- floor(runif(1, 1000, 5000))</span>
<span class="hljs-attr">n_num_vars</span> <span class="hljs-string"><- c(sample(2:10, 1), sample(2:10, 1))</span>
<span class="hljs-attr">n_cat_vars</span> <span class="hljs-string"><- c(0, 0)</span>
<span class="hljs-attr">n_noise_vars</span> <span class="hljs-string"><- sample(1:5, 1)</span>
<span class="hljs-attr">inter_degree</span> <span class="hljs-string"><- sample(2:3, 1)</span>
<span class="hljs-comment">
# Simulate data</span>
<span class="hljs-attr">sim</span> <span class="hljs-string"><- Xy(n = n, </span>
<span class="hljs-attr">numvars</span> = <span class="hljs-string">n_num_vars,</span>
<span class="hljs-attr">catvars</span> = <span class="hljs-string">n_cat_vars, </span>
<span class="hljs-attr">noisevars</span> = <span class="hljs-string">n_noise_vars, </span>
<span class="hljs-attr">task</span> = <span class="hljs-string">Xy_task(),</span>
<span class="hljs-attr">nlfun</span> = <span class="hljs-string">function(x) {x^2},</span>
<span class="hljs-attr">interactions</span> = <span class="hljs-string">1,</span>
<span class="hljs-attr">sig</span> = <span class="hljs-string">c(1,4), </span>
<span class="hljs-attr">cor</span> = <span class="hljs-string">c(0),</span>
<span class="hljs-attr">weights</span> = <span class="hljs-string">c(-10,10),</span>
<span class="hljs-attr">intercept</span> = <span class="hljs-string">TRUE,</span>
<span class="hljs-attr">stn</span> = <span class="hljs-string">4)</span>
<span class="hljs-comment">
# Get data and DGP</span>
<span class="hljs-attr">df</span> <span class="hljs-string"><- simdata</span>   <span class="hljs-attr">dgp</span> <span class="hljs-string"><- simdgp</span>
<span class="hljs-comment">
# Remove Intercept</span>
<span class="hljs-meta">df[,</span> <span class="hljs-string">"(Intercept)"] <- NULL</span>
<span class="hljs-comment">
# Rename columns</span>
<span class="hljs-meta">names(df)</span> <span class="hljs-string"><- gsub("(?<![0-9])0+", "", names(df), perl = TRUE)</span>
<span class="hljs-comment">
# Create test/train split</span>
<span class="hljs-attr">df</span> <span class="hljs-string"><- dplyr::rename(df, label = y)</span>
<span class="hljs-attr">in_train</span> <span class="hljs-string"><- createDataPartition(y = dflabel, p = 0.7, list = FALSE)</span>   <span class="hljs-attr">df_train</span> <span class="hljs-string"><- df[in_train, ]</span>   <span class="hljs-attr">df_test</span> <span class="hljs-string"><- df[-in_train, ]</span> <span class="hljs-comment">     # Path names</span>   <span class="hljs-attr">path_train</span> <span class="hljs-string"><- paste0("../data/Xy/", i, "_train.csv")</span>   <span class="hljs-attr">path_test</span> <span class="hljs-string"><- paste0("../data/Xy/", i, "_test.csv")</span> <span class="hljs-comment">     # Export</span>   <span class="hljs-meta">fwrite(df_train,</span> <span class="hljs-string">file = path_train)</span>   <span class="hljs-meta">fwrite(df_test,</span> <span class="hljs-string">file = path_test)</span>    <span class="hljs-attr">}</span> </code></pre> Since auto-sklearn is only available in Python, switching languages is necessary. Therefore, loading the raw data in Python is the next step: <pre><code class="language-python hljs"><span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd  <span class="hljs-comment"># Load data</span> df_train = pd.read_csv(<span class="hljs-string">"../data/Xy/1_train.csv"</span>) df_test = pd.read_csv(<span class="hljs-string">"../data/Xy/1_test.csv"</span>)  <span class="hljs-comment"># Columns</span> cols_train = df_train.columns.tolist() cols_test = df_test.columns.tolist()  <span class="hljs-comment"># Target and features</span> y_train = df_train.loc[:, <span class="hljs-string">"label"</span>] X_train = df_train.drop(<span class="hljs-string">"label"</span>, axis=<span class="hljs-number">1</span>)  y_test = df_test.loc[:, <span class="hljs-string">"label"</span>] X_test = df_test.drop(<span class="hljs-string">"label"</span>, axis=<span class="hljs-number">1</span>) </code></pre> Having the data in Python, the training procedure can start. In order to make the results comparable, both frameworks used, where possible, similar settings. This included 60 minutes of training for each dataset, 5-fold crossvalidation for model evaluation and ensemble building, no preprocessing (not available in H2O AutoML and therefore deactivated in auto-sklearn), and a limitation to similar algorithms (namely GLM, RF, XRF, and GBM).  As previously noted, H2O supports out-of-the-box parallelization. By default, auto-sklearn only uses two cores, while also supporting more cores, at least in theory. While there is a <a href="https://automl.github.io/auto-sklearn/stable/manual.html#parallel-computation">manual</a> on how to do that, I was not able to get it working on my system (OSX 10.13, Python 3.6.2 Anaconda). Therefore H2O was also limited to only two cores. <pre><code class="language-python hljs"><span class="hljs-keyword">from</span> autosklearn.regression <span class="hljs-keyword">import</span> AutoSklearnRegressor <span class="hljs-keyword">from</span> autosklearn.metrics <span class="hljs-keyword">import</span> mean_squared_error  <span class="hljs-comment"># Settings</span> estimators_to_use = [<span class="hljs-string">"random_forest"</span>, <span class="hljs-string">"extra_trees"</span>, <span class="hljs-string">"gradient_boosting"</span>, <span class="hljs-string">"ridge_regression"</span>] preprocessing_to_use = [<span class="hljs-string">"no_preprocessing"</span>]  <span class="hljs-comment"># Init auto-sklearn</span> auto_sklearn = AutoSklearnRegressor(time_left_for_this_task=<span class="hljs-number">60</span>*<span class="hljs-number">60</span>,                                     per_run_time_limit=<span class="hljs-number">360</span>,                                     include_estimators=estimators_to_use,                                     exclude_estimators=<span class="hljs-literal">None</span>,                                     include_preprocessors=preprocessing_to_use,                                     exclude_preprocessors=<span class="hljs-literal">None</span>,                                     ml_memory_limit=<span class="hljs-number">6156</span>,                                     resampling_strategy=<span class="hljs-string">"cv"</span>,                                     resampling_strategy_arguments={<span class="hljs-string">"folds"</span>: <span class="hljs-number">5</span>})  <span class="hljs-comment"># Train models</span> auto_sklearn.fit(X=X_train.copy(), y=y_train.copy(), metric=mean_squared_error) it_fits = auto_sklearn.refit(X=X_train.copy(), y=y_train.copy())  <span class="hljs-comment"># Predict</span> y_hat = auto_sklearn.predict(X_test)  <span class="hljs-comment"># Show results</span> auto_sklearn.cv_results_ auto_sklearn.sprint_statistics() auto_sklearn.show_models() auto_sklearn.get_models_with_weights() </code></pre> <pre><code class="language-python hljs"><span class="hljs-keyword">import</span> h2o <span class="hljs-keyword">from</span> h2o.automl <span class="hljs-keyword">import</span> H2OAutoML  <span class="hljs-comment"># Shart h2o cluster</span> h2o.init(max_mem_size=<span class="hljs-string">"8G"</span>, nthreads=<span class="hljs-number">2</span>)  <span class="hljs-comment"># Upload to h2o</span> df_train_h2o = h2o.H2OFrame(pd.concat([X_train, pd.DataFrame({<span class="hljs-string">"target"</span>: y_train})], axis=<span class="hljs-number">1</span>)) df_test_h2o = h2o.H2OFrame(X_test)  features = X_train.columns.values.tolist() target = <span class="hljs-string">"target"</span>  <span class="hljs-comment"># Training</span> auto_h2o = H2OAutoML(max_runtime_secs=<span class="hljs-number">60</span>*<span class="hljs-number">60</span>) auto_h2o.train(x=features,                y=target,                training_frame=df_train_h2o)  <span class="hljs-comment"># Leaderboard</span> auto_h2o.leaderboard auto_h2o = auto_h2o.leader  <span class="hljs-comment"># Testing</span> df_test_hat = auto_h2o.predict(df_test_h2o) y_hat = h2o.as_list(df_test_hat[<span class="hljs-string">"predict"</span>])  <span class="hljs-comment"># Close cluster</span> h2o.cluster().shutdown() </code></pre> The complete code, including all simulation runs and visualization of results can be find on my <a href="https://github.com/fabianmax/ML-Automation">GitHub repo</a>. <h2>Results</h2> First, some words of caution: The results presented in the next sections are by no mean representative. Both H2O and the authors of auto-sklearn recommend to run their frameworks for hours, if not even days. Given ten different datasets, this was beyond the scope of a blog post. For the same reason of feasibility, the datasets are restricted to a rather small size. For a more elaborated performance comparison see for example Balaji and Allen (2018).  Figure 1 shows the Mean Squared Error of both frameworks produced on the test sample. The horizontal line, indicating the result from a vanilla Random Forest (from scikit-learn), serves as a benchmark. As one can see, the results are pretty similar for both frameworks and all data sets. Actually, it is a tie, with five wins for H2O and five wins for auto-sklearn.  <img class="aligncenter size-full wp-image-15313" src="https://statworx-1727.demosrv.review/wp-content/uploads/results-ml-benchmark.png" alt="results ml benchmark" width="1920" height="857" />  The percentage difference between the average errors is1.04%in favor of auto-sklearn. Thus, auto-sklearn is on average about1%better than H2O. Compared with the vanilla RF, H2O's AutoML is on average23.4%better than the benchmark, while auto-sklearn is24.6%$ better.

The sheer closeness of the results can be further illustrated when taking a look at the predicted values. Figure 2 shows exemplary the predicted values for one particular dataset against all feature values (linear, non-linear and noise features). As one can see, the estimated effects for both frameworks are almost identical and pretty close to the actual relationship.

visualization ml benchmark

Summary

Automatic Machine Learning frameworks can provide promising results for standard machine learning task while keeping the manual efforts down to a minimum. This blog post compared two popular frameworks, namely H2O's AutoML and auto-sklearn. Both reached comparable results on ten simulated datasets, while outperforming vanilla models significantly. Beside predictive performance, H2O's AutoML offers some additional features like native parallelization, API for R, support for XGBoost and GPU training making it even more attractive.

References

Introduction

One of the highlights of this year’s H2O World was a Kaggle Grandmaster Panel. The attendees, Gilberto Titericz (Airbnb), Mathias Müller (H2O.ai), Dmitry Larko (H2O.ai), Marios Michailidis (H2O.ai), and Mark Landry (H2O.ai), answered various questions about Kaggle and data science in general.

One of the questions from the audience was which tools and algorithms the Grandmasters frequently use. As expected, every single of them named the gradient boosting implementation XGBoost (Chen and Guestrin 2016). This is not surprising, since it is long known that XGBoost is at the moment the probably most used algorithm in data science.

The popularity of XGBoost manifests itself in various blog posts. Including tutorials for R and Python, Hyperparameter for XGBoost, and even using XGBoost with Nvidia’s CUDA GPU support.

At STATWORX, we also frequently leverage XGBoost’s power for external and internal projects (see Sales Forecasting Automative Use-Case). One question that we lately asked ourselves was how big the difference between the two base learners (also called boosters) offered by XGBoost is? This posts tries to answer this question in a more systematic way.

Weak Learner

Gradient boosting can be interpreted as a combination of single models (so called base learners or weak learners) to an ensemble model (Natekin and Knoll 2013).
In theory, any base learner can be used in the boosting framework, whereas some base learners have proven themselves to be particularly useful: Linear and penalized models (Hastie et al. 2008), (B/P-) splines (Huang and Yang 2004), and especially decision trees (James et al. 2013). Among the less frequently used base learners are random effects (Tutz and Groll 2009), radial basis functions (Gomez-Verdejo et al. 2002), markov random fields (Dietterich et al. 2004) und wavlets (Dubossarsky et al. 2016).

Chen and Guestrin (2016) describe XGBoost as an additive function, given the data D = left{ left( x_{ i }, y_{ i } right) right}, of the following form:

    \[hat{y_{ i }} = Phi(x_{ i }) = sum_{ k = 1 }^K{ f_{ k }(x_{ i }), f_{ k } } in F\]

In their original paper, f_{ k }(x)forall k = 1, ... , K is defined as an classification or regression tree (CART). Apart from that, the alert reader of the technical documentation knows that one can alter the functional form of f_k(x) by using the booster argument in R/Python

# Example of XGBoost for regression in R with trees (CART)
xgboost(data = train_DMatrix,
        obj = "reg:linear".
        eval_metric = "rmse",
        booster = "gbtree")

One can choose between decision trees (gbtree and dart) and linear models (gblinear). Unfortunately, there is only limited literature on the comparison of different base learners for boosting (see for example Joshi et al. 2002). To our knowledge, for the special case of XGBoost no systematic comparison is available.

Simulation and Setup

In order to compare linear with tree base learners, we propose the following Monte Carlo simulation:

1) Draw a random number n from a uniform distribution [100, 2500].
2) Simulate four datasets, two for classification and two for regression, each having n observations.
3) On each dataset, train a boosting model with tree and linear base learners, respectively.
4) Calculate an appropriate error metric for each model on each dataset.

Repeat the outlined procedure m = 100 times.

As for simulation, we use the functions twoClassSim(), LPH07_1(), LPH07_2(), SLC14_1() from the caret package. In addition to the relevant features, a varying number of (correlated) random features was added. Note that in order to match real life data, all data generating processes involve non-linear components. For further details, we advise the reader to take a look at the caret package documentation.

For each dataset, we apply the same (random) splitting strategy, where 70% of the data goes to training, 15% is used for validation, and the last 15% is used for testing. Regarding hyperparameter tuning, we use a grid-search strategy in combination with 10-fold crossvalidation on the training data. Regardless of the base learner type, L1 (alpha) and L2 (lambda) regularization were tuned using a shared parameter space.
For tree boosting, the learning rate (eta) was held constant at 0.3 while tuning the optimal tree size (max_depth). Finally, we used a fixed number of 1000 boosting iterations (nrounds) in combination with ten early stopping rounds (early_stopping_rounds) on the validation frame. The final performance was evaluated by applying the model with the best crossvalidated parameters on the test dataset.

Results

Figure 1 and Figure 2 show the distributions of out of sample classification errors (AUC) and regression errors (RMSE) for both datasets. Associated summary statistics can be found in Table 1.

Table 1: Error summary statistics by datasets and base learners
Base learner Dataset Type Error metric Average error Error std.
Linear 1 Classification AUC 0.904 0.031
Tree 1 Classification AUC 0.934 0.090
Linear 2 Classification AUC 0.733 0.087
Tree 2 Classification AUC 0.730 0.062
Linear 3 Regression RMSE 45.182 2.915
Tree 3 Regression RMSE 17.207 9.067
Linear 4 Regression RMSE 17.383 1.454
Tree 4 Regression RMSE 6.595 3.104

For the first dataset, the models using tree learners are on average better than the models with linear learners. However, the tree models exhibit a greater variance. The relationships are reversed for the second dataset. On average, the linear models are slightly better and the tree models exhibit a lower variance.

result-oos-classification

In contrast to the classification case, there is for both regression datasets a substantial difference in performance in favor of the tree models. For the third dataset, the tree models are on average better than their linear counterparts. Also, the variance of the results is substantially higher for the tree models. The results are similar for the fourth dataset. The tree models are again better on average than their linear counterparts, but feature a higher variation.

result-oos-regression

Summary

The results from a Monte Carlo simulation with 100 artificial datasets indicate that XGBoost with tree and linear base learners yields comparable results for classification problems, while tree learners are superior for regression problems. Based on this result, there is no single recommendation which model specification one should use when trying to minimize the model bias. In addition, tree based XGBoost models suffer from higher estimation variance compared to their linear counterparts. This finding is probably related to the more sophisticated parameter space of tree models. The complete code can be found on github.

References

Das Perzeptron war der erste Typus eines künstlichen Neurons und wurde erstmals durch Frank Rosenbaltt in den späten 1950er Jahren vorgestellt. Das Design des Perzeptrons war durch das Neuronen-Modell nach McCulloch und Pitt inspiriert. Während heutzutage andere Typen von Neuronen das Perzeptron ersetzt haben, findet das grundlegende Design des Perzeptrons in modernen neuronalen Netzwerke weiterhin Anwendung.

Das Perzeptron kann zum Erlernen von linear separierbaren Klassifikationen eingesetzt werden. Hierfür rechnet es Inputs  left[ x_{1}, x_{2}, ... , x_{n} right] in einen binären Output y_{i} um. Gewichte  left[ w_{1}, w_{2}, ... , w_{n} right] beziffern die Wichtigkeit des jeweiligen Inputs für den Output. Der Output errechnet sich als die gewichtete Summe über die Inputs:

 y_{i }=sum_{i}{w _{i} x_{i}}

Schematische Darstellung des Perzeptrons

Abbildung Perzeptron

Um sicherzustellen, dass y_{ i } ein binärer Outcome ist, nutzt das Perzeptron eine Treppenfunktion (auch hard limiter genannt) mit einem geschätzten Schwellenwert, welcher auch Bias genannt wird (in der obenstehenden Abbildung als das Unit-Input dargestellt):

y_{i} = begin{cases} 0 &falls& wx + b lt 0 1 & text{sonst}end{cases}

Dabei ist  w times x equiv sum_{i}{{w_i} x_{i}} das Skalarprodukt von w und x mit dem Bias b. Eine Treppenfunktion ist eine nicht-lineare Funktion, welche die gewichtete Summe auf den gewünschten Wertebereich des Outputs abbildet. Eine Bemerkung am Rande: Auch moderne neuronale Netzwerke benötigen eine nicht-lineare Funktion – deren Form ist im Vergleich zur Treppenfunktion jedoch etwas glatter (und wird daher oft soft limiter genannt).

Beispiel einer Treppenfunktion

Abbildung Stufenfunktion

Das Perzeptron lernt durch das iterative Anpassen des Gewichtungsvektors w. Dies geschieht wie folgt:

w leftarrow dot w+v times (y_{ i } - hat y_{ i }) times x_{ i }

Dabei ist dot w der bisherige Gewichtungsvektors,  v in (0, infty) die Lernrate, und (y_{ i } - hat y_{ i }) der Fehler innerhalb der aktuellen Iteration x_{ i } bezeichnet das aktuelle Input. Die Anpassung der Gewichte erfolgt daher durch den mit dem bisherigen Fehler und der Lernrate gewichteten Input. Dies wird als Perzeptron-Lernregel bezeichnet.

Der nachstehende Python-Code illustriert den Mechanismus des Perzeptron-Lernens für das nachstehende, simple Problem:

Gegeben sind die Datenpunkte  x_{ i } = left{ [x_{ 1 }, x_{2}] right} und der Vektor y_{i}. der zugehörigen Outputs. Außerdem soll gelten y_{i} = 1 falls x_{1} = 1 oder  x_{2} = 1 und  y_{i} = 0 anderenfalls.

# Coden des Rosenblatt Perzeptrons  
# ---------------------------------------------------------- 
import numpy as np 
import random 
 
random.seed(1) 

# Treppenfunktion 
def unit_step(x): 
    if x  {} | {}".format('Index', 'Probability', 'Prediction', 'Target')) 
print('---') 

for index, x in enumerate(X): 
    y_hat = np.dot(x, w) 
    print("{}: {} -> {} | {}".format(index, round(y_hat, 3),
                                     unit_step(y_hat), y[index])) 
%matplotlib inline 
import matplotlib.pyplot as plt 

# Grafik Trainingsfehler  
plt.plot(range(n),errors) 
plt.xlabel('Epoche') 
plt.ylabel('Fehler') 
plt.title('Trainingsfehler') 
plt.show() 

Entwicklung des Trainingsfehlers über die Trainingsiterationen (Epochen)

Trainingsplot 1

Inputs, vorhergesagte Wahrscheinlichkeiten und Outputs

Index: Wahrscheinlichkeit Vorhersage Target
0 -0.033 0 0
1 0.382 1 1
2 0.521 1 1
3 0.936 1 1

Nach einigen hunderten Iterationen hat das Perzeptron die Gewichte so angepasst, dass alle Datenpunkte korrekt vorhergesagt werden. So ist das Perzeptron in der Lage, unser einfaches Klassifikationsproblem zu lösen. Wie aus der Grafik ersichtlich, sind 100 Epochen dabei mehr als genug und wir hätten den Lernprozess auch früher beenden können.

Wichtig ist hier zu beachten, dass es sich um eine reine In-sample Klassifikation handelt. Das berüchtigte Auswendiglernen (Overfitting) der Trainingsdaten spielt daher keine Rolle. In realen Problemstellungen wird Overfitting zum Beispiel dadurch verhindert, dass der Lernprozess bereits früher abgebrochen wird (das sogenannte Early-Stopping).

Einschränkungen und Limitierungen des Perzeptrons

Kurz nach seiner Publikation in den frühen 1960er Jahren, erregte das Perzeptron große Aufmerksamkeit und wurde allgemein als leistungsfähiger Lernalgorithmus angesehen. Diese Einschätzung änderte sich durch die berühmte Kritik von Minsky und Papert (1969) in den späten 1960er und frühen 1970er Jahren dramatisch.

Minsky and Papert bewiesen, dass das Perzeptron in dem, was es lernen kann, sehr eingeschränkt ist. Genauer zeigten sie, dass das Perzeptron die richtigen Features benötigt, um eine Klassifikationsaufgabe korrekt zu erlernen. Mit genug handverlesenen Features ist die Performanz des Perzeptrons äußerst gut. Ohne handverlesene Features verliert es jedoch unmittelbar an Lernvermögen.

Yudkowsky (2008 S. 15f.)(3) beschreibt ein Beispiel für das Versagen des Perzeptron aus den Anfangszeiten der neuronalen Netzwerke:

„Once upon a time, the US Army wanted to use neural networks to automatically detect camouflaged enemy tanks. The researchers trained a neural net on 50 photos of camouflaged tanks in trees, and 50 photos of trees without tanks. Using standard techniques for supervised learning, the researchers trained the neural network to a weighting that correctly loaded the training set—output “yes” for the 50 photos of camouflaged tanks, and output “no” for the 50 photos of forest. This did not ensure, or even imply, that new examples would be classified correctly.

The neural network might have “learned” 100 special cases that would not generalize to any new problem. Wisely, the researchers had originally taken 200 photos, 100 photos of tanks and 100 photos of trees. They had used only 50 of each for the training set. The researchers ran the neural network on the remaining 100 photos, and without further training the neural network classified all remaining photos correctly. Success confirmed! The researchers handed the finished work to the Pentagon, which soon handed it back, complaining that in their own tests the neural network did no better than chance at discriminating photos.

It turned out that in the researchers’ dataset, photos of camouflaged tanks had been taken on cloudy days, while photos of plain forest had been taken on sunny days. The neural network had learned to distinguish cloudy days from sunny days, instead of distinguishing camouflaged tanks from empty forest.“

Unser vorheriges Beispiel, in welchem das Perzeptron ausgehend von dem Input X= left{left[ 0,0 right], left[ 0,1 right], left[ 1,0 right], left[ 1,1 right] right} erfolgreich gelernt, hat den Output  y = left{ 0, 1, 1, 1 right} korrekt zu klassifizieren, kann als logische ODER-Funktion verstanden werden (y_{ 1 } = 1 falls  x_{ 1 } = 1 oder  x_{ 2 } = 1). Bei diesem Problem ist ODER als nicht exklusiv definiert.

Durch die folgende Abänderung, lässt sich das Problem in eine exklusive XODER-Funktion umgewandelt werden:  y = left{ 0, 1, 1, 0 right}

Es stellt sich die Frage, ob das Perzeptron immer noch in der Lage ist, diese neue Funktion korrekt zu approximieren?

random.seed(1)

# Dieselben Daten 
X = np.array([[0,0],  
              [0,1],  
              [1,0],  
              [1,1]]) 

# Zurücksetzen der Vektoren für Gewichte und Fehler 
w = np.random.rand(2) 
errors = [] 

# Aktualisieren der Outputs 
y = np.array([0,1,1,0]) 

# Nochmals: Training ... 
for i in range(n): 
    # Zeilenindex 
    index = random.randint(0,3) 

    # Minibatch 
    x_batch = X[index,:] 
    y_batch = y[index] 

    # Aktivierung berechnen 
    y_hat = unit_step(np.dot(w, x_batch)) 

    # Fehler berechnen und abspeichern 
    error = y_batch - y_hat 
    errors.append(error) 

    # Gewichte anpassen 
    w += eta * error * x_batch 

# ... und Vorhersage   
for index, x in enumerate(X): 
    y_hat = np.dot(x, w) 
    print("{}: {} -> {} | {}".format(index, round(y_hat, 3), unit_step(y_hat), y[index])) 
     
%matplotlib inline 
import matplotlib.pyplot as plt 

# Grafik Trainingsfehler  
plt.plot(range(n),errors)
plt.xlabel('Epoche') 
plt.ylabel('Fehler') 
plt.title('Trainingsfehler') 
plt.show() 

Entwicklung des Trainingsfehlers über die Trainingsiterationen (Epochen)

Trainingsplot 2

Inputs, vorhergesagte Wahrscheinlichkeiten und Outputs

Index: Wahrscheinlichkeit Vorhersage Target
0 0.000 1 0
1 0.024 1 1
2 0.156 1 1
3 0.180 1 1

Die Eigenheiten des veränderten Problems machen es dem Perzeptron schwer, die Aufgabe, gegeben der ausgewählten Inputs, korrekt zu erlernen. Wie von Minsky und Papert erörtert, sind die Features der Schlüssel zur Lösung des Problems durch das Perzeptron.

Fazit

Unglücklicherweise, wurde die Kritik von Minsky und Papert von einem großen Teil der Wissenschaftsgemeinschaft missverstanden: Wenn das Lernen der richtigen Features essentiell ist und neuronale Netzwerke allein nicht in der Lage sind, diese Features zu lernen, sind sie nutzlos für alle nicht-triviale Lernaufgaben. Diese Deutungsweise hielt sich für die nächsten 20 Jahre als weithin geteilter Konsens und führte zu einer dramatischen Verringerung des wissenschaftlichen Interesses an neuronalen Netzwerken als Lernalgorithmen.

Als weitere Konsequenz der Erkenntnisse von Minsky und Papert traten zwischenzeitlich andere Lernalgorithmen an die Stelle neuronaler Netze. Einer der prominentesten Algorithmen war die Support Vector Machine (SVM). Durch die Transformation des Input-Raums in einen nicht-linearen Feature-Raum, löste die SVM das Problem an denen neuronale Netzwerke scheinbar scheiterten.

Erst in den späten 1980er und frühen 1990er Jahren gelangte man langsam zu der Erkenntnis, dass neuronale Netze durchaus dazu fähig waren, nützliche, nicht-lineare Features aus Daten zu lernen. Möglich wurde dies durch die Aneinanderreihung mehrerer Schichten von Neuronen. Dabei lernt jede Schicht aus den Outputs der vorhergegangenen. Dies erlaubt das Ableiten von nützlichen Features aus den ursprünglichen Input Daten – also genau die Problemstellung die ein einzelnes Pezeptron nicht lösen kann.

Auch wenn heutiges Deep Learning (also Neuronale Netze mit vielen Schichten) deutlich komplexer ist als das ursprüngliche Perzeptron, baut es doch auf den Grundtechniken dessen auf.

Referenzen

  1. Rosenblatt, F. (1958). The perceptron: A probabilistic model for information storage and organization in the brain. Psychological review, 65(6), 386.
  2. Minsky, M. L., & Papert, S. A. (1987). Perceptrons-Expanded Edition: An Introduction to Computational Geometry.
  3. Yudkowsky, E. (2008). Artificial intelligence as a positive and negative factor in global risk. Global catastrophic risks, 1(303), 184.

Der Smalltalk mit einem Data Scientist endet früher oder später immer bei der Frage des Studienbackgrounds. Die Frage ist insofern naheliegend, als dass reine Data Science Studiengänge in Deutschland gerade erst am Anlaufen sind. Die bereits am Markt aktiven Data Scientists stammen daher fast ausschließlich aus fachfremden Studiengängen.

Das Unbegreifen ist zumeist groß, wenn die Antwort auf die Frage des Studienbackgrounds nicht den gängigen Erwartungen – Physik, Mathe, Statistik oder Informatik – entspricht, sondern stattdessen eine Sozialwissenschaft (wie in meinem Fall Politikwissenschaft) genannt wird. Im Folgenden also ein Plädoyer warum auch die Sozialwissenschaften gute Data Scientisten hervorbringen können.

Gutes Basiswissen

Zunächst einmal muss man wissen, dass in vielen Sozialwissenschaften (insbesondere Politikwissenschaft, Soziologie und Psychologie) eine stark datengestützte Forschung bereits seit vielen Jahrzehnten zum wissenschaftlichen Standard gehört. Vorlesungen in Statistik, aber auch Einführungen in verschiedene Programmiersprachen sind daher oftmals elementarer Bestandteil des Lehrplans. So gehören lineare, nicht-parametrische und hierarchische Modelle zum Standard in der modernen empirischen Politikwissenschaft. Das grundlegende Handwerkszeug eines Data Scientists ist damit bekannt. Ergänzt werden diese Grundlagen durch weitere nützliche Methoden, wie zum Beispiel Cluster- und Faktoranalyse, Survivalanalysen und Bayesianische Schätzverfahren.

Darüber hinaus ergeben sich insbesondere aus der eigenständigen Forschung, ein Kernbestandteil vieler sozialwissenschaftlicher Studiengänge, wichtige praktische Erfahrungen für den späteren Beruf des Data Scientists. Zu nennen ist hier zum einen der Umgang mit fehlenden Daten zum anderen aber auch Datenqualität im Allgemeinen.

Lernen für die Praxis

Aus meiner eigenen Erfahrung möchte ich auf drei Lehren aus meinem Studium eingehen, die mir später den Einstieg als Data Scientist erleichtert haben:

Erstens, formuliere und löse eine Fragestellung. Identifiziere eine Fragestellung, übertrage sie in ein durch Daten lösbares Modell und übersetze die Antworten deines Modells zurück auf deine Fragestellung. Diese Methodik, der Kern meiner wissenschaftlichen Ausbildung, lässt sich so eins zu eins auf jedes Projekt eines Data Scientists übersetzen. Auch wenn Art und Abstraktionsgrad der Frage variieren, bleibt die Herangehensweise doch immer dieselbe. Nicht zu vernachlässigen ist dabei die Übersetzung der Ergebnisse: Da in der Regel nicht der Data Scientist der Endanwender ist, muss dieser die Erkenntnisse seiner Arbeit entsprechend kommunizieren können.

Zweitens, erzähle eine Geschichte anhand der Daten. Um mit unserer Arbeit einen Mehrwert generieren zu können, benötigt es auch Überzeugungsarbeit. Auch ein noch so gutes Modell wird kein Gehör finden, wenn die Adressaten ihm nicht vertrauen. Als Data Scientist ist es auch unsere Aufgabe, dieses Vertrauen zu etablieren. Wie in den meisten sozialwissenschaftlichen Publikationen zu finden, ist es daher empfehlenswert, neben dem eigentlichen Modell auch eine fundierte Exploration der Daten zu erarbeiten. Diese hilft Außenstehenden das Modell und seine Wirkweise handhabbar zu machen. Darüber hinaus hilft sie uns aber auch selbst das Modell zu entwickeln und entscheidende Aspekte der Daten richtig zu modellieren.

Drittens, erkenne, dass dein Modell nur eine Abstraktion der Wirklichkeit ist. Auch wenn die uns zur Verfügung stehenden Methoden immer komplexer werden, so bleiben sie doch weiter nur Abstraktionen der Wirklichkeit. Als Sozialwissenschaftler weiß ich, ein Modell wird niemals jeden Einzelfall erklären können, sondern lediglich ein generalisierbares Muster vorhersagen. In den Sozialwissenschaften wird diesem Umstand Rechnung getragen, indem Unsicherheiten durch das Modell klar kommuniziert werden. Und etablierte Methoden, wie zum Beispiel Monte Carlo Simulationen, geben uns ein Werkzeug an die Hand mit der Unsicherheit modellierbar gemacht werden kann.

Fazit

Diese Skills, welche ich in meinem sozialwissenschaftlichem Studium erworben habe, machen mich heute zu einem erfolgreichen Data Scientist. Der Schritt von den Sozialwissenschaften zu Data Science ist für mich daher nicht überraschend, sondern äußerst naheliegend.

Der Smalltalk mit einem Data Scientist endet früher oder später immer bei der Frage des Studienbackgrounds. Die Frage ist insofern naheliegend, als dass reine Data Science Studiengänge in Deutschland gerade erst am Anlaufen sind. Die bereits am Markt aktiven Data Scientists stammen daher fast ausschließlich aus fachfremden Studiengängen.

Das Unbegreifen ist zumeist groß, wenn die Antwort auf die Frage des Studienbackgrounds nicht den gängigen Erwartungen – Physik, Mathe, Statistik oder Informatik – entspricht, sondern stattdessen eine Sozialwissenschaft (wie in meinem Fall Politikwissenschaft) genannt wird. Im Folgenden also ein Plädoyer warum auch die Sozialwissenschaften gute Data Scientisten hervorbringen können.

Gutes Basiswissen

Zunächst einmal muss man wissen, dass in vielen Sozialwissenschaften (insbesondere Politikwissenschaft, Soziologie und Psychologie) eine stark datengestützte Forschung bereits seit vielen Jahrzehnten zum wissenschaftlichen Standard gehört. Vorlesungen in Statistik, aber auch Einführungen in verschiedene Programmiersprachen sind daher oftmals elementarer Bestandteil des Lehrplans. So gehören lineare, nicht-parametrische und hierarchische Modelle zum Standard in der modernen empirischen Politikwissenschaft. Das grundlegende Handwerkszeug eines Data Scientists ist damit bekannt. Ergänzt werden diese Grundlagen durch weitere nützliche Methoden, wie zum Beispiel Cluster- und Faktoranalyse, Survivalanalysen und Bayesianische Schätzverfahren.

Darüber hinaus ergeben sich insbesondere aus der eigenständigen Forschung, ein Kernbestandteil vieler sozialwissenschaftlicher Studiengänge, wichtige praktische Erfahrungen für den späteren Beruf des Data Scientists. Zu nennen ist hier zum einen der Umgang mit fehlenden Daten zum anderen aber auch Datenqualität im Allgemeinen.

Lernen für die Praxis

Aus meiner eigenen Erfahrung möchte ich auf drei Lehren aus meinem Studium eingehen, die mir später den Einstieg als Data Scientist erleichtert haben:

Erstens, formuliere und löse eine Fragestellung. Identifiziere eine Fragestellung, übertrage sie in ein durch Daten lösbares Modell und übersetze die Antworten deines Modells zurück auf deine Fragestellung. Diese Methodik, der Kern meiner wissenschaftlichen Ausbildung, lässt sich so eins zu eins auf jedes Projekt eines Data Scientists übersetzen. Auch wenn Art und Abstraktionsgrad der Frage variieren, bleibt die Herangehensweise doch immer dieselbe. Nicht zu vernachlässigen ist dabei die Übersetzung der Ergebnisse: Da in der Regel nicht der Data Scientist der Endanwender ist, muss dieser die Erkenntnisse seiner Arbeit entsprechend kommunizieren können.

Zweitens, erzähle eine Geschichte anhand der Daten. Um mit unserer Arbeit einen Mehrwert generieren zu können, benötigt es auch Überzeugungsarbeit. Auch ein noch so gutes Modell wird kein Gehör finden, wenn die Adressaten ihm nicht vertrauen. Als Data Scientist ist es auch unsere Aufgabe, dieses Vertrauen zu etablieren. Wie in den meisten sozialwissenschaftlichen Publikationen zu finden, ist es daher empfehlenswert, neben dem eigentlichen Modell auch eine fundierte Exploration der Daten zu erarbeiten. Diese hilft Außenstehenden das Modell und seine Wirkweise handhabbar zu machen. Darüber hinaus hilft sie uns aber auch selbst das Modell zu entwickeln und entscheidende Aspekte der Daten richtig zu modellieren.

Drittens, erkenne, dass dein Modell nur eine Abstraktion der Wirklichkeit ist. Auch wenn die uns zur Verfügung stehenden Methoden immer komplexer werden, so bleiben sie doch weiter nur Abstraktionen der Wirklichkeit. Als Sozialwissenschaftler weiß ich, ein Modell wird niemals jeden Einzelfall erklären können, sondern lediglich ein generalisierbares Muster vorhersagen. In den Sozialwissenschaften wird diesem Umstand Rechnung getragen, indem Unsicherheiten durch das Modell klar kommuniziert werden. Und etablierte Methoden, wie zum Beispiel Monte Carlo Simulationen, geben uns ein Werkzeug an die Hand mit der Unsicherheit modellierbar gemacht werden kann.

Fazit

Diese Skills, welche ich in meinem sozialwissenschaftlichem Studium erworben habe, machen mich heute zu einem erfolgreichen Data Scientist. Der Schritt von den Sozialwissenschaften zu Data Science ist für mich daher nicht überraschend, sondern äußerst naheliegend.