Generierung synthetischer Daten am Beispiel des Myo Datensatzes¶
Dieses Notebook steht auch auf unserem GitHub repository zum Download bereit!
Die zielgerichtete Generierung synthetischer Daten stellt einen Aufgabengebiet dar, der in modernen Anwendungen immer mehr an Bedeutung gewinnt. Lösungsansätze werden immer dann benötigt, wenn datengetriebene Algorithmen in Bereichen angewandt werden sollen, in denen entweder die Nutzung der Daten durch Datenschutzrichtlinien verhindert wird oder die Anzahl der vorliegenden Daten unzureichend ist. In beiden Fällen sollen Muster und Strukturen der erzeugten Datensätze möglichst genau denen der Realität entsprechen.
Der Realdatensatz der Messungen aus dem ITS.ML Projekt „Transferlernen für Handprothesen im Alltagseinsatz“ ist relativ klein und soll durch den Einsatz von ML-basierten Techniken erweitert werden. Um die Güte der erzeugten Daten anschließend zu bewerten, sollen die Struktur der synthetischen sowie realen Messwerte untersucht und verglichen werden.
Wir verwenden in diesem Notebook die Python-Bibliotheken pickle, numpy, pandas, json, sdv, pomegranate und matplotlib.
Zuerst importieren wir einige der Bibliotheken, die wir allgemein benötigen.
import pickle as pkl
import numpy
import pandas as pd
import json
from sdv import Metadata
Nun laden wir den Datensatz aus dem Projekt „Transferlernen für Handprothesen im Alltagseinsatz“.
traindata = pkl.load(open('train_data.pkl', 'rb'))
Um einen Überblick zu erhalten, schauen wir uns die Daten an:
traindata
{'X_src': array([[ 0.26513725, 0.80925469, -0.17899497, ..., 1.90510437, 0.6089009 , 0.06501162], [ 0.46436403, 0.60281896, -0.35129274, ..., 1.77168589, 0.22558731, 0.1078886 ], [ 0.55298158, -0.22365066, 0.02378312, ..., 1.04053224, 0.90712137, 0.2639778 ], ..., [ 0.51281369, 0.03814471, 0.73968039, ..., 2.14570773, 0.57873488, 0.21001998], [ 0.58509906, -0.82795934, -0.35705888, ..., 1.64587802, -0.14940568, 0.17453354], [-0.54833626, 0.39404247, 0.10425345, ..., 2.6714668 , 1.15903582, -0.04043299]]), 'Y_src': array([0, 0, 0, ..., 0, 0, 0]), 'codebook': {0: 'no_movement', 1: 'hand_open', 2: 'hand_close', 3: 'supination', 4: 'pronation', 5: 'hand_open+supination', 6: 'hand_open+pronation', 7: 'hand_close+supination', 8: 'hand_close+pronation'}}
numpy.shape(traindata['X_src'])
(1538, 8)
numpy.shape(traindata['Y_src'])
(1538,)
Der Datensatz besteht also aus 1538 Messungen, In X_src
befinden sich die Messwerte der jeweils 8 Sensoren des Armbands und in Y_src
die zugehörigen Codes für die aktuelle Handbewegung.
Multi-Table Modell¶
Um mit diesen Daten ein Modell trainieren zu können, wandeln wir die in testdata
enthaltenen Arrays in Pandas DataFrames um und versehen die Tabelle der Messwerte zusätzlich mit einer Spalte, die den Messwerten die zugehörigen Code-Werte per ìd
zuordnet.
xsrc = pd.DataFrame(traindata['X_src'], columns=["sensor1", "sensor2", "sensor3", "sensor4", "sensor5", "sensor6", "sensor7", "sensor8"])
xsrc['id'] = xsrc.index
xsrc['ysrc_id'] = xsrc.index
ysrc = pd.DataFrame(traindata['Y_src'], columns=["code"])
ysrc['id'] = ysrc.index
xsrc
sensor1 | sensor2 | sensor3 | sensor4 | sensor5 | sensor6 | sensor7 | sensor8 | id | ysrc_id | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 0.265137 | 0.809255 | -0.178995 | 2.746354 | 3.166690 | 1.905104 | 0.608901 | 0.065012 | 0 | 0 |
1 | 0.464364 | 0.602819 | -0.351293 | 3.043960 | 3.325195 | 1.771686 | 0.225587 | 0.107889 | 1 | 1 |
2 | 0.552982 | -0.223651 | 0.023783 | 1.516311 | 2.093622 | 1.040532 | 0.907121 | 0.263978 | 2 | 2 |
3 | 0.229242 | 0.833957 | 1.271798 | 3.847606 | 4.113984 | 2.097045 | 1.042262 | -0.876377 | 3 | 3 |
4 | 0.192337 | 0.365837 | 0.897946 | 3.072840 | 3.754567 | 3.668033 | 1.467112 | 0.263822 | 4 | 4 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
1533 | 0.335240 | 1.033154 | 1.544523 | 2.227198 | 4.152546 | 3.885886 | 1.790236 | 0.186224 | 1533 | 1533 |
1534 | 0.044641 | 0.018909 | 0.594428 | 1.395340 | 3.335126 | 3.024916 | 0.721799 | -0.014882 | 1534 | 1534 |
1535 | 0.512814 | 0.038145 | 0.739680 | 0.719289 | 2.318140 | 2.145708 | 0.578735 | 0.210020 | 1535 | 1535 |
1536 | 0.585099 | -0.827959 | -0.357059 | 1.015795 | 2.322458 | 1.645878 | -0.149406 | 0.174534 | 1536 | 1536 |
1537 | -0.548336 | 0.394042 | 0.104253 | 2.914538 | 3.564147 | 2.671467 | 1.159036 | -0.040433 | 1537 | 1537 |
1538 rows × 10 columns
ysrc
code | id | |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
2 | 0 | 2 |
3 | 0 | 3 |
4 | 0 | 4 |
... | ... | ... |
1533 | 0 | 1533 |
1534 | 0 | 1534 |
1535 | 0 | 1535 |
1536 | 0 | 1536 |
1537 | 0 | 1537 |
1538 rows × 2 columns
Nun erstellen wir für unser erstes Modell ein Metadata
Objekt, welches die Tabellenstruktur abbildet.
metadata = Metadata()
metadata = Metadata()
metadata.add_table(
name='ysrc',
data=ysrc,
primary_key='id'
)
metadata.add_table(
name='xsrc',
data=xsrc,
primary_key='id',
parent='ysrc',
foreign_key='ysrc_id'
)
metadata.visualize()
Jetzt könnnen wir mit Erstellung und Training des Modells fortfahren. Wir nutzen dazu den HMA1
Algorithmus des sdv
Pakets. HMA1
ist ein Algorithmus, der auf relationalen Datensätzen tabellenweise ein auf Copula-Funktionen basierendes Modell anlernt.
from sdv.relational import HMA1
tables = {'xsrc': xsrc, 'ysrc': ysrc}
model = HMA1(metadata)
model.fit(tables)
Wir können nun einfach einen neuen Datensatz generieren:
new_data = model.sample()
new_data
{'ysrc': code id 0 2 0 1 3 1 2 3 2 3 4 3 4 5 4 ... ... ... 1533 0 1533 1534 3 1534 1535 2 1535 1536 0 1536 1537 3 1537 [1538 rows x 2 columns], 'xsrc': sensor1 sensor2 sensor3 sensor4 sensor5 sensor6 sensor7 \ 0 2.162605 1.088785 1.028656 4.104177 3.356234 2.921614 1.272582 1 2.014989 3.922892 4.214651 5.478746 3.461899 2.646017 1.682721 2 1.710409 1.879125 3.230420 4.696258 3.989288 3.716418 3.357383 3 6.728576 8.301437 7.229003 6.588645 6.673314 5.731959 5.921013 4 5.619570 7.874066 6.844800 5.658813 4.245956 4.363560 6.314679 ... ... ... ... ... ... ... ... 1533 1.454853 2.660503 2.464407 4.609172 4.385919 3.600616 2.852932 1534 3.970387 5.632429 4.626468 4.513498 2.649227 3.054752 2.804307 1535 -0.743566 2.306946 3.010297 2.328012 2.116801 2.803264 1.164116 1536 -1.015263 -1.172234 -1.212809 1.750661 2.845020 1.254808 -0.840084 1537 2.062646 4.929506 5.565104 1.986195 1.224052 1.704706 2.405658 sensor8 id ysrc_id 0 1.111557 0 0 1 1.556994 1 1 2 2.872081 2 2 3 5.480151 3 3 4 5.260422 4 4 ... ... ... ... 1533 -0.110270 1533 1533 1534 2.726098 1534 1534 1535 -0.281212 1535 1535 1536 -1.141569 1536 1536 1537 1.147466 1537 1537 [1538 rows x 10 columns]}
Im Folgenden wollen wir die Güte der erzeugten Daten untersuchen. Dazu betrachten wir zuerst die von sdv
mitgelieferte evaluate
Funktion, die eine Reihe von standartisierten Evaluationsmethoden anwendet.
from sdv.evaluation import evaluate
evaluate(new_data, tables, metadata=metadata, aggregate=False)
metric | name | raw_score | normalized_score | min_value | max_value | goal | error | |
---|---|---|---|---|---|---|---|---|
0 | CSTest | Chi-Squared | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | Cannot find fields of types ('boolean', 'categ... |
1 | KSTest | Inverted Kolmogorov-Smirnov D statistic | 0.791369 | 0.791369 | 0.0 | 1.0 | MAXIMIZE | None |
2 | KSTestExtended | Inverted Kolmogorov-Smirnov D statistic | 0.791369 | 0.791369 | 0.0 | 1.0 | MAXIMIZE | None |
3 | LogisticDetection | LogisticRegression Detection | 0.869660 | 0.869660 | 0.0 | 1.0 | MAXIMIZE | None |
4 | SVCDetection | SVC Detection | 0.399849 | 0.399849 | 0.0 | 1.0 | MAXIMIZE | None |
5 | BNLikelihood | BayesianNetwork Likelihood | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | Please install pomegranate with `pip install p... |
6 | BNLogLikelihood | BayesianNetwork Log Likelihood | NaN | NaN | -inf | 0.0 | MAXIMIZE | Please install pomegranate with `pip install p... |
7 | LogisticParentChildDetection | LogisticRegression Detection | 0.811774 | 0.811774 | 0.0 | 1.0 | MAXIMIZE | None |
8 | SVCParentChildDetection | SVC Detection | 0.123700 | 0.123700 | 0.0 | 1.0 | MAXIMIZE | None |
Während einige Verfahren wie zB. der KSTest
ein einigermaßen gutes Ergebnis liefern, erhalten wir bei der SVCDetection
und gerade bei der SVCParentChildDetection
ein schlechtes Ergebnis.
Um etwas mehr Einblick zu erhalten, versuchen wir nun die Daten zu visualisieren und dabei manuell zu untersuchen. Wir erzeugen die Tabellen results_o
und results_s
, die die originalen und synthetisierten Daten in jeweils einer Tabelle zusammenfassen.
results_o = xsrc
results_o['code'] = ysrc['code']
results_o = results_o.drop('ysrc_id', axis=1)
results_s = new_data['xsrc']
results_s['code'] = new_data['ysrc']['code']
results_s = results_s.drop('ysrc_id', axis=1)
Zunächst erzeugen wir Mittelwerte und Standardabweichungen aller Messungen (also für alle Codes aus der ursprünglichen Y_src
Tabelle zusammengefasst) und plotten diese für jeden Sensor. Die Mittelwerde befinden sich dabei in der linken und die Standardabweichungen in der rechten Tabelle.
results_combined_mean = pd.DataFrame(columns=['original data', 'synthetic data'])
results_combined_std = pd.DataFrame(columns=['original data', 'synthetic data'])
temp = results_o.mean()
temp = temp.drop(labels=['code'])
temp = temp.drop(labels=['id'])
results_combined_mean['original data'] = temp.tolist()
temp = results_s.mean()
temp = temp.drop(labels=['code'])
temp = temp.drop(labels=['id'])
results_combined_mean['synthetic data'] = temp.tolist()
temp = results_o.std()
temp = temp.drop(labels=['code'])
temp = temp.drop(labels=['id'])
results_combined_std['original data'] = temp.tolist()
temp = results_s.std()
temp = temp.drop(labels=['code'])
temp = temp.drop(labels=['id'])
results_combined_std['synthetic data'] = temp.tolist()
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 2)
results_combined_mean.plot.bar(rot=0, ax=axes[0], figsize=(20,5))
results_combined_std.plot.bar(rot=0, ax=axes[1])
Gezeigt werden die Werte für alle Handbewegung zusammengenommen, für die jeweils 8 Sensorwerte. Originaldaten in blau, synthetisch generierte Daten in orange; links: Mittelwerte; rechts: Standardabweichungen.
Diese Ergebnisse sehen zunächst sehr gut aus. Viel wichtiger für eine Qualitätsbeurteilung wäre es allerdings, wenn wir diese Plots für jeden Code aus Y_src
separat anfertigen.
Dies tun wir im Folgenden. Die Plots sind dabei wie folgt angeordnet: In der linken Spalte sind die Mittelwerte und in der rechten die Standardabweichungen dargestellt. Die einzelnen Reihen bezeichnen die Codes 0 - 8.
results_codes_mean = []
results_codes_std = []
for i in range(9):
results_codes_mean.insert(i, pd.DataFrame(columns=['original data', 'synthetic data']))
results_codes_std.insert(i, pd.DataFrame(columns=['original data', 'synthetic data']))
temp = results_o[results_o['code'] == i].mean()
temp = temp.drop(labels=['code'])
temp = temp.drop(labels=['id'])
results_codes_mean[i]['original data'] = temp.tolist()
temp = results_s[results_s['code'] == i].mean()
temp = temp.drop(labels=['code'])
temp = temp.drop(labels=['id'])
results_codes_mean[i]['synthetic data'] = temp.tolist()
temp = results_o[results_o['code'] == i].std()
temp = temp.drop(labels=['code'])
temp = temp.drop(labels=['id'])
results_codes_std[i]['original data'] = temp.tolist()
temp = results_s[results_s['code'] == i].std()
temp = temp.drop(labels=['code'])
temp = temp.drop(labels=['id'])
results_codes_std[i]['synthetic data'] = temp.tolist()
results_codes_mean[0]
original data | synthetic data | |
---|---|---|
0 | 0.169780 | 0.535260 |
1 | 0.538740 | 0.787182 |
2 | 0.638189 | 0.756521 |
3 | 2.198799 | 2.149765 |
4 | 2.869728 | 2.942243 |
5 | 2.386387 | 2.361488 |
6 | 0.909510 | 0.860333 |
7 | 0.096173 | 0.589965 |
import matplotlib.pyplot as plt
fig, axes = plt.subplots(9, 2)
for i in range(9):
results_codes_mean[i].plot.bar(rot=0, ax=axes[i,0], figsize=(20,30))
results_codes_std[i].plot.bar(rot=0, ax=axes[i,1])
Jede Zeile entspricht einer Handbewegung mit den jeweils 8 Sensorwerten und zeigt die Werte für Originaldaten, sowie die synthetisch generierten Daten; linke Spalte: Mittelwerte; rechte Spalte: Standardabweichungen. Wie wir den Plots entnehmen können, beinhalten die Mittelwerte der Codes 1 und 2 starke Abweichungen, während die Standardabweichungen für alle Codes relativ stark Abweichen. Für Code 8 ist keine Standardabweichung eingezeichnet, da nur ein Datenpunkt in den synthetischen Daten existiert. Die Modelle (sowohl Multi-Table als auch Single-Table) neigen dazu, wenige bis garkeine Daten für Code 8 zu erzeugen.
Selbst bei besserer Übereinstimmung der Plots wäre diese Art der Untersuchung immernoch unzulänglich, da zB. multimodale Verteilungen nicht durch Mittelwert und Standardabweichung erfasst werden können. In diesem Notebook werden wir die Evaluierung allerdings nicht weiter ausweiten.
Single-Table Modell¶
Zum Vergleich trainieren wir noch ein weiteres Modell, welches mit nur einer Tabelle angelernt wird. Wir fügen die Codes also einfach als Spalte zu den Messwerten hinzu.
xsrc_single = pd.DataFrame(traindata['X_src'], columns=["sensor1", "sensor2", "sensor3", "sensor4", "sensor5", "sensor6", "sensor7", "sensor8"])
xsrc_single['id'] = xsrc_single.index
xsrc_single['code'] = traindata['Y_src']
xsrc_single
sensor1 | sensor2 | sensor3 | sensor4 | sensor5 | sensor6 | sensor7 | sensor8 | id | code | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 0.265137 | 0.809255 | -0.178995 | 2.746354 | 3.166690 | 1.905104 | 0.608901 | 0.065012 | 0 | 0 |
1 | 0.464364 | 0.602819 | -0.351293 | 3.043960 | 3.325195 | 1.771686 | 0.225587 | 0.107889 | 1 | 0 |
2 | 0.552982 | -0.223651 | 0.023783 | 1.516311 | 2.093622 | 1.040532 | 0.907121 | 0.263978 | 2 | 0 |
3 | 0.229242 | 0.833957 | 1.271798 | 3.847606 | 4.113984 | 2.097045 | 1.042262 | -0.876377 | 3 | 0 |
4 | 0.192337 | 0.365837 | 0.897946 | 3.072840 | 3.754567 | 3.668033 | 1.467112 | 0.263822 | 4 | 0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
1533 | 0.335240 | 1.033154 | 1.544523 | 2.227198 | 4.152546 | 3.885886 | 1.790236 | 0.186224 | 1533 | 0 |
1534 | 0.044641 | 0.018909 | 0.594428 | 1.395340 | 3.335126 | 3.024916 | 0.721799 | -0.014882 | 1534 | 0 |
1535 | 0.512814 | 0.038145 | 0.739680 | 0.719289 | 2.318140 | 2.145708 | 0.578735 | 0.210020 | 1535 | 0 |
1536 | 0.585099 | -0.827959 | -0.357059 | 1.015795 | 2.322458 | 1.645878 | -0.149406 | 0.174534 | 1536 | 0 |
1537 | -0.548336 | 0.394042 | 0.104253 | 2.914538 | 3.564147 | 2.671467 | 1.159036 | -0.040433 | 1537 | 0 |
1538 rows × 10 columns
Nun nutzen wir den TVAE
Algorithmus der sdv
Umgebung. Dieser ist ein auf Variational Autoencodern basierender Deep Learning Algorithmus.
from sdv.tabular import TVAE
# sdv verwendet unter der Haube sklearn;
# dies führt zu Warnungen, auch wenn der fitting Prozess erfolgreich war:
# um einen sauberen Output zu erhalten, ignorieren wir sie hier
import warnings
warnings.filterwarnings('ignore')
ft = {
'sensor1' : 'float',
'sensor2' : 'float',
'sensor3' : 'float',
'sensor4' : 'float',
'sensor5' : 'float',
'sensor6' : 'float',
'sensor7' : 'float',
'sensor8' : 'float',
'id' : 'integer',
'code' : 'label_encoding',
}
model = TVAE(primary_key='id', field_transformers=ft)
model.fit(xsrc_single)
Die hier geworfenen Warnings stammen aus der Bibliothek sklearn
, die von sdv
unter der Haube verwendet wird. Das Modell funktioniert trotzdem.
Wir samplen im nächsten Schritt solange 10000 Messreihen, bis auch mindestens eine Reihe für Code 8 existiert. Dieses Modell neigt dazu, öfter selbst bei derartig vielen Messwerten keine solche Messreihe zu produzieren.
new_data_single = model.sample(10000)
while len(new_data_single[new_data_single['code'] == 8]) == 0:
new_data_single = model.sample(10000)
new_data_single
sensor1 | sensor2 | sensor3 | sensor4 | sensor5 | sensor6 | sensor7 | sensor8 | id | code | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 6.741613 | 7.375323 | 4.401880 | 6.079800 | 5.784067 | 4.553594 | 7.521529 | 5.319201 | 0 | 2 |
1 | 3.772678 | 4.574211 | 4.227165 | 3.258248 | 3.665359 | 0.865069 | 2.765168 | 2.654672 | 1 | 3 |
2 | -0.499255 | 0.044925 | 0.388512 | 2.303993 | 2.102518 | 3.449518 | 1.349845 | -0.010769 | 2 | 0 |
3 | 0.252672 | 0.031120 | 0.006317 | 1.296789 | 2.060465 | 1.464364 | 0.137632 | -0.076177 | 3 | 0 |
4 | 0.040773 | 2.030993 | 1.998497 | 3.221318 | 3.522142 | 1.619729 | 1.109277 | -0.065711 | 4 | 0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
9995 | 6.388094 | 6.857863 | 3.812273 | 4.947871 | 5.291178 | 3.245589 | 6.791325 | 4.703944 | 9995 | 6 |
9996 | -0.202907 | -0.080021 | 0.610676 | 0.209461 | 1.055550 | 1.600495 | 0.832057 | 0.491501 | 9996 | 0 |
9997 | 6.721471 | 7.455149 | 4.552982 | 5.612977 | 4.144782 | 5.761632 | 3.385506 | 4.224623 | 9997 | 6 |
9998 | 0.486511 | -0.191248 | 4.269514 | 3.882955 | 5.397642 | 4.347805 | 2.210230 | 0.173158 | 9998 | 0 |
9999 | 0.279127 | 0.537121 | 0.485732 | 0.984995 | 1.228147 | 1.338411 | 0.394215 | 0.578782 | 9999 | 0 |
10000 rows × 10 columns
len(new_data_single[new_data_single['code'] == 8])
1
Wie zuvor betrachten wir zuerst die evaluate
Funktion von sdv
.
from sdv.evaluation import evaluate
evaluate(new_data_single, xsrc_single, aggregate=False)
metric | name | raw_score | normalized_score | min_value | max_value | goal | error | |
---|---|---|---|---|---|---|---|---|
0 | BNLogLikelihood | BayesianNetwork Log Likelihood | NaN | NaN | -inf | 0.0 | MAXIMIZE | Please install pomegranate with `pip install p... |
1 | LogisticDetection | LogisticRegression Detection | 0.146451 | 0.146451 | 0.0 | 1.0 | MAXIMIZE | None |
2 | SVCDetection | SVC Detection | 0.072661 | 0.072661 | 0.0 | 1.0 | MAXIMIZE | None |
3 | BinaryDecisionTreeClassifier | None | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | `target` must be passed either directly or ins... |
4 | BinaryAdaBoostClassifier | None | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | `target` must be passed either directly or ins... |
5 | BinaryLogisticRegression | None | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | `target` must be passed either directly or ins... |
6 | BinaryMLPClassifier | None | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | `target` must be passed either directly or ins... |
7 | MulticlassDecisionTreeClassifier | None | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | `target` must be passed either directly or ins... |
8 | MulticlassMLPClassifier | None | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | `target` must be passed either directly or ins... |
9 | LinearRegression | None | NaN | NaN | -inf | 1.0 | MAXIMIZE | `target` must be passed either directly or ins... |
10 | MLPRegressor | None | NaN | NaN | -inf | 1.0 | MAXIMIZE | `target` must be passed either directly or ins... |
11 | GMLogLikelihood | GaussianMixture Log Likelihood | NaN | NaN | -inf | inf | MAXIMIZE | 'NoneType' object has no attribute 'split' |
12 | CSTest | Chi-Squared | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | Cannot find fields of types ('boolean', 'categ... |
13 | KSTest | Inverted Kolmogorov-Smirnov D statistic | 0.853785 | 0.853785 | 0.0 | 1.0 | MAXIMIZE | None |
14 | KSTestExtended | Inverted Kolmogorov-Smirnov D statistic | 0.853785 | 0.853785 | 0.0 | 1.0 | MAXIMIZE | None |
15 | CategoricalCAP | CategoricalCAP | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | `key_fields` must be passed either directly or... |
16 | CategoricalZeroCAP | 0CAP | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | `key_fields` must be passed either directly or... |
17 | CategoricalGeneralizedCAP | Categorical GeneralizedCAP | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | `key_fields` must be passed either directly or... |
18 | CategoricalNB | Categorical NaiveBayesian | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | `key_fields` must be passed either directly or... |
19 | CategoricalKNN | K-Nearest Neighbors | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | `key_fields` must be passed either directly or... |
20 | CategoricalRF | Categorical Random Forest | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | `key_fields` must be passed either directly or... |
21 | CategoricalSVM | Support Vector Classifier | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | `key_fields` must be passed either directly or... |
22 | CategoricalEnsemble | Ensemble | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | '<' not supported between instances of 'float'... |
23 | NumericalLR | Numerical Linear Regression | NaN | NaN | 0.0 | inf | MAXIMIZE | `key_fields` must be passed either directly or... |
24 | NumericalMLP | Multi-layer Perceptron Regression | NaN | NaN | 0.0 | inf | MAXIMIZE | `key_fields` must be passed either directly or... |
25 | NumericalSVR | Numerical Support-vector Regression | NaN | NaN | 0.0 | inf | MAXIMIZE | `key_fields` must be passed either directly or... |
26 | NumericalRadiusNearestNeighbor | Numerical Radius Nearest Neighbor | NaN | NaN | 0.0 | inf | MAXIMIZE | `key_fields` must be passed either directly or... |
27 | ContinuousKLDivergence | Continuous Kullback–Leibler Divergence | 0.590747 | 0.590747 | 0.0 | 1.0 | MAXIMIZE | None |
28 | DiscreteKLDivergence | Discrete Kullback–Leibler Divergence | NaN | NaN | 0.0 | 1.0 | MAXIMIZE | Cannot find fields of types ('boolean', 'categ... |
Viele Evaluationsmethoden scheinen mit unserem Datensatz nicht zu funktionieren. Die, die funktionieren, liefern uns kein eindeutiges Ergebnis.
Also wiederholen wir die Visualisierung mit Plots. Wieder starten wir mit Mittelwert und Standardabweichung auf dem gesamten Datensatz.
results_single_combined_mean = pd.DataFrame(columns=['original data', 'synthetic data'])
results_single_combined_std = pd.DataFrame(columns=['original data', 'synthetic data'])
temp = xsrc_single.mean()
temp = temp.drop(labels=['code'])
temp = temp.drop(labels=['id'])
results_single_combined_mean['original data'] = temp.tolist()
temp = new_data_single.mean()
temp = temp.drop(labels=['code'])
temp = temp.drop(labels=['id'])
results_single_combined_mean['synthetic data'] = temp.tolist()
temp = xsrc_single.std()
temp = temp.drop(labels=['code'])
temp = temp.drop(labels=['id'])
results_single_combined_std['original data'] = temp.tolist()
temp = new_data_single.std()
temp = temp.drop(labels=['code'])
temp = temp.drop(labels=['id'])
results_single_combined_std['synthetic data'] = temp.tolist()
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 2)
results_single_combined_mean.plot.bar(rot=0, ax=axes[0], figsize=(20,5))
results_single_combined_std.plot.bar(rot=0, ax=axes[1])
Die Ergebnisse sind, wie zuvor schon, auf den ersten Blick sehr gut. Nun fahren wir mit der Visualisierung für die einzelnen Codes fort.
results_single_codes_mean = []
results_single_codes_std = []
for i in range(9):
results_single_codes_mean.insert(i, pd.DataFrame(columns=['original data', 'synthetic data']))
results_single_codes_std.insert(i, pd.DataFrame(columns=['original data', 'synthetic data']))
temp = xsrc_single[xsrc_single['code'] == i].mean()
temp = temp.drop(labels=['code'])
temp = temp.drop(labels=['id'])
results_single_codes_mean[i]['original data'] = temp.tolist()
temp = new_data_single[new_data_single['code'] == i].mean()
temp = temp.drop(labels=['code'])
temp = temp.drop(labels=['id'])
results_single_codes_mean[i]['synthetic data'] = temp.tolist()
temp = xsrc_single[xsrc_single['code'] == i].std()
temp = temp.drop(labels=['code'])
temp = temp.drop(labels=['id'])
results_single_codes_std[i]['original data'] = temp.tolist()
temp = new_data_single[new_data_single['code'] == i].std()
temp = temp.drop(labels=['code'])
temp = temp.drop(labels=['id'])
results_single_codes_std[i]['synthetic data'] = temp.tolist()
results_single_codes_mean[0]
original data | synthetic data | |
---|---|---|
0 | 0.169780 | 0.767389 |
1 | 0.538740 | 1.056708 |
2 | 0.638189 | 1.352465 |
3 | 2.198799 | 2.700361 |
4 | 2.869728 | 3.022724 |
5 | 2.386387 | 2.387103 |
6 | 0.909510 | 1.212812 |
7 | 0.096173 | 0.609695 |
import matplotlib.pyplot as plt
fig, axes = plt.subplots(9, 2)
for i in range(9):
results_single_codes_mean[i].plot.bar(rot=0, ax=axes[i,0], figsize=(20,30))
results_single_codes_std[i].plot.bar(rot=0, ax=axes[i,1])
Zur Erinnerung: Jede Zeile entspricht einer Handbewegung mit den jeweils 8 Sensorwerten und zeigt die Werte für Originaldaten, sowie die synthetisch generierten Daten; linke Spalte: Mittelwerte; rechte Spalte: Standardabweichungen. Die Ergebnisse sind, beurteilt nach Augenmaß, besser als im Multi-Table Modell. Vor allem die Mittelwerte für die Codes 1 und 2 sind wesentlich ähnlicher.
Für eine gesicherte Aussage, ob die generierten Daten als würdiger Ersatz bzw. würdige Erweiterung des Originaldatensatzes taugen, empfehlen wir eine weiterführende Evaluation über eine visuelle Betrachtung von Mittelwerten und Standardabweichung hinaus.