Dies ist eine alte Version des Dokuments!
1. Einleitung
2. Projektbestandteile
2.1 Neuronales Netzwerk zur Emotionserkennung
2.2 Bild-Preprocessing
2.3 Spotipy
2.4 Telegram Bot
3. Bericht über den Verlauf
4. Fazit und Ausblick
5. Literaturangabe und verwendete Software
1. Einleitung
Der Name Apollon steht für affective, personal, online, located, laudable, overdue und network. Diese Wörter haben wir zusammen ausgewählt, weil sie unser Projekt sehr gut beschreiben. Die Anfangsbuchstaben der Wörter bilden unseren Projekt Namen Apollon. Dieser ist in der griechischen und römischen Mythologie der Gott des Lichts, der Heilung und der Musik. Die Musik spielt in unserem Projekt eine bedeutsame Rolle, weshalb wir den Namen so wählten.
Das Projekt Apollon ermöglicht durch ein trainiertes Netzwerk die Grundstimmung einer Person zu bestimmen. Das Netzwerk wurde mit TensorFlow und Keras trainiert. Die vier Grundstimmungen sind glücklich, traurig, aggressiv und entspannt. Anhand des Gesichts der abgebildeten Person, wird die Stimmung erkannt. Schließlich wird ein Spotify Link zu einem Lied aus einer der vier erstellten Playlists gesendet, das am Besten zu der Stimmung passt. Das Lied kann anschließend abgespielt werden. Somit ermöglicht Apollon Lieder zur passenden Stimmung durch nicht langes suchen zu finden.
Zusammenfassend kann man zu der Umsetzung unseres Projekts sagen, dass ein Selfie an unseren Telegram Bot gesendet werden kann, das Bild analysiert und durch Preprocessing verändert wird und schlussendlich die Stimmungen erkannt wird, ein passender Song wird zurückgesendet.
2. Projektbestandteile
Unser Projekt besteht grob gesagt aus zwei verschieden Skripten:
* Apollon-Programm (hier sind die einzelnen Bestandteile des Projekts gelistet)
* Apollon-Bot (quasi die Oberfläche, welcher das Apollon-Programm dann aufruft und ausführt)
Im Folgenden werden zunächst die einzelnen Bestandteile des Apollon-Programms erklärt und im Anschluss der Apollon-Bot dargestellt.
2.1 Neuronales Netzwerk zur Emotionserkennung
Mithilfe von Keras und TensorFlow haben wir ganz im vorhinein ein neuronales Netzwerk aufgesetzt und trainiert. Dies passiert in einem separaten Skript. Als Trainingsdatensatz haben wir fer2013 von kaggle benutzt. Dieser Datensatz enthält über 28.000 Bilder, 48×48 Pixel in Graustufen, welche auf 7 Emotionen hin beschriftet sind. Für unser Projekt mussten wir zunächst den Datensatz exportieren + aufbereiten. Zum Einlesen wurde die pandas library benutzt. Wir haben uns entschieden, nur 4 verschiedene Emotionen zu berücksichtigen, und mussten daher den Datensatz um 3 reduzieren, das wurde wie folgt gelöst:
# EXCLUDE data = raw_data[(raw_data.emotion != 1) & (raw_data.emotion != 2) & (raw_data.emotion != 5)] # data is the raw_data without the rows where emotion = 1, etc # RELABEL data.loc[data['emotion'] == 4, 'emotion'] = 1 data.loc[data['emotion'] == 6, 'emotion'] = 2 # relabel: 4, 6 to 1, 2
Für den Aufbau des neuronalen Netzwerks haben wir uns an einem ähnlichen Beispiel aus dem Buch von Aurélian Géron orientiert. Er beschreibt dort den Aufbau eines Netzwerks, welches Kleidungsstücke in Kategorien einordnet. Der Kleidungsstücke-Datensatz besteht genau wie der2013 ebenfalls aus sehr kleinen Bildern in Graustufen, und da wir selber bisher keine Erfahrung mit neuronalen Netzwerken mitbringen, haben wir uns für diesen Aufbau entschieden. Das Netzwerk besteht aus 14 layers. Zunächst sind 2D convolutional layers und pooling layers verbaut, im Anschluss fully connected layers.
Die finale Netzwerk-Architektur sieht so aus:
from functools import partial DefaultConv2D = partial(keras.layers.Conv2D, kernel_size=[3,3], activation='relu', padding="SAME") model_2 = keras.models.Sequential([ #keras.layers.InputLayer(input_shape = 48, 48,), DefaultConv2D(filters=64, kernel_size= [7,7], input_shape=[48, 48, 1]), keras.layers.MaxPooling2D(pool_size=2), DefaultConv2D(filters=128), DefaultConv2D(filters=128), keras.layers.MaxPooling2D(pool_size=2), DefaultConv2D(filters=256), DefaultConv2D(filters=256), keras.layers.MaxPooling2D(pool_size=2), keras.layers.Flatten(), keras.layers.Dense(units=128, activation='relu'), keras.layers.Dropout(0.5), keras.layers.Dense(units=64, activation='relu'), keras.layers.Dropout(0.5), keras.layers.Dense(units=7, activation='softmax'), ])
Trainiert wurde das Netzwerk mit den folgenden Parametern:
history = model_2.fit(train_pixels, train.emotion, epochs=50, validation_data=(test_pixels, test.emotion))
Auf dem Trainingsdatensatz erzielte es so eine Akkuratheit von mehr als 90%, aber die für den Test-Datensatz schwankte sie um die 64%, auch nach wiederholtem Training. Wenn man das Netzwerk weiter optimieren würde, hätte Apollon hier vermutlich das größte Verbesserungs-Potential. Das Netzwerk haben wir gespeichert und exportiert.
Mit der folgenden Funktion aus unserem Apollon-Programm wird das Netzwerk von unserem Bot genutzt, um die Emotion eines Selfies zu bestimmen:
# FOR PREDICT import tensorflow as tf from tensorflow.keras.models import load_model from numpy import asarray #load network that we trained before! model = load_model('netz_v1.h5') ## PREDICT def stimmungs_analyse(file_name): """diese Funktion analysiert das Bild und gibt eine Stimmung in Form einer Zahl von 0-3 aus""" image = Image.open(file_name) # convert image to numpy array + reshape data = asarray(image) data = data.reshape(-1, 48,48, 1) # this returns us a numpy.ndarray with 4 cells # in each cell is the probability that it belongs to this class probabilities = model.predict(data) # find maximum, this is the mood # x value gives us the class! #labels = (0 = "Angry", 1 ="Sad", 2="Neutral", 3 = "Happy") x = np.argmax(probabilities) return x
2.2 Bild-Preprocessing
Das Machine Learning ist vom richtigen Preprocessing abhängig. Das Netzwerk wird unter bestimmten Bedingungen und Einstellungen trainiert und kann mit diesen bestmöglich arbeiten. Bei der Bildvorverarbeitung wird das Bild für das Netzwerk kompatible geändert. Da unser Netzwerk mit 48×48 Pixel Bildern trainiert wurde, ist es wichtig dass die gesendeten Selfies zu diesem Bildformat verändert werden. Somit wird beim Bild-Preprocessing die Höhe und Breite verändert und das Bild in Graustufen umgewandelt und abgespeichert. Die Größe des Bildes wird mithilfe der PIL-Bildbibliothek geändert, die zuvor installiert werden sollte. Die Bilddatei kann als bmp, jpg, png oder gif gespeichert werden, in unserem Code als png.
Im Folgenden ist der entsprechende Code zum Bild-Preprocessing dargestellt, formuliert als Funktion mit dem Namen prepare(). Diese Funktion wird später von unserem Bot aufgerufen.
# FOR PREPARE # Import the Image from the PIL library from PIL import Image import numpy as np from skimage import transform ## PREPARE def prepare(filename_as_string, dithering=True): """diese Funktion bereitet das Bild vor, dass unser Netzwerk es analysieren kann""" # öffne das entsprechende File imageFile = filename_as_string im1 = Image.open(imageFile) # RESIZE und CROP to improve quality # width and height width = 48 height = 48 cropbox = (20, 30, 20, 35) teil = im1.crop(cropbox) # one of these filter options is used to resize the image im2 = im1.resize((width, height), Image.NEAREST) # use nearest neighbour # best down-sizing filter # CHANGE COLOR # converting the test-image into gray color_image = im2 if dithering: # color_image.convert is the function in the PIL library (to convert the colors of the function) # .convert('L') is the gray code conversion g = color_image.convert('L') else: g = color_image.convert('L', dither=Image.NONE) # SPEICHERE DAS BILD # new file name ext = ".png" new_file_name = "sg_{}{}".format(filename_as_string, ext) g.save(new_file_name) return new_file_name
2.3 Spotipy
Um Spotipy verwenden zu können wird zuallererst eine Nutzungsberechtigung (Token) benötigt. Diesen erhält man auf der Spotify for developers Seite. Dafür muss man einigen einfach beschriebenen Schritten folgen. Hat man das Token kann man anfangen Spotipy zu nutzen. Die Musiksuche im Anschluss funktioniert sehr einfach, da das Programm anhand von einem Zahlwert zwischen 0 und 4 entscheidet, aus welcher Liste ein Lied auswählt wird. Die ausgewählte Liste wird folgend Zeile für Zeile eingelesen und in zwei Listen eingefügt. In der ersten Liste stehen die Namen der Lieder und in der zweiten die Namen der Interpreten. Das Lied welches abgespielt werden soll wird letztendlich durch eine zufällig generierte Zahl ermittelt, welche zwischen 1 und 100 liegt. Im Anschluss wird eine Suche bei Spotify nach dem Namen des Liedes und des Interpreten gestartet.
Im Folgenden ist der entsprechende Code dargestellt, formuliert als Funktion. Diese Funktion wird später von unserem Bot aufgerufen:
# FOR SELECT import spotipy import spotipy.util as util import webbrowser from os import system import time import random ## SELECT def Musik_Suche(x): """diese Funktion sucht zu einer Stimmung x (einer Zahl von 0-3) einen Song aus und bereitet einen spotify Link vor:)""" token = util.oauth2.SpotifyClientCredentials(client_id="GEHEIM",client_secret="GEHEIM") cache_token = token.get_access_token() spotify = spotipy.Spotify(cache_token) y=[] a=[] z = random.randint(1, 100) #random zahl zur auswahl des liedes b="" if x == 0: f=open('angry_list.txt', 'r' ,encoding ='utf8', errors ='ignore') for i in range (100): b=(f.readline()) print(b) c = b.split(":",3) a.append(c[1]) y.append(c[2]) elif x == 2: f=open('relaxed_list.txt', 'r' ,encoding ='utf8', errors ='ignore') for i in range (100): b=(f.readline()) print(b) c = b.split(":",3) a.append(c[1]) y.append(c[2]) elif x == 1: f=open('sad_list.txt', 'r' ,encoding ='utf8', errors ='ignore') for i in range (100): b=(f.readline()) print(b) c = b.split(":",3) a.append(c[1]) y.append(c[2]) elif x == 3: f=open('happy_list.txt', 'r' ,encoding ='utf8', errors ='ignore') for i in range (100): b=(f.readline()) print(b) c = b.split(":",3) a.append(c[1]) y.append(c[2]) print (z) name = y[z-1] music =a[z-1] # Diesen track moechte ich abspielen results1 = spotify.search(q='artist:'+ name + ' track:'+ music, type='track', limit=1) test = results1["tracks"]["items"][0]["album"]["external_urls"]["spotify"] #gebe mir die URL des Album auf dem der Track ist test2 =results1["tracks"]["items"][0]["external_urls"]["spotify"] # gebe mir die URL des Tracks nachricht = test2 return nachricht #print(test) #Ich betrachte die URL von test #print(test2) #Ich betrachte die URL von test2 # die beiden print befehle habe ich gemacht um die URLs persönlich zu vergleichen # webbrowser.open(test2, new=1) # hiermit öffne ich die URL des Tracks, was mir dann den Track aus dem zughörigen Album öffnet aber leider nicht abspielt
2.4 Telegram Bot
Als “Benutzeroberfläche” für unser Programm haben wir uns für Telegram und einen Bot entschieden. Diesen haben wir mit der library python-telegram-bot erstellt. Der Bot ruft das Apollon-Programm auf und führt nacheinander die einzelnen Bestandteile von Apollon aus. Die Bestandteile sind im Programm als Funktionen definiert. Die ganze Funktion von Apollon steckt im folgenden Code-Ausschnitt des Bots:
#APOLLON Foto Stimmungs-Analyse und Bild Empfehlung def photo(update, context): # teil 1: receive + save selfie from user user = update.message.from_user photo = update.message.photo[-1].get_file() #add current time to file name to create unique file name in case user sends multiple pics now = datetime.now() current_time = now.strftime("%H:%M:%S") #define filename, add user name filename = 'selfie_{}{}.jpg'.format(user.first_name, current_time) # download + save foto under previously defined filename photo.download(filename) #respond this when the bot received a picture: context.bot.send_message(chat_id=update.effective_chat.id, text='Danke für dein Foto, ich berechne deine Stimmung') #teil 2: apollon programm #1 bild pre-processing image = apollon.prepare(filename) #2 analyse der stimmung stimmung = apollon.stimmungs_analyse(image) #3 auswahl des songs link = apollon.Musik_Suche(stimmung) # teil 3: Antwort von Apollon: sende den link nachricht = "Hier, für dich, probiere es mal mit diesem Lied: {}".format(link) context.bot.send_message(chat_id=update.effective_chat.id, text= nachricht) # set handler + dispatcher photo_handler = MessageHandler(Filters.photo, photo) dispatcher.add_handler(photo_handler)
Hier werden nacheinander die Funktionen des Apollon-Programms ausgeführt. Ansonsten ist unser Bot so programmiert, dass dieser auf jede mögliche Nachricht mit der Aufforderung:„Um eine Musikempfehlung zu erhalten, schicke mir bitte ein Selfie!“antwortet, außer die Person schickt ein Foto.
Unter dem folgenden Link können Telegram-Nutzer*innen unseren Bot finden:
(Achtung, er funktioniert derzeit nur, wenn das Programm auf einem PC läuft.)
3. Bericht über den Verlauf
Die erste Idee, die wir hatten, war es ein Programm zu schreiben, welches Musik abspielt aufgrund dessen welche Stimmung eine Person hat.Dazu haben wir uns in drei Teilbereiche unterteilt (Stimmungserkennung, Musikwiedergabe und Wiki-Dokumentation schreiben). Relativ schnell war klar, dass die Stimmungserkennung über Fotos stattfinden soll, und zwar mit Hilfe eines neuronalen Netzwerkes. Unsere zweite Überlegung war dann, ob die Musik die Stimmung heben soll oder ob die Musik die Stimmung wiederspiegeln soll und über welches Medium wir die Musik ausgeben lassen (Youtube, Spotify, nur eine Titel, etc.). Letztendlich sind wir zu dem Schluss gekommen, das sich Spotify besonders eignet, da ein Python Package zur Anbindung an Spotify existiert (Spotipy). Des Weiteren entschieden wir uns dafür, die Anwendung mit einem Telegram Bot zu verbinden, sodass ein über Telegram an den Bot geschicktes Bild in das Programm eingelesen werden kann.
Nach diesen anfänglichen gemeinsamen Überlegungen und Planungen in den ersten Terminen, verbrachten wir die nächste Zeit damit, uns zu unseren Bereichen zu informieren und die gesammelten Informationen einerseits in unserem Programm umzusetzen, aber auch damit die Info und den Fortschritt mit dem Rest des Teams zu teilen. Die letzten Schritte unserer Arbeit waren die fertigen Programmteile zusammenzufügen und die Dokumentation und Präsentation anzufertigen. Dies erfolgte größtenteils während der Blocktage.
Detaillierteres zum Ablauf des Projekts (nämlich unsere Protokolle) findest du unter ⇒ Projekt - Dokumentationen :)
4. Fazit und Ausblick
Insgesamt sind wir mit dem Projekt-Verlauf und dem Ergebnis sehr zufrieden, da wir alle vorher keinerlei Python-Kenntnisse hatten. Wir haben bei der Umsetzung des Projekts viel gelernt und haben gut zusammen gearbeitet.
Allerdings könnte man mit mehr Zeit unser Apollon-Programm erheblich verbessern, gerade ist es eher eine Art Musik-Zufalls-Generator .
Um die Stimmung-Erkennung zu verbessern, müsste man das Netzwerk optimiert, sei es durch die Auswahl eines besseren Traings-Data-Sets (hier würde sich eine gelabelte Selfie-Datenbank anbieten, wenn es so etwas gibt), die Auswahl einer besseren Architektur oder die Optimierung der Trainingsparameter. Auch eine Feedback-Funktion für die Nutzer*innen (Passte der Song zu deiner Stimmung?) könnte den Bot durch die Möglichkeit zum online-lernen verbessern. Des Weiteren wäre es schön, die Musik-Datenbank zu vergrößern (aktuell enthält jede Liste nur 100 Songs) und es wäre auch schön, eine Möglichkeit zu finden, die Songs ohne Spotify zu verschicken. Was den Bot betrifft, läuft unser Programm lokal auf einem Computer - daher funktioniert es nur, wenn der Computer auch online ist. Es wäre interessant, verschiedene Hosting-Optionen auszuprobieren (zB. Raspberry Pi), um eine dauerhafte Funktion zu gewährleisten.
5. Literaturangabe und verwendete Software
Neuronales Netzwerk und Emotionen:
Géron, A. (2019). Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow: Concepts, Tools, and Techniques to Build Intelligent Systems. O'Reilly Media.
https://medium.com/themlblog/how-to-do-facial-emotion-recognition-using-a-cnn-b7bbae79cd8f
https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6514572/
https://iopscience.iop.org/article/10.1088/1742-6596/1193/1/012004/pdf
https://developers.google.com/machine-learning/crash-course
https://vas3k.com/blog/machine_learning/
Fer von Kaggle: https://www.kaggle.com/c/challenges-in-representation-learning-facial-expression-recognition-challenge/data
Alles zum Preprocessing:
Pillow instalation: https://www.youtube.com/watch?v=xvhjcQY2lqM&feature=youtu.be
https://www.youtube.com/watch?v=6Qs3wObeWwc&feature=youtu.be
https://pillow.readthedocs.io/en/stable/installation.html
https://pillow.readthedocs.io/en/3.0.x/handbook/tutorial.html
https://pillow.readthedocs.io/en/3.1.x/reference/Image.html
http://www.pythonware.com/media/data/pil-handbook.pdf
Alles zur Musik und Spotipy:
https://cs.nju.edu.cn/sufeng/data/musicmood.htm
https://buildmedia.readthedocs.org/media/pdf/spotipy/latest/spotipy.pdf
https://developer.spotify.com/documentation/web-api/
https://spotipy.readthedocs.io/en/latest/
Telegram Bot:
https://python-telegram-bot.org
https://github.com/python-telegram-bot/python-telegram-bot/wiki/Introduction-to-the-API
https://www.youtube.com/watch?v=EympPr04WVw
https://stackoverflow.com/questions/31172302/how-to-receive-images-with-the-telegram-api
https://core.telegram.org/bots/api#getfile
Es war ein tolles Projekt! Viel Spaß bei euren :)