Erzeugung synthetischer Daten

Synthetische Daten mit Python

Roboter erzeugen synthetische Daten

Ein Problem des maschinellen Lernens, - insbesondere wenn man sich am Einarbeiten ist mehr über die Algorithmen erfahren möchte, - besteht darin, dass es oft schwierig ist, geeignete Testdaten zu erhalten. Einige kosten viel Geld, andere sind nicht frei verfügbar, weil sie urheberrechtlich geschützt sind. Deshalb können künstlich erzeugte Testdaten in einigen Fällen eine Lösung sein.

Aus diesem Grund befasst sich dieses Kapitel unseres Tutorials mit der künstlichen Datenerzeugung. In diesem Kapitel geht es darum, künstliche Daten zu erstellen. In den vorherigen Kapiteln unseres Tutorials haben wir gelernt, dass Scikit-Learn (sklearn) verschiedene Datensätze enthält. Einerseits gibt es kleine Spielzeug-Datensätze, andererseits bietet dieses Modul auch größere Datensätze, die häufig verwendet werden, um Algorithmen zu testen oder auch als Benchmark zu dienen. sklearn bietet uns Daten, die aus der "realen Welt" stammen.

All dies ist großartig, aber in vielen Fällen reicht dies immer noch nicht aus. Vielleicht findet man die richtigen Art von Daten, aber man benötigt mehr Daten dieser Art, oder die Daten entsprechen nicht vollständig dem, was man sucht. So benötigt man beispielsweise komplexere oder weniger komplexere Daten. Dies ist der Punkt, an dem man in Betracht ziehen sollte, die Daten selbst zu erstellen. Hier bietet sklearn Hilfe an. Es enthält verschiedene random-Beispielgeneratoren, mit denen benutzerdefinierte künstliche Datasets erstellt werden können. Datasets, die den eigenen Ideen von Größe und Komplexität entsprechen.

Der folgende Python-Code ist ein einfaches Beispiel, eines welches noch nicht sklearn benutzt. Wir erzeugen künstliche Wetterdaten für einige deutsche Städte. Dazu verwenden wir Pandas und Numpy, um die Daten zu erstellen:

In [2]:
import numpy as np
import pandas as pd


cities = ['Berlin', 'Frankfurt', 'Hamburg', 
          'Nuremberg', 'Munich', 'Stuttgart',
          'Hanover', 'Saarbruecken', 'Cologne',
          'Constance', 'Freiburg', 'Karlsruhe'
         ]

n= len(cities)
data = {'Temperature': np.random.normal(24, 3, n),
        'Humidity': np.random.normal(78, 2.5, n),
        'Wind': np.random.normal(15, 4, n)
       }
df = pd.DataFrame(data=data, index=cities)
df
Out[2]:
Temperature Humidity Wind
Berlin 29.670181 77.909789 15.326861
Frankfurt 25.089194 79.063827 10.017392
Hamburg 25.392229 82.519618 14.002017
Nuremberg 22.244233 77.554024 15.386597
Munich 22.495137 78.556159 10.213197
Stuttgart 26.127598 78.201994 17.273097
Hanover 25.404560 78.031744 16.247042
Saarbruecken 20.264288 77.069132 16.027832
Cologne 24.762441 78.144061 17.118195
Constance 30.681213 78.560835 18.075235
Freiburg 21.801197 77.634437 13.658234
Karlsruhe 25.058005 76.893427 13.194192

Ein weiteres Beispiel

Wir erzeugen nun synthetische Daten für vier nicht existierende Blumensorten:

  • Flos Pythonem
  • Flos Java
  • Flos Margarita
  • Flos artificialis

Die durchschnittlichen RGB-Farbwerte entsprechen folgenden Werten:

  • (255, 0, 0)
  • (245, 107, 0)
  • (206, 99, 1)
  • (255, 254, 101)

Der durchschnittliche Durchmesser des Calyx ist:

  • 3.8
  • 3.3
  • 4.1
  • 2.9
Flos pythonem
(254, 0, 0)
Flos Java
(245, 107, 0)
Flos margarita
(206, 99, 1)
Flos artificialis
(255, 254, 101)
In [3]:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

from scipy.stats import truncnorm

def truncated_normal(mean=0, sd=1, low=0, upp=10, type=int):
    return truncnorm(
        (low - mean) / sd, (upp - mean) / sd, loc=mean, scale=sd)

def truncated_normal_floats(mean=0, sd=1, low=0, upp=10, num=100):
    res = truncated_normal(mean=mean, sd=sd, low=low, upp=upp)
    return res.rvs(num)

def truncated_normal_ints(mean=0, sd=1, low=0, upp=10, num=100):
    res = truncated_normal(mean=mean, sd=sd, low=low, upp=upp)
    return res.rvs(num).astype(np.uint8)

# number of items for each flower class:
number_of_items_per_class = [190, 205, 230, 170]
flowers = {}
# flos Pythonem:
number_of_items = number_of_items_per_class[0]
reds = truncated_normal_ints(mean=254, sd=18, low=235, upp=256,
                             num=number_of_items)
greens = truncated_normal_ints(mean=107, sd=11, low=88, upp=127,
                             num=number_of_items)
blues = truncated_normal_ints(mean=0, sd=15, low=0, upp=20,
                             num=number_of_items)
calyx_dia = truncated_normal_floats(3.8, 0.3, 3.4, 4.2,
                             num=number_of_items)
data = np.column_stack((reds, greens, blues, calyx_dia))
flowers["flos_pythonem"] = data

# flos Java:
number_of_items = number_of_items_per_class[1]
reds = truncated_normal_ints(mean=245, sd=17, low=226, upp=256,
                             num=number_of_items)
greens = truncated_normal_ints(mean=107, sd=11, low=88, upp=127,
                             num=number_of_items)
blues = truncated_normal_ints(mean=0, sd=10, low=0, upp=20,
                             num=number_of_items)
calyx_dia = truncated_normal_floats(3.3, 0.3, 3.0, 3.5,
                             num=number_of_items)
data = np.column_stack((reds, greens, blues, calyx_dia))
flowers["flos_java"] = data

# flos Java:
number_of_items = number_of_items_per_class[2]
reds = truncated_normal_ints(mean=206, sd=17, low=175, upp=238,
                             num=number_of_items)
greens = truncated_normal_ints(mean=99, sd=14, low=80, upp=120,
                             num=number_of_items)
blues = truncated_normal_ints(mean=1, sd=5, low=0, upp=12,
                             num=number_of_items)
calyx_dia = truncated_normal_floats(4.1, 0.3, 3.8, 4.4,
                             num=number_of_items)
data = np.column_stack((reds, greens, blues, calyx_dia))
flowers["flos_margarita"] = data

# flos artificialis:
number_of_items = number_of_items_per_class[3]
reds = truncated_normal_ints(mean=255, sd=8, low=2245, upp=2255,
                             num=number_of_items)
greens = truncated_normal_ints(mean=254, sd=10, low=240, upp=255,
                             num=number_of_items)
blues = truncated_normal_ints(mean=101, sd=5, low=90, upp=112,
                             num=number_of_items)
calyx_dia = truncated_normal_floats(2.9, 0.4, 2.4, 3.5,
                             num=number_of_items)
data = np.column_stack((reds, greens, blues, calyx_dia))
flowers["flos_artificialis"] = data


data = np.concatenate((flowers["flos_pythonem"], 
                      flowers["flos_java"],
                      flowers["flos_margarita"],
                      flowers["flos_artificialis"]
                     ), axis=0)

# assigning the labels
target = np.zeros(sum(number_of_items_per_class)) # 4 flowers
previous_end = 0
for i in range(1, 5):
    num = number_of_items_per_class[i-1]
    beg = previous_end
    target[beg: beg + num] += i
    previous_end = beg + num
    
conc_data = np.concatenate((data, target.reshape(target.shape[0], 1)),
                           axis=1)

np.savetxt("data/strange_flowers.txt", conc_data, fmt="%2.2f",)
In [4]:
import matplotlib.pyplot as plt

target_names = list(flowers.keys())
feature_names = ['red', 'green', 'blue', 'calyx']
n = 4
fig, ax = plt.subplots(n, n, figsize=(16, 16))

colors = ['blue', 'red', 'green', 'yellow']

for x in range(n):
    for y in range(n):
        xname = feature_names[x]
        yname = feature_names[y]
        for color_ind in range(1, len(target_names)+1):
            ax[x, y].scatter(data[target==color_ind, x], 
                             data[target==color_ind, y],
                             label=target_names[color_ind-1],
                             c=colors[color_ind-1])

        ax[x, y].set_xlabel(xname)
        ax[x, y].set_ylabel(yname)
        ax[x, y].legend(loc='upper left')


plt.show()
synthetische_daten_erzeugen_maschinelles_lernen: Graph 0

Synthetische Daten mit sklearn erzeugen

Deutlich einfacher gestaltet sich die Erzeugung von synthetischen Daten mit dem Modul sklearn.

Die in sklearn verfügbaren Funktionalitäten können wir folgt gruppiert werden:

  1. Generatoren zur Klassifikation und Clustering
  2. Generatoren zur Erzeugung von Daten zur Regression
  3. Generatoren für "Manifold Learning"
  4. Generatoren für Zerlegungs-Probleme (Decomposition)

Generatoren zur Klassifikation und Clustering

Wir starten mit der Funktion make_blobs von sklearn.datasets um Klecks-ähnliche (englisch: blob) Daten-Strukturen zu erzeugen. Indem wir den Wert von centers auf n_classes setzen, bestimmen wir die Anzahl der Blobs, d. h. der Cluster. n_samples entspricht der Anzahl der Datenpunkte, gleichverteilt über alle Klassen. Falls random_state nicht gesetzt ist, werden wir jedesmal, wenn wir die Funktion aufrufen, andere Zufallwerte erhalten. Wir weisen diesem Parameter eine ganze Zahl zu, um nachvollziehbare WErte zu erzeugen.

In [5]:
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
import numpy as np

data, labels = make_blobs(n_samples=1000, 
                          #centers=n_classes, 
                          centers=np.array([[2, 3], [4, 5], [7, 9]]),
                          random_state=1)

labels = labels.reshape((labels.shape[0],1))
all_data = np.concatenate((data, labels), axis=1)
all_data[:10]
np.savetxt("squirrels.txt", all_data)
all_data[:10]
Out[5]:
array([[ 1.72415394,  4.22895559,  0.        ],
       [ 4.16466507,  5.77817418,  1.        ],
       [ 4.51441156,  4.98274913,  1.        ],
       [ 1.49102772,  2.83351405,  0.        ],
       [ 6.0386362 ,  7.57298437,  2.        ],
       [ 5.61044976,  9.83428321,  2.        ],
       [ 5.69202866, 10.47239631,  2.        ],
       [ 6.14017298,  8.56209179,  2.        ],
       [ 2.97620068,  5.56776474,  1.        ],
       [ 8.27980017,  8.54824406,  2.        ]])
In [ ]: