Ja, dann hallo liebe Hörerinnen und Hörer. Schön, dass ihr wieder da seid, dass ihr wieder zugeschaltet habt beim Python-Podcast in der siebten Episode heute. Heute geht es um Machine Learning. Und ja, wir haben heute wieder einen Gast dabei im Wintergarten. Hallo Nico.
Hi, schön weich zu sein.
Ja, und Jochen ist natürlich auch wieder da.
Ja, Jochen. Genau.
Genau, ich bin der Dominik. Wir sollten uns, glaube ich, nochmal kurz vorstellen für den Nico, weil wir wahrscheinlich diese Folge irgendwie auch bei Nico veröffentlichen dürfen. Auf Nicos tollen Podcast InnoCast heißt der noch?
InnoTechCast, genau. Der InnoTechCast ist mein Podcast-Projekt, läuft auch so seit ein bisschen über einem Jahr jetzt. Und da geht es halt nicht nur konkret um Python, sondern um alle möglichen Sachen, die irgendwie Nerds und ITler interessieren könnten.
Irgendwie von Containern über irgendwie so Desktop.
Ja, Forbes-Geschichten, einzelne Programmiersprachen. Was haben wir so gemacht? Wir haben Go gemacht, wir haben Rust behandelt. Wir hatten auch schon mal eine Folge so ein bisschen zu Python.
Also es ist auf jeden Fall sehr spannend. Hört da mal rein. Kann ich empfehlen. Klingt gut.
Genau.
Ja, dann fangen wir doch mal mit dem richtigen Thema an jetzt und gehen das Beispiel durch, was wir für das Machine Learning machen wollen.
Ja, wahrscheinlich sollten wir erstmal so ein bisschen...
Erstmal nochmal so allgemein vielleicht, was ist denn das überhaupt, das Machine Learning? Was macht das denn?
Wie viel Statistik ist das eigentlich? Oder ist das Statistik? Oder was ist das? Oder macht da irgendwelche magischen Dinge, der Computer einfach magische Sachen und dann kommt irgendwie wusch künstliche Intelligenz da raus?
Ja, das ist das Gefühl, was man heute in den Medien, finde ich, irgendwie immer bekommt.
Also es ist halt so ein Buzzword, was überall rumschwirbt, genauso wie irgendwie künstliche Intelligenz.
Genau. Aber im Wesentlichen geht es ja darum, im Endeffekt gewisse Strukturen in den Daten zu finden, um eben Prognosen über die Zukunft zu lernen.
Das heißt einfach, ich programmiere nicht mehr alles komplett deterministisch runter, was ich irgendwie wie von einem gewissen Eingabewert zu einem gewissen Ausgabewert komme, sondern ich habe halt eine gewisse große Menge an Trainingsdaten und habe dann Algorithmen, die gewisse Parameter anpassen bei solchen sogenannten Trainingsläufen und daraus quasi generelle Strukturen aus den Daten erkennen, um dann damit letztendlich Schlussfolgerungen für die Zukunft oder für unbekannte Daten zu ermöglichen.
So hätte ich das.
Hast du das zusammengefasst?
Ja, würde ich prinzipiell genauso.
Der Computer lernt so, was er mit neuen Sachen dann anfangen muss, aus alten Dingen.
Genau, es gibt keine allgemeingültige Definition, was jetzt Machine Learning ist, aber eine von Tom Mitchell wäre halt so ganz allgemein gehalten, ja, Machine Learning führt halt dazu, dass Computer oder dass Programme aus Erfahrung lernen irgendwie, mit Erfahrung besser werden.
Wenn man das jetzt mit traditionellem Programmieren vergleichen würde, dann ist es halt so, dass man eben selbst, wenn man eben programmiert, sozusagen diese Zuordnung von, man bekommt irgendwelche Eingaben und dann soll halt irgendwie irgendwas rauskommen am Schluss, machen muss und wenn man jetzt Machine Learning verwendet, dann macht diesen Schritt halt wie ein mathematisches Optimierungsverfahren und das sich orientiert an Trainingsbeispielen, an Beispielen für das ist reingegangen und das ist rausgekommen und so sollte das halt sein.
Aber genau, das ist ja auch das, was dann der Mensch dann doch wieder macht, ne? Das heißt, der Mensch macht ja diese Vorarbeit, indem er die Trainingsdaten mit den richtigen Zuordnungen, mit den richtigen Labels bereitstellt.
Genau, richtig, aber es ist halt schon ein Unterschied, ob man jetzt quasi einem Computer nur zeigen muss, was er tun soll oder ob man ihm tatsächlich explizit Schritt für Schritt sagen muss, was passieren soll. Das eine ist halt deutlich einfacher als das andere und damit kommen halt natürlich viel mehr Probleme in den Bereich der Lösbarkeit, als man halt vorher hatte.
Ja, es gibt auch diverse Probleme, die man sonst gar nicht in den Griff kriegt. Also wo man tatsächlich versucht hat, einen Algorithmus zu schreiben, der das tut, wie zum Beispiel eben sowas wie bei Bildern Katzen von Hunde unterscheiden oder so, aber das geht halt gar nicht, weil man das halt nicht wirklich in einen Algorithmus reinbekommt.
Man kann nicht so ein Gegenstand definieren, wo eine Katze ist irgendwie so ein kleines Tier und grau und weiß und so weiter.
Ja, das ist schwer.
Ja, oder auch das Arbeiten mit Sprache finde ich ein gutes Beispiel. Da hat man halt viele, viele Algorithmen entwickelt, die auch...
Auch zu einem gewissen Grad funktionieren, aber man ist mit Übersetzungen halt einfach quasi dann auf so einem Plateau spägen geblieben und kam da nicht mehr weiter. Und dank Deep Learning Verfahren, also tiefen neuronalen Netzen, ist es halt eben dann möglich geworden, dann nochmal ganz neue Lernraten zu erreichen.
Ja, genau. Das ist halt auch Stichwort Deep Learning eigentlich das Ding, was in den letzten paar Jahren halt diesen Hype irgendwie befeuert hat und da ist durchaus irgendwie ordentlich was dran. Also es gehen inzwischen viele Sachen, die früher überhaupt gar nicht gingen.
Ja, aber natürlich ist es immer schwer für Leute, die das jetzt nicht so in Detail verfolgen können und wer tut das halt, außer die nicht irgendwie tatsächlich damit arbeiten, zu unterscheiden, was denn jetzt irgendwie geht und was nicht geht und was eigentlich das Coole daran ist.
Und in den Medien hat man dann doch oft das Gefühl, dass da irgendwie man davon ausgeht, dass das irgendwas Magisches ist, dann geht halt alles und das ist ja jetzt auch nicht so.
Ja, aber das ist genau der spannende Punkt. Du hast nämlich genau gerade versucht zu erklären, dass...
Dass das, was da passiert, irgendwelche mathematischen Algorithmen sind, ja, und die halt zu verstehen als Mensch oder daran zu schrauben zu können, die Parameter, die man halt irgendwie bei Maschinenlöhne noch einstellt und das halt dann so durchblicken, also was das zu tun hat jetzt mit Statistik oder so, das ist vielleicht nochmal ganz spannend, weil dann würde man vielleicht auch nicht nur glauben, dass Magie, also ne, vielleicht sowas mit zu tun, wenn irgendjemand neue Sachen erfindet, irgendeine Geometrie entdeckt, dann ist das vielleicht auch ganz magisch für jemand, der sowas noch nie gesehen hat und deswegen ist halt diese künstliche Intelligenz gar nicht so künstlich.
Wie wir dachten, aber dann trotzdem so ein Phänomen, was gerade, ja, bei den Leuten, die sich damit nicht auskennen, so ankommt.
Ja, also wenn man das guckt, was, wenn man sich einfach nur die Ergebnisse anschaut, dann wirkt das so ein bisschen magisch, aber...
Genau, also warum kann der Computer jetzt einen Hund von einer Katze unterscheiden, wie soll das denn gehen, so, ne?
Genau, genau, ja.
Wie, der Computer weiß, was ich da gerade gesagt habe und kann mit mir sprechen oder so, das ist schon phänomenal irgendwo.
Ja, aber ist das nicht eigentlich immer so? Also, wenn man halt quasi irgendein Blackbox-Modell hat und da nicht reinschauen kann, dann...
Ist das, wirkt das wie Magie und wenn man halt nach und nach die Informationen da drin versteht, dann, ja, blickt man halt, okay, das hat gewisse Schlussfolgerungen, ich meine, das hat schon ein Grund.
Verstehen wir die wirklich bei Machine Learning? Das habe ich jetzt nämlich auch noch nicht so ganz verstanden.
Nee, genau, da gibt's, ja, gibt's natürlich einen Unterschied zwischen so eben Blackbox-Whitebox-Verfahren, bei denen man, also bei den, sozusagen, ja, Whitebox-Verfahren, sowas wie Decision Trees, kann man prinzipiell verstehen, wie die funktionieren, weil man einfach sicher den Entscheidungsbaum aufmalen kann, dann kann man den halt von Hand durchgehen.
Ähm, bei eben Blackbox-Verfahren wie so neuronalen Netzen oder so kann man das prinzipiell eigentlich nicht wirklich, also man kann zwar sehen, welche, äh, was das Ding gemacht hat und, und, ähm, auch irgendwie, was es erkannt hat und, und woran es sich orientiert, aber so letztlich genau verstehen, warum es jetzt zu einem bestimmten Ergebnis gekommen ist, kann man eigentlich nicht mehr.
Und ich fürchte, auch einer der größten, wichtigsten Punkte, weshalb das so prinzipiell eine schwierige Geschichte ist, ist, dass die Modelle, je komplexer sie werden und desto mehr Parameter sie haben.
Ähm, werden sie halt auch sehr groß, also wenn ich dann mehrere, ja, Millionen reicht ja schon, aber wenn ich dann mehrere hundert Millionen Parameter habe, dann, äh, ist das eine Menge, die ich als Mensch ja überhaupt nicht mehr überlegen kann, ne, das ist halt, äh.
Für gewisse Objekte zu verstehen, wie das Modell quasi zu diesem Schluss kam, ähm, also so Blackboxig sind diese, ähm, also diese neuronalen Netze jetzt gar nicht mal mehr, ähm, auch da gibt es Möglichkeiten, die zu verstehen, aber ja, also klar, das ist natürlich deutlich komplexer als jetzt ein Entscheidungsbaum, den ja wahrscheinlich jeder mehr oder weniger kennt, was nicht viel anders ist als ein Flussdiagramm.
Aber vielleicht wollen wir kurz dann nochmal ein, zwei Schritte zurückgehen und so ein bisschen die Hörer da abholen, wir wollten ja so ein bisschen das Beispiel erzählen und vielleicht kommen wir dann, welche Methoden man denn verwenden kann für bestimmte Datensätze da.
Also, ob wir jetzt so ein neuronales Netz oder so ein, so ein Decision Tree verwenden möchten, würde das Sinn machen, was sagt ihr?
Ja, also ich finde, man muss ja erstmal festhalten, dass es halt irgendwie nicht so ist, dass jetzt plötzlich für alles man Deep Learning benutzen sollte, nur weil es jetzt gerade gehypt ist, sondern es gibt ganz, ganz viele Verfahren, ähm, die schon lange existieren, die bewährt sind und die für einige Tasks halt auch sehr gut funktionieren, also wenn ich jetzt zum Beispiel einfach eine, ähm, gewisse Zeitreihe vorhersagen möchte, ähm,
ähm, mit relativ einfachen Mitteln, um vielleicht den Abverkauf von meinem Produkt, ähm, vorherzusehen oder die Temperatur oder sowas, dann gibt es da jetzt erstmal Regressionsverfahren, die so schon sehr gut funktionieren und da muss man jetzt nicht zwangsläufig irgendwie Deep Learning draufwerfen, ist halt eben was anderes wie bei der Bilderkennung, die du jetzt zum Beispiel erwähnt hast.
Gut, Regression ist natürlich immer nur so gut, wie, ähm, die Zukunft, äh, der Vergangenheit entspricht, ne, das ist ja vielleicht immer das Problem.
Ja.
Aber das Problem haben Deep Learning Algorithmen an der Stelle genauso.
Ähm, ja, okay.
Also, natürlich hast du da nicht unbedingt dann immer so einen linearen Zusammenhang, aber, äh, ja, im Grunde natürlich so Dinge, die komplett neu sind.
Ja, wenn, ja, Alph oder sowas, das ist ein Hund oder eine Katze, weiß man nicht so genau, ja, dann kommt was Neues auf die Welt.
Äh, äh, genau, ne, also da, es gibt auch Verfahren, die, die, die total einfach zu verstehen sind, ne, also sowas wie, wie Naive Base zum Beispiel, das ist eine der ersten, äh, äh,
Geschichten, die, äh, also, ich glaube, 2004 hatte Paul Graham halt da irgendwie den, einen Artikel veröffentlicht, The Plan for Spam, äh.
Jetzt musst du nochmal einmal kurz sagen, was Naive Base überhaupt ist, für alle, die das noch nie gehört haben.
Das ist auch ein Klassifikationsverfahren, und zwar, also, ich, genau, ich wollte, also, wenn man jetzt ein bisschen erklären möchte, wie funktioniert das, oder wie funktioniert Machine Learning, dann ist der allereinfachste Fall, äh, den man immer beschreiben kann, halt binäre Klassifikationen, vielleicht, so, äh, womit man anfangen kann, und, ähm, ja, äh, äh, Naive Base ist halt ein,
also binäre, quasi, Klassifikationen, also, drin oder nicht drin, äh, genau, man unterscheidet eigentlich nur zwei Klassen, genau, und, ähm,
im Supervised Machine Learning gibt's dann im Wesentlichen unterschiedliche Tasks, es gibt eine Regression, oder es gibt eine Klassifikation als die beiden typischsten Fälle, und, genau, jetzt, so, der einfachste Fall wäre dann halt eben eine binäre Klassifikation, wo ich einfach nur sage, in folgendem Fall, Daumen hoch, Daumen runter, ja oder nein.
Genau, ähm, ich such mal grad, ob ich das hier, äh, finde, Moment, äh.
In der näheren Klassifikation, bevor wir dann vielleicht tatsächlich darauf eingehen, was für eine Möglichkeit man hat, jetzt so diese Klassifikation durchzuführen, weil, weiß ich, mehr als zwei Dinge, also wenn du zwei Merkmale hast, dann kannst du ja relativ einfach voneinander trennen, wenn die homogen sind untereinander in ihren eigenen Klassen und das wird ja umso komplexer, umso weniger heterogen die untereinander sind, also umso mehr die sich gleichen, dann hast du natürlich größere Probleme, dann ist es vielleicht auch so, dass du die Merkmale falsch anguckst oder so, also du sollst halt schon unterscheidende Merkmale finden können.
Und das ist halt im Prinzip das, was du mit Base dann relativ gut machen kannst, du kannst halt dann sagen, ey, ist jetzt der Fighter grün oder nicht, oder schwarz oder so, oder ist das halt eine Katze, woran erkennt man das? Ich weiß nicht, ob sich jetzt Base für Katzen und Hunde schon eignen würde, weil, das wird vielleicht schon schwierig.
Ja, also für Bilder wird das sicherlich nicht gut funktionieren, aber wenn ich jetzt, eben, das ist halt auch so ein klassisches Beispiel habe, wenn ich jetzt Spam von Nicht-Spam-Mails unterscheiden möchte, da ist halt genau so, das war früher, waren die ganzen Spam-Filter,
irgendwie regelbasiert, also man erinnert sich da vielleicht noch so an Zeiten, ich habe das auch selber noch benutzt, das war so ein Perl-Script-Spam-Assassin, das hat sich dann halt die Header angeguckt und dann hat es halt manchen Providern mehr und manchen weniger vertraut und irgendwelche, und dann insgesamt halt irgendwie einen Score ausgerechnet und gesagt, ah, okay, das hat jetzt einen Spam-Score von so und so viel oder so und so viel und ab einer bestimmten Grenze wurde dann halt abgeschnitten und gesagt, das ist Spam.
Das hat ganz okay funktioniert, aber...
Das war immer leicht zu unterlaufen von Spammern und das war, und Spam war immer eine relativ ärgerliche Geschichte, bis dann halt eben Paul Graham, halt der, ja, wie heißt der Inkubator, den er gegründet hat, darüber ist er sehr bekannt geworden, habe ich jetzt gerade, ist mir entfallen, also der schreibt auch viele Essays und hat auch ein Buch geschrieben...
Y-Combinator tatsächlich.
Y-Combinator, natürlich.
Ich wollte hart unterzungen, ich habe mich nur nicht getraut.
Ja, der schrieb dann halt irgendwie 2004 ein Essay, so, a plan for Spam, also ich habe da irgendwie so eine Idee, wie wir dieses Problem endgültig in den Griff kriegen und da schlägt er halt vor, irgendwie so Naive Base zu verwenden, um inhaltsmäßig irgendwie Spams, Vernichtspams zu unterscheiden, weil, naja, irgendwie ein Spammer muss ja reinschreiben, dass er was verkaufen will, er muss ja irgendwie, er kann ja, den Content kann er im Grunde nicht wirklich so ändern, dass man das nicht mehr als Spam erkennen kann, weil dann...
Dann kann es auch der Mensch nicht mehr als Spammer kennen und dann verfehlt er seinen Zweck und der...
Liebe Freund, ich brauche in Hilfe.
Ja gut, okay, das ist jetzt eine bestimmte Art von Spam.
Das sind die, die bei mir gerade immer durchkommen, immer wieder.
Die kommen durch?
Ja.
Da ist aber irgendwas mit deinem Spamfilter nicht in Ordnung.
Was für ein Spamfilter?
Okay.
Nein, also die landen bei mir im Spamfilter und ich kann dann immer sehen, okay, es war eine Message im Spamfilter, was auch nicht so häufig passiert.
Achso, okay, gut, ja.
Genau, und zwar, das Verfahren ist halt super einfach.
Das ist halt...
Mach einfach aus den Mails, aus dem Inhalt deiner Mails im Spamordner eine lange Liste von Wörtern.
Zähle, wie oft ein Wort da drin halt vorkommt.
Merke dann die Gesamtzahl der Wörter und die Gesamtzahl der Mails, die da drin waren.
Und mach das Ganze auch nochmal für den Ordner mit den Nicht-Spam-Mails.
Und das ist schon das Training.
Das war's.
Also man hat jetzt quasi an jedem Wort sozusagen nur dranstehen, quasi, ja, wie oft das halt vorkommt.
Und man hat nochmal eine Zahl für, ja, wie viel Mail...
Da sagt der Eigentümer, uh, er hat Penis gesagt.
Und dann sagt der, nee, ist wahrscheinlich Spam.
Also so in etwa.
Genau.
Und die Klassifikation, also wenn man das gemacht hat, ist man mit dem Training schon fertig.
Und das ist auch schon das Modell.
Und die Klassifikation funktioniert jetzt so, wenn man jetzt eine zu prüfende Mail hat, dann zerlegt man die eben auch wieder in eine Liste von Wörtern.
Man macht aus dieser Wortliste eine Liste von Zahlen.
Und man ersetzt jetzt jedes Wort.
Und eben durch die Kategoriefrequenz.
Das hatte man ja vorher schon ausgerechnet sozusagen.
Man wusste halt, wie oft jedes Wort im Spam- und im Nicht-Spam-Ordner vorkommt.
Und sozusagen, ja, dann ersetzt man jetzt die Wörter eben durch die Kategoriefrequenz.
Und multipliziert jetzt einfach alle diese Zahlen miteinander.
Und bekommt dann halt eine Wahrscheinlichkeit, dass eine Mail Spam oder Nicht-Spam ist.
Und, ja, die Zahlen werden relativ klein sein, weil das sind alles relativ kleine Wahrscheinlichkeiten.
Und die noch miteinander multipliziert.
Das ist super.
Das heißt, man muss das Ganze noch so normalisieren, dass hinterher halt irgendwie quasi der Wert für Spam und Nicht-Spam, wenn man das addieren würde, 1 ergibt.
Aber, ja, das ist ja auch kein Problem.
Und das war es schon.
Also es ist wirklich nicht, es ist keine große, ja, es ist keine, wie soll man sagen, Raketenchirurgie.
Das ist eigentlich eine ziemlich einfache Geschichte.
Genau, das kann man ja einfach sich eigentlich auch noch so in einem Algorithmus darstellen.
Da braucht man, ja, keine komplexen Selbstlernverfahren eigentlich noch, nicht wahr.
Ja, also gut.
Und deswegen lernt man tatsächlich.
Also wenn ich jetzt sage, wenn jetzt eben, ich habe bisher nur Mails, die mir Produkte zur Vergrößerung meiner primären Geschlechtsmerkmale,
die mir so und so was andrehen wollen, bekommen.
Und ich kriege jetzt zum ersten Mal einen Nigeria-Spam, wo mich jemand halt dazu bringen will, einen kleinen Betrag ihm zu geben,
um einen viel größeren zu bekommen.
Dann würde ich halt das halt auch mit in die Spams, in den Spam-Montage reintun.
Und dann müsste ich halt wieder neu...
Diese Zahlen ermitteln.
Genau, die müssen halt wieder neu gebildet werden, aber...
Dann würden sich die Frequenzen wahrscheinlich zum Beispiel für so ein Wort wie Prinz oder so oder Schweizer Bankkonto entsprechend verändern.
Und dann hoffentlich beim nächsten Mal werden wir dann so eine Mail dieser Art...
Das finde ich ärgerlich für Prinz mit Schweizer Bankkonto.
Ja, die haben ein Problem damit.
Die werden, ja, die haben mit Meldern keinen Spaß mehr seitdem.
Genau, und das, ja, und dann passt sich das durchaus.
Mit quasi neuen Mustern an, wenn die nächste Masche um die Ecke kommt, dann braucht man halt nur die ersten paar sozusagen richtig zu sortieren
und dann sollte das automatisch funktionieren und mitlernen.
Ja.
Ja, ich habe ja kurz eine Base-Kalzifikation erklärt.
Wir sind aber immer noch nicht bei dem Beispiel angekommen, was wir irgendwie den Menschen...
Wie macht man das denn überhaupt?
Wir haben versprochen, dass wir irgendwie praktisch durchgehen wollen.
Da hätte ich mal gesagt, lass uns das mal machen.
Ja.
Wir packen mit Sicherheit unter die Folgen mal einen Link zu dem Repository,
was wir dafür benutzen wollen.
Das ist vom Jochen.
Da hat er halt eben eine Schulung drin, eine Data Science Schulung,
woraus wir jetzt vor allem gleich mal das Beispiel nehmen wollen für die Text-Klassifikation.
Und genau, wenn man da drauf geht, sieht man in dem Readme auch erstmal irgendwie alle Informationen,
wie man sich das Ganze zieht.
Also man klont sich halt erstmal das Gits-Repository.
Soweit, so gut.
Und dann kommen auch schon die ersten Python-spezifischen Sachen,
denn dann müssen wir uns erstmal irgendwie mit Conda eine entsprechende Umgebung aufsetzen.
Jochen, sag doch mal, warum sollte man das denn mal machen?
Und warum denn Conda?
Ja, also Conda ist sozusagen der Paketmanager des Teils von Python,
der sich mit so Data Science, Machine Learning-Geschichten beschäftigt.
Da steckt halt auch so eine Firma dahinter, Continuum Analytics.
Das sind auch die Leute, die die Firma gegründet haben,
sind auch die, die ursprünglich mal NumPy mitentwickelt haben und so.
Und, ja, es ist...
Früher hatte man ja irgendwie mal so die Hoffnung,
dass die Distribution, das ganze Paketinstallationsding halt irgendwie überflüssig machen würden
und sich halt um Abhängigkeiten und sowas kümmern.
Und das ist irgendwie nicht passiert.
Also stattdessen ist es so, dass irgendwie jede Programmiersprache so gefühlt irgendwie ihren eigenen Paketmanager hat.
Und bei Python ist es ein bisschen besonders schlimm,
weil da gibt es halt nicht nur einen, sondern mehrere.
Je nachdem.
In welcher Community man sich da aufhält,
wenn man irgendwie eher Webentwicklung oder sonst wie Python-Entwicklung macht,
dann ist es eher Pip und als Repository für Pakete halt PyPI.
Ich würde sagen, das Problem hat nicht nur Python.
Nee.
Rust ist da auch ganz schlimm, habe ich schon gelernt, aber ja.
Nee, genau.
Ja, das wäre auf jeden Fall ziemlich anstrengend.
Ich weiß gar nicht, ob ich Conda so verwenden würde, so außerhalb von Daten.
Nee, würde ich glaube ich auch nicht machen.
Mache ich auch nicht.
Also wenn ich Webprojekte mache, dann nehme ich...
Äh, eher Pip.
Und tatsächlich funktionieren die meisten Sachen auch, wenn man sie mit Pip installiert.
Also tatsächlich auch die ganzen Datensachen funktionieren über Pip?
Ja, schon.
Sie funktionieren zumindest gut genug, als dass man das meistens einfach so verwenden kann.
Wenn ich jetzt irgendwie tatsächlich ein Modell trainieren wollte,
ja, aber das will man halt, wenn man ein Selbstverwahrt,
der irgendwie auch nur irgendwie Pandas noch mitbraucht,
um halt irgendwelche Sachen anzupassen.
Ein Webserver, der jetzt halt hinter irgendeiner API ein Modell hat,
was irgendwas vorhersagt oder was irgendwas klassifiziert
und dann die Ergebnisse zurückgibt,
da ist es im Grunde egal, da kann man das bei Pip installieren,
aber wenn ich jetzt irgendwie was trainieren wollte,
dann würde ich eher die Conda-Pakete nehmen
oder vielleicht sogar spezielle Docker-Images oder sowas.
Also, ja, aus meiner Sicht hat halt Conda vor allem zwei Vorteile.
Zum einen ist es halt schon mal gekappt,
also zusammen das, was sonst Pip und Virtual Env ist.
Also ich habe die Möglichkeit,
sowohl eine virtuelle Umgebung hier zusammenzubauen
und dann halt eben in die die Pakete mit rein zu installieren,
was man häufig möchte, wenn man gerade zum Beispiel so ein Experiment macht,
dann möchte ich vielleicht meine Basis-Version an Paketen so lassen,
wie ich sie hatte und dann halt mal für jetzt eine Konferenz zum Beispiel
mir nochmal eine zweite daneben stellen.
Das müsste ich ja sonst mit Virtual Env und Pip quasi in zwei Schritten machen.
Ja gut, aber normalerweise machst du ja Virtual Env Rapper,
dann einen Befehl und dann ist das Ding ja oben.
Genau.
Ich habe da tatsächlich ein Python-Skript für sowas geschrieben.
Der auch genau das gleiche macht, wenn ich das will, aber ja.
Genau, und der zweite Punkt ist,
Conda kann halt einfach ja noch ein bisschen mehr als Pip.
Es kann halt vor allem auch quasi Abhängigkeiten,
die außerhalb von Python-Paketen liegen, lösen,
was dann halt auch interessant wird,
wenn es halt um Treiber-Support und so weiter geht.
Genau, aber ich glaube, du hast schon recht,
dass es auch so ein Stück weit ein Community-Ding ist,
dass es sich halt im Data-Science-Umfeld
auch durch den starken Support von Continuum da
und den finanziellen Mitteln, die dahinter stecken,
so durchgesetzt hat.
Ja, aber...
Ja, ja, also...
Weil es so einfach zu installieren ist auch.
Es gibt halt auch, glaube ich, nur Windows-Binarys einfach
und da ist dann alles mit dabei an Modulen, was man so hat
und man muss das gar nicht mehr groß bedienen.
Ich glaube, das ist auch so ein low-leveliger Ansatz.
Das ist auch der Grund, warum sie das so verbreitet hat.
Wobei ich jetzt ehrlich gesagt zum Beispiel
die große Anaconda-Distribution habe ich nie benutzt,
habe ich nie installiert.
Also das Anaconda-Distribution ist eigentlich sozusagen
das eigentliche Ding, was irgendwie da produziert wird.
Ich habe immer Mini-Conda verwendet
und dann halt das installiert, was ich gerade gebraucht habe.
Also der Unterschied ist,
Anaconda bringt halt schon mal irgendwie alle möglichen Pakete mit,
die man potenziell so gebrauchen könnte
und Mini-Conda ist halt erstmal klein und schlank
und dann muss man halt alles quasi mit dazu installieren,
was man dann noch gerne hätte.
Ja, und eben der Vorteil gegenüber Pip
oder sagen wir mal so,
das ist halt auch nicht mehr so ganz...
Das war früher ein Riesenunterschied,
ist heute nicht mehr ganz so relevant,
ist halt, dass man halt binäre Geschichten
bei Anaconda auch installieren kann.
Also man kann halt auch Bibliotheken,
die unten drunter liegen,
also eben nicht nur Python-Pakete,
sondern eben auch sowas.
Man kann auch theoretisch sowas wie
Libc irgendwie mit Conda installieren
und die dann halt in einem Conda-Environment verwenden.
Und muss halt...
Also das ist auch so ein Problem,
wenn man jetzt auf größeren Servern unterwegs ist,
die halt unter Umständen irgendwie
so eine antike Debian-Version irgendwie haben
und eine uralte Libc
und die funktioniert dann mit neueren,
anderen Bibliotheken nicht mehr gut.
Dann hatte ich auch schon den Fall,
dass ich dann per Conda irgendwie eine neue Libc
in meinem Environment installiert habe,
um überhaupt irgendwie
bestimmte Sachen benutzen zu können.
Und das geht halt mit Conda
und mit Pip wäre man da halt relativ aufgeschmissen.
Daher, ja,
für so wirkliche Data-Science-Projekte
ist das halt schon irgendwie deutlich netter.
Es ist ein bisschen schade,
dass jetzt man halt zwei Repositories von Paketen hat
und ja, manchmal ist es halt auch so ein bisschen kompliziert,
dann sich zu überlegen,
was man gerade machen will.
Aber ich würde die als Faustregel einfach nehmen.
Wenn es ein reines Data-Science-Projekt ist,
ja, dann Conda.
Und wenn es irgendwie eine Web-Geschichte ist
oder was ganz anderes,
dann ist es eher Pip.
Ist das denn immer genauso aktuell in Conda
wie auf PyPy?
Nee, Conda ist nicht so aktuell.
Es gibt ja noch Conda Forge,
also sozusagen ein...
Da kann man sich selber Sachen bauen.
Ja, das kann man sowieso,
aber es gibt halt einmal die Pakete,
die sozusagen von der Firma gebaut werden
und ein Repository von Paketen.
Das ist halt eine Distribution,
die halt sozusagen redaktionell
von Continuum Analytics gepflegt wird.
Und es gibt halt auch
ein Community-basiertes Repository,
das nennt sich dann Conda Forge.
Und man kann das halt in dem...
Man hat immer so ein Konfigurations-File
und da kann man halt die Channels sozusagen,
aus denen man Pakete bekommt,
halt eintragen.
Und ja, die meisten verwenden Conda Forge.
Ist es jetzt auch irgendwie so,
dass, glaube ich,
Anaconda selber irgendwie
auf Conda Forge umgestellt hat?
Ja, genau.
Die haben sich das umgestellt.
Das haben wir gehört.
Das gehört auf dem Parkcamp jetzt, ja.
Ja, richtig.
Das hatte ich auch gar nicht klar, aber ja.
Das fand ich manchmal auch echt so ein bisschen nervig.
Wenn man ein Paket installieren wollte,
habe ich dann doch meistens mal gegoogelt,
was ist denn jetzt hier das richtige Repo dafür
und das ist jetzt aber seitdem quasi weg.
Ja, ist tatsächlich anstrengend,
wenn man halt schon unterschiedliche Pakete
hinterhat und dann auch seine eigene Source dafür
irgendwie letztendlich bestimmen muss.
Ich kann mich da an ein oder andere Experimente erinnern
mit so Paketsachen und Sources.
Alles nicht einfach.
Aber hatten wir das schon erwähnt,
warum man das überhaupt machen sollte?
Ja, das...
Das Problem ist eben,
dass die Pakete in der Distribution
halt nicht wirklich aktuell sind meistens.
Also, wie sagte Barry Varsaw, glaube ich,
einer Python-Core-Entwickler,
sagt er mal,
the first rule of Python ist,
you don't use system Python.
Das ist halt einfach üblicherweise veraltet
bei vielen Distributionen,
weil sie halt auch das,
weil sie Python halt auch für so
distributionsinterne Zwecke benutzen,
mit halt Python 2 irgendwie noch als Default-Interpreter,
bei Mac auch.
Es gibt einen Counter für Python 2 mittlerweile.
Hatten wir den gesehen?
Desk Clock, ja.
Ich bin mal gespannt,
wenn die Vertriebssysteme es dann auch mal sich trauen.
Das war schon ein bisschen heiter so langsam, aber...
Ja, die haben halt alle Angst davor,
dass irgendwie, wenn sie das ändern,
irgendwie ihre Installationsroutinen
nicht mehr funktionieren,
weil die halt Python brauchen
und vielleicht noch nicht umgestellt sind.
Und die sind halt alle uralt.
Ja, doof.
Und daher muss man sowieso eigentlich
immer einen anderen Python-Interpreter verwenden,
als der, der vorinstalliert ist.
Und dann ist es halt so,
wenn man unterschiedliche Projekte hat,
dann haben die halt unterschiedliche
abhängige Projekte,
was bestimmte Bibliotheken angeht.
Und die widersprechen sich halt.
Und das kriegt man halt auch,
wenn man in einem Filesystem ist.
Und das kriegt man halt nicht irgendwie unter einen Hut.
Und daher macht man normalerweise pro Projekt
ein eigenes, sozusagen virtuelles Environment,
in dem man halt irgendwie
alle Abhängigkeiten installiert, die man braucht.
Ist dir schon mal vorgekommen,
dass du für ein Projekt zwei
virtuelle Environments brauchst,
weil da innerhalb des Projektes
sich unterschiedliche Module bespricht?
Nee, das geht auch gar nicht.
Oder, ja, also natürlich doch, stimmt,
man könnte es theoretisch machen.
Aber nee, das ist mir jetzt auch noch nicht
untergebracht.
Was meinst du denn halt von einem Projekt?
Ja, also man könnte ja irgendwie sagen,
dass es zwei Module in einem Projekt gibt,
die unterschiedliche Versionen brauchen,
Python-Versionen,
weil die Module das sonst nicht können
und die man dann irgendwie pipen muss,
damit die man da reden kann.
Und um das dann zu bauen,
müsste man halt dann zwei Versionen benutzen.
Nehmen wir zum Beispiel mal an,
man hätte ein OCR-Projekt
und man möchte jetzt irgendwie
ein aktuelles Ding gegen was Älteres
wie OCR-OPI oder Kraken oder so verwenden.
Nehmen wir mal an,
Kraken kann auch Patentreiber.
Das OCR-OPI kann nur,
OCR-OPI kann nur Python 2
und jetzt möchte man halt das Ding aber
testen gegen was Neues,
was halt vielleicht nur Python 3 kann
und dann müsste man zwei Interpreter halt haben
irgendwie in dem,
ja, das wäre hässlich.
Aber ich glaube,
so kompliziert müssen wir es gar nicht machen.
Nee.
Genau, also wenn ihr euch inzwischen
das Repo ausgeklont habt,
dann ist jetzt vielleicht der richtige Moment,
einmal Corner-Inf-Create auszuführen,
um die Umgebung nicht aufzusetzen.
Was an der Stelle ja passiert ist,
er zieht sich von lokal eben
eine hinterlegte,
äh, Environment-Datei,
die die ganzen Pakete
und die jeweiligen Versionen beinhaltet,
die man bitte zu installieren hat
und danach kann man das dann eben aktivieren
und hat das dann quasi als seinen
Default-Python-Interpreter gesetzt.
Also Corner-Inf-Create macht dann genau das,
dass er einfach ein Environment setzt,
die heißt dann Great, oder was?
Nee, das steht in dem, äh,
in dem File halt auch mit drin.
Der Name von dem.
Von dem, ähm, äh,
von dem Environment, genau.
Ähm, ähm,
ähm, warte mal, genau, äh,
das ist auch einfach ein YAML-File
und da steht dann eben das erste Name
und da steht dann DS-Tutorial
für das neue Tutorial.
Und dann Dependencies, ach, und dann werden die Pakete danach gepackt.
Und, ach so, das ist auch so,
man kann halt auch, äh, genau,
man kann auch Sachen per PIP installieren.
Ja, das ist auch sowas, also es ist halt
irgendwie, äh, ja,
Conda ist quasi eine echte Obermenge von
PIP, also man kann halt Conda und halt
auch PIP, aber umgekehrt geht's halt nicht.
PIP kann halt nicht Conda Sachen installieren.
Ähm, ja, und manche Sachen installiere ich
via PIP, weil das einfach aktuellere
Versionen dann sind, ähm.
Ja, oder halt, wenn man was sehr Spezielles
will, ist es vielleicht auch nicht unbedingt
immer in jedem Conda-Repository eben drin.
Ähm, da ist PIP, glaube ich, einfach ein bisschen
größer. Also ich kann auch einfach die Version angeben,
wie bei PIP auch, ne, mit gleich oder sowas.
Genau, genau, das kann ich auch machen, ja.
Genau, und danach müsst ihr, um das Repo
quasi so ausführen zu können, nochmal,
ähm, das lokale Paket
bauen, und zwar mit Minus E.
Ja. Ähm, warum wollen wir
denn erstmal überhaupt ein lokales
Paket, und warum wollen wir Minus E?
Mhm, ähm, ja,
ein lokales Paket, äh, deswegen, weil
man in vielen Notebooks halt,
also, ich hab das Ganze quasi ein bisschen
in unterschiedliche Notebooks aufgeteilt, und da
gibt's dann halt, ähm, einige, die halt
immer wieder den gleichen Code enthalten,
und jetzt könnte man natürlich auch in jedes Notebook
irgendwie reinkopieren, aber wenn man
dann gemerkt hat, oh, ich hab hier einen blöden Fehler gemacht,
dann muss man das halt auch in allen Notebooks wieder ändern, was ja
ein bisschen umständlich wäre, und
Code, den man halt, äh, in
mehreren unterschiedlichen Notebooks, äh,
benötigt, den, äh, kann man ja aber auch
einfach in eine Bibliothek, und das ist halt
im Grunde dieses Paket, äh, was man
da installiert, ähm, rausziehen,
und den importiert man dann halt
einfach nur in allen Notebooks, und wenn man da jetzt
ein Fehler fixt, dann ist der halt in allen Notebooks gleichzeitig
gefixt und Minus E installiert man deswegen, um Änderungen,
wenn man da jetzt eine Änderung macht, die sofort
sichtbar werden zu lassen. Also ansonsten müsste man ja eigentlich, wenn man
ein Paket zum Beispiel via PyPI oder so installiert oder normal installiert
sozusagen, jedes Mal, wenn man was ändert, ein neues Paket bauen
und dann dieses Paket wieder installieren. Und
am besten auch nochmal das Notebook neu starten. Und das ist
natürlich ein bisschen umständlich und wenn man sagt pip install minus
E, dann macht das sozusagen keine wirkliche
Paketsinstallation, sondern legt nur einen Link auf das
entsprechende Verzeichnis sozusagen mit nach Side-Packages und
wenn sich da irgendwas ändert
am Code, dann ist das halt sofort irgendwie sichtbar und
dann kann man noch auf...
Man kann das auch für alle einstellen oder für einige. Auf Notebook-Seite kann man zum Beispiel
sagen, A-Import statt Import,
ein bestimmtes Modul, dann wird halt die Aktualisierung
auch sofort wirksam, ohne dass man das Notebook neu starten muss, was ja auch praktisch ist.
Man kann das ja fein und granular einstellen. Man will das
vielleicht auch nicht für alle Module machen, aber wenn man das richtig
eingestellt hat, kann man halt, wenn man jetzt
beispielsweise bei einem Code-Teil, der halt
in vielen Notebooks verwendet wird, einen Teil ändert,
dann ist die Änderung sofort da und
bei der nächsten Ausführung
einer Zelle hat man halt auch
schon den geänderten Code mit ausgeführt
und muss da nichts neu installieren,
neu starten oder so.
Da kann ich auch jeden nur zu ermutigen,
zu gehen. Also ich habe mich da am Anfang lange vorgescheut
und habe einfach quasi am Anfang
von meinem Notebook einfach immer mit der
Run-Magic quasi in ein anderes
Notebook gestartet, wo ich alle diese Sachen
initialisiert habe. Das geht auch,
aber das muss man halt dann doch, wenn man in einem anderen Notebook
was ändert, immer noch einmal ausführen
und so viel Zauber
ist es gar nicht, quasi sich selber so ein Paket
zu erstellen. Vor allem ist es auch ein ganz cooles Gefühl.
Man kann es danach auch wirklich direkt mal weitergeben
und weiterleiten und es verleitet
zu ein bisschen besserer
Art Software-Code dann auch zu schreiben
an der Stelle. Man kann dann eine ordentliche
IDI dafür vielleicht auch benutzen und
entwickelt dann diese ausgelagerte
Funktionalität vielleicht nicht direkt im Notebook.
Welche IDI benutzt du?
PyCharm, halt ganz klassisch, wie die meisten glaube ich.
Und wir sind beides umgekehrt für die Studie,
Code? Ah ja, okay.
Ich benutze immer noch alles. Also ich benutze auch immer noch
vor allen Dingen häufig
Wim. Ich benutze auch
PyCharm und auch VSCode.
Ich hatte das mir auch schon gezeigt.
Das ist schon ziemlich gut, muss man sagen.
Genau. Ich meine, am Anfang ist so ein bisschen overhead
mit dem Anlegen von so einer Klasse da, aber
ihr benutzt, glaube ich, so Cookie-Cutter-Templates.
Ja.
Ich benutze PyScaffold,
was ein Projekt ist, was von einem Kollegen von mir
entwickelt wird, was mir da sehr geholfen hat.
Einfach am Anfang quasi so
Scaffolding, mir
mein Gerüst einmal aufzusetzen. Da kenne ich mich
dann direkt dran aus und dann trinke ich da noch meine
zwei, drei Zeilen ein und bin
gleich da. Und das hat mir
so ein bisschen die Angst davor genommen, eigene Pakete
an der Stelle zu schreiben.
Ja.
Genau. Gut. Und
dann können wir letztendlich wirklich
unser Notebook starten.
Du benutzt noch Jupyter
Notebox. Ja, stimmt.
Magst du JupyterLab lieber?
Oder warum magst du kein JupyterLab?
Doch.
Ja, stimmt. Mag ich auch.
Das ist jetzt auch nur so reine Gewohnheit eigentlich.
Aber ja, eigentlich im Grunde
müsste ich auch mal auf JupyterLab umsteigen.
Ja.
Genau. Also gerade die neuen Versionen mit den Plugins,
finde ich, es kommt... Also am Anfang haben wir
noch ein paar Funktionalitäten von JupyterNotebooks
gefehlt, aber jetzt gerade
mit dem sich ausbreitenden
Plugin-System gibt es da echt coole Sachen.
Zum Beispiel, was ich total liebe gerade,
ist halt die Sublime Keybinding
Magic-Funktionalität, die man jetzt einfach sich
so in die JupyterLab-Notebook
reinladen kann.
Und so. Aber genau.
Ja, ja, ja.
Das habe ich auch schon lange vor.
Ich habe es halt bisher nur immer ausprobiert, aber noch nicht
wirklich verwendet.
Und genau. Ja, nee. Sieht sehr, sehr gut aus.
JupyterLab ist...
Genau.
So. Egal, ob ihr jetzt ein
JupyterNotebook oder ein ehemaliger...
Ja. Egal, ob ihr jetzt ein JupyterNotebook
oder halt schon JupyterLab gestartet habt,
müsstet ihr jetzt quasi
eine Reihe an
Ordnern sehen und
im DS-Tutorial...
Nee, im
Ordner Notebooks gibt es halt eben
das Projekt TestTextClassification
und dem wollen wir uns mal ein bisschen widmen.
Genau. Ja.
Vielleicht kann man einfach direkt mal das
ExploreData-Notebook aufmachen.
Das ist
das Dataset, also
ist man natürlich auch mal ein bisschen darauf angewiesen, dass es
halt ein frei verfügbares
Dataset gibt, das ich
da verwendet habe, ist
das
Reuters
21578
Textklassifikations-Dataset.
Das ist so ein klassisches Ding,
wo
irgendwie eben entsprechend viele
21578
so Ticker-Meldungen
drin sind.
Und die sind halt klassifiziert
in
unterschiedliche Kategorien.
Und bei dem Set geht es auch
im Grunde darum, Modelle zu
bauen oder das benutzt man
eigentlich mal als Trainingsdatum
für
Textklassifikationsmodelle,
weil man halt dann damit
vorhersagen kann,
wenn man jetzt eine neue Ticker-Meldung reinbekommt,
welche Kategorie ist denn das? Also es ist im Grunde so ähnlich wie
das Spam-Klassifikationsbeispiel
von eben, nur dass
man halt nicht nur zwischen Spam und Nicht-Spam
unterscheiden können will, sondern
halt zwischen allen Kategorien, in denen
so eine Ticker-Meldung
liegen kann, beziehungsweise auch noch
ein Unterschied ist, es ist nicht
nicht, man würde, das wäre jetzt Multiklass,
wenn es nur eine gäbe,
aber das ist eigentlich ein Multilabel-Dataset,
das heißt, man möchte alle
Kategorien, in denen so eine Ticker-Meldung
drin liegt,
rausfinden. Also vielleicht
halt auch, es gibt welche, die halt,
wo es nicht nur um Politik geht
oder Sport, sondern auch Sport und Politik
oder sowas.
Genau. Das Erste, was man
finde ich bei so einem Datascience-Projekt, der am Anfang immer
machen muss, vor lauter Tools
und Frameworks und sonst was,
das Wichtigste sind immer noch die Daten. Und wenn die Daten
nicht passen, dann hilft es nicht. Und da muss man
sich halt echt erstmal reinknien und richtig verstehen,
was haben wir denn da überhaupt alles.
Und ich weiß nicht,
da gibt es so ein paar Befehle, die ich immer als erstes so
benutze. Also ich lade mir mein
Dataset irgendwie in Pandas
DataFrame rein und
dann
nutze ich darauf erstmal meist ein Head,
um einfach mal zu gucken, was habe ich überhaupt drin.
Dann ein D-Types,
um mir anzugucken, okay,
welche Spalten haben denn hier welchen Typ,
und danach mache ich dann
eben ein Describe, was mir dann
für alle numerischen
Spalten
da drin schon mal so ein bisschen eine erste
Übersichtsstatistik gibt. Also irgendwie
wie viele Einträge habe ich, was ist
der Mittelwert, was sind quasi
die Maximalwerte und so weiter.
Machst du das ähnlich oder wie geht es dir?
Ja, ja, doch, durchaus. Also
ich habe das jetzt auch hier gerade nochmal ausgeführt.
Genau.
Ja, also
wenn man jetzt zum Beispiel
ähm,
einige der ersten
Zeilen ausgeführt hat, dann hat man hier auch eben ein DataFrame,
in dem all die
Dokumente halt drin sind und wenn man
DFHat macht, dann sieht man halt
eine Spalte ModUp, das ist halt sozusagen
so der klassische Split in Trainings- und
Testdaten. Das ist halt auch ganz praktisch
bei dem Dataset, dass man das nicht selber machen muss,
weil das ist halt auch nur, eigentlich müsste man dann irgendwie erklären,
was Cross-Validation ist und wie man K-Fault
Cross-Validation, hier ist
das schon fertig, da muss man sich dann keine Gedanken mehr
drum machen sozusagen, das ist vielleicht
ganz gut. Dann
äh,
gibt es eine Spalte mit den Kategorien drin, das ist dann
einfach nur eine Liste der
äh, Kategorien, in denen
halt diese Meldung halt drin ist
und, ähm, genau
das Ganze nochmal, äh, sozusagen
nicht als Textkategorie, sondern
eben, äh, numerisches Label,
weil, äh, wenn man später
das an irgendwelche Modelle verfüttert, dann brauchen
die das halt als, äh,
ja, als Tal
und am besten auch als Talen, die halt
keine Lücken haben, sonst geht das halt
nicht gut, weil man das halt, äh,
one-hot-encoded immer, das
sozusagen hinterher kriegt halt jede Spalte, äh,
jedes Label eine eigene Spalte
und das geht eigentlich natürlich nur dann gut, wenn das halt irgendwie
keine Lücken hat. Ähm,
ja, dann gibt es da irgendwie noch ein Datum, was da dran
ist, äh, irgendwie ein Titel,
äh, irgendeine komische Dateline, ich weiß jetzt gar nicht mehr genau,
was das ist, und ein Body von dem
Artikel, äh, dann noch
eine ID und ich glaube, das
habe ich irgendwie mal dran gebastelt, einfach nur, um,
äh, weil, weil, äh,
die Idee war halt, dass, ähm,
in dieser, in dieser Machine Learning,
äh, Schulung, äh, man
ja,
äh, als Übung sozusagen vielleicht ein paar neue
Features dazu tun sollte, um zu gucken, ob
das irgendwie die, äh, mit,
äh, ob das den Score irgendwie verbessert, der Modelle
und da war halt so ein erstes Beispiel,
man könnte einfach mal den Wochentag reinschreiben,
der, äh, an dem das irgendwie
erschienen ist und vielleicht schickt er ja irgendwelche
Informationen drin.
Ja, genau, äh, DF-Info.
Wenn ihr irgendwie wissen wollt, wie man mit
diesen Data-Frame-Operationen noch ein bisschen
besser einen Einstieg findet, da gibt es ein ganz spannendes,
äh, Buch von Jake Vanderplast zu,
Data Science mit Python. Das hat er
auch, äh, frei auf seinem GitHub-Account
veröffentlicht und auf einer Webseite.
Ähm, findet ihr irgendwie, da kann man auch mit
Dupedia-Notebooks das tatsächlich auch mal live noch an anderen
Datensätzen üben? Also gerade für Pandas-
Einführung ist da nochmal ein großes Kapitel.
Ja, äh, also die, die Pandas-Dokumentation
ist auch, auch ziemlich gut. Die ist halt nur etwas
länglich und trocken, aber ansonsten ist sie auch super, äh,
zu empfehlen. Äh, und,
äh, ja, äh, ich finde die, die,
die modernen Pandas, äh,
Blog-Artikel-Serie von Tom Augsburger auch super.
Ja, ja, äh.
Was ich ansonsten immer noch ganz gerne mache,
wenn ich am Anfang so ein Dataset habe, es gibt
eine Library, die nennt sich Pandas Profiling.
Mhm. Ähm, die,
da wirfst du einfach so ein DataFrame rein
und die generiert dir daraus so einen kleinen
HTML-Report,
worin man dann quasi so ein paar
Summary-Statistiken nochmal ein bisschen hübscher aufbereitet
bekommt, also irgendwie. Das sieht ja auch super aus, ja.
Welche, äh, wie viele, wie viele Teilen hast du,
wie viele Spalten natürlich, aber dann schaut die halt eben
auch quasi, wie viel Prozent der Daten sind jetzt
halt irgendwie fehlen, sind Nullwerte,
ähm, du kriegst, ähm,
eine Übersicht quasi, welche Typen
haben, hat das Ganze und der sucht auch quasi nach
Korrelationen in den Daten, also wenn du jetzt halt
zwei Spalten hast, wo du eben zum Beispiel schon
so ein Feature-Engineering gemacht hast und hast
keine Ahnung, ähm,
den, den Tag und dann nochmal irgendwie
den Tag um ein Jahr verschoben
oder so, dann korrelieren diese beiden Spalten
ja komplett, sowas würdest du dann da drin direkt
sehen und dann würde dich zum Beispiel vorwarnen,
ähm. Ja, das ist ganz praktisch,
ne, wenn ich cleanen muss, dann irgendwie erstmal so
zu gucken, ah ja, das könnte ja ein Problem sein, dann
fangen wir doch mal an da oder so.
Genau. Geht jetzt immer die Zeile oder sowas, so, ne.
Genau, das ist immer irgendwie, finde ich, ganz praktisch, um,
ja, so einen ersten Übersicht zu bekommen.
Ähm, was würdet ihr dann machen?
Würdet ihr denn die Daten, die dann da zum Beispiel fehlen,
irgendwie versuchen zu füllen oder, ähm,
lässt man dann einfach erstmal die Spalten
weg, weil die irgendwie Quatsch sind oder?
Ja, kommt halt
komplett drauf an, ne, also. Ja.
Ähm, also, was du jetzt ansprichst,
ist ja Ausreißerbereinigung im Wesentlichen
und, ähm, Nullwerte füllen,
ähm,
die, die Frage ist, warum fehlen die Werte und
kannst du sie sinnvoll ersetzen? Also,
im Endeffekt hat man drei
Optionen. Man wirft die Daten einfach weg,
ähm, man versucht
sie zu füllen mit einem
Art von statistischem Maß,
was quasi zum Beispiel den Durchschnitt von
allen Spalten wiedergibt
oder, ähm,
vielleicht habe ich auch nur die beiden Optionen.
Ich dachte, ich hätte nur eine dritte.
Ja, man könnte ja irgendwie...
Es gibt diverse, also man kann auch bei, ähm,
also es gibt so ein Cyclic Learn zum Beispiel,
das ist so eine Bibliothek für Maschinen,
Diverse, unterschiedliche, also da nennen
es die Oberklasse quasi für all diese Verfahren
Imputer und man kann halt diverse unterschiedliche
benutzen, um eine Strategie dafür
zu haben, weil eben, es kommt halt darauf an,
wofür man die, äh, was der Grund
dafür ist, warum das fehlt und wie man das am besten
ausgleicht, äh, ja,
die dann, äh, sozusagen da irgendwas
reinschreiben oder irgendwas mit den Daten machen, ja.
Ja, die Frage ist halt, ob man
die rausrechnen kann, die Daten oder nicht, ne, also
ob das sinnvoll ist und was man halt mit der Spalte sonst macht
oder mit den Datensätzen, die fehlen oder
Korrelation, ob man dann Dummy reinrechnet,
einsetzt, weil man den noch irgendwie braucht und dann immer
merkt, oh, das ist das bei dem Dummy so und dann...
Ja, und auch, wie rum droppst du sie, wenn du sie
droppst? Also, schmeißt du die Zeilen raus
oder schmeißt du die Spalten raus?
Ähm...
Hängt halt immer von dem Tast ab, aber
genau das macht man an der Stelle halt immer auch ein bisschen...
Ja, aber da sieht man genau gerade, ne, man hat ja irgendwie die Warnings gesehen, wie viel irgendwie
das ganz viele aus einer Zeile oder Spalte
dann da fehlen oder so bei irgendwelchen Einträgen,
war bei den Warnings irgendwie mit dabei. Wie hieß das nochmal?
Ich hab das... Pandas Profiling.
Profiling, okay. Genau. Interessant.
Das muss auf jeden Fall auch in die Shownotes.
Ja, mach ich.
Ähm...
Ja, genau. Also,
hängt halt dann immer auch von den Operationen ab,
die man halt danach drauf irgendwie anwenden
möchte.
Ähm, genau. Ansonsten ist
glaube ich so eine gute Möglichkeit am Anfang
sich auch nochmal so ein bisschen einfach erstmal zu plotten.
Also, irgendwie diese statistischen Maße
sind immer irgendwie ganz nett, um
ein erstes Gefühl zu bekommen, wie groß ist
denn meine Spanne an Werten und so weiter.
Aber...
Da können halt einzelne Ausreißer das Ganze
eben ja komplett verziehen.
Oder ich hab zum Beispiel so einen leichten Shift
in den Daten, sowas in Kennzahlen zu erfassen
geht zwar, aber ist irgendwie immer nicht ganz so
eingängig. Und dann hilft halt meistens
mal irgendwie so ein Histogramm oder so.
Ja. Genau, würde ich auch sagen.
Das ist das
nützlichste Ding,
wenn man sich irgendwie, wenn man so ein bisschen
sehen will, wie das eigentlich verteilt ist.
Oft ist es halt sich ein Histogramm von
der Werte halt anzugucken.
Vor allen Dingen auch, wenn man jetzt unterschiedliche Klassen hat,
mal die Histogramme gegeneinander zu plotten. Das ist etwas,
was ich oft
mir angucke, halt
sozusagen, um zu sehen, ob das irgendwas ist, wo
interessante Informationen für
Klassifikationen oder so drinsteht,
dass man halt sagt so, okay, man plottet das
Histogramm der einen Klasse gegen das der anderen und
man halt sieht, okay, da gibt es einen großen Unterschied, dann ist
schon mal, oder auch selbst ein kleiner Unterschied
ist natürlich schon hilfreich irgendwie.
Wenn man sieht, die sind genau gleich verteilt, dann ist das natürlich doof.
Aber, genau.
Genau, in dem
Explore-Data-Block sieht man auch ganz unten
eben, finde ich, ein ganz klassisches, aber
sehr, sehr hilfreiches Diagramm an der Stelle,
wo du einfach mal so die
Länge der Texte
abgetragen hast, den Zeichen,
und da sieht man auch so ein ganz typisches Verhalten.
Man hat halt irgendwie so einen Peak
der Daten, der relativ am Anfang
liegt, also irgendwie, wir haben hier jetzt,
würde ich sagen, wahrscheinlich mal
80 Prozent
irgendwie im Bereich von 0
bis 600 Zeichen, oder
bis 750, glaube ich, sind es.
Und dann wird es weniger
und dann gibt es so ein Longtail hinten
mit so ein paar einzelnen Einträgen, die dann halt
irgendwie 3.000, 4.000, 5.000,
6.000 plus Einträge haben.
Irgendwann hat ja jemand sein Buch bei Reuters hochgeladen, oder?
Ja, genau.
Aber das ist so ein ganz klassisches Problem, was man halt im echten
Welt-Data-Science halt immer hat,
dass es halt irgendwie so Ausreißer
gibt, die die Diagramme
verzerren und die dann auch immer
den Aufwand beim Programmieren eigentlich bedeuten,
weil das dann immer die Fälle sind, an die man vielleicht
so nicht gedacht hat.
Ja.
Genau. Ja, wir sehen hier, dass es halt
so irgendwie ungefähr 10.000
Dinger gibt, die irgendwie
im Test- oder Training
zugeordnet sind.
Also dieser
Mod-Up-Display teilt
diese Ticker-Meldung halt in
die, auf denen man trainieren kann und die
sozusagen, die man hinterher benutzt,
um zu evaluieren, wie gut denn jetzt
die Modelle funktioniert haben.
Und es gibt irgendwie
119 Klassen, aber
tatsächlich sind es deutlich
weniger, wenn man jetzt alle
und
es gibt so Meta-Superklassen
quasi, die bestimmte Dinge beinhalten,
sowas wie Sport, und dann gibt es Unterkategorien,
Fußball, Hockey. Es gibt einige, die
deutlich häufiger sind,
aber es ist halt so, dass
wenn man jetzt alle rausnimmt,
auch alle Beispiele rausnimmt,
auch alle Kategorien rausnimmt, zu denen es nicht mindestens
ein Trainings- und ein Testbeispiel
gibt, weil das ist ja auch irgendwie so ein bisschen sinnlos,
also Sachen, die man nicht gesehen hat, kann man auch schlecht vorhersagen,
dann sind es
nochmal deutlich weniger, sind es irgendwie nur 90 Kategorien,
die übrig bleiben, glaube ich, und halt irgendwie
die Kategorien, die rausgefallen sind, sonstiges oder so.
Ja, ich weiß es
gar nicht genau, ich habe es mir auch nicht
angeguckt, was das genau für Kategorien sind, aber
ja, also
das ist halt tatsächlich, sind die Zahlen für alle,
aber
in Wirklichkeit ist es halt nochmal ein bisschen weniger,
also die dann hinterher tatsächlich
zum Trainieren verwendet werden, dann
eben durchschnittliche Anzahl von
Wörtern ist halt irgendwie auch so 90
und genau,
die,
also es gibt so eine Quote
Anzahl der Beispiele, also das
Anzahl der Zeilen im DataFrame
geteilt durch die Anzahl der Wörter pro
Tickermeldung
ist halt auch eine ganz
interessante
Zahl, die man später benutzen kann, um halt
irgendwie rauszukriegen, welche Art
von Modell da irgendwie besser funktionieren
könnte.
Ja,
genau, also überhaupt diese ganze, also das
ist halt so irgendwie explorative Phase
irgendwie bei so einem Projekt,
man versucht halt rauszukriegen, gibt es irgendwelche
interessanten Regelmäßigkeiten,
wie groß sind die Daten,
was prinzipiell kann ich da eigentlich für Modelle
verwenden und so. Ja, also erstmal visualisieren,
ist glaube ich tatsächlich eine gute Idee, ne, auch verschiedene
Arten, mal gucken, WordCloud bauen vielleicht
oder so und dann schauen, was ist das denn.
Genau, und nebenher finde ich durchaus irgendwie
nochmal so ein Scratchpad offen haben und wenn man
da irgendwie eine Auffälligkeit hat,
die immer gleich reinschreiben, also
man verfällt dann finde ich auch relativ leicht
in so einem, ja, Liebe
zu Charts, weil so Charts
basteln ist was, was ich zum Beispiel total gerne
mache. Also ich finde es total spannend, dann
mir die Daten irgendwie so in Diagrammen
zu visualisieren und die irgendwie hübsch zu machen
und mächtig von ihrer Aussagekraft und so weiter,
aber dabei
verfalle ich da manchmal so ein bisschen in der
Liebe zum Tun und
eigentlich geht es ja darum, Insights zu generieren.
Deswegen finde ich es dann
immer relativ wichtig, sich irgendwo nochmal so eine Liste
runterzuschreiben mit den Auffälligkeiten, wo man so
gedacht hat, ah ja, stimmt, das ist ja interessant,
und das ist interessant, weil
im Endeffekt sind das nachher dann Sachen, aus denen man
dann Ideen bekommt, um zum Beispiel
Features zu generieren, die
dann eben meinem Modell wirklich weiterhelfen könnten.
Ja, also
das ist halt an der Stelle auch definitiv dann
ein iterativer Prozess, wo man
quasi
sich die Daten anschaut, man entwickelt eine Idee
dafür, man probiert sie aus und man geht dann
wieder zurück und guckt sich mal wieder die Daten an
und guckt, ah ja, jetzt habe ich aber ja
ein besseres Verständnis dafür bekommen, weil
ja, da ist so viel
Information in so einem Datensystem, dass es eine ganze Zeit
lang braucht, bis man wirklich
versteht, was man damit alles machen könnte.
Ja, man muss ja natürlich auch immer das Problem erstmal
so ein bisschen reindenken, damit man irgendwie dann mit den Daten
aber auch was anfangen kann.
Genau, und
ja, dabei
hilft halt diese explorative Sache und
ja,
neben diesem ganzen Histogramm, ich glaube, wir haben angefangen
erstmal mit, wie man die Daten noch bekommt, damit waren wir noch gar nicht
so durch. Achso, richtig, ja, stimmt.
Wir sollten irgendwie noch so die Art und Weise
der Methode, wir haben jetzt irgendwie das Describe irgendwie von
DataFrames ein bisschen gemacht, aber wie bekomme ich denn
die Daten überhaupt irgendwie so rein? Das ist ein guter Punkt,
ja.
Ja, also das ist irgendwie
eine URL, die da irgendwo
mit drin steht.
Moment, ich glaube, das ist jetzt mittlerweile gut versteckt
irgendwie in dem
DS-Tutorial-Paket.
Datasets, da gibt es
einen Pfeil. Genau, normalerweise in der
echten Welt würde ich sagen, ist der Fall meist
entweder habe ich den CSV irgendwo rübergeworfen
bekommen, dann lese ich die einfach über Pandas ein
oder ich habe halt irgendwie für einen
Spielprojekt eine Web-URL, dann ziehe ich es mir
direkt von dort.
Kann man ja direkt auch mit Pandas, glaube ich.
Oder eigene Webdaten-Straight mit einer eigenen Datenbank
oder sowas. Oder von irgendwelchen Sendoren,
die Sachen in irgendeine Datenbank packen.
Eine Datenbank-Anwendung ist natürlich auch guter Punkt,
definitiv. Ist irgendwie in der
echten Welt gefühlt zu selten bei so
Notebooks. Irgendwie landen die Dateien dann doch immer
als Datei da, aber
genau, in dem Fall ist es ja relativ
tief im Code direkt versteckt, wie du es lädst.
Also wenn die Dateien da laden,
also wirklich big ist die Datei dann aber
irgendwie noch nicht.
Das hier ist alles super small.
Man will in so einem Notebook auch
kein Big Data haben.
Kann man schon, aber...
Kann man schon, aber ich weiß nicht.
Meine Präferenz an der Stelle ist
vor allem immer erstmal auch Latenz nicht richtig zu halten
und schnell mit Daten zu iterieren
und lieber erstmal ein kleines Sample ziehen
und erstmal viel Zeit in diesem kleinen Sample
zu verbringen und dann
natürlich sehr bewusst wählen,
wie wähle ich jetzt mein Sample? Mache ich es wirklich
komplett random oder habe ich eine Zeitreihe? Muss ich jetzt gewisse
Aspekte dabei berücksichtigen?
Aber erstmal darauf eine Weile iterieren
und dann sich nachher
die Embedded auf dem Kompletten ausprobieren.
Machst du das anders?
Nee, nee, also genau. Subsamplen ist
halt auch, wenn die Daten groß sind, auf jeden Fall
eine super Strategie, um halt
schnell iterieren zu können.
Man kann aber, wenn man jetzt
tatsächlich irgendwie
größere Daten hat
und das auch nicht irgendwie vielleicht kleiner
machen will, sich zum Beispiel
von den
importiert man sich halt ein DataFrame nicht direkt von Pandas
sozusagen, sondern man importiert das halt
von DAS und
das Ding
verteilt dann automatisch
irgendwie die Sachen, die man da drauf tut, halt
auf zum Beispiel, das hat auch, das ist schon
ziemlich cool, das hat eine Anbindung an
Kubernetes und halt auch an die ganzen
Cloud-Geschichten wie so irgendwo
Google Compute Engine und so
und da kann man dann halt einfach sagen,
okay, ich nehme mir jetzt mal so hier 100 Maschinen
und füge die dann
hinzu und man kann auch mehr an
während Sachen da drauf laufen, also
während man auf einem DataFrame halt irgendwas
kopiert oder so, kann man sagen, okay, ach,
das geht mir zu langsam, nochmal 10 Maschinen dazu oder so
und dann wird das
automatisch halt irgendwie,
werden die mit reingenommen und
werden die Sachen halt irgendwie magisch schneller
und man sieht halt auch irgendwie sehr schön,
was da passiert.
Da habe ich jetzt normalerweise kein Beispiel für, aber
das geht
durchaus auch und das
Benutzerinterface ist dann da auch nicht anders,
als wenn man das jetzt auf einer lokalen Maschine macht,
sondern ist auch immer ein Notebook eigentlich.
Ich glaube, zu diesem
Big-Dating müssen wir nochmal eine eigene Folge machen, so mit
Kubernetes, das ist dann nochmal spannend.
Genau, an der Stelle kann ich mal
meine Kubernetes-Folge droppen.
Ach, der ist noch? Ja, genau.
Es gibt eine zu Docker und eine zu Kubernetes.
Ja, und das
Schöne ist halt, dass die Sachen dann auch, wenn man sie nicht mehr braucht,
wieder verschwinden, während halt, das ist ja auch etwas,
was man sonst, also gibt es dann vielleicht auch irgendwie,
in vielen Firmen gibt es dann halt
so ein Cluster, wo man Sachen machen kann, aber die Maschinen sind dann halt
da und
man braucht sie aber irgendwie nur,
man braucht eigentlich so alle halbe Stunde mal so wirklich
volle Power, aber ansonsten braucht man das
eigentlich nicht und
ja,
das ist, glaube ich, auch irgendwie, also das ist
schon ein sehr gutes Argument für diese ganzen
Cloud-Geschichten, weil man zahlt halt tatsächlich nur
in dem Moment, wo man halt die Rechenzeit
braucht und
Also du hast Dasks jetzt auch
schon öfter eingesetzt und das funktioniert für dich gut?
Ja, für mich funktioniert das eigentlich ziemlich gut.
Ich habe dann nämlich praktisch so noch keine Erfahrung, ich habe nur
irgendwie im Hinterkopf, dass man dafür einiges
umschreiben muss, dass das nicht komplett kompatibel ist
mit den Pandas. Nicht komplett, aber
also sagen wir mal so, das meiste geht eigentlich
schon. Es gibt also ein paar, ja natürlich,
es gibt natürlich immer so ein paar Ecken.
Ja, intern ist es halt so, dass der das
auf eine Menge DataFrames aufteilt
und das muss man halt irgendwie, die
unterschiedlichen Teile liegen halt auf unterschiedlichen Rechnern
und ja. Ansonsten,
wenn man Pandas einfach
nur Multicore betreiben möchte, was ja
Pandas per Default jetzt aktuell noch nicht bietet,
gibt es auch ein paar nette Projekte,
da gibt es einmal Modin, das ehemalige
Pandas on Ray,
was quasi einfach über einen Import
einfach nur,
du änderst den Import und dann kannst du
prinzipiell Multicore für
die allermeisten Pandas-Operationen.
Wenn du also eher in die Nische kommst,
geht es da vielleicht dann irgendwann nicht mehr.
Und was ich heute kennengelernt habe und noch nicht ausprobiert
habe, ist Pandarallel,
also
wie Panda und dann
Parallel quasi und
das unterstützt halt eben auch quasi oder verspricht
ein Multicore-Processing für Pandas-Operationen.
Genau. Also bevor man vielleicht
wirklich auch ein Cluster muss,
ist das noch eine gute Option.
Ja.
Ja, ja, ja. Ne, das kann ich auch noch nicht.
Also, ja. Sehr interessant.
Genau.
Aber wir waren hier bei dem...
Wie bekomme ich eigentlich meine Daten?
Ja, ich hatte einfach das entsprechende Notpunkt nicht aufgemacht.
Das heißt hier Preparing Data.
Keine Ahnung, vielleicht ist es unsinnig genannt.
Und das ist einfach das Ding,
also David Lewis ist auch der, der den Modellorten,
den Modellort-Split gemacht hat und auch überhaupt
der, der das ganze Dataset kuratiert hat,
hat das halt irgendwie auf seiner Seite liegen.
Und, ähm,
genau, dann, ähm, ja.
Ähm, was du
auch benutzt an der Stelle im Notebook ist ja
ähm, die, ähm,
die Path Library.
Ja. Ähm, hab ich auch noch nicht so
lange in Benutzung, aber ist eigentlich mega cool.
Ja, ja, genau. Sag dazu doch nochmal zwei Sätze.
Ja, also, äh, früher hab ich das auch
mal mit, äh, OS
und ListDir und OS Paths
Join und so gemacht, aber das ist alles ganz schön
genau, glaub,
ganz schön umständlich und, äh,
ähm, da gibt's halt, äh,
ich weiß gar nicht, wann das in, seit wann
das in der Standard Library ist, aber, äh,
also es gibt da ein, ein, ähm,
ein Modul namens Path Slip
und, äh, da, äh,
kann man, also,
zum Beispiel, es geht
hier los mit, äh, Path.Home, ist halt
sozusagen dann ein Part und, ähm,
die, das Ding funktioniert
im Grunde, wenn man jetzt Pfade erweitern möchte oder so,
über, äh, so Operator
Overloading und, äh,
die haben dann halt einfach das, äh, den
Slash, äh, überladen und jetzt kann man halt
schreiben, äh, irgendwie Path.Home, gibt dann
das Home-Verzeichnis, Slash, Data
Slash TMP
und das ist natürlich viel kürzer, also würde man jetzt schreiben
OS Paths Join,
Klammer auf, und dann gibt man eine Liste oder macht
irgendwie, ja, äh, irgendwie, äh,
muss dann einen absoluten Pfad angeben oder
man gibt irgendwie einen relativen Ankommer, irgendwie
das nächste Ding und, äh,
das ist alles ganz schrecklich. Man kann sehr schön,
ähm, Variablen quasi mit reinbetten
und, ähm, man bekommt
ja am Ende dann auch immer quasi ein Path-Objekt
wieder raus, was dann auch nochmal so
Operationen bietet wie, ähm, okay,
jetzt verraten wir von dem noch bitte die Subdirectories
oder, ähm, ja,
halt Operationen dann immer direkt auf
dem Pfad ausführen. Ja, du kannst einfach ein Dot-Exist
machen oder sowas, das wäre auch ziemlich cool, ja. Genau.
Ja, und ansonsten hätte man immer
OS Paths Exist und so verwenden müssen
und dann, also das ist, es ist
wirklich viel, viel angenehmer damit zu
arbeiten und, ähm, ja, äh,
also sonst hat man immer so, also ich hatte sonst immer das Problem,
dass irgendwie die ganzen, diese ganzen Geschichten
wurden immer relativ festlich und auch die Zeilen wurden sehr
lang und das, das Problem ist irgendwie weg,
seitdem ich Parsel verwende und das ist schon mal echt ein
Vorteil. Vielleicht um das zu kurz zu zeigen,
ich lösche jetzt einfach mal die Daten, die ich hier bei mir
lokal habe, äh,
Data, äh,
TMP, ja,
genau.
Äh, ah.
Da muss man echt mit aufpassen. Ja, ja.
Ja, wenn du willst so ein Git-Repo löschen
oder so, machst dann irgendwie .slash oder so,
und dann immer aus Versehen löscht man den Punkt weg,
dann machst du r-r-slash und dann
Ja,
dann, äh. Immer alles als
Super-User machen. Muss man hoffen, dass
das nicht nur das Backup immer funktioniert hat, sondern
dass das Restore auch mal funktioniert.
Ja.
Äh, genau. Äh, ja.
Ähm, ich verwende hier
einfach so, äh, irgendwie so
Download-from-URL-Funktion,
die benutzt TQDM
um, äh, das ist,
das ist eine sehr schöne Library, mit der man halt so
Progress-Bars ganz gut hinkriegt. Man kann eigentlich alle
Iteratoren irgendwie damit rappen. Ja, man braucht nur
ein Iterat-Bar und, genau.
Und das ist, das ist auch sehr,
sehr hübsch, dann sieht man ungefähr, wo man ist.
Äh, und, äh, ja.
Ja, und jetzt hat das, äh, irgendwie
dieses Dataset halt, äh, irgendwie so ein, so ein
Data-Verzeichnis geschrieben.
Dann, und das ist halt auch irgendwie das Tolle bei Python,
dass man, äh, irgendwie einen Riesenhaufen Funktionalität
schon, äh, in der Standard-Library mit
drin hat, äh, ist eben auch sowas drin
wie tar-File, ne, dass man halt mit, mit, mit
tar, äh, äh,
Archiven halt direkt, äh, irgendwie umgehen kann
und, ja, dem kann man dann halt einfach
sagen, okay, pack doch mal alles aus.
Und, ähm,
ja, dann, äh.
Dann haben wir einen Pickel gebaut, oder? Du hast einen Pickel gebaut. Genau, dann
parse ich das Ganze auf und, äh,
pickel das wieder raus, sozusagen, damit ich
dieses Parsen nicht jedes Mal machen muss. An der Stelle hier ist es
relativ egal, weil das ist so schnell. Also,
Pickel speichert einfach das Python-Objekt in der Binary
einfach, also als Binär-Daten auf
der Platte in einem Objekt und kann es genauso wieder einlesen. Genau.
Pickel ist auch ein Modul aus der Standard-Bibliothek,
genau, und es serialisiert einfach
äh, Python-Objekte, beliebige Python-Objekte
einfach auf, ja, in ein
Format, das man dann halt auf die Platte schreiben kann und
hinterher auch wieder lesen kann. Und es ist
halt relativ schnell. Also,
CSV, äh, genau, ist oft das,
was man bekommt und das, was am meisten
verwendet wird und, äh, ja, Pandas
Reads CSV-Funktion ist sicherlich so eine der
wichtigsten. Aber
das ist halt nicht schnell, äh,
sondern das ist halt irgendwie so, ja,
sagen wir mal, weiß ich nicht,
vielleicht 50 MB pro
Sekunde oder sowas da so durchgeht. Kommt darauf an,
wie komplex das CSV ist.
Und gut, für die meisten Datensätze ist das vielleicht
auch, oder was die meisten Leute so haben, auch
schnell genug, dass das halt irgendwie nicht so
furchtbar merkbare Wartezeiten
erzeugt, aber wenn man
jetzt so ein paar Gigabyte, äh, einlesen will, dann ist
das schon, dann muss man bei CSV schon immer warten.
Und, äh, was ich da oft
auch mache, ist halt die, äh, wenn ich CSVs
habe, die, ähm, zuerst
irgendwie in DataFrame einzulesen mit ReadCSV
und dann halt nochmal, äh,
irgendwie ein Pickel-File davon zu cachen,
weil das Pickel, äh, liest halt
äh, quasi mit Plattengeschwindigkeit, also
da kann man auch so ein Gigabyte pro
Sekunde lesen. Das, das, dann
dauert das halt, wenn man, wenn man halt so ein
DataFrame, der halt irgendwie zwei, drei Gigabyte groß ist,
äh, äh, äh, liest, der, der
und Jochen unterhalten sich über die Programmiersprache Python
und im Grunde, wenn man jetzt, also bei Scikit-Learn ist es jedenfalls so,
eigentlich orientieren sich fast alle anderen Maschinen-Learning-Bibliotheken,
die es so gibt, auch an der entsprechenden API von Scikit-Learn,
ist die Estimator-API für Modelle eben,
da übergibt man immer Arrays, also NumPy-Arrays.
Und sowas wie Listen oder so kann man da nicht übergeben.
Das heißt, man muss erstmal das so in Format bringen,
dass man das überhaupt den entsprechenden Modellen übergeben kann.
Und für Multilabel ist es eben so,
dass das richtige Format dann halt ist,
ein NumPy-Array zu haben,
das halt auch wieder genauso viele Zeilen hat,
wie man in der Feature-Matrix hat,
oder eben genauso viele Zeilen, wie es eben Dokumente gibt,
die zum Training-Set gehören,
und dann so viel spalten, wie es Kategorien gibt,
oder Labels gibt, in denen ein Dokument liegen kann,
und sozusagen die, für jede Spalte gibt es dann halt sozusagen,
also jede Spalte ist halt eine Kategorie,
und wenn da ein 0 drinsteht, bedeutet das im Grunde,
dass das Dokument liegt nicht in dieser Kategorie,
oder hat dieses Tag eben nicht.
Wenn da ein 1 drinsteht, dann heißt das,
okay, ja, das ist sozusagen die richtige Kategorie für dieses Dokument.
Und genau, dafür gibt es aus dem Scikit-Learn-Pre-Processing-Modul
halt ein Ding, das nennt sich Multilabel-Binarizer,
und dem gibt man einfach diese Liste von Listen,
und dann spuckt das halt ein richtig formatiertes NumPy-Array aus.
Also das ist auch sowas.
Also diese ganze Transformation, das muss man nicht selber machen.
Da gibt es eigentlich für alle Geschichten, die man da so haben kann,
fertige Lösungen schon.
Ja, man kann sich, glaube ich, auch aussuchen,
was dann mit den Werten, die da nicht reinpassen,
irgendwie geschehen soll und sowas.
Ja, ja, bestimmt.
Ich weiß jetzt gar nicht genau, was der Multilabel-Binarizer
da sonst auch so macht.
Und langfristig ist es vor allem auch nicht sinnvoll,
sich das zu machen.
Also am Anfang, wenn man das noch nie gemacht hat,
denkt man so,
also man muss ja schon auch einmal erstmal durchsteigen,
wie das überhaupt alles funktioniert,
mit diesen Transform-Prozessen und den Modellen und so weiter.
Und dann denkt man,
ich will doch eigentlich nur so ein Modell benutzen,
ich kann den ganzen Rest auch von Hand machen
und fängt damit gerne erstmal an,
weil man auch gerne ein bisschen programmiert
und nicht ewig Doku liest.
Aber faktisch ist man halt letztendlich dann doch viel schneller,
wenn man sich das halt einmal aneignet,
wie denn diese Binarizer und so weiter funktionieren.
Ja.
Genau.
Ja.
Und dann hat man quasi das gleiche Problem,
nur irgendwie ein bisschen anders.
Auf der Feature-Matrix-Seite halt eben auch,
weil man hatte erstmal Text.
Den kann man jetzt so in ein Modell nicht reinwerfen,
weil, ja, ist halt,
also letztlich ist eine Feature-Matrix eigentlich immer,
sollten das eigentlich immer mehr oder weniger Float-Werte sein,
die da letztlich drin landen.
Und das ist halt,
das ist halt ein NumPy-Array von irgendwie Float-Geschichten
und das ist Text halt nicht.
Man muss jetzt erstmal Text irgendwie in Zahlen irgendwie umwandeln
und das ist halt schon so eine Art,
man nennt das halt Feature-Extraktion.
Also man macht sozusagen aus Text irgendwie eine Reihe von Features,
also Spalten in der Feature-Matrix.
Und so eine ganz, ja, alte Standard-Methode,
wie man das halt tun kann,
ist einmal, man repräsentiert Text halt als Back-of-Words,
nennt sich das.
Das bedeutet, man wirft die Reihenfolge-Informationen da weiter weg,
was irgendwie schlimm klingt, aber dann doch nicht so schlimm ist.
Und merkt sich sozusagen, also jede,
man kann sich das ungefähr so vorstellen,
man hat jetzt, ja, weiß nicht, ob das eine gute Idee ist zu sagen,
man sollte sich das wie eine Excel-Tabelle vorstellen.
Wahrscheinlich nicht.
Aber das hat jemand schon niemals, wahrscheinlich schon gesehen.
Also warum nicht?
Ja, es ist auch so ein bisschen so ähnlich wie bei den Labels.
Es ist auch so eine Art One-Hot-Encoding.
Man hat halt pro Spalte quasi, also man macht eine Spalte pro Wort.
Und wenn das Wort in dem Text vorkommt, dann macht man da eine Eins rein,
ansonsten macht man da ein Null rein in die Spalten.
Also man hat sozusagen so viele Spalten,
wie es überhaupt unterschiedliche Worte im gesamten Dokument-Korpus gibt.
Also über alle Dokumente drüber.
Wobei Back-of-Words macht doch schon auch die Anzahl gleich rein, oder?
Ja, ich weiß nicht.
Man kann auch die Anzahl direkt rein.
Also der einfachste Fall wäre, dass man einfach nur reinschreibt,
ist es da oder nicht, wie näher.
Und genau, dann die nächste etwas,
also das funktioniert auch schon erstaunlich gut.
Aber das hat halt auch diverse Probleme.
Unter anderem ist es halt so,
dass längere Texte natürlich viel,
bei denen steigt die Wahrscheinlichkeit,
dass sie in allen möglichen Kategorien sind.
Einfach deswegen, weil eine Menge Worte darin auftauchen,
die halt in allen möglichen Kategorien auftauchen.
Oder derjenige, die den Nachrichtenbeitrag geschrieben hat,
war SEO-Experte oder so.
Ja, ja, genau.
Das wäre halt etwas, was man versuchen wollen würde zu verhindern,
dass das halt so eine große Auswirkung hat,
wenn da irgendwie eine lange Liste von vielen unterschiedlichen Worten drinsteht.
Und das ist eine klassische Methode, die man da verwendet.
Es kommt auch aus dem Information-Retrieval-Bereich.
Es ist halt einmal zu zählen, wie oft ein Wort drin vorkommt.
Das braucht man halt vor allen Dingen deswegen,
weil man damit mit der Information, wie oft kommt ein Wort in dem Text vor,
plus wie lang ist der Text, kann man halt die Frequenz ausrechnen.
Also sozusagen, wie ist die Frequenz von dem Wort in dem Text.
Das ist einfach nur die Anzahl, wie oft kommt es vor,
geteilt durch die Länge des Textes.
Und das funktioniert auch.
Das funktioniert auch schon besser.
Also da könnte man dann irgendwie einen Count-Vectorizer nehmen.
Der würde halt dann zählen, nicht nur binär irgendwie einen großen Vektor bauen,
in dem einzelne Nullen drinstehen, je nachdem, ob ein Wort drin vorkommt oder nicht.
Das wäre dann schon die Term-Frequency, oder?
Ja, damit hätte man erst mal nur die Counts.
Und dann, ich weiß nicht, ob es noch einen...
Aber es gibt keinen Vectorizer, der dann nur Term-Frequenz macht,
sondern dann würde man direkt so etwas nehmen wie TF-EDF-Vectorizer.
Das Problem bei der Term-Frequenz ist halt...
Ich glaube, das habt ihr alle ein bisschen verstanden.
Ich habe mich ein bisschen abgehängt.
Erst mal, was ist ein Term-Vectorizer?
Was ist ein TF-ID?
Ja, also im Grunde kann man eigentlich direkt mit dem TF-EDF einsteigen.
Wenn man sich jetzt einen Vector anguckt, wo nur drinsteht, ob ein Wort in einem Text vorkommt oder nicht,
also da stehen nur Einsen und Nullen drin, dann ist das halt schlecht.
Man möchte eigentlich ein anderes Gewicht darinstehen haben für Worte,
weil halt manche Worte sind halt wichtiger oder charakteristischer für eine Kategorie als andere.
Wenn ich die Kategorie Politik habe und da kommt irgendwie Parlament drin vor,
dann ist das sozusagen dafür irgendwie eine aussagekräftigere Geschichte,
als wenn jetzt das Wort "das" drin vorkommt oder so, was einfach in jedem Text vorkommt.
Und wenn man jetzt einfach nur einen Einsen und Nullen reinschreibt,
dann würde das aber gleich behandelt irgendwie.
Und das kann ja nicht so richtig sein.
Und die Methode, mit der man das versucht, besser zu machen,
ist uralt, ich glaube mittlerweile, ja, über 40 Jahre alt.
Ich glaube Anfang der 70er oder so, dass Karen Speck-Jones, glaube ich, irgendwie sich mal überlegt, ich weiß nicht genau.
TF-EDF, vor allen Dingen für Suchmaschinen entwickelt, da möchte man das halt auch haben,
man hat halt einen Query und möchte dazu die relevantesten Dokumente irgendwie rausfiltern.
Und dieses Gewichtungsdienst nennt sich halt,
TF-EDF, also Term Frequency mal Inverse Document Frequency.
Term Frequency ist einfach nur die Frequenz von dem Wort in dem Text,
also das ist einfach nur Count, wie oft kommt das Wort vor,
in dem Text geteilt durch wie lang ist der Text,
mal Logarithmus von...
Ja, genau, ich kann den an der Stelle auch nicht genau...
Geteilt durch wie oft kommt das Wort irgendwie
in allen Texten vor, sozusagen, also das Ding wird halt höher, je höher, desto seltener es ist, also sozusagen...
Und man nimmt dann mal den Logarithmus, damit es nicht ganz so schlimm wird, damit es halt so ein bisschen...
Ein bisschen flacher wird.
Genau, genau, weil man möchte halt auch nicht so total krasse Unterschiede haben,
das soll sich so ein bisschen auswirken, aber...
Genau, das ist vom Prinzip her die Spezifität des Wortes, also wie charakteristisch...
Wie ist das ein Wort, das ständig vorkommt oder was halt eben relativ selten vorkommt,
gewichtet mit wie häufig kommt dieses Wort in dem Text vor,
das ist ja ein ganz gutes Maß darüber...
Ja, ist das speziell für diese Kategorie, da oder nicht, ja.
Genau.
Ja, und da müsste man tatsächlich aber auch eine Datenbank haben,
der alle Wörter irgendwo schon in gewisser Häufigkeit vorkommen, also zumindest die, die so allgemein rausfallen...
Ja, das ist natürlich so eine interessante Geschichte, wie kommt man an diese Gewichte eigentlich ran,
und wenn man jetzt zum Beispiel irgendwie das mit dem Cross-Validation und Trennen in Trainings- und Testsets selber machen würde,
dann läuft man direkt da in solche Probleme rein, wo berechnet man eigentlich die Gewichte, nimmt man alle Daten, die man hat, um die auszurechnen,
hat man da nicht, liegt man da nicht die Tester, dann liegt man nicht die Informationen über das Test-Dataset schon in das Training mit rein?
Ja, also vor allem...
Darf man das machen?
Ist halt auch die Frage, wie gut sind deine Tester, also sind die halt verteilt, sind die homogen, hast du da eine schiefe Verteilung zum Beispiel oder sowas,
ist ja dann auch irgendwie doof, weil dann die Sachen, die schief drin liegen, dann überprofessionell darin vertreten sind,
und du vielleicht von den Sachen, die da selten drin sind, nicht so viele...
Genau, aber dieses Problem, was du gerade sagst, mit dem Training und den Tester, das ist halt wirklich so ein klassisches...
Also du hast eigentlich nur zwei Optionen, entweder ich sage, ich trainiere mein TF-IDF auf allen meinen Daten, auch vom Test-Set,
dann habe ich halt alle Wörter wirklich drin, die potenziell vorkommen können nachher, aber dann habe ich halt eben die Gefahr von Information Leakage,
also das heißt einfach, die Daten, die mir eigentlich ja potenziell unbekannt sind, ich will ja nachher auf Daten generalisieren,
die ich noch wirklich gar nicht kenne, das Test-Set ist ja nur eine Abstraktion, um irgendwie am Anfang damit trainieren zu können,
dann tue ich so, als würde ich diese Information eigentlich nicht kennen, aber in Wirklichkeit habe ich sie ja doch irgendwie verarbeitet.
Auf der anderen Seite, wenn ich das nicht mache, passiert es mir halt, dass ich im Test-Set irgendwelche Wörter habe, die ich so gar nicht habe,
und dann habe ich dazu quasi keinen IDF-Score, keinen validen, genau, und dann gibt es halt im Endeffekt...
Brauchst du halt entweder ein sehr, sehr großes Datenset, dass du das irgendwie selber machen kannst und hast, oder du kannst halt auch quasi ein Datenset benutzen, quasi...
Also für so eine Generalität von Wörtern könntest du jetzt auch ein öffentliches Datenset benutzen.
Wikipedia oder sowas.
Genau, aber dann ist es halt nicht so spezifisch aus deinem Anwendungsfall wahrscheinlich, und vielleicht ist das Wort Enzyklopädie in der Wikipedia selbst irgendwie viel häufiger
und viel unspezifischer, als es bei dir jetzt im Wort vielleicht wäre, also bei dir in deinen Daten vielleicht wäre.
Genau, also es ist auch wieder so ein Abwägungsding, wie geht man damit um?
Ja.
Und ja, hier ist es zum Glück halt irgendwie alles schon, sozusagen, sind diese Geschichten vorgegeben und wir ermitteln die Zahlen halt sozusagen nur auf den Trainingsdaten und fitten das halt nur auf den Testdaten, also, ja.
Äh, wir fitten das nur auf den Trainingsdaten und transformieren nur die Testdaten, also sozusagen, ja.
Und es ist gerade spannend, mir kommt gerade der Gedanke, ist es denn tatsächlich dann so, dass nicht ein größeres Datenset eine bessere Methode verursacht,
sondern tatsächlich das richtige Datenset, was das Wichtige ist, als die Menge an Daten? Hört es sich gerade ein bisschen so an, also ich hätte jetzt intuitiv andersrum gedacht,
das heißt, je mehr Testdaten ich zur Verfügung hätte, auf denen ich meinen Algorithmus testen kann, desto besser wird mein Ergebnis und das, was gerade so ein bisschen durchging, war, dass...
Trainingsdaten sind das, was dein Modell besser macht.
Ja, genau.
Testdaten...
Also, das meinte ich mit...
Achso, ich hab's gerade nochmal verwechselt wahrscheinlich.
Ja, okay, ja, genau.
Je mehr Trainingsdaten hast, desto besser, aber...
Ja, weil dann würde man Wikipedia zum Beispiel schon nehmen können, wenn das korrekt gelabelt ist, ja? Dann würde das dazu führen, also selbst wenn da mehr...
Nee, aber Wikipedia ist ja jetzt für deinen Anwendungsfall nicht gelabelt.
Ja, genau, wenn ich das gelabelt hätte, das war ja dann...
Und das ist natürlich wahrscheinlich immer anders, das ist halt das Problem, das ist anders verteilt, als das, was du irgendwie benutzen willst.
Also ist dann aber doch halt nicht die Menge der Daten, sondern die müssen halt schon matchen, also ich brauche halt so den Skop in die richtige Richtung schon.
Ja, klar.
Das heißt, da kann halt ein Riesenproblem daraus entstehen, wenn ich irgendwas rausfinden will, was ich überhaupt nicht beschrieben habe in den Daten, dann ist Murks.
Genau, und ein Stück weit muss man da jetzt auch nochmal unterscheiden. Wir haben ja gerade so ein bisschen davon geredet, wie kriege ich denn die Spezifizität von den Wörtern.
Da geht es ja darum, wie überführe ich unseren Text überhaupt erstmal in so eine Zahlenmatrix, auf denen wir dann basierend unser Training ausführen können.
Und um das, ja, das Transformieren von den Textdaten zu so einer Zahlenmatrix zu machen, da kann ich vielleicht durchaus einen größeren globalen Korpus nehmen, den ich da quasi für die Wörter benutze zum Umwandeln.
Wenn ich dann allerdings wirklich mein Modell trainiere, dann...
Dann macht es natürlich überhaupt keinen Sinn, das irgendwie mit Wikipedia-Daten zu machen, wenn ich eigentlich dann meine Art Artikel machen möchte, weil das ist wahrscheinlich dann anders.
Ja, das gibt es auch noch in diversen anderen... also das Problem kriegt man halt auch immer wieder, also wenn man jetzt zum Beispiel... das ist jetzt TF-IDF, ist halt auch so ein klassisches Verfahren und man kann es auch noch relativ leicht verstehen, insofern ist das eigentlich, glaube ich, ein ganz guter Kandidat, um das da zu benutzen.
Was man jetzt, wenn man ein echtes Problem hätte, auf jeden Fall wahrscheinlich auch noch machen wollen würde, ist sogenannte Word-Embeddings, also man würde halt nicht nur, einfach nur, sozusagen pro Wort eine Dimension nehmen, sondern man würde, ja, diese Worte halt aus jedem Wort mehrere Dimensionen erzeugen und da hat sich sozusagen eingebettet in so ein...
Ja, was man mit Word-Embeddings macht...
Ja, was man mit Word-Embeddings macht, ist, man projiziert jedes Wort in einen mehrdimensionalen Vektorraum und dadurch, dass in diesem Vektorraum alle Wörter irgendwie abgebildet sind, kann ich jetzt mathematische Operationen darauf abbilden und kann vor allem gucken, ob sich zwei Wörter ungefähr im selben Bereich dieses Vektorraums befinden und damit kriege ich sowas hin, wie das irgendwie Kirche und Kathedrale plötzlich irgendwie vergleichbar sind, obwohl die jetzt in einem normalen Text natürlich jetzt irgendwie nichts gemeint haben, das sind komplett unterschiedliche Wörter, auch wenn sie halt eine ähnliche inhaltliche Bedeutung haben.
Und das ist halt was, was halt eben mit Word-Embeddings vor irgendwie drei, vier, fünf Jahren irgendwie sehr, sehr populär wurde und...
Ja, wie kommen die da an die gleiche Stelle in diesem Vektorraum? Das habe ich jetzt noch nicht ganz...
Für jedes der Wörter, die ich habe, quasi nachgucken, okay, was ist denn diese Dimensionalität, weil um so ein Word-Embedding wirklich selber zu trainieren, dann brauchst du zum einen sehr, sehr viele Trainingsdaten und zum anderen halt auch wirklich viel Rechenleistung für eine ganze Weile.
Wo würde ich solche Modelle runterladen?
Ja, es gibt zum Beispiel Word-to-Vac, das Modell von Google, das kann man halt benutzen.
Das ist nett, ist gern benutzt.
Ja, und es hängt aber dann... ist halt die Frage, worauf das denn trainiert ist, also wahrscheinlich ist viel Wikipedia dabei, aber bei Google ist bestimmt auch noch...
Es sind auch diverse Webseiten mit drunter.
Die einen oder andere.
Ja, und das sind halt so ein paar Gigabyte, die man dann so runterladen kann.
Und das verbessert halt, wenn man jetzt eben statt TF-EDF, oder normalerweise würde man nicht sagen statt, sondern man nimmt die TF-EDF-Scores natürlich auch mit dazu als Features
und nimmt dann aber auch die Vektoren aus den Word-Embeddings, vieles Wort mit dazu, dann verbessert das halt irgendwie...
Das ist irgendwie so ein Klassifikationsergebnis, schon deutlich.
Aber da hat man halt auch das Problem, man weiß halt dann nicht, wenn man das jetzt einfach so diese vortrainierten Geschichten benutzt...
Ja, aber das ist ja schon wieder... das Nussen der Schwarmintelligenz, das ist ja keine eigene Entwicklung in dem Sinne, sondern man nutzt halt das, was irgendwie einer der Großen dann schon angestellt hat mit seiner Hardware,
auf so einer ganzen Sprache, auf so einer ganzen Grammatik und das Knowledge, das man dann irgendwie...
Sag mal so, wenn du genug Trainingsdaten hättest und auch genug Rechenkraften, um das dann halt alles auszurechnen, wäre es wahrscheinlich besser, wenn du das auf deine eigene...
Auf deine eigenen Daten machst und nicht die auf Wikipedia trainiert nimmst.
Wie viel... also hast du gesagt, es waren bei der Reuters-Daten drin 20.000 irgendwie Datensätze und das heißt, das war nicht historisch, weil da sind sicher mehr?
Das ist ein bestimmter Ausschnitt. Ich weiß nicht genau, nach welchen Kriterien die jetzt ausgewählt worden sind, aber es sind ein paar halt nur...
Es gibt jetzt auch ein neueres Dataset, RCV1, und das heißt irgendwie, da sind dann 800.000 drin oder so.
Ja.
Genau. Aber wie gesagt, an der Stelle würde man...
Dann könnte man halt in der Stelle jetzt eigentlich erstmal diesen Back-of-Words-Ansatz vielleicht wählen oder halt vielleicht auch mit Wörter-Embeddings, aber genau, wir waren ja so ein bisschen stehen geblieben bei den Label-Binarizern und ich finde, man sollte da nochmal so dieses Interface ein bisschen beschreiben, was wir benutzen, weil das ist sowas ganz generisch, was sich auch inzwischen alle möglichen Bibliotheken auch außerhalb von Scikit-Learn eigentlich abgeschaut haben.
Da gibt es halt immer im Wesentlichen zwei Operationen. Es gibt eben Fit und Transform.
Git nimmt quasi gewisse Daten und trainiert darauf jetzt mein Modell. Das heißt, dieser Multilabel-Binarizer guckt sich alle Daten an und berechnet für sich daraus, okay, aus jedem dieser Labels, die ich habe, muss ich folgende Zahl nachher rausgenerieren.
Und wenn ich das einmal gemacht habe, gefittet habe, dann kann ich danach halt diesen Label-Binarizer immer transformieren auf irgendeinen beliebigen Text.
Der nimmt dann quasi diesen selben, diese selbe Abbildungsmatrix von Wörtern zu Zahlen und überträgt die auf den neuen Text.
Und ich kann natürlich auch beides gleichzeitig anwenden. Dann mache ich halt eben so wie in dem Notebook hier Fit-Transform. Dann fittet er erst darauf und dann transformiert er auch gleich die Eingangsdaten.
Genau. Ja, das ist jetzt halt inzwischen echt so ein super Standard-Interface geworden und das Schöne ist halt, dass ich im Grunde...
Jedes Modell, wenn ich jetzt eine Klasse habe in Python, die diese Methoden implementiert, ich brauche vielleicht noch ein bisschen mehr, muss man nochmal gucken, aber kann ich die halt auch in allen, dazu kommen wir später auch nochmal, Pipelines und so verwenden und Modelle, die halt diese API implementieren, werden dadurch austauschbar. Das ist halt auch total toll.
Und dann werfe ich die da rein und gucke, ob hinterher was rauskommt und ich muss das halt nicht für jedes Modell irgendwie einzeln nochmal anpassen. Ja.
Genau. Ja, was wollten wir machen?
Ja, jetzt haben wir eigentlich unsere Zahlenmatrix. Eigentlich sind wir jetzt bereit, um darauf Modelle langsam zu trainieren, oder?
Genau. Ich habe hier einen TF-EDF-Facturizer sozusagen instanziiert, dann den gefittet auf den Trainingsdaten. Dem kann man auch noch alle möglichen Optionen mitgeben, was jetzt hier nicht geht.
Das habe ich getan, aber man kann ihm noch sagen, wie er es organisieren soll.
Gewisse Stop-Wörter rauswerfen, was man auf jeden Fall machen würde, aber ja.
Ja, genau. Und ob er halt irgendwie das noch irgendwie smoothen soll, oder ob man kann, welche Normen, man kann.
alles mögliche angeben.
Und
genau, den habe ich in Trainingsdaten gefüttert
und ich benutze jetzt die Transform-Methode,
um damit sozusagen die Texte
aus den
Trainingsdaten und aus den Testdaten
in so Feature-Matrizen zu
verwandeln. Und
genau,
das sind dann hinterher
ja,
das ist auch nochmal so eine kleine Spezialität.
Das ist jetzt natürlich
das, was dabei rauskommt, hat
relativ, also die Matrix, die da rauskommt,
normalerweise muss man jetzt denken, da kommt einfach nur ein
NumPy-Array raus, irgendwie
Zahlen so viel, wie es Dokumente gibt,
Spalten so viele, wie es Wörter gibt.
Das Problem dabei ist
natürlich so ein bisschen,
die allermeisten Werte in dieser
Tabelle sind null, weil ja die allermeisten
Worte halt nicht in
allen Texten vorkommen, sondern
ja, also
pro Text habe ich
halt eine Teilmenge der Worte, die überhaupt vorkommen, aber
eben nicht alle
und die allermeisten kommen halt nicht in den Text vor.
Also man hat ja natürlich, je mehr Nullen,
je mehr Texte man hat.
Je mehr unterschiedliche Wörter man hat.
Ja, genau, richtig.
Ja, genau, es ist ja dann wahrscheinlicher, dass du mehr Texte hast, dass du mehr unterschiedliche Wörter hast.
Ja, wobei, also
genau, wenn ich dann
Texte habe, die alle sehr, sehr ähnlich sind, habe ich dann natürlich auch
viele Einser vom Prinzip her.
Das heißt einfach quasi, wie sparse ist
meine Matrix, das Wort dafür.
Genau, sparse.
Die Daten sind halt
sehr sparse und
das ist halt ein Problem
oder ist halt sehr verschwenderisch,
was Hauptsprecher angeht, wenn man tatsächlich die Werte speichern
würde. Und es gibt dann so
ja, also ein SciPy
Modul
irgendwie gibt es dann Klassen, die
sich halt mit Spars,
mit dem Speichern von Sparsen Matrizen
irgendwie beschäftigen.
Und
ja,
da
der Trick ist im Grunde, dass man halt die ganzen Nullen
einfach nicht speichert. Dass man sagt, irgendwie man
speichert
nur, an welchen Stellen irgendwie
Zahlen sind. Alles andere ist halt
defaultmäßig null
und muss man halt auch nicht speichern.
Und man kann sich das auch im Grunde so vorstellen, also
die interessante Operation, die man jetzt auf sowas
ausführt, zum Beispiel wenn man jetzt eine Suchmaschine hat,
würde man irgendwie eine
Cosinus-Distanz
ausrechnen zwischen einem Dokument und einer Query.
Das heißt
sozusagen einfach nur das Skalarprodukt. Man würde den
Query in Vektor verwandeln,
ein Dokument in Vektor
verwandeln und dann bildet man einfach das Skalarprodukt.
Zwischen den beiden. Und
dafür muss man ja sozusagen nur die
Teile miteinander multiplizieren, die nicht null sind.
Weil
die anderen sind alle eh null. Das wird
aufsummiert. Also es ist
einfach weg. Und
daher ist das natürlich toll, wenn man eine Struktur hat,
wo sowieso nur die Teile gespeichert sind,
die nicht null sind. Und dann ist das halt
alles, muss man nicht so viel speichern und
hat trotzdem alles, was man braucht,
um halt damit rechnen zu können.
Ja, das
funktioniert auch.
Tatsächlich irgendwie als Eingabe
für diverse Modelle. Aber halt
auch nicht für alle. Also da muss man dann halt auch wieder
aufpassen. Das ist so ein bisschen blöd. Also es gibt
halt Modelle, die gut damit klarkommen, wenn man
den Feature-Matrizen
gibt, die sparse sind.
Es gibt auch welche, bei denen das nicht geht.
Und da muss man halt unter Bestimmten auch nochmal ein bisschen was
machen.
Was man üblicherweise tun würde, also was
ein Modell, was immer gut funktioniert,
was man normalerweise immer braucht, aber was halt nicht
klarkommt mit sparsen Daten, wäre halt sowas
wie XGBoost zum Beispiel.
So ein
Distribution-Tree-Modell.
XGBoost?
XGBoost.
Extreme Gradient Boosting.
Ah, okay.
Und
da macht man
das eigentlich, oder für alle,
ich meine auch, was auch nicht mit Neuronalen
Netze kommt, auch normalerweise nicht mit sparsen
Eingaben, klar. Was man da vorher
macht, ist, man reduziert die Dimensionen.
Also wenn das,
ich weiß jetzt nicht, was das, was ich glaube,
auch irgendwas 25.000 Wörter gibt es da drin, oder so.
Wenn man jetzt
aus diesen 25.000 Dimensionen
etwas machen möchte, was das dense,
was das dicht ist, dann
projiziert man halt das aus dem
hochdimensionalen
Vektorraum halt runter auf irgendwas
mit 200 Dimensionen oder 300 oder so.
Und dafür gibt es dann unterschiedliche Verfahren,
aber das, was man normalerweise immer verwendet, ist
Singular Value Decomposition
und dann auch die Truncated
Variante davon. Also Truncated
Singular Value Decomposition ist halt
immer so das Ding, was man dann nutzt und
projiziert, um die Sachen runter zu projizieren.
Es muss einem halt irgendwie mal klar sein,
wenn man halt so einen Korpus hat und dann hat man jetzt
in seinen Daten irgendwie
eine Million Dokumente
und diese Millionen Dokumente haben halt
irgendwie 30.000
unterschiedliche Wörter, dann habe ich halt nachher
prinzipiell erstmal eine Matrix, wenn ich jetzt nichts
davon wegwerfe oder so,
von einer Million mal 30.000.
Und das kann natürlich
dann beliebig mit größeren Datensätzen
noch größer werden und der
Gedanke an der Stelle ist einfach,
mit so einer Dimensionalitätsreduktion
verliere ich theoretisch Informationen,
aber
es gibt halt viele
dieser Spalten, die sich sehr
ähnlich verhalten werden. Also
der Optimalfall
für eine Dimensionalitätsreduktion ist,
dass zwei Spalten oder
zwei Zahlen quasi komplett gleich sind,
weil dann verliere ich quasi
keine Informationen darüber, wenn ich einfach
sage, dass diese beiden Spalten,
wenn ich die beiden quasi zusammenlege.
Und wenn zwei Spalten
sehr, sehr stark korreliert sind,
dann verliere ich auch
nicht viele Informationen in dem Moment, wo ich
die beiden irgendwie zusammen auf 1 abbilde.
Also die, wenn die Spalten ja Wörter sind,
dann ist ja gleiche Wörter relativ unwahrscheinlich,
aber dann muss man tatsächlich diese Kombination
finden aus der Grammatik, die wir eben hatten,
halt das Modul laden dann, was war das,
WordConf, irgendwas,
von Google? Achso, WordSpec,
aber... WordSpec, genau.
Das könnte man ja nehmen als Information, um irgendwie so eine
Kovarianz in den Wörtern festzustellen und die da
halt dann so zu gruppieren, dass die halt dann
zusammenfassen und die... Theoretisch kann es aber auch vorher
der Fall sein. Also mal angenommen, ich habe zum Beispiel
einen Namen dran, mit einem Vornamen und einem Nachnamen
und der kommt halt tatsächlich nur in einem der Texte
vor. Dann habe ich da
natürlich zwei Spalten für,
einmal der Vorname, einmal der Nachname
und die haben genau einmal eine 1 für ein Dokument.
Dann sind diese
beiden Spalten natürlich perfekt korreliert.
Und dann können die natürlich auch beliebig
von der Dimensionalität
auf einer projiziert werden.
Also würde man da nur einen von
beiden wegschmeißen oder sagt man einfach, ja, das ist eigentlich nur
in einem Dokument, ist eigentlich egal.
Letztendlich machst du das selber gar nicht, sondern der Algorithmus
überlegt sich einfach selbstständig, wo
verliere ich am wenigsten Informationen, indem ich
die runtergebe und was ich von außen nur sage, ist
auf welche Zieldimension will ich gerne?
Also das heißt, dass da einfach 300 reicht.
Genau. Und der projiziert
es für dich runter, aber das ist die Logik, die dahinter steht.
Genau. Und dabei geht praktisch
nur sehr wenig Information
verloren. Das funktioniert halt ziemlich gut.
Genau.
Also das ist halt auch sowas. Aber das ist halt
irgendwie dann doch so eine Geschichte, wo dann
man nicht immer irgendwie alles gleich
verwenden kann, weil es gibt halt Modelle, die kommen
halt mit Spasen-Daten klar und manche halt nicht.
Ja.
Genau, genau, genau.
Gut, aber jetzt wollen wir mal ein Modell trainieren.
Ja, ein Modell trainieren, genau. Dafür gibt es dann halt auch
irgendwie einmal
eine Abstraktionsgeschichte, um halt dieses
Multilabel-Ding sozusagen
es gibt einige Modelle, die
automatisch Multiclass können, aber Multilabel
da muss man halt
braucht man halt noch so einen Wörter außenrum
und eine einfache Art,
wie man halt sozusagen aus einem
Modell, also was alle Modelle können, ist halt
binäre Klassifikation, das ist halt auch das Einfachste
und man kann jetzt natürlich aus
binärer Klassifikation quasi automatisch eine
Multilabel-Klassifikation machen, indem
man einfach für jede Klasse
eins gegen alle anderen
ein Modell trainiert, was dann binäre Klassifikation
macht.
Und dann, das muss man aber auch nicht selber machen,
sondern da gibt es dann halt schon
ein vorgefertigtes Ding,
aus Scikit-Learn Multiclass,
das Ding heißt One-Versus-Rest-Klassifier
und der tut das dann
und man kriegt das gar nicht mit, sondern
steckt einfach nur das eigene Modell, was man verwenden
möchte, da rein und dann passiert das automatisch.
Und dann gibt es halt noch
aus dem
Scikit-Learn-Metrics-Modul
eine Klassification-Report-Funktion,
die halt einem dann so
eine ganz schöne Übersicht
gibt, was dann jetzt passiert ist mit den Testdaten,
wenn man die dann nochmal durch das
trainierte Modell durchgibt und
ja, so
eines der ersten Modelle, die man immer so
ausprobieren würde,
denke ich mal, ist so
Logistic Regression, das ist
auch ein
Klassifikationsmodell,
das heißt bloß Regression,
das ist nicht so.
Ja, ich bin auch total,
ich habe mich am Anfang auch lange Zeit verwirrt,
dass ein
eine Methode, die
Regression im Namen hat, in Wirklichkeit eine
Klassifikation ist, aber...
Das ist halt irgendwie...
Das ist halt dieser Algorithmus, der im Endeffekt dahinter steht.
Das sollte man nicht zu tief drauf reingehen.
Genau, genau.
Und das ist halt so ein Standard-Ding.
Das ist immer etwas, was man immer am Anfang verwendet,
weil das ist halt, funktioniert auf
vielen Sachen ganz gut, es ist sehr schnell.
Ja.
Daher
kann man gut verstehen, was es macht.
Daher
man würde gar nicht mit so
totalen Fancy-Verfahren anfangen,
sondern eher so mit simplen und dann erstmal gucken,
was da überhaupt passiert.
Und dann kann man ja immer,
noch komplizierteres Dinge machen, wenn das halt nicht reicht.
Ja.
Genau.
Model ist
jetzt sozusagen einfach nur Run-versus-Rest-Classifier
und da das
Logistic Regression
Modell reingesteckt und
dann
einmal Fit auf, um das Modell zu trainieren
und dann einmal
Predict, um
ja, die Voraussagen
zu bekommen für die
Test-Dokumente.
Ich finde das sehr gut.
Das ist ein sehr gutes Beispiel für,
wie so ein echtes Welt-Data-Science-Projekt aussieht.
Weil, wenn man jetzt irgendwie
sagt, also wenn man von außen dann
naiv draufschaut
und denkt sich so,
so schwer kann es doch nicht sein
und du machst irgendwie Hello World
irgendwie Regression oder Klassifikation,
dann findest du halt einfach
irgendwo werden Daten vorgefertigt, richtig
geladen und dann
nimmst du das Modell, importierst es,
machst ein Fit, machst ein Predict und hast dein Ergebnis.
Also das eigentliche Modell-Training,
ist kein Problem, aber wir reden
jetzt hier schon eine Stunde, eine halbe
oder so darüber,
wie wir da hinkommen und das
ist die Schwierigkeit bei dem Ganzen, dass es halt
in Wirklichkeit dann doch nicht so trivial ist, dass man halt
eben irgendwie Multiclass oder Multilabel
hat oder sehr große Matrizen oder
Dimensionalitäten reduzieren muss und
Features ingenieren und genau,
dieser Weg dahin ist das Komplizierte.
Ja, und dann haben wir aber jetzt
eine Prognose raus und
müssen uns überlegen, wie gehen wir mit der um,
wo es auch wieder anfängt komplizierter zu werden.
Genau, ist halt wahrscheinlich ein bisschen
unübersichtlich beim Classification-Report, das halt über
alle 90 Kategorien zu machen, deswegen habe ich jetzt einfach mal
die Top-10-Kategorien
genommen. Das ist auch so, wenn man sich
dafür interessiert, gibt es halt
die Doktorarbeit von Thorsten
Joachims, die ist glaube ich
eines der meistzitierten Papers
sozusagen im Textklassifikationsbereich,
irgendwie ist es anderthalb tausend Mal zitiert worden oder so.
Der hat sich
damit zuerst 1998 irgendwie
beschäftigt und
das Ding gibt es als Buch oder man kann halt
irgendwie zu so einer Schattenbibliothek gehen.
Da gibt es das auch.
Und da kann man sich halt auch,
man guckt sich da auch mal die Top-10-Kategorien
an und kann das einfach mal
vergleichen, die Zahlen, die er da rauskriegt,
mit dem, was man da selber irgendwie dann sozusagen
da
als Ergebnis hat und
sieht eigentlich alles ganz gut aus.
Genau, die,
was man da in Spalten in dem
Klassifikationsreport drin stehen hat,
sind halt einmal die Namen
der Kategorien, dann
Precision, Recall, F1-Score
und ja, wie viele
Beispiele es gab.
Aber...
Genau, das ist was, womit man den Umgang auf jeden Fall wirklich lernen muss.
Da hat mich ganz am Anfang, als ich angefangen habe mit
Data Science, konnte ich mit den ganzen Zahlen auch nicht wirklich
was anfangen.
Was man ja immer im Wesentlichen
hat, ist, man hat
prinzipiell vier Fälle.
Ich kann einfach
bei einer binären
Klassifikation, was so das Einfachste ist,
da kann es sein,
dass ein Ergebnis quasi wahr ist
und ich habe auch gesagt, dass
es wahr ist, das ist das Einfachste, dann habe ich
ein True Positive.
Es kann auch sein, dass es
falsch ist und ich habe aber gesagt,
dass es wahr ist, dann habe ich
ein
False Positive.
Ich kann sein,
dass ich richtig gelegen
habe und es negativ ist,
dann habe ich ein True Negative.
Oder es kann negativ sein und
ich lag auch noch daneben, dann habe ich ein
False Negative.
So, und jetzt
gibt es quasi Kombinationen von diesen
Feldern für Precision und Recall.
Und die inhaltliche Bedeutung davon ist
vom Prinzip, also man kann sich das auch in den Formeln
anschauen, ich finde das hilft aber weniger, als
einfach zu sagen, die Precision
ist, wie viele von
meinen Daten, wo ich gesagt
habe, dass sie wahr sind, sind auch wirklich wahr.
Und Recall ist,
wie viele von denen, die
prinzipiell wahr sind, die ich quasi
prognostizieren wollte,
habe ich dann auch erwischt.
Das sind so die beiden.
Und dazwischen habe ich so ein Trade-Off.
Und da muss ich halt abwägen.
Und das ist, was der F1-Score im Endeffekt macht.
Der tut nämlich einfach
quasi die beiden so verrechnen,
dass nachher ein Zahlenwert rauskommt
zwischen 0 und 1. Und wenn ich quasi
die 1 habe, dann habe ich quasi sowohl Precision
als auch Recall perfekt bei 1.
Und wenn ich bei 0 habe, lag ich bei beiden
komplett falsch. Und der gibt
so eine Art Bonus, durch das
mir berechnet wird, dass ich quasi die beiden
auch relativ ausgewogen habe. Also wenn ich
da quasi die Precision auf 1 habe, aber
den Recall bei 0, dann bin ich da nachher trotzdem
bei einer 0. Und
genau, das sind die drei wesentlichen
Metriken dieses Reports. Und dann
gibt es halt eben noch den Support, der einfach
quasi aussagt, okay, wie häufig ist denn
der Fall überhaupt, um mir so eine
Absolut-Skala zu geben. Weil diese Zahlenwerte
sind jetzt natürlich alles relativ. Wenn ich nur
ein Beispiel habe, und das habe ich richtig klassifiziert,
dann habe ich einen F1-Score von 1 und alles
ist super. Aber wenn es halt nur ein Beispiel war,
dann hilft mir das real im echten Leben natürlich wenig.
Deswegen ist dieser F1-Score,
dieser Support dann so eine Matrix für
wie wichtig es ist, denn in absoluten Zahlen.
Ja, ja, ja, ne, finde ich
genau, super erklärt.
Also so F1-Score
finde ich jetzt mal, also man braucht
das sowas in der Art häufig, wenn man
jetzt zum Beispiel Modelle ranken möchte.
Man möchte jetzt das beste Modell haben.
Wenn man jetzt Precision und Recall-Werte hat, dann ist das halt blöd.
Man kann halt nicht so richtig gut nach,
wenn man jetzt 2-Werte hat, irgendwie, wie will man dann sortieren?
Daher
muss man
das halt irgendwie in einem Wert
zusammenfassen und dann nimmt man halt
einen F1-Wert. Man kann das auch irgendwie anders ausrechnen,
aber das ist halt das, was meistens
gemacht wird.
Ja, und
einer der Gründe, weshalb man
sowas überhaupt machen muss, man kann jetzt nicht einfach
vorstellen, warum immer nicht einfach Accuracy
oder so. Ja, das
geht halt nicht so gut.
Also Accuracy funktioniert halt dann gut, wenn
das halt,
wenn alle Klassen irgendwie die gleiche Anzahl
von Beispielen haben und so,
dann geht das vielleicht, aber das wird auch schwer,
sich vorzustellen, wenn man jetzt
irgendwie was anderes hat als nur 2.
Und
es hat auch sonst irgendwie blöde
Eigenschaften. Also wenn man sich jetzt nochmal
das Beispiel mit dem Spam und Nicht-Spam
vorstellt.
Also bei dir scheint das ja nicht so zu sein.
Du kriegst ja auch nicht so viel Spam, aber bei mir
mag das schon so mit dem,
was andere Leute auch beobachten,
übereinstimmen, dass halt 99% der Mails,
die ich bekomme, sind halt Spam.
Und wenn ich jetzt...
Was dann noch darunter fällt vielleicht.
Ja.
Also ich habe sozusagen eine sehr ungleiche Verteilung
der
Klassen. Und wenn ich jetzt
sozusagen Accuracy nehme
als Maß oder als Metrik dafür, wie gut
ist das jetzt, dann könnte
ich jetzt einen Classifier bauen, der sagt
irgendwie immer, es ist Spam.
Und der hätte dann Accuracy von 99%.
Klingt doch super, voll gut. Aber der macht ja eigentlich
gar nichts oder macht auf jeden Fall
nichts Sinnvolles, weil hinterher kriege ich halt gar keine
Mail mehr. Und
äh,
so, das...
Ja, diese Irrtumswahrscheinlichkeit ist tatsächlich, glaube ich,
relativ relevant auch für den Untersuchungsgegenstand, den man hat.
Ich weiß nicht, ob ihr Alpha- oder Beta-Fehler nennt man das, glaube ich,
so in der klassischen Statistik.
Ja, oder viele Erste-Ordnung oder Zweite-Ordnung oder sowas.
Und da ist ja schon
sehr entscheidend, also was für
Untersuchungsgegenstände man hat. Manchmal ist 5%
Abweichung dann ganz okay, weil 95% Wahrscheinlichkeit
ist ausreichend irgendwie. Vielleicht bei Hund-Katze
unterscheiden, vielleicht auch nicht ganz so wichtig.
Aber bei anderen Dingen ist halt dann das
1%, 1% zu viel. Also gerade wenn man
so Häufigkeitsverteilungen hat wie, es kommt nur 1%
der Fälle sowas raus.
Und dann Fallstieg ist vielleicht nicht so super.
Ja, aber eben bei Accuracy würde man
halt gar nicht sehen, dass man da ein Riesenproblem hat,
wenn man die 99% sieht.
Vielleicht muss man dann
einen Negativfehler nehmen. Dann muss man vielleicht nicht versuchen,
Spam zu tracken, sondern richtigen Mail.
Aber wenn man jetzt Precision-Recall nehmen würde, würde man sagen,
okay, dann sieht man es halt sofort, weil Precision wäre 0.
Ja, also andersrum ist halt tatsächlich
dieses Ding, wenn du halt den Spam
immer versuchst zu tracken und er sagt immer
Spam, dann müsstest du halt umdrehen, musstest sagen,
hey, ich möchte richtigen Tag tracken. Und dann hättest du halt
vielleicht eine bessere Erkenntnis.
Oder so, weil du das halt nicht
wegschmeißen würdest durch die Fehlerwahrscheinlichkeit.
Ja,
auf jeden Fall, also
für so im allgemeinen
Fall, wenn man viele Klassen oder
Labels hat und
unterschiedlich verteilte
Klassen, dann sind halt
Precision-Recall halt ein viel besseres Maß
als irgendwie
Accuracy. Daher nimmt man das als,
es gibt auch noch andere Metriken, die man irgendwie
benutzen kann, aber das ist schon irgendwie
so, dass
verbreiteten
verbreitetste, denke ich mal.
Das kommt halt auch so aus diesem Information-Retrieval-Bereich.
Genau, und diese Metriken sind halt so ein bisschen,
ja, man muss damit halt lernen, umzugehen.
Jedes Problem ist ein bisschen anders, also
wir haben jetzt hier, wie gesagt, so eine Multi-Label-Klassifikation.
Wenn ich jetzt
eine Regression habe, habe ich plötzlich wieder komplett
andere Metriken, die ich da benutze.
Ich setze mich gerade das erste Mal
in meinem Leben so ein bisschen mit Recommender-Engines
auseinander, wo man quasi gleich
ganze Listen immer zurückgibt
und quasi sagt, okay,
ich glaube, das erste ist das Wahrscheinlichste,
das zweite ist das zweitwahrscheinlichste und so weiter.
Dann braucht man auch wieder ganz andere Metriken.
Also, genau, gerade in diesem
Metriken-Feld wird es dann interessant,
in welchem Bereich von Data Science
man sich begibt und was man gerne machen
möchte.
Da kann man viel Zeit reinstecken.
Das ist auch wichtig.
Genau, und das ist teilweise auch sehr schwer
zu erklären, also wenn man dann irgendwie
ja,
dass dann halt auch Ergebnisse
irgendwie reportet,
ist es manchmal nicht so einfach zu erklären,
warum man das jetzt so macht und nicht irgendwie
einen einfachen einzelnen Wert nimmt oder so.
Ja, da muss man
halt gut sein im Erklären.
Keine Ahnung.
Genau, beim Wesentlichen sind wir jetzt die Basis-Pipeline
ja einmal durch. Also wir haben unsere Daten geladen,
wir haben sie uns ein bisschen angeschaut,
wir haben
darauf jetzt zwar kein großes Feature-Engineering
gemacht, also wir haben jetzt nicht versucht, irgendwie noch
zusätzlich sowas dran zu spielen,
wie zum Beispiel die Textlänge, die noch nicht vorher
in den Daten so mit drin war und auf den mitzunehmen,
zu lernen oder sowas man machen könnte, aber wir haben
zumindest den ganzen Text umgewandelt
in eine Zahlmatrix und die reduziert
und darauf jetzt halt eben ein Modell
berechnet und können sagen,
wie gut dieses Modell ist.
Wenn man jetzt
einfach da eine Spalte dran hängt, geht das
einfach so? Also wenn man das schon so ein bisschen modelliert hat,
dass man dann sagt, hey, ich hänge jetzt sowas dran,
wie die Länge.
Ja, genau.
Also hier erzeuge ich ja sozusagen
die
Feature-Matrizen direkt,
aus dem Text,
aber wenn wir uns jetzt zum Beispiel
mal angucken,
na, ich glaube,
das
Grid-Search-Ding macht das dann halt
auch schon so ein bisschen anders. Was man da
normalerweise dann hat, ist halt
eben
nicht so eine naive Geschichte, wo man das einfach
so erzeugt, sondern man hat halt
eine, die Feature-Matrix wird erzeugt
durch, oder überhaupt das komplette
Ding ist halt
eine Pipeline.
Das ist halt
auch eine Klasse aus Cycle-Learn
und die kombiniert im Grunde
viele Schritte
sozusagen zu einem Ding, das
sich insgesamt wieder so verhält wie ein Modell.
Also zum Beispiel
jetzt bei dieser,
in diesem
Notebook ist es halt so, da
sind die Schritte, aus denen
die Pipeline besteht, halt
eine zweite
Geschichte, die ich irgendwie da noch einführen muss.
Das Ding nennt sich Feature-Union.
Und man kann sich das im Grunde so vorstellen, also Feature-Union
macht genau das,
was du jetzt vorhattest, einfach irgendwie Dinge
an die Feature-Matrix spalten, an die Feature-Matrix
dran bauen,
sozusagen. Also sozusagen, ich hab halt
unterschiedliche
Spalten, ja, und ich möchte die halt
zusammen kombinieren zu einer
Feature-Matrix, dann nehme ich Feature-Union,
das fügt halt die Spalten zusammen
und die Pipeline
integriert die ganzen Schritte, die nötig
sind, um irgendwas zu tun.
Also Feature-Extraction,
unterschiedliche Arten von
Feature-Extraction und halt
am Schluss irgendwie ein Modell trainieren und
ja,
auf den Festdaten halt
Predictions machen,
sodass das
Ganze sich wieder als ein Modell irgendwie
benutzen lässt. Und was
das hier tut, ist
ja, ich übergib der
Feature-Union halt
eine Liste von Transformern, die irgendwie Dinge tun
und man kann die
jetzt ineinander wieder verschachteln.
Das ist jetzt wieder eine Pipeline.
Und die Pipeline, die da drin
ist, die macht halt, das ist der erste
Schritt, der nimmt
eine Spalte aus dem
Data-Frame, und zwar den Titel
und
macht dann eben so etwas wie
ja,
Text-Statistiken drauf.
Also sowas wie, wie lang ist der Titel,
wie viele Buchstaben sind da drin,
weiß nicht, wie viele Wörter sind da drin oder sowas,
ich weiß jetzt gar nicht mehr genau, was das tut.
Und dann skaliert er halt
diese, weil das,
das wäre bei TF-EDF, das ist halt automatisch
drin, das ist halt irgendwie
sozusagen
ja, skaliert
ist auf wie ein
Intervall zwischen, weiß ich nicht, 0 und 1
und
normalisiert, sodass es halt irgendwie
alle Vektoren, die Länge 1 haben.
Bei den Text-Statistiken ist das
natürlich nicht, da kommt dann halt so eine Zahl raus, wie
der Titel hat jetzt 25
Buchstaben. Und da,
was man eigentlich immer macht, wenn man
jetzt solche Zahlen hat, ist
das so zu skalieren, dass
alle Features irgendwie immer
zwischen, ja,
zwischen minus 1 und 1 oder
zwischen 0 und 1 sind und
auch eine Standardabweichung von 1 haben
irgendwie so. Das hängt halt auch mal viel damit
zusammen, was für ein Algorithmus du im Nachhinein dann
benutzt. Manche Algorithmen können
gut umgehen mit
komplett absolut skalierten
Werten, also so im Entscheidungsbaum zum Beispiel,
dem ist das relativ egal, wo er
jetzt zwischen, ob du jetzt den Wert zwischen
0 und 1 reingibst und Werte
zwischen 0 und 10.000, weil
der entscheidet einfach irgendwo, okay, zwischen
diesen zwei bitte splitte und
das rechte Ast, das linke Ast,
aber bei so einer linearen
Regression zum Beispiel, da ist
es halt von Bedeutung, wenn deine
Achsen alle ganz unterschiedlich
sind. Also ich könnte, das Wichtige ist eigentlich nur,
dass sie halbwegs miteinander vergleichbar sind.
Ich könnte sie auch alle von
0 bis 100 skalieren oder so, aber die Konvention ist
halt einfach, man macht das halt einfach alles zwischen 0 und
1 oder minus 1 und 1, je nachdem,
was man da vielleicht bezwecken möchte
und damit macht man in der Regel nichts
falsch. Ja, also kann man sich
ganz einfach vorstellen, bei einem linearen Modell, ein lineares
Modell bedeutet, dass du im Grunde
einen
Vektor hast, der genau die
gleiche Dimension hat, wie
du Spalten hast, sozusagen.
Und die Klassifikation
ist einfach nur das Skalarprodukt aus
einem neuen Feature-Vektor und diesem Modell.
Und
jetzt gibt es ganz
viele unterschiedliche Arten dieses
Modells.
Dieses Modell zu trainieren, aber das ist immer das
Gleiche, wenn das ein lineares Modell ist.
Also ob ich jetzt eine Support-Vektor-Maschine
nehme, die das halt trainiert, oder
halt Logistic Regression, oder halt
irgendwie sonst irgendwie eins von den
Generalized Linear Models
oder so, die es da so gibt,
da fällt immer so ein Vektor dabei raus und die
Klassifikation ist eigentlich immer gleich.
Und ich kann mir natürlich jetzt leicht vorstellen,
was passiert, wenn ich irgendwie überall
Werte zwischen 0 und 1 habe in den Spalten.
Nur in einer Spalte habe ich jetzt Werte um die 10.000
oder so. Dann dominiert diese Spalte
natürlich.
Das Klassifikationsergebnis ist massiv.
Das heißt, ich muss die alle so
normiert haben, dass hinterher die Länge der
Vektoren 1 ist, damit
nicht ein
Feature halt da so irgendwie...
Alle Features sollten irgendwie so quasi
numerisch gleich wichtig sein.
Ja, deswegen muss ich das halt
skalieren am Schluss. Und dafür
gibt es auch unterschiedliche Skalierer
und der Standard-Scaler
macht glaube ich irgendwie...
Weiß ich gar nicht.
Ja, das ist ganz
validity halt.
Ist validity halt irgendwie oder. Genau.
Der wesentliche Punkt eigentlich bei diesen Pipelines ist,
ich könnte diese ganzen Schritte
auch alle nacheinander ausführen. Also ich kann
prinzipiell in meinem Notebook,
das haben wir vorher quasi auch gesehen,
in dem linearen Regressionsnotebook,
ich kann
erst meinen Text umwandeln,
Dimensionalitätsreaktionen ausführen
in der nächsten Zelle und quasi immer dazwischen
meine Werte irgendwie in ein neues DataFrame
schreiben und das wieder in das nächste reinführen.
Ist zum Debuggen
vielleicht auch mal ganz hilfreich. Aber
wenn ich nachher quasi dann einfach damit
spielen möchte, verschiedene Werte zu verändern,
dann will ich, dass das möglichst einfach hintereinander
durchläuft. Und ich weiß nicht,
cashen so Pipelines auch zwischendrin
Schritte? Genau. Genau, das heißt, das macht
es auch deutlich effizienter, wenn ich dann
quasi in einem relativ späten Step
in der Pipeline eine Kleinigkeit ändere,
dann muss die halt nicht komplett von vorne durchgeführt werden.
Und
genau, das mache ich prinzipiell eher,
wenn ich schon so die erste
Exploration durch habe, dann würde ich anfangen
sowas in eine Pipeline zu stecken oder machst du das wirklich von Anfang
an? Ne, genau, ich mache das auch erst, wenn
ich ungefähr weiß, was ich da
damit tun möchte und
ja, genau,
man braucht es halt auch vor allen Dingen deswegen, wenn man jetzt
unterschiedliche Sachen ausprobieren möchte. Das ist
halt, Stichwort
wäre Hyperparameter-Optimierung,
also sozusagen, welche Features verwende ich.
Man kann ja, man könnte ja dann
doch wieder gewichten, wenn man möchte, oder?
Ja, natürlich,
man kann auch einzelne Teile davon,
man könnte jetzt unterschiedliche Feature-Unions
bauen, so eine zum Beispiel, die sich nur mit dem
also, das ist ja
im Grunde hier auch schon so, also ein
Ding, das sich halt nur mit dem Text beschäftigt, oder
eins wird, dass sich mit dem Body,
mit dem Titel beschäftigt, und eins, dass sich mit dem
Body dieser Tickermeldung
beschäftigt, und jetzt könnte man
diesen Dingern unterschiedliche Gewichte geben auch.
Das auf jeden Fall.
Ja, und
aber all diese Änderungen, die man macht,
wenn man jetzt da an irgendwelchen
Parametern dreht, die möchte man ja
hinterher auch, also das muss man sich ja
irgendwie merken, und eigentlich möchte man das ja auch
systematisch durchprobieren, und das
nicht von Hand immer wieder verändern,
und ein Verfahren, um das halt systematisch
durchzuprobieren, ist halt GridSearch,
und das ist halt auch ein Vorteil, wenn man jetzt so
eine Feature-Pipeline hat, dass man
sozusagen die Bereiche für
die Parameter, die man halt
durchprobieren möchte, die gibt man halt irgendwie an,
und dann
ruft man das halt mit GridSearch auf, und dann
werden diese ganzen Parameter, dieses Parameter-Grid
halt durchprobiert, und am Schluss
ja, also fällt halt das Modell
raus, was halt am besten funktioniert hat, beziehungsweise alle
anderen Ergebnisse werden auch gespeichert, normalerweise
will man jetzt da irgendwie aus diesem
GridSearch-Ding halt
rausholen und irgendwo ein CSV
schreiben, oder irgendwie, und dann kann man sich das halt hinterher
visualisieren, und dann halt gucken,
okay, wann habe ich das mit welchen Parametern
irgendwie ausprobiert, und was ist dabei rausgekommen.
Genau, man muss, also ich würde da
die Hyperparameter-Tuning noch mal
kurz ein bisschen aufgreifen.
Jeder dieser Algorithmen,
die wir jetzt erwähnt haben, die haben jede
Menge Parameter, die man da mitgeben kann.
Also, eine ganz einfache
lineare Regression ist vielleicht noch relativ
beschränkt, aber sobald wir
die jetzt zum Beispiel
regularisieren, das heißt irgendwie
verhindern, dass die irgendwie zu stark sich den Daten
anpasst, bekommt die Hyperparameter
oder noch einfacher finde ich es sich vorzustellen
bei so einem Entscheidungsbaum, der hat halt
eine gewisse Tiefe, der kann irgendwie auf
100 Leveln verschachtelt sein, oder nur auf
2, und der kann auch
zum Beispiel beschränkt werden in der Anzahl der
letztendlichen Blätter, also wie viele mögliche
Output-Ergebnisse kommen dahin, oder
wie viele Samples müssen in
jede dieser Blätter letztendlich fallen. Das sind zum
Beispiel schon drei verschiedene Parameter, die man
da so mitgeben kann, und die können die
Art, wie gut
dieses Modell performt,
extrem beeinflussen. Das ist
wirklich sehr, sehr entscheidend,
und deswegen ist es wichtig, die
einfach nach und nach durchzuprobieren.
Und man sagt jetzt, wir haben noch irgendwie selbstlernende Algorithmen,
irgendwie, warum
muss ich denn da jetzt noch Parameter-Tuning, aber
ich passe ja quasi einfach
die, man muss
es sind halt zwei verschiedene Arten von Parametern.
Die einen sind quasi die internen, die
berechnet werden, innerhalb des Algorithmuses,
die lernt er aus
den Daten, und das andere sind quasi so
die Konfigurationsobjekte,
wie man das, wie
die Berechnung von diesen anderen
Parametern zustande kommt, und
die muss ich ihm halt eben mitgeben,
oder ich versuche sie halt einfach alle,
probiere sie halt einfach alle durch, und das ist,
was ich mehr oder weniger bei so einem Hyper-Parameter-Search
mache. Ich überlege mir
irgendeine Strategie, dass er einfach
möglichst viele von denen durchprobiert, da gibt's
auch Algorithmen, die versuchen quasi selbstständig
so ein bisschen zu wählen, welche
an welcher Schraube drehe ich als nächstes
um halt dann den Algorithmus
in der Stelle optimal zu tunen
und das ist ein ganz entscheidender Prozess
innerhalb von so einem Data Science Projekt
Ja
Ja auch um halt die
man möchte es auch deshalb schon formalisieren
damit man die Ergebnisse kommuniziert bekommt
weil wenn ich jetzt im Notebook irgendwie Parameter erinnere
und das durchprobiere, dann weiß ich das vielleicht noch
obwohl meistens vergisst man das ja auch relativ schnell
wieder, was man alles probiert hat
dann weiß man vielleicht noch so, ach vor zwei Wochen hatte ich da irgendwas gemacht
das war ziemlich gut, oh was war das nochmal
man möchte das ja auch
wenn da mehrere Leute dran arbeiten
denen das ja auch irgendwie kommunizieren
und da ist es natürlich dann schon gut
wenn man das systematisch gemacht hat
weil dann fallen da eben solche CSVs einfach schon bei raus
und man kann halt auch gucken, was andere Leute schon probiert haben
und muss das dann auch nicht nochmal selber machen
Genau und diese Hyperparameter Search
gibt einem halt am Ende auch so eine Operation
irgendwie Best Parameter oder Best Fit
oder sowas
und dann gibt er einem halt einfach das Beste von allen
die er ausprobiert hat wieder
aber ansonsten
wenn du quasi auch so tracken möchtest
was hat gut funktioniert und auch Features reiningenieren möchtest
du schreibst das dann einfach in CSVs weg
oder wie machst du das?
Ja, meistens tatsächlich, ja
Das finde ich immer noch ein bisschen überraschend
dass da Second Learn an der Stelle irgendwie
nichts von sich aus bietet
also ich habe mir jetzt gerade da auch diverse Tools angeschaut
ich finde da jetzt MLflow
von Dataprix
ganz cool
die ja auch quasi Spark gebaut haben
die haben da ein Tool für
es gibt da auch irgendwie Sacred und DVC
die ich mir alle mal angeschaut habe
aber irgendwie finde ich es merkwürdig
jeder kommt in diese Predolje
dass er jetzt quasi ein bisschen damit rumspielt
und seine ganzen Ergebnisse an und nach noch tracken möchte
und jeder schreibt sich die irgendwie so ein bisschen
auf seine eigene Art weg
aber der Standardsupport von irgendwelchen Bibliotheken
ist da finde ich noch ein bisschen
jung und schwach
Ja, ja
Wie fandest du DVC?
Das wollte ich mir demnächst mal irgendwie angucken
DVC ist halt nicht
Python
Pythonic, ja
also das basiert halt komplett auf
Kommandozahlenaufrufe und Shell-Skripten
ich habe letztendlich quasi so die Abwägung
würde ich sagen
wenn du schnell Sachen ausprobieren willst
und mehr so in der Exploration bist
dann guck dir lieber MLflow an
das finde ich dafür deutlich angenehmer
wenn du sehr viel Wert auf Reproduzierbarkeit
deiner Experimente legst
und vielleicht auch große Experimente machst
und dafür bereit bist
mehr Zeit zu investieren
und mehr Zeit zu investieren
as Maturity Level vom Prinzip her hast
und auch im Team arbeitest, dann ist DVC
meiner Meinung nach die bessere Wahl.
Also ich habe Kollegen, die benutzen das eine oder das andere.
Das hängt ein bisschen mehr von deinem Projekt ab,
aber gerade für die Exploration finde ich
DVC einfach
zu viel Boilerplate Code.
Ich verlinke dazu mal meinen Artikel,
den ich auch gerade noch geschrieben habe, genau darüber.
Ja.
Jetzt könnte man natürlich
alle möglichen verschiedenen Algorithmen draufwerfen
und so, aber ich würde sagen, das überlassen wir mal
den Hörern, da könnte man jetzt auch ewig Zeit
draufwerfen.
Haben wir irgendwas Wesentliches
vergessen?
Den grundsätzlichen Prozess, dieses Datenbeispiel
da von Reuters mal kurz so
zu skizzieren, haben wir schon durch, oder?
Also es funktioniert auf jeden Fall,
wir haben jetzt tatsächlich gelernt,
wie man so eine neue
Nachricht dann mit so einem Label versieht,
dass dann die Klasse so, was ist das für eine Kategorie,
rausfällt unten, oder?
Genau. Also wir sind jetzt in der Lage,
ähm,
haben jetzt ein Modell da liegen,
was auf den Testdaten
Prognosen rechnen könnte und wenn jetzt ein neuer
vergleichbarer Textartikel reinkommt,
der dieselben Spalten hat, egal wo der herkommt,
dann können wir den auch reinwerfen und dann wird
der einen der Text voraussagen,
die wir jetzt
quasi, ähm,
da drin hatten, da können jetzt kein
originär neuer
Artikel natürlich rausfallen oder so,
aber, ähm,
das haben wir und können auch bewerten, wie gut
wir daran sind.
Ja.
Ja, ja. Ja, also haben wir quasi unser erstes
Projekt erfolgreich abgeschlossen. Wenn wir das jetzt
auf dem Server implementiert haben, dann läuft das
so auch. Ja, wir könnten, wir könnten eben jetzt
so ein Modell hinter so einer, äh,
API irgendwie, äh, deployen
und, ähm, dann schicken wir
dann einen Artikel dahin und bekommen dann halt zurück,
äh, in welchen Dingern das liegt und dann,
dann muss man sich dann natürlich noch überlegen, okay,
gibt man halt zurück, in welcher Wahrscheinlichkeit,
mit welcher Wahrscheinlichkeit das in welcher, äh,
oder einfach nur eine Liste der
Kategorien, in denen es liegt,
ähm, und, ähm, wann,
wann sagt man eigentlich, äh, es liegt drin
oder nicht, äh, genau. Und das
ist, äh, da, da müssen wir natürlich noch eine Menge,
äh, Entscheidungen treffen. Das hängt halt
dann auch davon ab, wie das Problem aussieht,
ne? Ist es wichtig, möglichst alle,
äh, in den Kategorien, in denen ein
Artikel liegen könnte, mitzuliefern
Das ist besonders wichtig, möglichst keine Fehler zu machen. Und das hängt dann halt davon ab, welche Art von Problem man gerade hat. Und dann würde man das halt entsprechend machen.
Wie groß ist das Modell auf dem Server?
Die hier sind relativ klein, wenige Megabyte, denke ich, sind die größten.
Das hängt vor allem auch viel von der Art des Modells ab, was du jetzt benutzt. Also so ein Entscheidungsbaum zum Beispiel ist relativ klein.
Wenn du jetzt halt komplexere Algorithmen nimmst, wie zum Beispiel einen Random Forest, was quasi einfach ganz, ganz viele Entscheidungsbäume sind, oder einen Boosting Tree, was quasi verschachtelte Entscheidungsbäume sind, dann hast du natürlich ein Vielfaches dessen.
Dann kommst du auf mehrere Megabyte, ein einzelner Entscheidungsbaum ist wahrscheinlich sogar im Kilobyte-Bereich. Je nachdem, wie tief du den halt eben machst.
Und hängt halt auch davon ab, wie deine Trainingsdaten, also bei den linearen Modellen, die sind halt einfach genauso groß wie deine Feature-Matrix, also sozusagen haben genauso viele Dimensionen wie deine Feature-Matrix.
Und dann kannst du es ja einfach ausrechnen, wenn du pro Gewicht irgendwie vier Byte brauchst für einen Single Precision Float, dann ist das halt viermal, bei denen ist es wahrscheinlich mal 25.000, also viermal 25.000, also 100 Kilobyte, das ist halt nichts.
Aber wenn du jetzt zum Beispiel viele Trainingsdaten hast und du hast halt ein...
Also das ist ja auch noch...
So eine Geschichte, es gab in dem Explorations-Notebook auch so einen quasi manuellen Entscheidungsbaum dafür, welches Modell man denn jetzt für welches Klassifikationsproblem nehmen könnte.
Und da gibt es halt auch so irgendwie zum Beispiel so rekurrente neuronale Netze, die man nehmen kann und die können halt unter Umständen sehr, sehr viel mehr Parameter haben.
Also das kann sein, also das Ding halt macht natürlich nur Sinn, wenn man viel, viel mehr Trainingsdaten hat, aber wenn man jetzt irgendwie ein Modell hat,
50 Millionen Parametern, dann muss man die halt irgendwie speichern, das ist halt dann das Modell und dann wird das halt auch sehr, sehr groß unter Umständen.
Ja, aber selbst das ist ja für den Server heute alles nicht mehr so...
Es wird dann interessant noch, wenn man das nicht irgendwie auf den Server deployen möchte, sondern was heute oft gemacht wird, auch auf dem Telefon.
Und da muss man halt vielleicht nochmal ein bisschen gucken, was die Größe angeht, aber...
Das Schöne ist ja vor allem, eben eine Prognose jetzt mit so einem Modell zu berechnen, ist überhaupt nicht mehr kompliziert.
Aufwendig ist natürlich...
zu trainieren, wobei das mit den Modellen, die wir jetzt hier haben, alles relativ schnell
geht und man es einfach auf dem lokalen Rechner mal durchspielen kann. Bei einem neuronalen
Netzwerk wäre das bei dem Training was anderes, aber die Prognose ist hier nachher dann trotzdem
super einfach, weil dann gehe ich halt einfach nur einmal quasi den Entscheidungsbaum lang
oder laufe einmal mein neuronales Netzwerk von vorne nach hinten durch und fertig.
Wenn ich den Weg kenne, dann kann ich natürlich relativ schnell sagen, wo ich hin soll.
Genau, das kann auf schwacher Hardware laufen.
Ja, cool. Dann habe ich tatsächlich heute wieder ganz viel über Machine Learning gelernt.
Ich hoffe, ihr habt auch noch irgendwas mitgenommen.
Ja, wenn du noch ein bisschen mehr lernen willst. Also ich finde immer, so eigene Projekte
sind super und ansonsten einfach bei Kaggle mal einen Account machen.
Ja, Kaggle, ja.
Selbst wenn man da am Anfang nicht mitmacht. Also selber mitmachen ist sehr viel Aufwand,
aber es reicht eigentlich auch einfach nur mal ein paar Challenges zu beobachten und mal
ein paar Kernels sich aufzumessen.
Oder mal so nachbauen oder so welche, die es schon gab.
Mal gucken, wie gut man an die tollen Lösungen irgendwann rankommt.
Genau. Das Schöne ist ja bei Kaggle, dass diese ganzen Lösungen eigentlich immer einfach
alle online sind. Also diese Kernels, die da sind, die sind ja wirklich immer sehr, sehr
gut. Gerade auch bei der Exploration, wo man, also so ein Kernel ist ja vom Prinzip her
so ein Notebook.
Ja, vielleicht noch ganz kurz. In Kaggle gibt es Bounties dafür, wenn man total tolle Prognosen
herstellt. Also nur das vielleicht mal so für die Leute, die das schon nicht gesehen
hatten.
Genau, genau. Es ist also ein Wettbewerb für Data Science Projekte und Leute sind da so
nett und stellen halt quasi.
Ihre Kernels online, ihre Notebooks online und man kann da einfach mal gucken, wie die
das lösen. Da kriegt man sowohl mit, welche Tools sind gerade irgendwie angesagt, wie
geht man damit um. Man lernt so die Syntax und genau. Also ich lerne da auch jedes Mal,
wenn ich so eines der top gerateten Notebooks da durchlese, lerne ich jedes Mal wieder was.
Ja, finde ich auch super hilfreich.
Ja, findet ihr, wie ihr alle eingeschaltet habt?
Jo.
Ihr findet immer die Informationen in den Shownotes.
Jo, sehr.
Genau.
Wie kommt das von dir?
Ich glaube, ich bin nicht lang, ja.
Ja, ja, das kennen wir schon. Und ja, dann hört wieder rein, bleibt gewogen, wann auch
immer ihr uns hört. Wenn ihr irgendwelche Feedback habt, schickt an eine E-Mail an uns,
hallo at peißenpodcast.de oder Nico. Und ja, bis dahin.
Jo.
Genau. Hört wieder rein und bis bald.
Tschüss.
Jo, tschüss.