Zum Inhalt springen →

ChatGPT im Test: KI schreibt Data Augmentation Methoden für Time Series

Aktuelle lese ich immer und überall darüber, wie ChatGPT die Welt begeistert in dem sie Webseiten programmiert (Warum ChatGPT die Welt verändern wird), Informatik Prüfungsaufgagen löst (Professor gegen KI! Kann ChatGPT meine Klausur lösen?) und viele andere faszinierende Sachen macht.

Grund genug, um mal selbst einen Test zu machen und ChatGPT eine Aufgabe zu stellen vor der sich selbst gerade stehe.

Die Aufgabe

ChatGPT soll mir ein paar Data Augmentation Methoden in Python schreiben, mit denen ich eine Menge von Time-Series Daten synthetisch vergrößern kann.

Data Augmentation Methoden werden eingesetzt, um die Datenmenge zu vergrößern, indem leicht veränderte Kopien bereits vorhandener Daten oder neu erstellte synthetische Daten aus vorhandenen Daten hinzugefügt werden. Sie fungiert als Regularisierer und trägt dazu bei, die Überanpassung beim Training eines maschinellen Lernmodells zu reduzieren und ist eng mit dem Oversampling in der Datenanalyse verwandt.

https://en.wikipedia.org/wiki/Data_augmentation

Für den Test logge ich mich unter https://chat.openai.com/chat ein und gebe folgenden Text in das Eingabefeld ein:

„create a data augmentation method for time series data“

Daraufhin erhalte ich folgende Antwort:

Data augmentation is a technique used to increase the size and diversity of a dataset by creating new, synthetic samples from existing ones. This can be particularly useful for time series data, as it can help to improve the performance and generalization of machine learning models. Here are a few examples of data augmentation methods that can be used with time series data:

  1. Time shifting: This involves shifting the time series data forward or backward in time by a certain number of time steps. This can help to expose the model to different temporal patterns in the data.
  2. Amplitude scaling: This involves scaling the amplitudes (or values) of the time series data by a certain factor. This can help the model to be more robust to changes in the scale of the data.
  3. Noise injection: This involves adding noise to the time series data to simulate the effects of measurement error or other sources of noise.
  4. Data aggregation: This involves aggregating the time series data over different time periods (e.g., daily, weekly, monthly). This can help the model to learn more general patterns in the data.
  5. Data interpolation: This involves filling in missing values in the time series data using interpolation techniques, such as linear interpolation or spline interpolation. This can be useful if there are gaps in the time series data.
  6. Data extrapolation: This involves using the existing time series data to generate new data points beyond the original time range of the data. This can be useful for forecasting or predicting future values in the time series.

These are just a few examples of data augmentation methods that can be used with time series data. It’s important to carefully consider the specific characteristics of the time series data and the goals of the machine learning model when deciding which data augmentation methods to use.

Schon mal nicht schlecht. Es ist zwar kein Python Code, aber da war ich mit meiner Anfrage wohl etwas zu ungenau. Time shifting, Amplitude scaling, Noise injection, Data aggregation, Data interpolation und extrapolation sind tatsächlich typische Data Augmentation Methoden für Time Series Daten. Aber ich brauche das Ganze in Python, also schreibe ich:

„Can you write code for it in python“

Da ChatGPT auch mit Folgefragen umgehen kann, sollte mit ChatGPT nun den Python Code ausspucken…

Und tatsächlich antwortet ChatGPT mir mit:

Certainly! Here is an example of how you could implement some of the data augmentation methods described above in Python:

import numpy as np

def time_shift(time_series, n_steps):
  """Shift the time series data by n_steps time steps.
  
  Args:
    time_series: A 1D numpy array representing the time series data.
    n_steps: An integer representing the number of time steps to shift the data.
      
  Returns:
    A 1D numpy array representing the time-shifted time series data.
  """
  return np.roll(time_series, n_steps)

def amplitude_scale(time_series, scale_factor):
  """Scale the amplitudes of the time series data by a scale_factor.
  
  Args:
    time_series: A 1D numpy array representing the time series data.
    scale_factor: A float representing the scaling factor to apply to the amplitudes.
      
  Returns:
    A 1D numpy array representing the scaled time series data.
  """
  return time_series * scale_factor

def noise_inject(time_series, noise_factor):
  """Add noise to the time series data.
  
  Args:
    time_series: A 1D numpy array representing the time series data.
    noise_factor: A float representing the standard deviation of the noise to add.
      
  Returns:
    A 1D numpy array representing the time series data with noise injected.
  """
  return time_series + np.random.normal(scale=noise_factor, size=time_series.shape)

def data_aggregate(time_series, aggregation_period):
  """Aggregate the time series data over a given period.
  
  Args:
    time_series: A 1D numpy array representing the time series data.
    aggregation_period: An integer representing the time period over which to aggregate the data (in time steps).
      
  Returns:
    A 1D numpy array representing the aggregated time series data.
  """
  return np.mean(time_series.reshape(-1, aggregation_period), axis=1)

def data_interpolate(time_series, missing_mask):
  """Interpolate missing values in the time series data.
  
  Args:
    time_series: A 1D numpy array representing the time series data.
    missing_mask: A 1D numpy boolean array indicating which values in the time series are missing.
      
  Returns:
    A 1D numpy array representing the interpolated time series data.
  """
  from scipy.interpolate import interp1d
  interp_func = interp1d(np.arange(len(time_series))[~missing_mask], time_series[~missing_mask])
  return interp_func(np.arange(len(time_series)))

def data_extrapolate(time_series, n_steps):
  """Extrapolate the time series data by n_steps time steps.
  
  Args:
    time_series: A 1D numpy array representing the time series data.
    n_steps: An integer representing the number of time steps to extrapolate the data.

Das ist echt der Hammer. Python Code mit ausführlichen Kommentaren der genau die oben beschriebenen Data Augmentation Methoden implementiert. Bleibt noch die Frage, ob der Code wirklich hält, was er verspricht.

Um das zu testen, kopiere ich den Code mal in meine IDE (PyCharm) und lasse mal ein paar Daten durch Laufen.

Fehler Nummer 1: Der Kommentar am Ende der Datei hat kein „Kommentar-Ende“. „SyntaxError: EOF while scanning triple-quoted string literal“. Kann aber einfach korrigiert werden :-). Allerdings fällt auch schnell auf, dass die Methode data_extrapolate keine Implementierung hat. Da frage ich sicherheitshalber noch mal bei ChatGPT nach.

„Please implement the data_extrapolate method“

Certainly! Here is an implementation of the data_extrapolate method in Python using linear extrapolation:

def data_extrapolate(time_series, n_steps):
  """Extrapolate the time series data by n_steps time steps.
  
  Args:
    time_series: A 1D numpy array representing the time series data.
    n_steps: An integer representing the number of time steps to extrapolate the data.
      
  Returns:
    A 1D numpy array representing the extrapolated time series data.
  """
  from scipy.interpolate import interp1d
  
  # Fit a linear model to the time series data
  fit = np.polyfit(np.arange(len(time_series)), time_series, 1)
  
  # Use the linear model to generate new data points for the extrapolated time steps
  time_steps = np.arange(len(time_series) + n_steps)
  extrapolated_data = np.polyval(fit, time_steps)
  
  return extrapolated_data

This method first fits a linear model to the existing time series data using the np.polyfit function. It then generates new data points for the extrapolated time steps using the np.polyval function, which applies the linear model to the specified time steps. The resulting array represents the extrapolated time series data.

Note that this is just one possible approach to data extrapolation, and there are many other methods that could be used as well. For example, you could use a different type of model (e.g., a polynomial model or a spline model) or use a different interpolation/extrapolation method (e.g., interp1d from the scipy library). The choice of method will depend on the specific characteristics of the time series data and the goals of the machine learning model.

OK, damit ist ja alles klar. Ich übernehme die Methode und probiere sie direkt mal aus.

Test 1:

import matplotlib.pylab as plt
x = np.linspace(-np.pi, np.pi, 201)
sin = np.sin(x)

xtest = np.linspace(-np.pi, np.pi, 301)
test = data_extrapolate(sin, 100)
plt.plot(x, sin)
plt.plot(xtest, test)
plt.xlabel('Angle [rad]')
plt.ylabel('sin(x)')
plt.axis('tight')
plt.show()

Mal sehen, ob eine Sinus Kurve extrapoliert werden kann.

Leider ein Fail!

Offensichtlich wird der Sinus (Blau) nicht extrapoliert (Orange)

OK, ist auch nicht gerade eine einfache Aufgabe, einen Sinus bzw. jede beliebige Kurve weiter zu extrapolieren und auch nicht unbedingt eine Data-Augmentation Methode ….

Also dann mal weiter zur data_interpolate Methode, die fehlender Werte in den Zeitreihendaten interpolieren können soll. Da erhoffe ich mir schon deutlich mehr. Mit folgendem Code teste ich die Methode:

import matplotlib.pylab as plt
x = np.linspace(-np.pi, np.pi, 201)
sin = np.sin(x)

sin = np.where(np.logical_and(np.absolute(sin) % 0.1 < 0.08, np.absolute(sin) > 0.06) , 0, sin)
mask = np.ma.masked_where(sin != 0, sin)
mask = np.asarray([True if val == 0 else False for val in mask])
print (mask)

test = data_interpolate(sin, mask)
plt.plot(x, sin)
plt.plot(x, test - 2)
plt.xlabel('Angle [rad]')
plt.ylabel('sin(x)')
plt.axis('tight')
plt.show()

Es wird wieder ein Sinus erzeugt und dann mir np.where(np.logical_and(np.absolute(sin) % 0.1 < 0.08, np.absolute(sin) > 0.06) , 0, sin) ein paar Werte aus dem Array entfernt die dann mit der data_interpolate Methode wieder eingefügt werden sollen.

Das Ergebnis kann sich sehen lassen:

Die Blaue Kurve zeigt die von mir manipulierte Sinus Kurve aus der die data_interpolate Methode die orange Kurve erzeugt.

Das kann man so stehen lassen. Die Methode macht, was sie soll. Wichtig ist nur, dass man darauf achtet, dass der erste und der letzte Wert vorhanden sind, da die Interpolation immer zwischen 2 Punkten erfolgt. Auch hier nicht direkt eine Data-Augmentation Methode, aber trotzdem kann man diese Methode nutzen, um andere Data-Augmentation Methoden zu implementieren.

Der nächste Test git der data_aggregate Methode. Die Aggregation von Zeitreihendaten ist eine Technik, bei der die Auflösung der Zeitreihendaten über einen bestimmten Zeitraum reduziert wird, wobei die zugrunde liegenden Trends und Muster in den Daten (so weit möglich) erhalten bleiben.

Den Test der Methode data_aggregate führe mit dem folgenden Code durch. Zum einfachen Vergleich erweitere ich die aggregierten Daten mit 0-WErten, damit die beiden Arras wieder die gleiche größe haben.

import matplotlib.pylab as plt
x = np.linspace(-np.pi, np.pi, 201)
sin = np.sin(x)

test = data_aggregate(sin, 3)
test = np.concatenate((test, np.zeros(201-67)), axis=0)

plt.plot(x, sin)
plt.plot(x, test)
plt.xlabel('Angle [rad]')
plt.ylabel('sin(x)')
plt.axis('tight')
plt.show()

Das Ergebnis kann sich wieder sehen lassen. Die Daten werden genau wie erwartet zusammengefasst.

Sinus Kurve auf 1/3 reduziert.

Bei der Methode muss man aber darauf achten, dass die Größe des Input-Arrays durch den Aggregat-Wert teilbar sein muss. In dem obigen Beispiel funktioniert das (201/3 = 67). Wenn das nicht passt, kann man zur Not vorne oder hinten einfach ein paar 0-Werte ergänzen.

Als nächstes teste ich mal noise_inject. Diese Methode. Eine klassische Data-Augmentation Methode, die man immer mal wieder benötigt. Hier kann nicht viel schief gehen. Die Methode ergänzt die Eingabe-Werte mit ein wenig Rauschen. Also dann mal los.

import matplotlib.pylab as plt
x = np.linspace(-np.pi, np.pi, 201)
sin = np.sin(x)
test = noise_inject(sin, 0.3)
plt.plot(x, sin)
plt.plot(x, test)
plt.xlabel('Angle [rad]')
plt.ylabel('sin(x)')
plt.axis('tight')
plt.show()
Das Ergebnis von noise_inject

Wie erwartet funktioniert die Methode einfach so. Schon verrückt, wie schnell man eine Erwartungshaltung gegenüber einem Stück Sourcecode einnimmt, den eine KI generiert hat. Vor ein paar Tagen hätte ich das nicht für möglich gehalten.

Die Methoden „amplitude_scale“ und „time_shift“ spare ich mir hier mal, da ein Blick auf die Implementierung reicht, um zu sehen, dass hier meine Erwartungshaltung wieder erfüllt wird.

So weit so gut. Alle Methoden bis auf eine mit Bravour gelöst. Klar, die Implementierung ist dank numpy und scipy keine Kunst, aber trotzdem. Eine KI, aus einer verbalen Beschreibung eine Implementierung diverse Data-Augmentation ist echt der Wahnsinn.

Darüber hinaus fehlen noch ein paar wichtige Data-Augmentation Methoden wie z.B. Time-Warp, Quantize, Drift, etc.

Bonus-Aufgabe

Mal sehen, ob ChatGPT auch noch eine funktionierende Time-Warp Methode ausspucken wird. Die Methode kann man jedenfalls nicht einfach so in 1-2 Zeilen numpy Code implementieren. Also mal sehen, was ChatGPT ausspuckt, wenn ich folgendes eingebe.

„Please create a python data augmentation method for time warping“

Here is a Python function that performs time warping data augmentation on a time series dataset:

import numpy as np

def time_warp(data, rate=1.0):
  """
  Perform time warping data augmentation on a time series dataset.
  
  Parameters:
  - data: A numpy array of shape (timesteps, features) representing the time series data.
  - rate: A float representing the rate of time warping to apply. A rate of 1.0 corresponds to no warping.
  
  Returns:
  - A numpy array of shape (timesteps, features) representing the time-warped data.
  """
  timesteps = data.shape[0]
  
  # Generate a random warping factor
  if rate != 1.0:
    alpha = 1.0 + (rate * (np.random.rand() - 0.5))
  else:
    alpha = 1.0
  
  # Compute the warping indices
  indices = np.arange(timesteps)
  indices = indices * alpha
  indices = indices.astype(np.int32)
  
  # Perform time warping
  return data[indices]

This function takes in a time series dataset represented as a numpy array with shape (timesteps, features) and a rate of time warping to apply. The function applies time warping by stretching or compressing the time axis of the data by a random factor, determined by the rate parameter. The function then returns the time-warped data as a numpy array.

You can use this function by calling it with the time series data and the desired rate of time warping. For example:

warped_data = time_warp(data, rate=0.5)

Wow, sogar mit Beispiel. Also mal sehen, was dabei rauskommt. Mein Testcode sieht wie folgt aus:

import matplotlib.pylab as plt
x = np.linspace(-np.pi, np.pi, 201)
sin = np.sin(x)

data = np.vstack((x, sin)).T
test = time_warp(data, rate=0.5)
data = test.T
test = data[1]

plt.plot(x, sin)
plt.plot(x, test)
plt.xlabel('Angle [rad]')
plt.ylabel('sin(x)')
plt.axis('tight')
plt.show()

Das Ergebnis sieht auf den ersten Blick aus, als könnte es sich um eine time_warp Funktion handeln, aber sieht man sich den Sourcecode der Methode mal genauer an, erkennt man schnell, dass ChatGPT hier an seine Grenzen gekommen ist.

  1. Ist der Wert „alpha“ > 1, sind die Indizes größer als das Input-Array was auf einen IndexError läuft. „IndexError: index 201 is out of bounds for axis 0 with size 201“. Die Methode funktioniert also nur, wenn „(rate * (np.random.rand() – 0.5))“ < 1 ist.
  2. Die Idee, die Time-Series Daten zu transponieren und dann nur bestimmte Indizes auszuwählen ist erst mal gar nicht Schlecht, aber die Indizes mit „indices = indices * alpha“ auszuwählen, entspricht nicht dem, was ich unter Time-Warp versehe.

Hier müsste man noch mal ein wenig nacharbeiten, damit das korrekt funktioniert – oder man nutzt einfach die tsaug Bibliothek. „tsaug“ ist ein Python-Paket für die Time-Series Augmentation und bietet eine Reihe von Methode für Zeitreihen sowie eine einfache API, um mehrere Augmentierer in einer Pipeline zu verbinden. Ich bleibe also erst mal bei tsaug …

TimeWarp eines Sinus mit tsaug.TimeWarp().augment(sin,x) erzeugt

Fazit

Nicht perfekt, aber trotzdem der Hammer.
Aber noch kein Grund für Software-Entwickler sich beim Arbeitsamt zu melden. Es ist noch eine ganze Menge Arbeit für uns übrig :-). Vor allem Aufgaben, die das Wissen über ein firmeninternes API/SDK erfordern machen es ChatGPT unmöglich, eine Lösung zu finden, da hier ein sehr spezifisches Wissen nötig ist, dass in die Regeln nur die Mitarbeiter besitzen.

Aber wir bekommen mal wieder ein neues, sehr cooles Werkzeug an die Hand, dass hoffentlich noch sehr lange kostenlos zur Verfügung stehen wird.

Veröffentlicht in Allgemein