Masterarbeit HOCHSCHULE MÜNCHEN - ELIB-DLR

Die Seite wird erstellt Gustav Friedrich
 
WEITER LESEN
Masterarbeit HOCHSCHULE MÜNCHEN - ELIB-DLR
HOCHSCHULE MÜNCHEN

                 Masterarbeit

     Automatische Generierung von
Softwarebeschreibungen aus Source Code

                      Elias Porcio

                        31.08.2021

  Fachbereich:    Fakultät für Informatik und Mathematik
  Prüfer:         Prof. Dr. Alfred Nischwitz
  Zweitprüfer:    Prof. Dr. David Spieler
  Betreuer (DLR): Dr. Tobias Hecking
  Anmeldedatum: 01.03.2021
Masterarbeit HOCHSCHULE MÜNCHEN - ELIB-DLR
Inhalt

1 Einleitung                                                                                                          7
  1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                      7
  1.2 Ziel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                    7

2 Grundlagen                                                                                                           8
  2.1 Stand der Technik . . . . . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    8
  2.2 Neural code representation . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    9
      2.2.1 Embeddings . . . . . . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    9
      2.2.2 Sequence-to-Sequence Modelle          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    9
      2.2.3 code2vec . . . . . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   10
      2.2.4 code2seq . . . . . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   12
  2.3 Textähnlichkeit . . . . . . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   14
      2.3.1 BLEU . . . . . . . . . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   14
      2.3.2 ROUGE . . . . . . . . . . . .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   15
  2.4 Zentralitätsmetriken . . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   16
      2.4.1 Eigenvektor-Zentralität . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   16
      2.4.2 Katz-Zentralität . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   17
  2.5 Transformer Architekturen . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   18

3 Umsetzung                                                                                                           22
  3.1 Inlining-Ansatz . . . . . . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   24
      3.1.1 Datensatz . . . . . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   24
      3.1.2 Call-Graph Erstellung . . . .         .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   25
      3.1.3 Call-Graph Zentralität . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   27
      3.1.4 Clustering . . . . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   31
      3.1.5 Inlining . . . . . . . . . . . .      .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   32
      3.1.6 Parsing . . . . . . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   33
      3.1.7 Erstellung der Zielsequenzen .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   35
  3.2 Summary-Ansatz . . . . . . . . . . .        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   37
      3.2.1 Datensatz . . . . . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   37
      3.2.2 Textzusammenfassung . . . .           .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   38
  3.3 Erkennung von Lizenzen und Autoren          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   40
  3.4 Evaluation . . . . . . . . . . . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   41

                                          1
Masterarbeit HOCHSCHULE MÜNCHEN - ELIB-DLR
4 Ergebnisse                                                                                                                 43
  4.1 Inlining-Ansatz . . . . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   43
  4.2 Summary-Ansatz . . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   46
      4.2.1 Docstring Generierung        .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   46
      4.2.2 Textzusammenfassung          .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   47
  4.3 Diskussion . . . . . . . . . . .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   50

5 Schlussfolgerung / Ausblick                                                                                                51

                                         2
Masterarbeit HOCHSCHULE MÜNCHEN - ELIB-DLR
Abbildungen

 2.1    Beispiel für die Übersetzung eines englischen Satzes mithilfe eines
        Sequence-to-Sequence Modells. [1] . . . . . . . . . . . . . . . . . . .       10
 2.2    Beispiel für einen Path-Kontext, der aus dem Ausdruck “x = 7;”
        entstanden ist. [2] . . . . . . . . . . . . . . . . . . . . . . . . . . . .   10
 2.3    Übersicht über die Datenverarbeitung bei code2vec. . . . . . . . . .          11
 2.4    Übersicht über die Datenverarbeitung bei code2seq. . . . . . . . . .          12
 2.5    Beispiel zur Generierung eines Satzes mit code2seq. [3] . . . . . . .         13
 2.6    Formel zur Berechnung von BLEUs Strafe für zu kurze Übersetzun-
        gen. BP: Brevity Penalty; c: Länge der zu bewertenden Übersetzung;
        r: Länge der Referenzübersetzung [4] . . . . . . . . . . . . . . . . .        14
 2.7    Formel zur Berechnung des BLEU-Scores mit Sequenzlänge 1 und
        einer Referenzübersetzung. BP: brevity penalty; Text: zu bewertende
        Übersetzung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     15
 2.8    Formel zur Berechnung des ROUGE-1-Scores mit einer einzigen
        Referenzzusammenfassung. . . . . . . . . . . . . . . . . . . . . . . .        15
 2.9    Intuitive Formel zur Berechnung der Eigenvektor-Zentralität. x:
        Eigenvektor-Zentralität; A: Adjazenzmatrix des Graphen . . . . . .            16
 2.10   Definition eines Eigenvektors bzw. der Eigenvektor-Zentralität. x:
        Eigenvektor-Zentralität; A: Adjazenzmatrix des Graphen; λ: Eigenwert          16
 2.11   Formel zur Berechnung der Katz-Zentralität. xi : Katz-Zentralität von
        Knoten i; α: Gewichtungsfaktor; A: Adjazenzmatrix des Graphen; n:
        Anzahl Knoten . . . . . . . . . . . . . . . . . . . . . . . . . . . . .       17
 2.12   Self-Attention-Matrix eines Transformers über einen Inputsatz. [5] .          18
 2.13   Transformer Architektur in der Übersicht. [6] . . . . . . . . . . . . .       20
 2.14   Attention-Matrix zwischen Encoder und Decoder eines Transformers
        bei einer Übersetzung von Englisch nach Spanisch. [7] . . . . . . . .         21

 3.1 Übersicht über den Inlining-Ansatz. . . . . . . . . . . . . . . . . . .          22
 3.2 Übersicht über den Summary-Ansatz. . . . . . . . . . . . . . . . . .             23
 3.3 Ausschnitt aus dem Call-Graph des Projekts “TwitterTap”. Die Größe
     der Knoten entspricht ihrer Zentralität. Blätter sind ausgegraut.
     Core-Funktionen sind rot. . . . . . . . . . . . . . . . . . . . . . . .          26
 3.4 Java AST für den Ausdruck “x = 1” (links), sowie der von astminer
     verwendete Python AST für denselben Ausdruck (rechts). . . . . . .               34

                                          3
Tabellen

 3.1    Einige Pyan Funktionen, sortiert nach ihrer Eigenvektor-Zentralität
        im ungerichteten Call-Graph. Dunkelgrün: sehr wichtig; grün: wichtig;
        weiß: neutral; rot: unwichtig . . . . . . . . . . . . . . . . . . . . . .    27
 3.2    Einige Pyan Funktionen, sortiert nach ihrer Katz-Zentralität im
        transponierten Call-Graph. Dunkelgrün: sehr wichtig; grün: wichtig;
        weiß: neutral; rot: unwichtig . . . . . . . . . . . . . . . . . . . . . .    28
 3.3    Verschiedenen Zentralitätsmetriken sowie die Scores die sie durch
        den Bewertungsalgorithmus erhalten. . . . . . . . . . . . . . . . . .        30
 3.4    Die ersten zwei Sätze der “What”-Sektion der Readme-Dateien einiger
        GitHub Repositories, sowie deren About-Texte. . . . . . . . . . . .          36
 3.5    Vergleich von PEGASUS’ Vokabular mit dem Output-Vokabular des
        Docstring-Generators bzw. dem Output-Vokabular von code2seq aus
        dem Inlining-Ansatz. . . . . . . . . . . . . . . . . . . . . . . . . . .     39

 4.1    Hyperparameter und Evaluationsergebnisse verschiedener Trainings-
        durchläufe des Inlining-Ansatzes. . . . . . . . . . . . . . . . . . . .      43
 4.2    Originale GitHub About-Texte einiger Repositories, sowie die Be-
        schreibungen, die vom code2seq Modell generiert wurden. . . . . . .          44
 4.3    Umfrageergebnisse des Inlining-Ansatzes. . . . . . . . . . . . . . . .       44
 4.4    Statistiken über die Generierung von Readme-Dateien für 20 ausge-
        wählte Projekte mit gutem Ergebnis, sowie für den ganzen Validie-
        rungsdatensatz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    45
 4.5    Hyperparameter und Evaluationsergebnisse des Docstring Trainings.            46
 4.6    Einige vom code2seq Modell generierte Docstrings, sowie die zuge-
        hörigen Originale. . . . . . . . . . . . . . . . . . . . . . . . . . . . .   47
 4.7    Hyperparameter und Evaluationsergebnisse verschiedener Trainings-
        durchläufe des Textzusammenfassers für den Summary-Ansatz. . . .             48
 4.8    Einige Beispiele für generierte Texte des Textzusammenfassers, sowie
        die zugehörigen Zielsequenzen. . . . . . . . . . . . . . . . . . . . . .     48
 4.9    Umfrageergebnisse des Summary-Ansatzes. . . . . . . . . . . . . . .          49
 4.10   Statistiken über die Generierung von Readme-Dateien für 20 ausge-
        wählte Projekte mit gutem Ergebnis, sowie für den ganzen Validie-
        rungsdatensatz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    49

                                         4
Abkürzungen

AST Abstract syntax tree
BERT Bidirectional Encoder Representations from Transformers
BLEU Bilingual evaluation understudy
DLR Deutsches Zentrum für Luft- und Raumfahrt
LSTM Long short-term memory
MST Minimum spanning tree
PEGASUS Pre-training with Extracted Gap-sentences for Abstractive
    Summarization
RNN Recurrent neural network
ROUGE Recall-Oriented Understudy for Gisting Evaluation

                                    5
Zusammenfassung

In dieser Arbeit geht es darum, mithilfe von neuronalen Netzen Readme-Dateien
automatisch aus dem Sourcecode von Projekten zu erstellen. Die Readme-Dateien
sollen den Zweck des Projekts beschreiben und als Basis für Sourcecode-Retrieval
Systeme geeignet sein. Das vereinfacht den Umgang mit undokumentierter Software,
insbesondere wenn man mit großen Mengen davon konfrontiert ist.
Zur Umsetzung werden zwei verschiedenen Ansätze verfolgt. In beiden werden
zunächst die wichtigsten Funktionen des Projekts bestimmt, indem der Call-Graph
des Projekts erstellt und die zentralsten Knoten in diesem ermittelt werden. Im sog.
Inlining-Ansatz werden die wichtigsten Funktionen ineinander geinlined, sodass
eine einzige Funktion entsteht, die das gesamte Projekt repräsentiert. Diese dient
anschließend als Input für code2seq - einem neuronalen Netz, das Beschreibungen
für einzelne Funktionen erstellen kann. Der so entstandene Text wird dann als
Readme verwendet.
Im sog. Summary-Ansatz generiert code2seq für die wichtigsten Funktionen zuerst
einen Docstring. Die Docstrings werden konkateniert und mithilfe von PEGASUS
zu einem Readme zusammengefasst. PEGASUS ist ein neuronales Netz, das auf
Textzusammenfassung trainiert ist. In beiden Ansätzen wird ein Fine-Tuning der
Netze auf ihre neue Aufgabe durchgeführt.
Die Ergebnisse werden bewertet, indem sie mit den originalen Readme-Dateien
verglichen werden. Dazu kommen die Metriken ROUGE und BLEU, sowie ein
BERT-Modell zur Bewertung semantischer Ähnlichkeit zum Einsatz. Zum Schluss
wird die Qualität auch durch eine Umfrage unter Softwareexperten bewertet.
Das Generieren von sinnvollen Readme-Dateien gelingt mit beiden Ansätzen in
seltenen Fällen. Bei den meisten Projekten fehlt den generierten Beschreibun-
gen ein inhaltlicher Bezug zum tatsächlichen Projekt. Deshalb wäre eine weitere
Verbesserung der Ansätze nötig, bevor sie für die genannten Zwecke einsetzbar
sind.

                                         6
1. Einleitung

1.1     Motivation
Readme-Dateien sind ein wichtiges Element der Dokumentation von Softwareprojek-
ten. Sie geben einen Überblick darüber, welchen Zweck die Software erfüllt und wie
sie bedient wird. Außerdem spielen sie eine wichtige Rolle in Sourcecode-Retrieval
Systemen. Entwickler, die nach bestimmten Funktionalitäten suchen, formulieren
ihre Anfragen in natürlicher Sprache. Um gute Suchergebnisse zu erzielen, sollte der
Software deshalb immer eine Beschreibung in natürlicher Sprache beiliegen.
Leider wird die Dokumentation von Software oft vernachlässigt, sodass man mit
Projekten ohne Readme-Datei konfrontiert wird. In diesem Fall muss man mühsam
den Sourcecode lesen, um ein Verständnis für das Projekt zu erhalten. Auch das
Sourcecode-Retrieval könnte nur auf Basis bestimmter Keywords im Sourcecode
funktionieren.

1.2     Ziel
Ziel dieser Arbeit ist es zu untersuchen, ob es möglich ist, Readme-Dateien automa-
tisch zu generieren. Als Basis hierfür soll nur der Sourcecode des Projekts verwendet
werden. Dazu werden verschiedene neuronale Netze eingesetzt. Die Readme-Dateien
sollen geeignet sein, um Entwicklern des Projekts eine Basis zum Verfassen ei-
nes ausführlichen Readmes zu liefern, um fremde Entwickler über den Zweck des
Projekts zu informieren und um Sourcecode-Retrieval zu ermöglichen.
Diese Arbeit ist eine Kooperation mit dem Deutschen Zentrum für Luft- und
Raumfahrt (DLR). Auf Wunsch des DLR soll die Generierung der Readme-Dateien
anhand von Python Projekten demonstriert werden.

                                         7
2. Grundlagen

2.1     Stand der Technik
Es existieren bereits mehrere Projekte, deren Ziel es ist, Readme-Dateien zu
generieren. Nennenswerte Beispiele sind hier readme-md-generator [8] und generate-
readme [9]. Beide Projekte erstellen Readme-Dateien für Javascript Projekte und
arbeiten auf dieselbe Weise. Der Nutzer wird in der Kommandozeile aufgefordert,
die relevanten Daten einzugeben, welche dann in ein festes Template eingefügt
werden. Soweit möglich werden dabei automatisch Vorschläge gemacht, indem
auch Teile des Projekts analysiert werden. Vor allem die “package.json”-Datei
enthält viele relevante Informationen über das Projekt, wie beispielsweise den
Projektnamen, Version, Autoren und eine kurze Beschreibung. Eine inhaltliche
Analyse des Sourcecodes findet jedoch nicht statt.
Für das Generieren von Softwarebeschreibungen aus Sourcecode existieren bereits
Ansätze, die einzelne Funktionen beschreiben. Ein Beispiel hierfür ist code2seq
[3], welches viele Teile aus dem Abstract syntax tree (AST) einer Java oder C#
Funktion sampelt und anschließend mit einem Long short-term memory (LSTM)
weiterverarbeitet. code2seq wird in Kapitel 2.2.4 ausführlich vorgestellt. Daneben
gibt es auch DeepCom [10], das auf eine ähnliche Weise funktioniert. Statt ein-
zelne Teile des ASTs zu verarbeiten wird dieser aber serialisiert und als ganzes
verarbeitet.
Die oben beschriebenen Projekte unterscheiden sich von dieser Arbeit, da keins
davon dazu in der Lage ist, eine Beschreibung für ein ganzes Projekt durch Analyse
des Sourcecodes automatisch zu generieren. Ein existierendes Projekt, das dazu in
der Lage ist, ist dem Verfasser nicht bekannt.

                                        8
2.2     Neural code representation
2.2.1    Embeddings
Machine Learning Modelle verarbeiten Daten in Form von ein- oder mehrdimen-
sionalen Vektoren. Andere Datenformen wie Sourcecode oder natürliche Sprache
in Textform müssen daher zuerst in eine Vektorrepräsentation überführt werden.
Diese Repräsentation in Vektorform nennt man Embedding.
Bei Text wird dazu in der Regel eine Embedding-Matrix verwendet. Jede Zeile der
Embedding-Matrix repräsentiert ein zu übersetzendes Wort. Die transponierte Zeile
ist dann der zum Wort gehörige Vektor. Um die Größe der Matrix in Grenzen zu
halten, enthält sie meist nicht alle existierenden Wörter, sondern beschränkt sich
auf ein Vokabular mit sinnvoller Größe. Für alle nicht erfassten Wörter enthält das
Vokabular ein Spezielles “UNK” (“Unknown”) Symbol. Die Werte der Matrix werden
durch einen Trainingsprozess iterativ angepasst. Embedding-Matrizen können
auch zur Repräsentation von Code genutzt werden, indem man statt Wörtern die
einzelnen Token der Programmiersprache übersetzen lässt.

2.2.2    Sequence-to-Sequence Modelle
Mithilfe von Embedding-Matrizen lassen sich jedoch nur einzelne Token übersetzen.
Möchte man die Bedeutung eines ganzen Satzes oder einer Funktion erfassen,
sind komplexere Modelle notwendig, da Embedding-Matrizen die große Menge
an möglichen Token-Kombinationen nicht mehr abbilden können. Dazu werden
häufig Sequence-to-Sequence Modelle [11] verwendet, die eine Sequenz von Token
in eine andere Sequenz überführt, beispielsweise zur Übersetzung von natürlichen
Sprachen.
Die Token werden hierfür mithilfe einer Embedding-Matrix in Vektoren umge-
wandelt und nacheinander von einem Encoder-LSTM, eingelesen. LSTMs sind
neuronale Netze, die Sequenzen von Vektoren verarbeiten, wobei sie mithilfe eines
internen Zustandsvektors Informationen speichern und so einen Bezug zwischen
Elementen an verschiedenen Positionen in der Inputsequenz herstellen. Die Ausga-
ben des Encoder-LSTMs werden verworfen, nur der Zustandsvektor am Ende der
Sequenz wird behalten. Er kann als Embedding für die ganze Sequenz betrachtet
werden und dient als Anfangszustand für ein weiteres LSTM, dem Decoder, dessen
Output schließlich die Zielsequenz ist. Neben dem Anfangszustand liest der Decoder
das Wort ein, das er je zuletzt produziert hat. Abbildung 2.1 zeigt diesen Prozess
an einem Beispiel.

                                        9
Abb. 2.1: Beispiel für die Übersetzung eines englischen Satzes mithilfe eines
Sequence-to-Sequence Modells. [1]

2.2.3    code2vec
code2vec [2] ist eine Technik, die ein Embedding für ganze Sourcecode-Funktionen
erstellt. Als naiven Ansatz für dieses Problem könnte man eine Programmiersprache
genau so wie eine natürliche Sprache behandeln und die Token der Programmier-
sprache als Wörter auffassen. Der Nachteil dieses Ansatzes ist jedoch, dass das
Modell dabei die Syntax der Programmiersprache erlernen muss. code2vec ver-
ringert diesen Aufwand, indem es den Code zunächst von einem Parser in einen
Abstract-Syntax-Tree (AST) umwandeln lässt und diesen weiterverarbeitet. Wie in
Abbildung 2.2 zu sehen ist, enthält der AST beispielsweise bereits die Information,
dass es sich bei “x = 7;” um eine Zuweisung handelt.
Um den AST zu verarbeiten, werden zunächst zwei Blätter ausgewählt und der Pfad
zwischen ihnen ermittelt. Blätter und Pfad bilden ein 3-Tupel, den sogenannten
Path-Kontext. Abbildung 2.2 zeigt ein Beispiel hierzu.

Abb. 2.2: Beispiel für einen Path-Kontext, der aus dem Ausdruck “x = 7;” entstanden
ist. [2]

Mit zufällig ausgewählten Blättern werden bis zu 200 dieser Path-Kontexte erstellt,
die gemeinsam eine ausreichende Repräsentation der ganzen Funktion darstellen
sollen. Eine Besonderheit ist hier, dass die Funktion nicht sequenziell von vorne nach
hinten verarbeitet wird. Stattdessen spielt die Reihenfolge der zufällig erstellten
Path-Kontexte keine Rolle.

                                         10
Die Blätter jedes Path-Kontexts werden nun mit einer Embedding-Matrix in
Vektoren umgewandelt. Auch für Pfade gibt es eine eigene Embedding-Matrix.
Wegen der großen Anzahl an möglichen Pfaden ist dieses Vorgehen ungewöhnlich.
Um die Größe der Path-Embedding-Matrix in Grenzen zu halten, darf der Pfad
eine bestimmte Länge an Token nicht überschreiten.
Anschließend werden die drei Vektoren des Path-Kontexts konkateniert und einer
Fully-connected-Layer übergeben, deren Output als Combined-Context-Vector
bezeichnet wird. Eine Fully-connected-Layer ist eine Schicht in einem neuronalen
Netz, in der jedes Neuron eine direkte Verbindung zu jedem Neuron der vorherigen
Schicht besitzt. Die bis zu 200 Combined-Context-Vectors sollen danach zu einem
einzigen Vektor vereint werden. Da nicht alle dieser Kontexte gleichermaßen wichtig
sind, sollten sie vom Netzwerk unterschiedlich stark berücksichtigt werden. Hierfür
dient ein Attention-Mechanismus.
Attention-Mechanismen finden in verschiedenen neuronalen Netzen Anwendung,
funktionieren jedoch nicht immer auf die gleiche Weise. code2vec besitzt einen
globalen Attention-Vektor, dessen Werte als Teil des Trainingsprozesses angepasst
werden. Um zu bestimmen welcher der Kontexte besonders wichtig ist, wird das
Skalarprodukt der Combined-Context-Vectors mit dem Attention-Vektor berechnet.
Das Skalarprodukt wird nun als Gewicht zur Berechnung eines gewichteten Mit-
telwerts aller Combined-Context-Vectors verwendet. Dieser gewichtete Mittelwert
ist schließlich das Embedding der verarbeiteten Funktion. Abbildung 2.3 zeigt den
ganzen Prozess noch einmal in der Übersicht.
Über das bloße Embedding hinaus kann code2vec einer Funktion auch Tags zuord-
nen, die ihren Nutzen beschreiben. Hierzu trainiert man eine Embedding-Matrix
mithilfe der Funktionsnamen und verwendet diese umgekehrt.

          Abb. 2.3: Übersicht über die Datenverarbeitung bei code2vec.

                                        11
2.2.4    code2seq
code2seq [3] ist eine Technik, die auf code2vec beruht und die natürliche Sprache
aus Sourcecode generiert. Der generierte Text beschreibt die Funktion und kann
beispielsweise zur Dokumentation genutzt werden.
Der Ablauf der Datenverarbeitung ähnelt stark dem von code2vec, jedoch werden
die Pfade mit einem bidirektionalem LSTM encoded, statt mit einer Embedding-
Matrix. Genauso wird die Output-Sequenz nicht mit einer Matrix, sondern durch
ein bidirektionales LSTM erzeugt.
Außerdem kommt bei code2seq Subtokenization zum Einsatz. Das bedeutet, dass
Token wie “toString” anhand der Camel-Case Schreibweise in die Subtoken “to” und
“String” aufgeteilt werden. Die Subtoken werden mithilfe der Embedding-Matrix in
Vektoren übersetzt und anschließend addiert.

         Abb. 2.4: Übersicht über die Datenverarbeitung bei code2seq.

Abbildung 2.5 zeigt ein Beispiel zur Generierung eines Satzes mit code2seq. Die
generierten Wörter sind im unteren Satz eingefärbt. Im Code sind in gleicher
Farbe die Path-Kontexte eingezeichnet, die bei der Generierung des zugehörigen
Wortes laut Attention-Mechanismus die höchste Relevanz hatten. Die AST-Pfade
der Path-Kontexte gehen aus diesem Bild nicht direkt hervor, die eingekreisten
Blätter scheinen aber gut zum jeweils generierten Wort zu passen.

                                       12
Abb. 2.5: Beispiel zur Generierung eines Satzes mit code2seq. [3]

                               13
2.3     Textähnlichkeit
Im Bereich Natural-Language-Processing werden häufig Metriken benötigt, die
die Ähnlichkeit zweier Texte bewerten. Sie kommen beispielsweise bei Problemen
wie Dokumentenclustering oder der Erstellung von Textzusammenfassungen zum
Einsatz [12]. In dieser Arbeit werden sie in Kapitel 3.4 verwendet, um die Qualität
der generierten Readme-Dateien zu bewerten. Im Folgenden werden zwei solcher
Metriken vorgestellt.

2.3.1    BLEU
BLEU [4] ist eine Metrik, die die Ähnlichkeit einer computergenerierten Überset-
zung mit einer oder mehreren korrekten Referenzübersetzungen vergleicht, um die
Qualität der generierten Übersetzung zu bewerten. Die grundlegende Idee besteht
darin, die Übersetzung wie einen binären Klassifikator zu bewerten, wobei ein Wort
aus der Übersetzung als richtig positiv gilt, wenn es auch in der Referenzübersetzung
enthalten ist und als falsch positiv, wenn nicht. Anschließend wird die Accuracy
der Übersetzung berechnet.
Um ein gutes Maß für die Qualität der Übersetzung zu erhalten, muss die Formel zur
Berechnung der Accuracy jedoch noch erweitert werden. Zu kurze Übersetzungen
müssen bestraft werden, da sonst ein einziges korrektes Wort bereits als perfekte
Übersetzung gewertet wird. Hierzu dient die Formel in Abbildung 2.6.

Abb. 2.6: Formel zur Berechnung von BLEUs Strafe für zu kurze Übersetzungen.
BP: Brevity Penalty; c: Länge der zu bewertenden Übersetzung; r: Länge der
Referenzübersetzung [4]

Außerdem darf ein korrektes Wort, das Mehrfach in der Übersetzung vorkommt, nur
so häufig als korrekt gewertet werden, wie es in der Referenzübersetzung vorkommt.
Dadurch wird verhindert, dass eine Übersetzung aus einem einzigen korrekten Wort,
welches sich ständig wiederholt, gut bewertet wird.
Schließlich ist es auch wichtig, dass sich die Wörter in der Übersetzung in der rich-
tigen Reihenfolge befinden. Um das sicherzustellen, berechnet BLEU die Accuracy
nicht nur mit einzelnen Wörtern, sondern auch mit kurzen Wortsequenzen, die
nur dann als korrekt gelten, wenn die gesamte Sequenz übereinstimmt. Genauer
berechnet BLEU die Accuracy N-mal für Sequenzen der Länge 1 bis N und bildet
zum Schluss das geometrische Mittel. Die Formel zur Berechnung des BLEU-Scores
mit Sequenzlänge 1 ist in Abbildung 2.7 dargestellt.

                                         14
P
                                   Satz∈T ext AnzahlKorrekteW örter(Satz)
        BLEU1 (T ext) = BP ∗          P
                                          Satz∈T ext AnzahlW örter(Satz)

Abb. 2.7: Formel zur Berechnung des BLEU-Scores mit Sequenzlänge 1 und einer
Referenzübersetzung. BP: brevity penalty; Text: zu bewertende Übersetzung

2.3.2    ROUGE
ROUGE [13] ist ein Satz von Metriken, der entwickelt wurde, um die Qualität
von Textzusammenfassungen zu bewerten. Ähnlich wie bei BLEU werden dazu die
Wörter aus der zu bewertenden sowie einer oder mehrerer Referenzzusammenfas-
sungen verglichen. Statt auf Accuracy beruhen die meisten ROUGE Metriken aber
auf dem Recall und einige weitere auf der längsten übereinstimmenden Sequenz.
ROUGE-1 beispielsweise berechnet den Recall mit einzelnen Wörter, wohingegen
ROUGE-S ihn mit Skip-Grams berechnet. Skip-Grams sind Paare aus Wörtern die
zwar in der richtigen Reihenfolge stehen müssen, die aber nicht direkt aufeinander
folgen müssen.
Bei der Berechnung von ROUGE wird die Länge der Zusammenfassung nicht
betrachtet. Es muss also anderweitig sichergestellt werden, dass die Zusammen-
fassung nicht zu lang ist. Im Vergleich zu BLEU enftällt bei ROUGE auch die
Unterteilung in Sätze, da Zusammenfassungen diese frei festlegen können, während
sie bei Übersetzungen durch den Originaltext relativ eindeutig vorgegeben ist.

                                  AnzahlKorrekteW örter
                    ROU GE1 =
                                 AnzahlW örterInRef erenz

Abb. 2.8: Formel zur Berechnung des ROUGE-1-Scores mit einer einzigen Referenz-
zusammenfassung.

                                           15
2.4     Zentralitätsmetriken
Zentralitätsmetriken messen wie zentral sich ein Knoten in einem Graph befindet.
In Kapitel 3.1.3 werden Zentralitätsmetriken in Verbindung mit dem Call-Graph
genutzt, um die wichtigsten Funktionen eines Projekts zu bestimmen. Folgendes
Kapitel stellt zwei Zentralitätsmetriken vor.

2.4.1    Eigenvektor-Zentralität
Nach der Eigenvektor-Zentralität ist ein Knoten sehr zentral, wenn er viele Nach-
barn hat und diese Nachbarn ihrerseits sehr zentral sind. Intuitiv lässt sich diese
Idee mit der rekursiven Formel in Abbildung 2.9 beschreiben. x ist ein Vektor, der
aus den Eigenvektor-Zentralitäten der einzelnen Netzwerkknoten besteht. A ist
die Adjazenzmatrix, die die Kanten des Graphen durch Einsen an den Positionen,
die zu den entsprechenden Knoten gehört, darstellt. Durch die Matrixmultiplika-
tion Ax werden für jeden Knoten die Eigenvektor-Zentralitäten seiner Nachbarn
aufsummiert.

                                   xt+1 = Axt

Abb. 2.9: Intuitive Formel zur Berechnung der Eigenvektor-Zentralität. x:
Eigenvektor-Zentralität; A: Adjazenzmatrix des Graphen

Damit x nach genügend Iterationen konvergiert und nicht divergiert, muss noch
ein konstanter Faktor λ hinzugefügt werden. Der Bezug zu Eigenvektoren wird
offensichtlich, wenn x konvergiert, sodass näherungsweise gilt xt+1 = xt = x. Damit
ergibt sich die Gleichung in Abbildung 2.10, welche der Definition eines Eigenvektors
entspricht.

                                    λx = Ax

Abb. 2.10: Definition eines Eigenvektors bzw. der Eigenvektor-Zentralität. x:
Eigenvektor-Zentralität; A: Adjazenzmatrix des Graphen; λ: Eigenwert

Darüber hinaus soll die Eigenvektor-Zentralität nicht negativ sein. Nach dem Satz
von Perron-Frobenius ist diese Bedingung für den betragsmäßig größten Eigenwert
erfüllt.

                                        16
Für gerichtete Graphen funktioniert die Eigenvektor-Zentralität nur eingeschränkt.
Bei einem gerichteten azyklischen Graph sind beispielsweise alle Eigenvektor-
Zentralitäten 0 und sind damit kein Maß für die Zentralität der Knoten mehr.

2.4.2    Katz-Zentralität
Die Katz-Zentralität ähnelt der Eigenvektor-Zentralität, funktioniert aber auch
mit gerichteten Graphen. Sie zählt die Anzahl der Pfade, die zu einem bestimmten
Knoten führen, wobei längere Pfade niedriger gewichtet werden als kürzere. Die
Formel für die Katz-Zentralität ist in Abbildung 2.11 zu sehen.

                                    n
                                  ∞ X
                                  X
                           xi =             (αk ∗ Ak )i,j
                                  k≥1 j=1

Abb. 2.11: Formel zur Berechnung der Katz-Zentralität. xi : Katz-Zentralität von
Knoten i; α: Gewichtungsfaktor; A: Adjazenzmatrix des Graphen; n: Anzahl Knoten

k ist die Länge der Pfade, die betrachtet werden. Der Gewichtungsfaktor α liegt
zwischen 0 und 1 und wird durch die Potenzierung mit k immer kleiner, je länger
die betrachteten Pfade werden. Die mit k potenzierte Adjazenzmatrix A beschreibt
wie viele Pfade der entsprechenden Länge es von einem Knoten zu einem anderen
gibt.

                                       17
2.5     Transformer Architekturen
Ein Transformer [6] ist eine sequence-to-sequence Architektur, die für eine beson-
ders gute Parallelisierbarkeit sorgt. Sequence-to-sequence Architekturen benutzen
normalerweise rekurrente neuronale Netze (RNNs), welche neben dem aktuellen In-
putvektor der Sequenz auch den vorangegangenen Outputvektor verarbeiten, sodass
eine parallele Berechnung unmöglich wird. Bei Transformern werden stattdessen
Fully-connected-Layer mit einem komplexen Attention-Mechanismus verbunden.
Transformer gehören besonders bei der Verarbeitung natürlicher Sprache zum
Stand der Technik und bilden die Grundlagen für bekannte Techniken wie BERT
[14].
LSTMs eignen sich normalerweise deshalb gut zur Verarbeitung von Sequenzen, weil
sie durch ihren Zustandsvektor einen Bezug zwischen verschiedenen Elementen der
Inputsequenz herstellen können. Transformer nutzen stattdessen Scaled Dot-Product
Attention. Dieser Attention-Mechanismus bestimmt nicht (nur) die allgemeine
Wichtigkeit der Inputelemente, sondern gibt mithilfe einer Matrix an wie stark der
Bezug eines Elements zu jedem anderen Element ist. Dazu wird ein Inputelement
von drei unterschiedlichen Fully-connected-Layern in je einen Key-, Value- und
Query-Vektor umgewandelt. Der Query-Vektor eines Elements gibt an, welche
Eigenschaften ein anderes Element haben muss, um einen Bezug zu diesem Element
zu haben. Der Key-Vektor encoded eben diese Eigenschaften, die das Element
besitzt. Alle Query- und Key-Vektoren werden in je einer Matrix zusammengefasst
und diese Matrizen miteinander multipliziert. Durch diese Multiplikation von Query-
und Key-Vektoren unterschiedlicher Elemente entstehen hohe Werte also dann,
wenn sich beide Vektoren ähneln. Abbildung 2.12 zeigt ein Beispiel für die so
entstandene Matrix. Erwartungsgemäß zeigen die hohen Werte auf der Diagonalen,
dass jedes Wort einen starken Bezug zu sich selbst hat.

  Abb. 2.12: Self-Attention-Matrix eines Transformers über einen Inputsatz. [5]

                                        18
Der Value-Vektor beinhaltet schließlich die Daten, die Weiterverarbeitet werden
sollen. Eine Matrix aus Value-Vektoren wird mit der soeben berechneten Matrix
multipliziert, sodass jeder Value-Vektor auch Informationen von anderen Wörtern
mit hohem Bezug erhält.
Tatsächlich findet dieser Prozess nicht nur einmal statt, sondern mehrmals parallel
mit unterschiedlichen Fully-connected-Layern. Dieses Vorgehen ähnelt Convolutional-
Layern, die mehrere Filterkerne parallel anwenden und nennt sich Multi-Head
Attention.
Auch Transformer besitzen einen Encoder und einen Decoder. Jede Schicht des
Encoders berechnet einen Vektor, der Input für die entsprechende Schicht des
Decoders ist. Der Decoder erhält als Input, neben dem Output des Encoders, das
je zuletzt generierte Element der Sequenz. Damit ist der Decoder bei der Inferenz
nicht parallelisierbar, sondern nur während des Trainings, wo die gewünschte
Output-Sequenz als Input für den Decoder dienen kann.
Abbildung 2.13 zeigt eine Übersicht über die Architektur von Transformer-Netzen.
Die grau eingerahmten Elemente stellen je eine Schicht des Encoders (links) bzw.
Decoders (rechts) dar und wiederholen sich mehrfach. Das “Positional Encoding”
nutzt Sinusfunktionen, um die Vektoren mit Informationen über die Positionen des
entsprechenden Wortes in der Sequenz anzureichern.

                                       19
Abb. 2.13: Transformer Architektur in der Übersicht. [6]

                          20
Wie die Abbildung zeigt, wird der Attention-Mechanismus auf den Input jeder
Schicht des Encoders und Decoders angewendet. Beim Decoder gibt es zwei Beson-
derheiten: bei der Masked Multi-Head Attention wird eine Maske angewendet, um
zu verhindern, dass der Transformer auf nachfolgende Wörter zugreift. Diese sind
nur durch die Label der Trainingsdaten bekannt und werden bei der Inferenz fehlen.
Im mittleren Teil der Decoder Schicht bekommt die Unterteilung in Query- und
Key-Vektoren eine besondere Relevanz, da hier die Query-Vektoren aus dem Deco-
der und die Key-Vektoren des Encoders verwendet werden. Abbildung 2.14 zeigt
eine Attention-Matrix wie in Abbildung 2.12 bei einer Übersetzung von Englisch in
Spanisch. Hierbei werden die englischen Wörter aus dem Encoder, den spanischen
Wörtern im Decoder zugeordnet.

Abb. 2.14: Attention-Matrix zwischen Encoder und Decoder eines Transformers
bei einer Übersetzung von Englisch nach Spanisch. [7]

                                       21
3. Umsetzung

Um Readme-Dateien zu generieren werden in dieser Arbeit zwei Ansätze verfolgt.
Der erste wird als Inlining-Ansatz bezeichnet. Er sieht vor, das zunächst die
wichtigsten Funktionen eines Projekts bestimmt werden. Dem Call-Graph des
Projekts entsprechend, werden diese Funktionen dann ineinander geinlined, sodass
eine lange Funktion entsteht, die das ganze Projekt repräsentieren soll. Anschließend
wird ein code2seq Modell darauf trainiert aus dieser Funktion ein Readme zu
generieren. Das liegt nahe an seiner originalen Aufgabe des Code-Captioning.
Der zweite Ansatz wird Summary-Ansatz genannt. Auch hier werden zunächst die
wichtigsten Funktionen eines Projekts bestimmt. Danach wird mithilfe von code2seq
je ein Docstring erstellt, der die einzelnen Funktionen beschreibt. Die Docstrings
werden anschließend zu einem Text konkateniert. Das neuronale Netz PEGASUS,
welches normalerweise Textzusammenfassungen erstellt, wird dann darauf trainiert
aus den konkatenierten Docstrings eine Readme-Datei zu erstellen.
Die Abbildungen 3.1 und 3.2 zeigen beide Ansätze im Überblick. In diesem Kapitel
werden die Ansätze ausführlich vorgestellt.

                  Abb. 3.1: Übersicht über den Inlining-Ansatz.

                                         22
Abb. 3.2: Übersicht über den Summary-Ansatz.

                    23
3.1     Inlining-Ansatz
3.1.1    Datensatz
Als Basis für den Datensatz des Inlining-Ansatzes wird GHTorrent [15] verwendet.
GHTorrent sammelt seit 2013 umfangreiche Metadaten zu GitHub Repositories
und bietet einen entsprechenden Datensatz zum Download an. Aus diesem Daten-
satz werden die Links zu Repositories, die überwiegend Python Code beinhalten
extrahiert und die Repositories anschließend von GitHub heruntergeladen.
Kalliamvakou et. al [16] haben einige Gefahren bei der Verwendung des GHTorrent
Datensatzes bzw. von GitHub als Datenquelle allgemein identifiziert. Da diese
Arbeit nicht an der Analyse von Metadaten interessiert ist, sind die meisten dieser
Gefahren hier unproblematisch. Ein relevanter Punkt ist jedoch, dass nicht jedes
Repository auch ein Projekt ist. Laut den Autoren sieht ein beliebter Workflow vor,
dass Entwickler ein Repository forken, den Code bearbeiten und danach wieder
einen Pull-Request stellen. Dies könnte ein Problem darstellen, da hierdurch viele,
beinahe identische Repositories heruntergeladen werden. Diese zusätzlichen Kopien
bieten keinen Mehrwert, sondern erhöhen den Rechenaufwand bei der Verarbeitung
des Datensatzes. Um dieses Problem zu beheben, werden aus dem Datensatz alle
Projekte gefiltert, die ein Fork eines anderen Projektes sind.
Eine weiteres Problem das die Autoren erwähnen ist, dass nur ca. 63% der Repo-
sitories tatsächlich für Softwareentwicklung genutzt werden. Die anderen Zwecke
beinhalten beispielsweise das Speichern persönlicher Daten oder das Bearbeiten
von Studienaufgaben an Universitäten. Solche Repositories sollen gefiltert wer-
den, indem nur Repositories mit mindestens zehn Watchers in den Datensatz mit
aufgenommen werden. Ein Watcher ist ein GitHub-Nutzer, der regelmäßig über
Neuerungen im Projekt informiert werden möchte. Wenn sich viele Nutzer für ein
Projekt interessieren, ist es außerdem wahrscheinlich, dass Code und Readme-Datei
eine hohe Qualität haben, was das Training von neuronalen Netzen auf diesen
Daten begünstigt.
Da die generierten Readme-Dateien englisch sein sollen, werden zuletzt auch nicht
englische Projekte gefiltert. Aus 80% der übrig gebliebenen Projekten wird der
Trainingsdatensatz erzeugt. Test- und Validierungsdatensatz bestehen aus jeweils
10% der Daten.

                                        24
3.1.2    Call-Graph Erstellung
Die verwendeten Technologien zur semantischen Analyse von Sourcecode arbeiten
für gewöhnlich mit Inputdaten von begrenzter Größe. code2seq beispielsweise wurde
von den Autoren mit bis zu 30 Zeilen langen Funktionen getestet und zeigte bei
einer Länge zwischen 9 und 30 Zeilen annähernd konstant gute Ergebnisse [3]. Die
in dieser Arbeit verarbeiteten Projekte können jedoch sehr groß sein. Damit dies
die Qualität des Outputs nicht beeinflusst, soll die Größe der verarbeiteten Daten
begrenzt werden. Hierzu werden die Projekte nicht vollständig analysiert, sondern
nur deren jeweils wichtigste Funktionen. Diese Funktionen werden im Folgenden
als “Core-Funktionen” bezeichnet.
Die Schwierigkeit besteht darin, die Wichtigkeit einer Funktion automatisiert zu
bestimmen. In Anbetracht der beiden vorgesehenen Ansätze bezeichnet “wichtig”
dabei, inwieweit Funktionen aufgrund ihres Namens bzw. Docstrings einen Rück-
schluss auf den Zweck des Projekts zulassen oder eine Funktionalität umsetzt,
die elementar für den Zweck des Projekts ist. Da der Zweck eines Projekts zum
Zeitpunkt der Verarbeitung noch nicht bekannt ist, ist die Wichtigkeit nicht direkt
bestimmbar.
Als Heuristik für die Wichtigkeit kann jedoch die Zentralität im Call-Graph ver-
wendet werden. Für gewöhnlich bauen Funktionen in Projekten aufeinander auf:
diejenigen am Rand des Call-Graphen erfüllen kleine, spezifische Aufgaben und
werden von Funktionen im Inneren des Call-Graphen aufgerufen, welche überge-
ordnete Funktionalitäten umsetzen, die einen direkteren Bezug zum Zweck des
Projekts selbst haben.
Call-Graphen können entweder dynamisch oder statisch generiert werden. Bei
dynamischen Call-Graphen wird der Code ausgeführt und tatsächliche Funktions-
aufrufe aufgezeichnet. Dies ist für diese Arbeit nicht praktikabel, da nicht immer
alle Dependencies eines Projekts installiert werden können und außerdem nicht
bekannt ist, welche Inputdaten für ein Projekt sinnvoll sind. Zur Generierung stati-
scher Call-Graphen wird der Code eines Projektes analysiert, um zu bestimmen
welche Funktionsaufrufe theoretisch möglich sind. Das Erstellen eines statischen
Call-Graphen ist in Python aufgrund seiner dynamischen Typisierung ebenfalls
nicht trivial. Wenn es mehrere Funktionen mit gleichem Namen gibt, kann nicht
ohne weiteres bestimmt werden, welche davon aufgerufen wird. Das Projekt Pyan
[17] ist in der Lage durch Analyse des Codes auch in solchen Fällen häufig noch
eine korrekte Zuordnung durchzuführen.
Für die Zwecke dieses Projekts muss Pyan noch wie folgt modifiziert werden:
   • Das Importieren von externen Modulen und Funktionen wird nicht mehr als
     Funktionsaufruf gewertet
   • Das Aufrufen von geerbten Methoden erzeugt einen einzigen Knoten, statt
     mehrere

                                        25
• Konstruktoraufrufe erzeugen einen einzigen Knoten, statt mehrere
   • Importierte externe Funktionen werden dem korrekten Modul zugeordnet
Problematisch ist, dass Pyan nur Python3 Projekte verarbeiten kann. Bei Syntax-
fehlern während der Call-Graph Erstellung wird deshalb versucht, das verarbeitete
Projekt von Python2 zu Python3 zu konvertieren. Python3 enthält ein Tool namens
2to3, das diese Konversion automatisiert. Sollte danach immer noch ein Syntaxfehler
erkannt werden, muss das Projekt aus dem Datensatz entfernt werden.

Abb. 3.3: Ausschnitt aus dem Call-Graph des Projekts “TwitterTap”. Die Größe
der Knoten entspricht ihrer Zentralität. Blätter sind ausgegraut. Core-Funktionen
sind rot.

Abbildung 3.3 zeigt einen Ausschnitt aus dem Call-Graph des Projekts “TwitterTap”
[18]. Zur besseren Übersicht wurden Funktionen, die keine anderen Funktionen
aufrufen ausgegraut und nicht beschriftet.

                                        26
3.1.3     Call-Graph Zentralität
Als Heuristik für die Wichtigkeit einer Funktion soll ihre Zentralität im Call-Graph
gemessen werden. Zu bestimmen wie zentral sich ein Knoten in einem Graph
befindet, ist in der Graphentheorie ein gut untersuchtes Problem. Da der Begriff
“zentral” nicht fest definiert ist, gibt es eine Vielzahl an Metriken, die unterschiedliche
Eigenschaften der Knoten betrachten, um zu bestimmen wie zentral sie sind. Je
nach Anwendungsfall können unterschiedliche Zentralitätsmetriken die “Wichtigkeit”
eines Knotens unterschiedlich gut widerspiegeln. Welche Metrik für diese Arbeit
am geeignetsten ist, soll deshalb empirisch bestimmt werden.
Dazu werden die Funktionen von fünf Python Projekten manuell in die Kategorien
“Sehr wichtig”, “Wichtig”, “Neutral” und “Unwichtig” eingeteilt. Anschließend wird
mit Pyan der Call-Graph der Projekte erstellt und die Zentralität aller Funktionen
mit den zu testenden Metriken berechnet. Tabelle 3.1 zeigt einige Funktionen
sortiert nach der Eigenvektor-Zentralität. Das analysierte Projekt ist hier ebenfalls
Pyan.

 eigenvector_centrality                                                      Score: 5
 1                                                           0.291
 2                                                    0.216
 3             0.199
 4               0.197
 5                                                        0.196
 6                                    0.195
 7                    0.184
 8                      0.184
 9             0.179
 10                                                       0.173
 11             0.169
 12               0.168
 13                                                          0.168
 14                0.146
 15                                                          0.145
 16                                  0.144
 17                                                         0.139

Tab. 3.1: Einige Pyan Funktionen, sortiert nach ihrer Eigenvektor-Zentralität im
ungerichteten Call-Graph. Dunkelgrün: sehr wichtig; grün: wichtig; weiß: neutral;
rot: unwichtig

                                           27
Tabelle 3.1 zeigt, dass einige Funktionen wie logger.debug von Pyan nicht zugeordnet
werden konnten und deshalb nur als *.debug bezeichnet werden. Bei der Eigenvektor-
Zentralität, die hier auf dem ungerichteten Call-Graphen berechnet wurde, sieht
man außerdem, dass relativ viele unwichtige Utility-Funktionen hohe Positionen
besetzen. Das liegt daran, dass Utility-Funktionen wie logger.debug von sehr vielen
Funktionen benutzt werden und deshalb sehr viele Nachbarn im Call-Graphen
haben. Um das zu verhindern, macht es Sinn der Anzahl der Funktionen die eine
Funktion aufruft (Ausgangsgrad) eine höhere Relevanz beizumessen, als der Anzahl
der Funktionen von der eine Funktion aufgerufen wird (Eingangsgrad).

 reverse_katz_centrality                                                 Score: 12
 1                                            0.171
 2              0.134
 3                                  0.128
 4         0.121
 5                               0.114
 6                       0.113
 7           0.111
 8                              0.106
 9         0.104
 10               0.102
 11             0.098
 12           0.097
 13         0.096
 14              0.092
 15     0.091
 16    0.090
 17            0.087

Tab. 3.2: Einige Pyan Funktionen, sortiert nach ihrer Katz-Zentralität im trans-
ponierten Call-Graph. Dunkelgrün: sehr wichtig; grün: wichtig; weiß: neutral; rot:
unwichtig

Die in Tabelle 3.2 gezeigte Reverse Katz centrality berechnet die Katz-Zentralität
auf einem transponierten Call-Graphen. Transponiert bedeutet, dass die Richtung
aller Kanten umgekehrt wurde. Obiger Überlegung entsprechend berücksichtigt die
Katz-Zentralität auf diese Weise nicht mehr die Anzahl der Pfade, die zu einem
Knoten führen, sondern die, die an dem Knoten beginnen.

                                        28
Um zu messen wie gut eine Metrik mit der manuellen Bewertung der Funktionen
übereinstimmt, wird ein Score berechnet. Dazu wird den sehr wichtigen, wichtigen,
neutralen und unwichtigen Funktionen je ein Wert von 2, 1, 0 und -1 Punkten
zugewiesen und anschließend die Punkte der Funktionen, die die Metrik für die
zentralsten hält, aufsummiert. Wie viele Funktionen eines Projekts später sinnvoll
verarbeitet werden können steht zum Zeitpunkt dieser Analyse noch nicht fest.
Schätzungsweise liegt der Wert jedoch bei 5 - 15 Funktionen. Tabellen 3.1 und 3.2
zeigen den Score, der anhand der Top 15 Funktionen berechnet wurde.
Nun sollen die Scores der Metriken über die fünf bewerteten Projekte hinweg vergli-
chen werden. Die berechneten Scores sind unter den verschiedenen Projekten nicht
direkt vergleichbar, da sie eine unterschiedliche Anzahl wichtiger und unwichtiger
Funktionen haben und die Scores entsprechend zu hohen oder niedrigen Werten
neigen. Stattdessen werden pro Projekt die drei Metriken mit den höchsten Scores
betrachtet.
Die Berechnung wird dreimal wiederholt, wobei die zentralsten 15, 10 und 5 Funk-
tionen berücksichtigt werden. Mit diesen drei Kategorien und den fünf untersuchten
Projekten kann eine Metrik also bis zu 15-mal den ersten Platz belegen. Bei gleichem
Score Teilen sich die Metriken den jeweiligen Platz. Besonders bei der Betrachtung
der fünf zentralsten Funktionen kommt das häufig vor, da dort der Wertebereich
der erreichbaren Scores am kleinsten ist.
Genauso wie oben ein Score für die Metriken berechnet wurde, lässt sich nun ein
Gesamtscore anhand der belegten Platze einer Metrik berechnen. Dabei werden
erster, zweiter und dritter Platz je mit 3, 2 und 1 Punkten bewertet und über alle
Kategorien und Projekte aufsummiert.
Tabelle 3.3 zeigt die Scores, die die Metriken nach dieser Berechnung erzielen.
“Reverse” bedeutet dabei, dass der Call-Graph vor der Berechnung der Metrik
transponiert wurde. “Inverse” bedeutet, dass die Werte die die Metriken berechnen
mit -1 potenziert wurden.

                                        29
Reverse Katz centrality                         24
           Outdegree centrality                            23
           Reverse closeness centrality                    22
           Local reaching centrality                       17
           Pagerank                                         9
           Degree centrality                                9
           Inverse harmonic centrality                      8
           Inverse second order centrality                  4
           Eigenvector centrality                           1
           Reverse pagerank                                 0
           Degeneracy                                       0
           Katz centrality                                  0
           Harmonic centrality                              0
           Closeness centrality                             0
           Second order centrality                          0

Tab. 3.3: Verschiedenen Zentralitätsmetriken sowie die Scores die sie durch den
Bewertungsalgorithmus erhalten.

Die Reverse Katz centrality erzielt hier den höchsten Score. Da eine Objektive
Beurteilung der Eignung der Metriken für dieses Projekt nicht möglich ist, wur-
den bei der Bewertung der Metriken einige subjektive Einschätzungen getroffen,
beispielsweise die Einteilung der Funktionen in Wichtigkeitsklassen. Nachdem die
Differenz unter den besten Metriken sehr knapp ausfällt, kann nicht mit Sicherheit
gesagt werden, dass die Reverse Katz centrality tatsächlich die beste Metrik ist.
Die Berechnung zeigt aber, dass sie für dieses Projekt geeignet ist.
Abbildung 3.3 zeigt den Call-Graph eines Projekts, in dem die Reverse Katz
centrality als Größe der Knoten dargestellt wird.

                                         30
3.1.4    Clustering
Nachdem die Core-Funktionen bestimmt wurden, muss festgestellt werden in
welcher Reihenfolge diese geinlined werden. Häufig rufen sich Core-Funktionen nicht
gegenseitig auf, sondern sind im Call-Graph weit voneinander entfernt. Statt einem
Inlining werden diese Funktionen konkateniert. Ein Inlining wird durchgeführt,
wenn es einen Pfad zwischen zwei Funktionen gibt, der höchstens zwei Knoten lang
ist.
Nun werden die Core-Funktionen in Cluster von Funktionen unterteilt, die gein-
lined werden können. Später werden die Funktionen, die durch das Inlining aller
Funktionen eines Clusters entstehen konkateniert.
Nachdem die Cluster erstellt wurden, wird die Inlining Reihenfolge innerhalb des
Clusters bestimmt. Dazu wird zuerst ein Subgraph erstellt, der die Knoten des
Clusters und deren Nachbarn beinhaltet. Anschließend wird ein Minimum-Spanning-
Tree erstellt, der alle Funktionen des Clusters umfasst. Ein solcher MST, der nicht
alle Knoten des Graphen, sondern nur eine bestimmte Menge enthält, ist auch
als Steiner-Tree bekannt [19]. Der Steiner-Tree definiert schließlich die Inlining
Reihenfolge, wobei jede Funktion in ihren Elternknoten geinlined wird.

                                        31
3.1.5    Inlining
Die Readme-Datei soll in diesem Ansatz durch code2seq erstellt werden. code2seq
kann jedoch nur einzelne Funktionen verarbeiten. Damit also alle relevanten In-
formationen aus den Core-Funktionen verarbeitet werden können, müssen sie in
eine einzige Funktion kombiniert werden. Um das zu erreichen, ohne dabei die
Semantik zu verändern, sollen die Core-Funktionen ineinander geinlined werden.
Wie im Vorangegangenen beschrieben, ist das natürlich nicht immer möglich. Zum
Inlining einer Python Funktion in eine andere sind folgende Schritte nötig:
  1. Extraktion des Sourcecodes der aufrufenden und aufgerufenen Funktion
  2. Ermitteln der Stelle des Funktionsaufrufs in der aufrufenden Funktion
  3. Anpassen der Einrückung des Codes der aufgerufenen Funktion an die Stelle
     des Funktionsaufrufs
  4. Entfernen von Return-Statements aus der aufgerufenen Funktion
  5. Einfügen des Codes vor die Zeile des Funktionsaufrufs
  6. Ersetzen des Funktionsaufrufs mit dem Rückgabewert der Funktion
Für die Umsetzung des Inlinings müssen an mehreren Stellen Kompromisse ein-
gegangen werden. Im 2. Schritt kann es sein, dass die Funktion gleich mehrfach
aufgerufen wird. In diesem Fall wird nur der erste Aufruf geinlined, um der ein-
gesetzten Funktion keine zu hohe Bedeutung zukommen zu lassen. Außerdem ist
ein Inlining streng genommen nur dann möglich, wenn die aufgerufene Funktion
höchstens ein Return-Statement besitzt. In Schritt 4 werden jedoch alle Return-
Statements außer dem Letzten entfernt. Damit wird eine Veränderung der Semantik
in Kauf genommen, um dennoch ein Inlining durchführen zu können. Um evtl.
entstehende Grammatikfehler zu vermeiden, werden die Return-Statements durch
ein “pass”-Statement ersetzt.
Zudem wird nicht darauf geachtet, ob es durch die Vereinigung der Namespaces zu
Konflikten kommt. Eine entsprechende Umbenennung von Variablen wäre allerdings
auch nicht sinnvoll, da code2seq ein Token-Vokabular besitzt, das die Bedeutung
einer Variable anhand ihres Namens bestimmt. Die Interpretation des Codes durch
code2seq würde deshalb durch eine Umbenennung stärker verändert werden, als
durch die doppelte Verwendung eines Namens.

                                      32
3.1.6    Parsing
Nachdem das Inlining durchgeführt wurde, ist für jedes Projekt eine Funktion
entstanden, die das gesamte Projekt repräsentieren soll. Bevor code2seq mit dieser
trainiert werden kann, muss ein AST erstellt und Path-Kontexte aus diesem ge-
sampelt werden, wie es in Kapitel 2.2.3 beschrieben wurde. code2seq bietet einen
entsprechenden Präprozessor nur für Java und C# an, verweist jedoch auf einen
Präprozessor von JetBrains namens “astminer” [20], der für Python verwendet
werden kann. Obwohl dieser Präprozessor eine Funktion für code2seq bietet, ist sein
Output mit code2seq nicht kompatibel und muss im Anschluss noch umformatiert
werden.
Ein weiteres Problem besteht darin, dass der AST, aus dem der astminer die
Path-Kontexte sampelt, alle Knoten enthält, die Pythons Grammatik spezifiziert.
Typischerweise werden irrelevante Knoten nicht in einem AST dargestellt. Bei
Python ist das besonders problematisch, da durch Pythons Grammatik sehr lange
Ketten von Knoten entstehen, die keinen klaren Bezug zum Code haben. Abbildung
3.4 verdeutlicht dieses Problem. Die linke Seite zeigt den AST für den Ausdruck “x
= 1” in Java, wie er von code2seq verwendet wird. Die rechte Seite zeigt den AST
für den gleichen Ausdruck in Python, wie der astminer ihn verwendet. Um das
Problem zu beheben, werden Sequenzen von Knoten mit nur einem Kindknoten
nachträglich entfernt.

                                        33
Abb. 3.4: Java AST für den Ausdruck “x = 1” (links), sowie der von astminer
verwendete Python AST für denselben Ausdruck (rechts).

Die Anzahl der Path-Kontexte die vom Parser gesampelt werden sollen, lässt sich
variieren. Durch das Inlining mehrerer Funktionen ist eine besonders lange Funktion
entstanden. Entsprechend macht es Sinn, auch die Anzahl der Path-Kontexte zu
erhöhen. Neben dem ursprünglichen Wert von 200 wird deshalb in einem weiteren
Trainingsdurchlauf getestet, ob sich die Ergebnisse mit 400 Path-Kontexten pro
Funktion verbessern.

                                        34
3.1.7    Erstellung der Zielsequenzen
Zum Training von code2seq muss für jede Funktion auch eine Zielsequenz vorgegeben
werden. Das Readme eines Projekts ist meist zu lang, um ein neuronales Netz
sinnvoll darauf zu trainieren. Außerdem enthält es oft Informationen, die nicht aus
dem Sourcecode hervorgehen wie bspw. die Lizenz oder Links auf die Webseite der
Autoren.
Um eine sinnvolle Zielsequenz zu erstellen wird der READMEClassifier [21] verwen-
det. Der READMEClassifier ist ein neuronales Netz, das Sektionen von Readme-
Dateien in bestimmte Klassen einteilt. Zu diesen Klassen gehört beispielsweise
“What”, wo Einleitung und Hintergrund beschrieben wird, oder “Who”: Namen und
Kontaktdaten von Autoren.
Als Zielsequenz wird die “What”-Sektion des Readmes verwendet, da diese meistens
den Zweck des Projekts beschreiben. Da diese Sektionen mitunter auch sehr lang
sein können, werden nur die ersten Sätze verwendet. In verschiedenen Trainings-
durchläufen werden hier einmal die ersten zwei und einmal die ersten zehn Sätze
verwendet.
Bei vielen Projekten kann der READMEClassifier keine “What”-Sektion identifi-
zieren. Auf GitHub kann für jedes Projekt neben einer Readme-Datei auch ein
About-Text angegeben werden, der den Inhalt des Projekts prägnant wiedergibt.
Diese About-Texte werden als Zielsequenz verwendet, wenn der READMEClassifier
keine “What”-Sektion identifizieren kann. Sollte auch kein About-Text existieren,
muss das Projekt aus dem Datensatz entfernt werden. In einem weiteren Trainings-
durchlauf werden ausschließlich die About-Texte als Zielsequenzen verwendet.
Der Datensatz, der bis zu zehn Sätze der Readme “What”-Sektion als Zielsequenz
enthält, verwendet ersatzweise keine GitHub About-Texte. Letztere sind meist nur
einen Satz lang, sodass der Längenunterschied zu groß ist, um noch ein angemessener
Ersatz für die “What”-Sektion zu sein.

                                        35
Projektname     Readme “What”-Sektion                             GitHub About-Text
                 Overview Surprise is a Python scikit for
                 building and analyzing recommender
                 systems that deal with explicit rating data.      A Python scikit for building and analyzing
 Surprise
                 Surprise was designed with the following          recommender systems
                 purposes in mind: Give users perfect control
                 over their experiments.
                 I’ve noticed that many people have
 machine
                 out-of-date forks. Thus, I recommend not          A collection of machine learning examples
 learning
                 forking this repository if you take one of my     and tutorials.
 examples
                 courses.
                 30 seconds of code Short Python code
                 snippets for all your development needs
 30 seconds of                                                     Python implementation of
                 Visit our website to view our snippet
 python code                                                       30-seconds-of-code
                 collection. Use the Search page to find
                 snippets that suit your needs.
                                                                   Extending and consolidating hosts files from
                 This repository offers 15 different host file
                                                                   a variety of sources like adaway.org,
                 variants, in addition to the base variant.
                                                                   mvps.org, malwaredomains.com,
                 The Non GitHub mirror is the link to use
 hosts                                                             someonewhocares.org, yoyo.org, and
                 for some hosts file managers like Hostsman
                                                                   potentially others. You can optionally
                 for Windows that don’t work with GitHub
                                                                   invoke extensions to block additional sites
                 download links.
                                                                   by category.
                 percol is an interactive grep tool in your
                 terminal. percol receives input lines from
                 stdin or a file, lists up the input lines, waits   adds flavor of interactive filtering to the
 percol
                 for your input that filter/select the line(s),     traditional pipe concept of UNIX shell
                 and finally outputs the selected line(s) to
                 stdout.

Tab. 3.4: Die ersten zwei Sätze der “What”-Sektion der Readme-Dateien einiger
GitHub Repositories, sowie deren About-Texte.

Tabelle 3.4 zeigt einige Beispiele für “What”-Sektion und About-Texte. Die meisten
Sonderzeichen wurden entfernt.

                                                       36
3.2     Summary-Ansatz
3.2.1    Datensatz
Um den Datensatz für den Summary-Ansatz zu erstellen, muss code2seq zuerst
darauf trainiert werden, Docstrings für eine Funktion zu generieren. Da code2seq
jedoch normalerweise nur mit Java und C# arbeitet, ist ein Finetuning mit dem
Checkpoint, den die Autoren bereitstellen, nicht möglich. Zwar sorgt der Parser für
einen gewissen Grad an Abstraktion, das Vokabular der AST-Knoten unterscheidet
sich bei den Sprachen jedoch zu stark.
Stattdessen wird ein neues Vokabular erstellt und ein neues code2seq Modell
trainiert. Hierzu wird der Datensatz von CodeSearchNet [22] verwendet. Dieser
enthält etwa 2 Mio. Funktionen sowie deren Docstrings. Davon sind rund 535.000
Python Funktionen.
Sobald das Training des neuen Modells abgeschlossen ist, kann der Trainingsda-
tensatz für den Textzusammenfasser erstellt werden. Dabei kommt wieder der
GHTorrent Datensatz zum Einsatz. Genau wie beim Inlining-Ansatz wird dazu der
Call-Graph erstellt und die Core-Funktionen bestimmt. Für die Core-Funktionen
wird dann je ein Docstring erstellt und diese konkateniert. Die Docstrings die-
nen dann als Input für den Textzusammenfasser. Die Docstrings sind nach ihrer
Erstellung absteigend nach Zentralität der zugehörigen Funktion sortiert. Diese
Sortierung wird beibehalten.
Die Zielsequenzen werden wie beim Inlining-Ansatz mithilfe des READMEClassifiers
erstellt.

                                        37
Sie können auch lesen