Über Algorithmen

Das Thema „künstliche Intelligenz“ (AI, artifical intelligence) ist anläßlich der Enthüllungen um das Prism-Projekt der NSA brandaktuell.

Die pure Erfassung von Datenbeständen durch die Geheimdienste ist ja zunächst gar nicht das Problem. Problematisch wird es erst, wenn man diese Datensätze zueinander in Beziehung setzt, und aus diesen Beziehungen eine Bedeutung ableitet, die dann zu einem Handeln führt. Der Erhebung von Daten folgt deren Sichtung, und erst dann kann man bestimmen, ob es lohnt, einen Anfangsverdacht (auf was auch immer) zu formulieren, dem man dann weiter nachgeht.

Das Verfahren gab es schon immer: man sammelt Beobachtungen (aka Intelligence), und läßt diese dann von Experten auswerten. Früher – zB. bei Echelon – brauchte man ganze Abteilungen in der NSA oder der CIA, deren Mitarbeiter die (analogen) Daten „zu Fuß” abhörten und analysierten, um gelegentlich Alarm zu schlagen.

Die unfaßbar hohe Anzahl der Datensätze, die bei Prism (und anderen, verwandten Projekten) abgegriffen werden, läßt sich unmöglich von einem auch noch so großen Stab von Geheimdienstlern auswerten. Zumindest eine grobe Vorauswahl muß zwangsläufig automatisiert erfolgen, mittels „intelligenter“ Computeralgorithmen.

(Disclaimer: Ich habe keine Ausbildung in der Wissenschaft der AI – ich habe aber einige Algorithmen in diesem Bereich geschrieben, und kenne die Forschung zumindest soweit, wie ich sie für meine konkrete Arbeit hilfreich fand.)

Grundsätzlich kann man mE. festhalten, daß die Forschung im Bereich der AI letztlich zum Stillstand gekommen ist. Nach einer großen Aufbruchstimmung in den 80ern des letzten Jahrhunderts folgte eine Welle der Ernüchterung, von der die allermeisten Projekte weggespült wurden.

Ein Algorithmus ist letztlich immer ein Computerprogramm: eine Abfolge von Befehlen, die von der CPU ausgeführt werden.

Man versuchte jedoch schon immer, sich von der Prozessorlogik soweit wie möglich zu entfernen, indem man von ihr abstrahiert. Solche Abstraktionen sind Grundbestand im Werkzeugkasten der Computerwissenschaft; im Alltag benutzt sie jeder Programmierer, der eine „Hochsprache“ benutzt, um Sourcecode zu schreiben (Assembler, C++, Basic, Javascript – selbst HTML-Quellcode ist letztlich solch eine „Sprache“).

Ein Algorithmus setzt hier noch eine Abstraktionsebene oben drauf: er formuliert ein Konzept, wie bestimmte „hochsprachliche Konstrukte“ ineinander greifen müssen, um ein bestimmtes Ergebnis zu produzieren.

Ein gutes Beispiel für einen (letztlich gar nicht so) einfachen Algorithmus ist das Sortieren von Datensätzen. Wenn man etwa – als User – vom Betriebssystem (MS Windows; Mac OSX; Linux; etc.) seine Dateien zB. nach Namen sortiert haben will, aktiviert man einen Algorithmus, der über ein bestimmtes Wissen über das Alphabet verfügt. Dieses Wissen ist seinerseits wieder ein Computerprogramm, das auf Tabellen zurückgreift, die einer Zahl (aus denen jeder Datensatz letztlich besteht) ein Symbol (im Alphabet einer bestimmten Sprache) zuordnet.

Auf solch ganz rudimentären Verhältnisse muß jeder Algorithmus immer zurück – unabhängig davon, welche „Magie“ sich hinter ihm zu verbergen scheint.



Auf eine – wie auch immer verfeinerte - Suche läuft letztlich jeder Algorithmus hinaus: ein Computerprogramm läuft über Datenbestände, und liefert ein Ergebnis – einen für den User sichtbaren Output –, wo die Daten nach bestimmten Kriterien geordnet bzw. gefiltert sind.

Das ist ein grundsätzliches Verfahren, dem auch zB. die Algorithmen hinter dem Score-Editor in Cubase gewissermaßen gehorchen. Die Datensätze hier sind die musikalischen Daten – eine Folge aus „Note-On“- und „Note-Off“-Befehlen –, die dann von den Algorithmen so sortiert werden, daß sie für eine Ausgabe in Notenschrift auf dem Bildschirm taugen.

Ich will versuchen, das ein wenig konkreter zu fassen.

Zum Ersten: Wenn man einen Datenstrom, der aus „Note-On“ und „Note-Off“-Befehlen besteht, in ein lesbares Notenbild transformieren will, braucht es als erstes ein Raster in der Timeline: man kann rhythmische Strukturen nur dann erkennen, wenn man das Taktmaß kennt – und das Problem der Darstellung von rhythmischen Verhältnissen ist womöglich das zentrale Problem, wenn man musikalische Datenströme in Notenschrift übersetzen will.

In Cubase ist dafür bekanntlich der der Signaturetrack zuständig. Hier findet sich das Raster, an dem sich die Algorithmen in den Scores entlang tasten.

Bevor man etwas ordnet, muß man also die Kriterien der Ordnung schon im Vorfeld festlegen. Die Algorithmen in den Scores von Cubase ähneln hier, auf eine freilich sehr abstrakte Weise, jenen bei der Rasterfahndung von welchem Geheimdienst auch immer – und verweisen auf eine grundsätzliche Gemeinsamkeit von Algorithmen, welcher Art auch immer: man muß sortieren; und das geht nur, wenn man schon im Vorfeld festlegt, nach welchen Kriterien dies zu geschehen hat.



Zum Zweiten: Im Score-Editor von Cubase folgt die Interpretation von rhythmischen Strukturen einem eher rudimentären Verfahren: es gibt einen Grundbestand an Regeln, die dann von Ausnahmen begleitet werden.

Eine grundlegende Regel ist etwa, daß eine Note, die über einen Taktstrich „hinausragt“, in zwei grafische Repräsentationen zerteilt werden muß.

Eine zweite Regel besagt, daß dies auch dann zu geschehen hat, wenn eine Note die Taktmitte überschreitet.

Eine erste Ausnahme von der Regel (die der User im Setup aktivieren kann) geht damit um, daß es Stilistiken in der Musik gibt, in denen man ein anderes Verständnis vom „Offbeat“ hat: ein Jazzmusiker etwa wird an dieser Stelle eine etwas andere Darstellung der Noten erwarten.

Gelegentlich „kippt” eine Ausnahme von der Regel: im Beispiel rechts werden Noten an jedem Offbeat zerteilt, nur weil der User hier eine Option aktiviert hat, die für diese spezielle Situation eher wenig Sinn macht (ich deute diesen Zusammenhang hier nur an).

Der Code in Cubase, der die Rhythmik von MIDI-Daten in Notenschrift übersetzt, funktioniert nur deshalb einigermaßen gut, weil er von Ausnahmeregeln geradezu verseucht ist - wobei die User aber immer noch, mehr oder weniger detailliert, bestimmen können, wo diese greifen.

Eine grundsätzliche These: Ein im Lesen von Notenschrift geübter Betrachter eines Notats weiß, wenn die Darstellung „Sinn“ ergibt (oder nicht); er kann aber nicht erklären, warum das so ist.

Eine zweite These (und die wird man wohl nur dann als Spekulation zurückweisen, wenn man auch die erste These verneint): Wenn man dieses „Wissen“ mit einem Computeralgorithmus nachstellen will, läuft man in das grundsätzliche Problem, daß ein Computer immer nur ein „wenn-dann“, nicht aber ein „nebeneinander“ kennt.

Wenn man davon ausgeht, daß beide Thesen zutreffen, kann man folgern, daß sie für eine große Zahl von Algorithmen zutreffen, die so etwas wie Verhältnisse in der „realen Welt“ modellieren versuchen. Man hat es in der Realität eben nur manchmal (These 1) mit Beziehungen zwischen „Ursache“ und „Wirkung“ zu tun. Oft sind die Beziehungen zwischen Verhältnissen aber etwas ganz anderes, nämlich, zumindest auf den ersten Blick, scheinbar zufällige „Koinzidenzen“.

Dabei verstellt schon diese Unterscheidung (zwischen „Kausalität” und „Indetermination”) den Blick auf Wege der Erkenntnis, die Menschen halt mal so eben machen. Man „sieht” etwas, und zieht, ohne darüber näher Rechenschaft ablegen zu können, Schlüsse, hinter denen man auch nach einigem Nachdenken immer noch stehen kann (die sich dann womöglich sogar als „richtig” erweisen). – Das kann man, auf ganz prinzipieller Ebene, im Computer nicht nachstellen (These 2).

Diese Erkenntnis ist letztlich nichts Neues. Man trickst dann halt weiter herum.



Zum Dritten: Einem Computer kann man durchaus beibringen, sich von einer strikten „wenn-dann“-Logik zu entfernen. Es gibt hier einen Mythos um die sog. „Fuzzy-Logic“, wo ein Algorithmus – freilich nur auf den ersten Blick – nicht mehr deterministisch vorgeht, sondern Wahrscheinlichkeiten implementiert (der Artikel in der Wikipedia schreibt hier am Mythos mit).

In der Praxis ist das relativ einfach. Statt einem „if-else“ (dem „wenn-dann“-Befehl in praktisch allen Hochsprachen) testet man, was solch ein „if-else“ für ein Ergebnis haben würde, bevor man dieses Ergebnis tatsächlich „abholt“.

Im Rahmen des Chordtrack-Features von Cubase habe ich dieses Verfahren für die Bestimmung der Folge von „Voicings“ verwendet.

Ich will hier nicht näher ins Detail; mir kommt es hier nur auf das Verfahren an (das ich nicht erfunden habe, und das man wohl bei ganz anderen Gelegenheiten ebenfalls benutzt).

Hier ist das Problem (bei dem es sich letztlich nur um ein allgemeines Problem beim Sortieren von Datensätzen handelt, das man auf ganz andere Computer-relevante Probleme recht unaufgeregt übertragen kann):

Auf einen C-Dur-Akkord, der aus einem „Datensatz“ aus konkreten Tönen besteht (einem sog. „Voicing”) , soll ein F-Dur-Akkord folgen. Für den F-Dur-Akkord gibt es eine Liste aus „Datensätzen von Tönen“, mit dem man ihn spielen kann. Der Algorithmus soll bestimmen, welcher Datensatz – welches Voicing – jenem nach C-Dur „am Besten” folgt.

Innerhalb der „Fuzzy-Logic“ wird jedes mögliche Voicing in F-Dur darauf getestet, wieweit es dem Vorgänger (dem in C-Dur) „am Besten“ folgen würde. Dafür gibt es einen Satz von Regeln, die musiktheoretischen Überlegungen über Stimmführung folgen. Eine Regel ist zB., daß der Sopran (die höchste Stimme innerhalb eines Voicings) möglichst kleine Schritte gehen soll (und nicht, wie der Baß, eine Quinte nach unten oder eine Quarte nach oben springt).

Jene „kleine-Schritte-Regel“ für den Sopran zB. halte ich für extrem wichig; deshalb gebe ich ihr zB. 8 Punkte, wenn der Übergang zwischen C- zu F-Dur, mit dem entsprechenden Voicing, sie einhält. Wenn der Tenor sich an dieselbe Regel hält, ist das zwar ebenfalls „gut“, aber etwas weniger wichtig; dem Befolgen dieser Regel gebe ich dann zB. nur 4 Punkte; etc. pp.

Ein Übergang von einem Voicing in das nächste beruht auf einem ganzen Satz von Regeln. Dabei kommt es darauf an, welche Regel wie gewichtet wird. Das macht hier nicht der Computer, sondern der User; in diesem Fall: der Programmierer. (Ich habe ganz erheblich Zeit investiert, um, mit dem Ohr, Gewichtungen für diese spezielle Fuzzy-Logic zu finden).

Zum Schluß – nachdem alle möglichen Voicings (Datensätze) eines F-Dur-Akkords, die einem C-Dur-Voicing folgen könnten – von der Fuzzy-Logic des Algorithmus „ausprobiert“ wurden, „gewinnt” das Voicing mit den meisten Punkten, und wird von Cubase angeboten. Easy as pie.



Zum Vierten: Spannend wird es dann, wenn Algorithmen von einem Algorithmus gesteuert werden.

Bis zu diesem Punkt habe ich Verfahren beschrieben, die Daten algorithmisch verändern. Man kann aber auch in Abläufe (Logiken) eingreifen – zB. in den Ablauf eines Programms; und zwar auch dann, während es läuft.

Für ein eher einfaches Beispiel komme ich noch einmal auf die Logik hinter den Übergängen von Voicings zurück. Die dort waltende „Fuzzy-Logic“ ist „hart“ kodiert. Man könnte das aber auch anders implementieren, nämlich als einen Algorithmus, der, nach Vorgaben der User, lernt.

Mögliche Übergänge von einem Akkord in einen anderen könnte man einem (menschlichen) „Lehrer“ anbieten, der, aufgrund seiner Erfahrung und seines Wissens, darüber entscheidet, ob die „Lösung“, die das Programm gerade anbietet, „richtig“ ist, oder vielleicht einfach nur (mehr oder weniger) „gut gefällt“. Das Programm führt Buch über jene Entscheidungen, und pflegt zB. eine Datenbank, in der die „Wertschätzung“ jenes „Experten“ festgehalten wird, um sie später weiter zu verwenden.

Ich hatte, bei dem Chordtrack-Feature, die Idee, nicht hart-kodierte Fuzzy-Logic zu verwenden, sondern ein Programm zu schreiben, mit dessen Hilfe der Algorithmus auf ein Feedback reagieren kann und die „Wahrscheinlichkeiten“ (für die „Güte“ von Übergängen zwischen Voicings) aus diesem Feedback zu generieren. Im konkreten Fall hätte ich viel Code für ein vergleichsweise triviales Problem zu schreiben – ich habe das dann bleiben lassen, und das „Fine-Tuning“ des Algorithmus „zu Fuß“ erledigt.

Für die Aufgabe, einem Computer die Erkennung von Sprachbefehlen oder die Interpretation von von Hand geschriebenem Text beizubringen, bleibt jedoch, meiner Einschätzung nach, nichts anderes übrig, als sich mit solcher Meta-Ebene zu beschäftigen.



Zum Fünften: Über die Theorie über Algorithmen finden sich ganze Bibliotheken; man kommt hier leicht vom Hundertsten ins Tausendste.

Es geht mir – hier – nicht um die Optimierungen, die man mit „gutem” Softwaredesign (Stichwort: objektorientierte Ansätze) oder „distributed Processing“ (Stichwort: Algorithmen, die über ein Netzwerk von Computern verteilt sind; MapReduce zB.) erreichen kann.

Dem Gebrauch von Computern zwecks Abschaffung der demokratisch verfaßten Gesellschaften durch die Geheimdienste rund um die Welt geht voraus eine überaus positiv konnotierte Hoffnung, was man mit Computeralgorithmen für die Erkenntnis der Welt, in der wir leben, erreichen könnte.

Der Begriff des „Neuronalen Netzwerks“ etwa ist/war ein Buzz-Word in der AI-Forschung, wo man versucht(e), „Knoten“ in einem Computer-Network so zu vernetzen, wie man sich das Feuern von Neuronen im menschlichen Hirn vorstellt(e). Die Hoffnung war, daß man irgendwann Maschinen bauen kann, die in ihrer Komplexität ähnlich verdrahtet sind wie das menschliche Gehirn – daß man, ab einem bestimmten Punkt, durch das Anhäufen von Komplexität (Algorithmen, die Algorithmen steuern, die wiederum von Algorithmen überwacht werden, etc.pp.) ein „Umschlagen“ von Maschinenlogik in Intelligenz erreicht.

Das Ziel hatte man lange im Auge – womöglich inspiriert von SciFi-Storries, die das schon vor langer Zeit thematisiert haben, und die jeder Nerd, der etwas auf sich hält (und womöglich heute als Programmierer sein Geld verdient), alle gelesen hat.

Allein: von diesem Ziel war man nie weiter entfernt als ausgerechnet heute: zu einem Zeitpunkt, wo die CPU-Power des Smartphones in der Hand von Jedermann jene der Computer in den Apollo-Raumschiffen auf dem Flug zum Mond Ende der 60er um einen Faktor überschreitet, von dem ich nicht die Nullen weiß (eine Billion? - mindestens).



Kommentieren [Drucken]