This commit is contained in:
2023-01-09 18:23:11 +01:00
commit 2211e7abda
17 changed files with 34615 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
**/.DS_Store

BIN
Seminararbeit.pdf Normal file

Binary file not shown.

689
src/Deepfake.ipynb Normal file

File diff suppressed because one or more lines are too long

247
src/Gesichterextrahierer.py Normal file
View File

@@ -0,0 +1,247 @@
#!/usr/bin/python3
"""
Ein Skript das sowohl als eigenständiges Programm genutzt werden kann, als auch als
Modul importiert werden kann. Ermöglicht das Extrahieren und bearbeiten von Gesichtern
in Videos.
Nutzung als Modul:
1. Mit dem Pfad zur Kaskade initalisieren.
2. Optional die Bildergröße der Ausgabe mit 'setzeBildergroesse' setzen.
3. Video laden mit 'lade'.
4. Video mit den anderen Methoden
- fürGesichterMache
- extrahiereGesichter
- extrahiereUndValidiereGesichter
bearbeiten.
Nutzung als Skript:
-> Mit Python3 und der '-h' Option starten um die Hilfe angezeigt zu bekommen.
"""
import sys, cv2, time, os
import numpy as np
import face_recognition
class Gesichterextrahierer:
def __init__(self, pfad_cascade: str):
self.CASCADE = cv2.CascadeClassifier(pfad_cascade)
self.bildgroesse_output = 128
self.counter = 0
def setzeBildgroesse(self, bildgroesse_output):
self.bildgroesse_output = bildgroesse_output
def lade(self, pfad_video):
self.pfad_video = pfad_video
self.video = cv2.VideoCapture(pfad_video)
self.fps = self.video.get(cv2.CAP_PROP_FPS)
self.frame_height = int(self.video.get(cv2.CAP_PROP_FRAME_HEIGHT))
self.frame_width = int(self.video.get(cv2.CAP_PROP_FRAME_WIDTH))
def play(self):
"""
Spielt das geladene Video in einem neuen Fenster ab. Escape beendet die Wiedergabe.
"""
while True:
ist_bild, bild = self.video.read() # Nächsten Frame einlesen
if cv2.waitKey(1) == 27 or not ist_bild:
break
cv2.imshow('Deepfakeskript - Video', bild)
time.sleep(1/self.fps-1)
self.video.set(cv2.CAP_PROP_POS_FRAMES, 1) # Video zurücksetzen, damit es wieder eingelesen werden kann
cv2.destroyAllWindows()
def fuerUnskalierteGesichterMache(self, funktion, max_anzahl_gesichter, speichern=True) -> int: # Nachträglich hinzugefügt, benötigt Verbesserungen
if speichern: # Vorbereitung für das Abspeichern des neuen Videos
pfad_Video_ohne_Endung = ".".join(self.pfad_video.split(".")[:-1])
video_writer = cv2.VideoWriter(f'{pfad_Video_ohne_Endung}_geändert.mp4', cv2.VideoWriter_fourcc(*'mp4v'), self.fps, (self.frame_width, self.frame_height))
ist_bild, bild = self.video.read() # Ersten Frame einlesen
while ist_bild: # Solange das Video nicht zu Ende ist
bild_grau = cv2.cvtColor(bild, cv2.COLOR_BGR2GRAY)
gesichter = self.CASCADE.detectMultiScale(bild_grau, scaleFactor=1.3, minNeighbors=5, minSize=(self.bildgroesse_output,)*2 ) # Gesichter erkennen
if len(gesichter):
x, y, breite, hoehe = gesichter[0] # Bearbeitet nur das erste Gesicht
gesicht = bild[y:y+hoehe, x:x+breite] # Kopieren des Gesichts
rueckgabe_funktion = funktion( gesicht ) # Die übergebene Funktion ausführen
if type(rueckgabe_funktion) == bool and rueckgabe_funktion == False: break # Mit dem nächsten Frame fortfahren
if speichern:
if type(rueckgabe_funktion) != bool:
bild[y:y+hoehe, x:x+breite] = rueckgabe_funktion # Rückgabe der Funktion in das Bild einsetzen
video_writer.write(bild) # Frame an das neue Video anhängen
print("\r"+"Es wurden erfolgreich %d Bilder bearbeitet." % self.counter, end='')
self.counter += 1
elif speichern:
video_writer.write(bild) # Frame an das neue Video anhängen auch wenn kein Gesicht gefunden wurde
ist_bild, bild = self.video.read() # Nächsten Frame einlesen
if self.counter >= max_anzahl_gesichter:
break
if speichern: video_writer.release()
self.video.set(cv2.CAP_PROP_POS_FRAMES, 1) # Video zurücksetzen, damit es wieder eingelesen werden kann
x = self.counter
self.counter = 0
return x
def gesichterEinrahmen(self):
def rahmenHinzufuegen(bild):
farbe = [0, 0, 255]
for y in range(len(bild)):
for x in range(len(bild[0])):
if x <= 4 or x >= len(bild[0])-4 or y <= 4 or y >= len(bild)-4:
bild[y][x] = farbe
return bild
self.fuerGesichterMache(rahmenHinzufuegen, 10000000)
def fuerGesichterMache(self, funktion, max_anzahl_gesichter, speichern=True) -> int:
"""
Fürt die übergebene Funktion für alle Gesichter in dem Video aus. Es werden maximal ein Gesicht pro Bild erkannt.
:funktion: Eine Funktion, der beim Ausführen ein Parameter, der Bildausschnitt des Gesichts, als Liste übergeben wird.
Gibt die Funktion False zurück wird das gerade bearbeitete Bild übersprungen.
Gibt die Funktion True wird der Counter erhöht und das nächste Bild geladen, sofern nicht das Maximum erreicht wurde.
Gibt die Funktion ein Bild als Liste zurück, wird dies anstelle des übergebenen Gesichts in das Bild des Videos eingesetzt. Das Ein- und Ausgabeliste muss die gleiche Form haben.
Es kann von der Funktion aus `self.counter` zugegriffen werden, um die Anzahl an bearbeiteten Bildern zu erhalten.
:max_anzahl_gesichter: Definiert die Anzahl an Gesichtern, die maximal bearbeitet werden. Die Funktion endet vorzeitig wenn die Videodatei zu Ende ist.
:speichern: Bei True wird eine Videodatei abgespeichert, die die mögliche Änderungen beinhaltet.
Bei False ist dies nicht der Fall.
:return: Gibt die Anzahl an bearbeiteten Bildern zurück.
"""
if speichern: # Vorbereitung für das Abspeichern des neuen Videos
pfad_Video_ohne_Endung = ".".join(self.pfad_video.split(".")[:-1])
video_writer = cv2.VideoWriter(f'{pfad_Video_ohne_Endung}_geändert.mp4', cv2.VideoWriter_fourcc(*'mp4v'), self.fps, (self.frame_width, self.frame_height))
ist_bild, bild = self.video.read() # Ersten Frame einlesen
while ist_bild: # Solange das Video nicht zu Ende ist
bild_grau = cv2.cvtColor(bild, cv2.COLOR_BGR2GRAY)
gesichter = self.CASCADE.detectMultiScale(bild_grau, scaleFactor=1.3, minNeighbors=5, minSize=(self.bildgroesse_output,)*2 ) # Gesichter erkennen
if len(gesichter):
x, y, breite, hoehe = gesichter[0] # Bearbeitet nur das erste Gesicht
gesicht = bild[y:y+hoehe, x:x+breite] # Kopieren des Gesichts
gesicht_skaliert = cv2.resize( gesicht, (self.bildgroesse_output,)*2 )
rueckgabe_funktion = funktion( gesicht_skaliert ) # Die übergebene Funktion ausführen
if type(rueckgabe_funktion) == bool and rueckgabe_funktion == False: break # Mit dem nächsten Frame fortfahren
if speichern:
if type(rueckgabe_funktion) != bool:
rueckgabe_funktion = cv2.resize(rueckgabe_funktion, (breite, hoehe))
bild[y:y+hoehe, x:x+breite] = rueckgabe_funktion # Rückgabe der Funktion in das Bild einsetzen
video_writer.write(bild) # Frame an das neue Video anhängen
print("\r"+"Es wurden erfolgreich %d Bilder bearbeitet." % self.counter, end='')
self.counter += 1
elif speichern:
video_writer.write(bild) # Frame an das neue Video anhängen auch wenn kein Gesicht gefunden wurde
ist_bild, bild = self.video.read() # Nächsten Frame einlesen
if self.counter >= max_anzahl_gesichter:
break
if speichern: video_writer.release()
self.video.set(cv2.CAP_PROP_POS_FRAMES, 1) # Video zurücksetzen, damit es wieder eingelesen werden kann
x = self.counter
self.counter = 0
return x
def extrahiereGesichter(self, max_anzahl_gesichter: int, ordner_ausgabe: str):
"""
Findet alle Gesichter aus dem Video und speichert sie ab.
:max_anzahl_gesichter: Definiert die Anzahl an Gesichtern, die maximal bearbeitet werden. Die Funktion endet vorzeitig wenn die Videodatei zu Ende ist.
:ordner_ausgabe: Den Pfad zum Ordner in dem die Bilder abgespeichert werden, dieser muss vorhanden sein.
"""
def funktion(bild):
cv2.imwrite(os.path.join(ordner_ausgabe, f'Gesicht_{self.counter}.png'), bild)
return True
print("Exportiere Bilder nach %s." % ordner_ausgabe)
anzahl_extrahierter_bilder = self.fuerGesichterMache(funktion, max_anzahl_gesichter, speichern=False)
print("\r"+"Es wurden erfolgreich %d Bilder nach %s exportiert." % (anzahl_extrahierter_bilder, ordner_ausgabe))
def extrahiereUndValidiereGesichter(self, referenzbild: list, max_anzahl_gesichter: int, ordner_ausgabe: str, toleranz=0.6):
"""
Findet alle Gesichter aus dem Video und speichert sie ab, falls sie dem Referenzgesicht ausreichend ähneln.
:referenzbild: Ein Bild in Form einer Liste das zum Vergleich bei der Validierung verwendet wird.
:max_anzahl_gesichter: Definiert die Anzahl an Gesichtern, die maximal bearbeitet werden. Die Funktion endet vorzeitig wenn die Videodatei zu Ende ist.
:ordner_ausgabe: Den Pfad zum Ordner in dem die Bilder abgespeichert werden, dieser muss vorhanden sein.
:toleranz: Die euklidische Distanz, die maximal zwischen den Vekoren, die die zu vergleichenden Gesichter repräsentieren,
liegen darf, damit diese als von der gleichen Person gelten. (siehe face_recongnition.compare_faces)
"""
encoding_referenz = face_recognition.face_encodings(referenzbild)
def funktion(bild):
try:
encoding_bild = face_recognition.face_encodings(bild)[0] # Gesicht in ein Vektorrepräsentation umwandeln
if face_recognition.compare_faces(encoding_referenz, encoding_bild, tolerance=toleranz)[0]:
cv2.imwrite(os.path.join(ordner_ausgabe, f'Gesicht_{self.counter}.png'), bild)
return True
except Exception: pass
return False
print("Validiere und exportiere Bilder nach %s." % ordner_ausgabe)
anzahl_extrahierter_bilder = self.fuerGesichterMache(funktion, max_anzahl_gesichter, speichern=False)
print("\r"+"Es wurden erfolgreich %d Bilder nach %s exportiert." % (anzahl_extrahierter_bilder, ordner_ausgabe))
def main(argv): # Wird ausgeführt, wenn das Skript direkt ausgeführt wird
bildgroesse_ausgabe = 128
max_anzahl_bilder = 50000
pfad_cascade = "./daten/cascades/haarcascade_frontalface_default.xml"
ordner_ausgabe = "./daten/Gesichter"
pfad_validierungbild = ""
toleranz = 0.6
gesichter_einrahmen = False
for index, argument in enumerate(argv):
if argument[0] == '-':
if 'g' == argument[1]:
bildgroesse_ausgabe = int(argv[index+1])
elif 'a' == argument[1]:
max_anzahl_bilder = int(argv[index+1])
elif 'c' == argument[1]:
pfad_cascade = argv[index+1]
elif 'o' == argument[1]:
ordner_ausgabe = argv[index+1]
elif 'v' == argument[1]:
pfad_validierungbild = argv[index+1]
elif 't' == argument[1]:
toleranz = float(argv[index+1])
elif 'r' == argument[1]:
gesichter_einrahmen = True
elif 'h' == argument[1]:
print("Nutzung: %s [Optionen] [Videodatei]\n" % argv[0])
print("""Optionen:
-g : Größe der Ausgabe Bilder in Pixel
-a : Maximale Anzahl der zu extrahierenden Bildern
-c : Pfad für die Haarcascade
-o : Zielordner für die Ausgabe
-h : Drucken dieser Hilfenachricht
-v : Pfad zu einem Validierungsbild
-t : Toleranz für die Gesichtsvalidierung\n""")
return
g = Gesichterextrahierer(pfad_cascade)
g.setzeBildgroesse(bildgroesse_ausgabe)
g.lade(argv[-1])
if pfad_validierungbild:
validierungsbild = cv2.imread(pfad_validierungbild)
g.extrahiereUndValidiereGesichter(validierungsbild, max_anzahl_bilder, ordner_ausgabe, toleranz=toleranz)
elif gesichter_einrahmen:
g.gesichterEinrahmen()
else:
g.extrahiereGesichter(max_anzahl_bilder, ordner_ausgabe)
if __name__ == "__main__":
main(sys.argv)

60
src/LoggingCallback.py Normal file
View File

@@ -0,0 +1,60 @@
"""
Die Klasse LoggingCallback, erbt von tensorflow.keras.callbacks.Callback
Wird als Instanz beim Trianieren mit der fit-Methode als callback übergeben. Die Methoden, welche mit "on_..."
beginnen werden zu dem entsprechenden Zeitpunkt wärend des Trainingsprozesses aufgerufen und dokumentieren den
Trainingsvortschritt in der Datei 'train.log' im Verzeichnis des Modells. Bei jedem Trainingsbeginn wird ein
Gesicht von jeder der beiden Personen durch das Modell geschickt und im Verzeichnis 'Bilder' Abgespeichert.
"""
import tensorflow.keras.callbacks
import time, os, cv2
import numpy as np
class LoggingCallback(tensorflow.keras.callbacks.Callback):
def __init__(self, pfad_modell: str, bild_A: list, bild_B: list):
self.pfad_modell = pfad_modell
self.bild_A = bild_A.reshape(1, 128, 128, 3)
self.bild_B = bild_B.reshape(1, 128, 128, 3)
try: os.mkdir(os.path.join(self.pfad_modell, 'Bilder/'))
except FileExistsError: pass
def log(self, text: str):
with open(os.path.join(self.pfad_modell, 'train.log'), "a") as datei:
datei.writelines("{};{}\n".format(time.time(), text) )
def speichereBild(self, bild: list, pfad: str):
bild = cv2.normalize(bild, None, alpha = 0, beta = 255, norm_type = cv2.NORM_MINMAX, dtype = cv2.CV_32F)
bild = bild.astype(np.uint8)
cv2.imwrite(pfad, bild)
def on_train_begin(self, logs=None):
self.log("Starte Training;")
predicted_A = self.model.predict(self.bild_A)[0]
predicted_B = self.model.predict(self.bild_B)[0]
self.speichereBild(predicted_A, os.path.join(self.pfad_modell, "Bilder/{}_Bild_A.png".format(time.time()) ))
self.speichereBild(predicted_B, os.path.join(self.pfad_modell, "Bilder/{}_Bild_B.png".format(time.time()) ))
def on_epoch_begin(self, epoch, logs=None):
logString = "on_epoch_begin;epoch;{};".format(epoch)
for key, val in logs.items():
logString += "{};{};".format(key, val)
self.log(logString)
def on_epoch_end(self, epoch, logs=None):
logString = "on_epoch_end;epoch;{};".format(epoch)
for key, val in logs.items():
logString += "{};{};".format(key, val)
self.log(logString)
def on_train_batch_begin(self, batch, logs=None):
logString = "on_train_batch_begin;batch;{};".format(batch)
for key, val in logs.items():
logString += "{};{};".format(key, val)
self.log(logString)
def on_train_batch_end(self, batch, logs=None):
logString = "on_train_batch_end;batch;{};".format(batch)
for key, val in logs.items():
logString += "{};{};".format(key, val)
self.log(logString)

79
src/README.md Normal file
View File

@@ -0,0 +1,79 @@
___
Das Projekt ist wie folgt strukturiert:
```
├── Deepfake.ipynb
├── Gesichterextrahierer.py
├── LoggingCallback.py
├── README.md
├── daten
│   ├── cascades
│   │   └── haarcascade_frontalface_default.xml
│   ├── lernen
│   │   └── Gesichter
│   │   ├── A
│   │   └── B
│   └── modelle
│       └── Autoencoder
│ ├── modell.info
│      ├── Biden
│   │   ├── train.log
│   │   └── Gesichter
│      └── Norris
│      ├── train.log
│      └── Gesichter
└── skripte
├── extrahiere_val_loss.awk
├── frame_shuffle.py
├── log_Analyse.awk
└── plot.sh
```
```📒 Deepfake.ipynb```
_Das zentrale Jupyter Notebook, führe dies aus um ein künstliches Neuronales Netz zu erstellen und zu trainieren._
```🐍 Gesichterextrahierer.py```
_Der Teil des Programms, der es ermöglicht Gesichter aus Videos auszuschneiden und zu manipulieren._
```🐍 LoggingCallback.py```
_Ein Klasse, die dafür zuständig ist den Vortschritt während des Trainierens zu dokumentieren._
```📃 README.md```
_Diese Datei._
```🧑🏼‍🦲 haarcascade_frontalface_default.xml```
_Eine essenzielle Datei, die für das erkennen von Gesichtern im Gesichterextrahierer notwendig ist._
```📂 Gesichter```
_In diesem Ordner werden in den Unterordnern A und B die Extrahierten Gesichter der Personen gespeichert, deren Gesichter ausgetauscht werden sollen. Diese Bilder werden dann zum Lernen verwendet._
```📂 modelle```
_Hier werden, in einzelnen Unterordnern, die Modelle gespeichert. Wird einem Modell ein neuer Name gegeben, wird hier ein neuer Ordner erstellt._
```📂 Autoencoder```
_Ein beispielhafter Ordner, der die Daten zu dem Modell mit dem Namen 'Autoencoder' enthält. Alle Dateien und Ordner, die hier enthalten sind werden automatisch generiert. Die Unterordner 'Biden' und 'Norris' enthalten jeweils einen Autoencoder, deren Namen zuvor gewählt werden kann._
```🪵 train.log```
_Eine Textdatei, in der Lernfortschritt dokumentiert wird._
```📂 Bilder```
_Im Laufe des Trainings werden immer wieder Bilder mit dem Modell erstellt, welche hier dann abgespeichert werden. Die Bilder, die ein 'A' im Namen haben, wurden mit dem Modell komprimiert und so ähnlich wie möglich wiederhergestellt. Die Bilder, die ein 'B' im Namen haben, wurden komprimiert und als die jeweils andere Person wiederhergestellt. Die Zahl im Namen ist die Unixzeit zu der das Bild entstanden ist._
``` modell.info```
_Eine Textdatei mit einer Übersicht über die Struktur des Modells._
```📂 skripte```
_Ein Ordner, der ein paar hilfreich Hilfsprogramme enthält._
```📄 extrahiere_val_loss.awk```
_Ein AWK-Skript, dass eine train.log-Datei einliest und eine Liste an Zeitpunkten mit dem passenden Fehlerwert zurückgibt. Es wird für die plot.sh-Datei benötigt._
```🐍 frame_shuffle.py```
_Ein Python-Programm, dass die Bilder in einem Video mischt._
```📄 log_Analyse.awk```
_Ein AWK-Skript, dass eine übergebene train.log-Datei analysiert und eine Zusammenfassung über den Trainingsverlauf gibt._
```💲 plot.sh```
_Ein Bash-Skript, dass aus einer oder mehreren übergebenen train.log-Dateien einen Diagramm erstellt._

File diff suppressed because it is too large Load Diff

View File

View File

7
src/requirements.txt Normal file
View File

@@ -0,0 +1,7 @@
tensorflow>=2.6.0
opencv-contrib-python
matplotlib
face_recognition
pyyaml
h5py
jupyterlab

View File

@@ -0,0 +1,30 @@
/{/ && gruppe_model_weights { gruppe_model_weights++ }
/}/ && gruppe_model_weights { gruppe_model_weights-- }
/{/ && gruppe_model_weights && gruppe_layers_1 { gruppe_layers_1++ }
/}/ && gruppe_model_weights && gruppe_layers_1 { gruppe_layers_1--; if(!gruppe_layers_1) sub(" >.*$", "", gruppen_namen); }
/{/ && gruppe_model_weights && gruppe_layers_2 { gruppe_layers_2++ }
/}/ && gruppe_model_weights && gruppe_layers_2 { gruppe_layers_2--; if(!gruppe_layers_2) sub(" >.*$", "", gruppen_namen); }
/GROUP "model_weights"/ { gruppe_model_weights = 1; gruppen_namen = "model_weights" }
/GROUP/ && !/model_weights/ && gruppe_model_weights {
gsub("\"", "", $2);
if(!gruppe_layers_1) { gruppe_layers_1 = 1; }
else { gruppe_layers_2 = 1; }
gruppen_namen = gruppen_namen " > " $2;
print "\n#G " gruppen_namen;
}
/(.*): / && gruppe_model_weights && gruppe_layers_1 && gruppe_layers_2 {
if( !match($1, idx_regex) || !idx_regex) {
idx_regex = $1;
sub("[0-9]+\\):", "[0-9]+\\):", idx_regex);
sub("\\(", "\\(", idx_regex);
name = idx_regex
gsub("[\\\\|:]", "", name)
print "\n#N " name;
}
printf("%.4f %.4f %.4f %.4f ", $2, $3, $4, $5);
}

View File

@@ -0,0 +1,7 @@
#!/bin/python3
from tensorflow.keras.models import load_model
import sys
modell = load_model(sys.argv[1])
modell.save(f"./modell.h5")

View File

@@ -0,0 +1,38 @@
#!/usr/bin/python3
"""
Ein simples Skript, das es ermöglicht die Bilder in einem Video zu mischen.
Wenn die Datei direkt als Skript ausgeführt wird, wird das erste Argument als Pfad für das zu bearbeitende Video verwendet.
"""
import cv2, sys, random, time
def shuffle(pfad_video):
video = cv2.VideoCapture(pfad_video)
fps = video.get(cv2.CAP_PROP_FPS)
frame_count = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
frame_height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
frame_width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
pfad_video_ohne_endung = '.'.join(pfad_video.split('.')[:-1])
video_writer = cv2.VideoWriter(f'{pfad_video_ohne_endung}_gemischt.mp4', cv2.VideoWriter_fourcc(*'mp4v'), fps, (frame_width, frame_height))
frame_num_liste = list(range(1, frame_count+1))
random.shuffle(frame_num_liste)
startzeit = time.time()
for ind, val in enumerate(frame_num_liste):
if ind%30 == 0:
verbleibende_min = ( (time.time()-startzeit)/(ind+1) )*( frame_count-ind )/60
print(f"\rFortschritt: {int(ind/frame_count*100)}%, geschätzte verbleibende Zeit: {verbleibende_min:.2f} Minuten", end='')
video.set(cv2.CAP_PROP_POS_FRAMES, val)
ist_bild, bild = video.read()
video_writer.write(bild)
video.release()
video_writer.release()
if __name__ == '__main__':
shuffle(sys.argv[1])

View File

@@ -0,0 +1,21 @@
# AWK-Skript, dass die train.log Datei eines Modells einliest und eine Liste der 'val_loss' Werte
# mit den Sekunden die bereits fürs Training verwendet wurden ausgibt. Wird von 'plot.sh' verwendet.
#
# Output-Format:
# <Trainierte Sekunden> <Loss-Wert zu dem Zeitpunkt>
func gibWert(pattern) {
for(i=1; i<=NF; i++){
if($i == pattern){return i+1;}
}
return 0;
}
BEGIN { FS = ";"; }
/Starte Training/{ letzter_zeitpunkt = $1 }
/val_loss/{
dauer += $1-letzter_zeitpunkt;
letzter_zeitpunkt = $1;
print dauer, "\t", $gibWert("val_loss")
}

View File

@@ -0,0 +1,68 @@
# Awk-Skript zum Analysieren der Log-Dateien, die beim Trainieren des Neuronalen Netzes anfallen
BEGIN { FS = ";"; }
func gibFeldnummer(pattern) {
for(i=1; i<=NF; i++){
if($i == pattern){return i;}
}
return 0;
}
func runden(zahl) {
return int(zahl+0.5)
}
# Trainingsdauer -----------------------------------------------------------------------------------------
{
if(match($0, "Starte Training")){
trainingsrunde_nr += 1
sekunden += vorheriger_Zeitstempel-zeitstempel_Start;
zeitstempel_Start = $1;
if(sekunden > 200000){print trainingsrunde_nr}
}
vorheriger_Zeitstempel = $1;
}
END {
sekunden += vorheriger_Zeitstempel-zeitstempel_Start;
stunden = int(sekunden/3600); sekunden -= stunden*3600;
minuten = int(sekunden/60); sekunden -= minuten*60;
printf("Trainingsdauer:\t\t %sh %smin %ss\n", stunden, minuten, runden(sekunden));
}
# Anzahl/Dauer an Epochen -----------------------------------------------------------------------------------------
/on_epoch_begin/{ epochen++; epochen_start=$1;}
/on_epoch_end/{ avg_epochen_dauer += $1-epochen_start; avg_counter_epochen++;}
END {avg_epochen_dauer /= avg_counter_epochen; printf("Epochen:\t\t %s a ~%ss\n", epochen, avg_epochen_dauer);}
# Anzahl/Dauer an Batches -----------------------------------------------------------------------------------------
/on_train_batch_begin/{ batches++; train_batch_start=$1;}
/on_train_batch_end/{ avg_train_batch_dauer += $1-train_batch_start; avg_counter_batch++;}
END {avg_train_batch_dauer /= avg_counter_batch; printf("Trainierte Batches:\t %s a ~%ss\n", batches, avg_train_batch_dauer);}
# Loss -----------------------------------------------------------------------------------------
BEGIN { min_loss = 1000000000;}
/loss/ {
loss = $(gibFeldnummer("loss")+1);
avg_loss+=loss; x++;
if(max_loss < loss){max_loss=loss;};
if(min_loss > loss){min_loss=loss;};
}
END {
avg_loss /= x;
printf("LOSS (MIN/MAX/AVG):\t %.10f / %.10f / %.10f \n", min_loss, max_loss, avg_loss);
}
# Val Loss ------------------------------------------------------------------------------------
BEGIN { min_val_loss = 1000000000;}
/val_loss/{
val_loss = $(gibFeldnummer("val_loss")+1);
avg_val_loss+=val_loss; y++;
if(max_val_loss < val_loss){max_val_loss=val_loss;};
if(min_val_loss > val_loss){min_val_loss=val_loss;};
}
END {
avg_val_loss /= y;
printf("VAL_LOSS (MIN/MAX/AVG):\t %.10f / %.10f / %.10f \n", min_val_loss, max_val_loss, avg_val_loss);
}

40
src/skripte/plot.sh Normal file
View File

@@ -0,0 +1,40 @@
#!/bin/bash
# Bash-Skript, das aus den als Argumente übergebenen 'train.log' Dateien ein Diagramm macht.
# Verwendet AWK um die Daten zu formatieren und gnuplot um das Diagramm zu erstellen.
plot_befehl="plot "
for dateiname in $@; do
name=$(echo $dateiname | awk 'BEGIN {FS="/";} { print $3 }' - | sed -e "s/DenseLayers\?_.*_//g" ) # Namen aus dem Pfad extrahieren
dateiname_daten=plot-script_$name.temp
awk -f skripte/gib_val_loss.awk $dateiname > $dateiname_daten # Datei mit den zu plotenden Daten erstellen
plot_befehl+="'$dateiname_daten' using (\$1/3600):2 title \"$(echo $name | sed -e "s/e-/x10\^-^/" )\" with line linewidth 4," # An den Plot-Befehl anfügen
done
echo $plot_befehl
GNUPLOT_COMMAND=$(cat << EOF
set xrange [-0.1:];
set yrange [:];
set xlabel "Trainingzeit in Stunden" font ",16";
set xlabel offset 2;
set xtics offset 1;
set ylabel "Fehler" font ",16";
set ylabel offset -2;
set ytics offset -1;
set logscale y;
set grid;
set border 3;
set border linewidth 2;
set tics scale 1;
set tics font ",14";
set key font ",14";
set tics nomirror;
set terminal qt size 700, 400;
$plot_befehl
EOF
)
gnuplot -p -e "$GNUPLOT_COMMAND" # Diagramm erstellen
rm -f plot-script_*.temp # Zuvor erstellte Dateien entfernen

View File

@@ -0,0 +1,14 @@
#!/bin/bash
run_with_latentspace_changed () {
LATENT_SPACE=$1
echo "Starte LatentSpace$LATENT_SPACE..." >> train.info
sed -i "s/NAME = '.*'/NAME = 'DenseLayers_LatentSpace$LATENT_SPACE-SGD_lr=5e-1'/" Deepfake.py
sed -i "s/tf\.keras\.optimizers\..*(.*)/tf.keras.optimizers.SGD(learning_rate=5e-1)/" Deepfake.py
sed -i "s/Dense(.*)) #MARKER_LATENT_SPACE/Dense( $LATENT_SPACE )) #MARKER_LATENT_SPACE/" Deepfake.py
sed -i "s/shape=(.*,) )) #MARKER_INPUT_DECODER/shape=($LATENT_SPACE,) )) #MARKER_INPUT_DECODER/" Deepfake.py
sed -i "s/+.*#MARKER_ZEIT/+ 6*60*60 #MARKER_ZEIT/" Deepfake.py
python Deepfake.py
}
run_with_latentspace_changed 100