SlideShare ist ein Scribd-Unternehmen logo
1 von 63
Downloaden Sie, um offline zu lesen
Bachelorarbeit
Effizienteres Dokumenten-Clustering durch
Grafikprozessoren
Marco Janc
Betreuer:
Prof. Dr. – Ing. Norbert Fuhr
Dipl, -Inform. Marc Lechtenfeld
Universität Duisburg-Essen
Fakultät für Ingenieurwissenschaften
Abteilung Informatik und angewandte Kognitionswissenschaft
Fachgebiet Informationssysteme
47084 Duisburg
Inhaltsverzeichnis 2
Inhaltsverzeichnis
Inhaltsverzeichnis ........................................................................................................... 2
Einleitung......................................................................................................................... 4
1.1 Motivation ............................................................................................................. 4
1.2 Aufgabenstellung................................................................................................... 5
1.3 Aufbau der Arbeit.................................................................................................. 6
2 Verwandte Arbeiten ............................................................................................ 7
2.1 Textvorverarbeitung .............................................................................................. 7
2.2 Clusteranalyse........................................................................................................ 7
2.2.1 Shaderprogramme.................................................................................................. 7
2.2.2 GPGPU.................................................................................................................. 8
3 Grundlagen .......................................................................................................... 9
3.1 Textvorverarbeitung .............................................................................................. 9
3.1.1 Entfernung von Stoppwörtern ............................................................................... 9
3.1.2 Reduzierung auf Wortstämme (Stemming)......................................................... 10
3.2 Dokumentenrepräsentation.................................................................................. 10
3.2.1 TF-IDF-Maß........................................................................................................ 11
3.3 Ähnlichkeitsanalyse............................................................................................. 12
3.3.1 Ähnlichkeitsmaß.................................................................................................. 12
3.3.2 Dreiecksmatrix .................................................................................................... 13
3.4 Clusteranalyse...................................................................................................... 13
3.4.1 Clusterdefinition.................................................................................................. 13
3.4.2 Clusterverfahren .................................................................................................. 14
3.4.3 Partitionierende Verfahren .................................................................................. 14
3.4.4 Hierarchische Verfahren...................................................................................... 14
3.5 Ergebnispräsentation ........................................................................................... 15
3.6 General Purpose Computation on Graphics Processing Unit (GPGPU)............. 15
3.6.1 Grafikkarten-Architektur..................................................................................... 16
3.6.2 Compute Unified Device Architecture (Cuda).................................................... 16
3.6.3 Grundlegender Arbeitsvorgang ........................................................................... 16
3.6.4 Kernelausführung ................................................................................................ 17
3.6.5 Speichermodell.................................................................................................... 18
4 Eingeschlagener Realisierungsweg .................................................................. 20
4.1 Allgemeine Implementierungsdetails.................................................................. 21
4.1.1 Ähnlichkeitsanalyse............................................................................................. 21
Inhaltsverzeichnis 3
4.1.2 Update der Nachbarn........................................................................................... 22
4.2 CPU ..................................................................................................................... 23
4.2.1 Dokumentenrepräsentation.................................................................................. 23
4.2.2 Ähnlichkeitsanalyse............................................................................................. 23
4.2.3 Clusteranalyse...................................................................................................... 24
4.3 GPU ..................................................................................................................... 27
4.3.1 Ressourcenausnutzung ........................................................................................ 27
4.3.2 JCuda................................................................................................................... 28
4.3.3 Hilfsklassen zur dynamischen Speicherallokation und Kopierung..................... 28
4.3.4 Parallele Reduktion ............................................................................................. 30
4.3.5 Dokumentenrepräsentation.................................................................................. 32
4.3.6 Ähnlichkeitsanalyse............................................................................................. 37
4.3.7 Clusteranalyse...................................................................................................... 40
5 Evaluation .......................................................................................................... 44
5.1 Eingesetzte Hardware.......................................................................................... 44
5.2 Ergebnisse............................................................................................................ 44
6 Zusammenfassung und Ausblick ..................................................................... 48
6.1 Zusammenfassung ............................................................................................... 48
6.2 Ausblick............................................................................................................... 48
Anhang A: Grafikkartenarchitektur und Cuda ........................................................ 50
Anhang B: Formeln ...................................................................................................... 52
Anhang C: Erweiterter Cuda-Code............................................................................ 53
Anhang D: Java CPU-Clusterupdate-Algorithmus auf der Clusterliste/
Cluster-Binärbaum............................................................................................ 55
Anhang E: Anwendung ................................................................................................ 56
Abbildungsverzeichnis.................................................................................................. 57
Tabellenverzeichnis ...................................................................................................... 58
Abkürzungsverzeichnis ................................................................................................ 59
Literaturverzeichnis ..................................................................................................... 60
Einleitung 4
Einleitung
Die Wiederbeschaffung von relevanten Informationen, engl. „Information Retrieval―
(IR), gewinnt aufgrund der enormen Masse an Informationen, die heute für jeden Men-
schen vor allem durch das Internet verfügbar sind, immer stärker an Bedeutung. Der
Mensch ist auf seiner Suche zeitlich nicht mehr in der Lage alle Informationen seines
Suchkontextes selbst nach den für ihn relevanten Informationen zu durchsuchen. Eine
Clusteranalyse kann bei diesem Problem hilfreich sein. Dieses analysiert nach zumeist
statistisch-mathematischen Verfahren einen vorgegebenen Datenbestand zur Erkennung
neuer Muster und Relationen, wie die Ähnlichkeit der Daten untereinander. Für kleinere
Datenbestände wird dies vereinfacht schon durchgeführt. Ein Beispiel dafür sind Kon-
textempfehlungen aufgrund von Schlagwörtern. Die Analysierung des Informationsin-
haltes von sehr großen Datenbeständen, in unserem Falle Text-Dokumente, ist mit ei-
nem zu großen Rechenaufwand verbunden, um sie alleinig auf dem Hauptprozessor
durchführen zu können.
1.1 Motivation
Aufgrund physikalischer Grenzen endete um das Jahre 2005 das Rennen namhafter
Hauptprozessor-(CPU) Hersteller den CPU mit der höchsten Taktfrequenz herzustellen.
Um jedoch weiterhin die Computer der Kunden beschleunigen zu können, erfolgte ein
Paradigmenwechsel in der Geschichte der Prozessorentwicklung. Anstelle von immer
höheren Taktfrequenzen wurden fortan mehrere Kerne in einem Prozessor zu sogenann-
ten Mehrkernprozessoren verbaut. Damit eine einzelne Anwendung davon optimal pro-
fitieren kann, muss sie explizit auf Mehrkernsysteme hin optimiert werden. Der ak-
tuellste Mehrkernprozessor der Firma Intel besitzt jedoch erst 6 physische Kerne [1],
eine aktuelle Nvidia Grafikkarte jedoch mehr als 500 [2].
Seit dem Beginn der Videospielindustrie ist diese bestrebt, immer realitätsgetreuere
virtuelle Umgebungen, insbesondere im Bereich der Visualisierung, zu erschaffen. Dies
hat einen steigenden mathematischen Rechenaufwand zur Folge, weshalb die Grafikkar-
tenhersteller immer schnellere Grafikkarten produzieren. Aktuelle Grafikprozessoren
(GPU) besitzen eine skalierbare parallele SIMD Rechnerarchitektur und mit ihrer star-
ken Verbreitung in Desktop Computern, Videospielkonsolen, Workstations und mitt-
lerweile sogar in Rechenzentren sollte jeder Anwendungsentwickler parallelisierbare
Aufgaben, falls möglich, auf die GPU auslagern [2].
Bei den Grafikkarten ist eine dauerhafte Erhöhung des Taktes aufgrund ihrer skalierba-
ren Rechnerarchitektur nicht von höchster Priorität, sondern die Anzahl der internen
Einleitung 5
Stream-Prozessoren sowie der Speicherdurchsatz und die Größe des Videospeichers
sind von höherer Bedeutung. Zudem ermöglichen die meisten Mainboards den Einbau
von mehreren Grafikkarten, wovon Entwickler profitieren können. Wie die folgende
Abbildung 1 verdeutlicht, ist ein aktueller Prozessor einer aktuellen Grafikkarte bei
Fließkommazahlenberechnungen mit einfacher Präzision weit unterlegen. Die Nutzung
dieser möglichen Rechenkraft der GPU sollte die notwendigen Schritte einer Dokumen-
ten-Clusteranalyse, welche meistens aus gleichförmigen und voneinander unabhängigen
Prozeduren bestehen, beschleunigen. Dies wird in dieser Arbeit untersucht und gegen-
über einer gleichwertigen CPU-Variante in Kapitel 5 evaluiert.
Woodcrest
Harpertown
Bloomfield
Westmere
7800 GTX
8800 GTX
GTX 285
GTX 480
GTX 580
0
200
400
600
800
1000
1200
1400
1600
1800
Intel CPU
Nvidia GPU
Abbildung 1: CPU und GPU Performanz-Vergleich in Gigaflops bei Berechnungen mit
Fließkommazahlen mit einfacher Präzision (SP) [3]
1.2 Aufgabenstellung
In dieser Arbeit wird die Möglichkeit untersucht, den Vorgang des Dokumenten Cluste-
ring unter Zunahme einer auf parallele Berechnungen hin optimierten Hardware zu be-
schleunigen. Ein Ziel dieser Abschlussarbeit ist es, einen Rahmen zur Eignung von ak-
tuellen GPUs zur hierarchischen Clusteranalyse aufzustellen. In diesem Prozess sollen
die zu definierenden Schritte dieser auf ihre Parallelisierbarkeit und Adaption auf GPUs
überprüft und falls möglich umgesetzt werden.
Weiterhin soll die Ausführung möglicher Schritte auf der GPU eine bessere Performanz
gegenüber der CPU bieten. Um die Performanz einer GPU mit einer gleichwertigen1
CPU vergleichen zu können, sollte die CPU-Variante, falls möglich, auch parallelisiert
werden, um alle Kerne der CPU nutzen zu können. Weiterhin sollten beide für qualitati-
1
Gleichwertig bedeutet in diesem Kontext, dass die zu vergleichende CPU aus demselben Jahr und dem-
selben Verbrauchermarkt wie die GPU stammt
Einleitung 6
ve Evaluationsergebnisse, ähnliche jedoch für die Architektur optimale und angepasste
Algorithmen benutzen.
1.3 Aufbau der Arbeit
Nach dieser Einleitung folgt die Festlegung der zu realisierenden Ziele dieser Arbeit. In
Kapitel 2 werden verschiedene wissenschaftliche Arbeiten im Bereich des Dokumenten-
Clustering im Kontext der Grafikkartenprogrammierung (GPGPU) vorgestellt. Darauf-
hin folgt in dem nächsten Kapitel eine detaillierte Beschreibung der notwendigen
Grundlagen zu den Themen Dokumenten-Clustering und der allgemeinen Programmie-
rung auf Grafikkarten. Der schließlich eingeschlagene Realisierungsweg und Details der
Implementierung werden in Kapitel 4 vorgestellt. Schließlich erfolgt im nächsten Kapi-
tel die Evaluierung der Ergebnisse dieser Arbeit. Diese schließt mit einer Zusammenfas-
sung und dem Ausblick.
2 Verwandte Arbeiten 7
2 Verwandte Arbeiten
Literatur zum Dokumenten-Clustering und speziell dem hierarchischen ist in Bibliothe-
ken und dem Internet sehr leicht auffindbar und in vielfältiger Form vorhanden [4] [5]
[6]. Ziele dieser wissenschaftlichen Arbeiten sind die Verbesserung der Qualität der
Clusterverfahren [7], Analysen und Vergleiche vorhandener Verfahren [8], Erhöhung
der Performanz mit besonderem Fokus auf die parallele Ausführung der Clusteralgo-
rithmen [9] [10] [11] [12] [13] [14].
Diese Arbeit konzentriert sich auf die effiziente parallele Ausführung des Dokumenten-
Clustering auf Grafikkarten, weshalb in diesem Kapitel Arbeiten, die die Grafikkarte als
Co-Prozessor für das hierarchische Dokumenten-Clustering verwenden, vorgestellt
werden. Alle hier vorgestellten GPU-Arbeiten erreichten eine viel höhere Performanz
gegenüber ihrer jeweiligen CPU-Variante.
2.1 Textvorverarbeitung
Die Konvertierung der Text-Dokumente in eine statistisch-mathematisch repräsentative
Form ist für die eigentliche Clusteranalyse von hoher Bedeutung. Y. Zhang et al. wid-
men sich diesem Thema und erstellten in ihrer Arbeit einen Algorithmus zur Beschleu-
nigung des Text Mining mittels GPUs [15] [16]. Die einzelnen Schritte der Textvorver-
arbeitung sowie Repräsentierung der Dokumente im Vektorraum-Modell wurden kom-
plett auf der GPU realisiert. Aufgabe der CPU ist es, die Aufgaben der GPU zu syn-
chronisieren, und einen asynchronen dokumentenweisen Eingabestrom an die GPU zu
gewährleisten. Zur Realisierung des Vektorraum-Modells im Speicher der Grafikkarte
erstellten sie eine eigene, eindimensionale Hash-Tabellen-Datenstruktur. Mittels ihrer
Implementierung erreichten sie eine Verkürzung der Laufzeit um das bis zu 30-fache.
2.2 Clusteranalyse
Bis zu diesem Zeitpunkt ist erst eine kleine Anzahl an Arbeiten über das Dokumenten-
Clustering mithilfe der GPU verfasst worden [17] [18] [19] [20], von denen sich ein
kleiner Teil mit dem hierarchischen Verfahren beschäftigt [21] [22] [23] [24].
2.2.1 Shaderprogramme
Vor der Entwicklung von Schnittstellen zur Programmierung auf Grafikkarten wurde
die parallele SIMD-Rechnerarchitektur der Grafikkarten mithilfe von Pixel-Shader-
Programmen ausgenutzt. Diese waren grundsätzlich für die Pixel-basierte Berechnung
der Oberflächeneigenschaften von 3D-Objekten einer virtuellen Szene vorgesehen. Die
2 Verwandte Arbeiten 8
Ergebnisse der entwickelten Algorithmen wurden in den Framebuffer geschrieben und
konnten somit gespeichert und ausgewertet werden. Arbeiten über das Dokumenten-
Clustering mit Verwendung dieser Technik realisierten meistens das partitionierende K-
Means-Clusterverfahren [17] [19].
2.2.2 GPGPU
D. Chang et al. erarbeiteten in dem Jahre 2008 einen CUDA-Algorithmus zur parallelen
Berechnung der paarweisen euklidischen Distanzen von Datenpunkten [22], welchen D.
Chang und M. Ouyang mit M. Kantardzic ein Jahr später als Basis zur Berechnung der
paarweisen Pearson-Korrelation verwenden [25]. Anschließend führten sie eine hierar-
chische Single-Linkage Clusteranalyse auf Basis der zweidimensionalen Pearson-
Korrelation-Dreiecksmatrix durch.
Die Ähnlichkeitsmatrix-Dreiecksmatrix in der obigen Arbeit besitzt zwei Dimensionen,
jedoch ist sie nur spärlich besetzt. A. Shalom et al. optimierten diese zu einem eindi-
mensionalen Array mit Zeilen- und Spaltenindizes zur Erreichung einer höheren Per-
formanz [21] und untersuchten die Performanz in Abhängigkeit von unterschiedlichen
Cuda-Blockgrößen [26].
3 Grundlagen 9
3 Grundlagen
Die notwendigen Schritte einer vollständigen Dokumenten-Clusteranalyse sind folgend
aufgelistet und werden anschließend detailliert beschrieben:
1. Textvorverarbeitung
2. Dokumentenrepräsentation
3. Ähnlichkeitsanalyse
4. Clusteranalyse
5. Ergebnispräsentation
Die Komplexität der einzelnen Schritte ist von mehreren Faktoren abhängig, wie der Art
der Dokumente, dem jeweiligen Ziel der Analyse und den damit ausgesuchten Cluster-
verfahren.
3.1 Textvorverarbeitung
Bevor die Dokumente in eine maschinenlesbare Repräsentation konvertiert werden (s.
Abschnitt 3.2) können, sind einige textvorverarbeitende Schritte, welche in diesem Ab-
schnitt vorgestellt werden, nötig.
Die Sprachdimension der zu analysierenden Kollektion sowie deren Sprachen sind für
die Textvorverarbeitung von hoher Bedeutung. Bei der Indizierung der einzelnen Wort-
terme müssen bei multilingualen Kollektionen komplexere Verfahren angewendet wer-
den, außerdem die Parametrisierung der Stoppwortentfernung und des Stemming von
der Sprache abhängig [13] [27].
In Textdokumenten wie Büchern oder Artikeln besitzen Wörter gegenüber Sonderzei-
chen oder numerischen Werten einen sehr hohen relevanten Informationsgehalt, wes-
halb zuerst alle nichtalphabetischen Zeichen entfernt werden. Um einzelne Wörter mit-
einander vergleichen zu können, müssen ihre Buchstaben anschließend in dieselbe Stel-
lung konvertiert werden. Daraufhin folgt die Tokenisierung, in welcher der Text in seine
einzelnen Wortterme aufgeteilt wird. Die lexikalische Bedeutung aufeinanderfolgenden
von Worten und ganzen Haupt- wie Nebensätzen gehen durch diesen Ansatz verloren.
3.1.1 Entfernung von Stoppwörtern
Die natürliche Sprache und deren Texte enthalten eine große Anzahl von lexikalisch
irrelevanten Worten. Diese sogenannten Stoppwörter verbinden Textteile meistens se-
mantisch sowie syntaktisch miteinander. Klassische Beispiele für Stoppwörter sind
Konjunktionen, Artikel und Präpositionen. Da diese Wörter zum Textinhalt sehr wenig
3 Grundlagen 10
beitragen, aber gleichzeitig mit einer hohen Frequenz vorhanden sind, werden sie in
diesem Schritt entfernt. Zur Identifizierung und Entfernung dieser werden Listen ver-
wendet, in denen alle Stoppwörter einer Sprache vorhanden sind.
3.1.2 Reduzierung auf Wortstämme (Stemming)
In der natürlichen Sprache kann sich die Grundbedeutung vieler Wörter sehr ähnlich bis
sogar gleich sein. Diese Wörter mit unterschiedlichen morphologischen Varianten eines
gemeinsamen Terms werden beim Stemming zu einem Wortstamm zusammengefasst.
Im Gegensatz zur Lemmatisierung muss dieser in der Sprache jedoch nicht als selbst-
ständiges Wort existieren. Dieses Verfahren entfernt Flexionsendungen und Derivati-
onssuffixe der Worte. Je nach Algorithmus können auch Kompositionen in ihre einzel-
nen Terme zerlegt werden. Das Stemming-Verfahren kann in zwei Kategorien unterteilt
werden, die linguistische und die nichtlinguistische Kategorie. Die nichtlinguistischen
Verfahren basieren meist auf großen Tabellen, in welchen jedem Term ein jeweiliger
Wortstamm zugeordnet ist. Dagegen basiert das linguistische Verfahren auf dem
sprachspezifischen Wissen über Suffixe, Ableitungen etc., weshalb es für unterschiedli-
che Sprachen verschiedene Implementierungen gibt. Letzteres wird beim Dokumenten-
Clustering meistens angewendet, da hier keine große Wortstammtabelle nötig ist, da die
Findung eines Terms in dieser mit einem hohen Suchaufwand verbunden ist. Mittels
dieses Vorgangs ist es nicht mehr nötig alle möglichen Terme einer Kollektion eindeu-
tig speichern zu müssen, was zu einer Reduzierung der Termmenge um bis zu 50%
führt.
3.2 Dokumentenrepräsentation
Ziel der Dokumentenrepräsentation der Dokumente einer Kollektion ist es, Textinhalte
durch Merkmale wider zu spiegeln. Häufig genutzte Kandidaten für eine Merkmalsaus-
prägung sind Metadaten über das Dokument, einzelne Bereiche dessen sowie die ein-
zelnen Dokumententerme. Diese basieren in der Regel auf statistisch-mathematischen
Verfahren. Schon 1957 kam H. Luhn zu dem Forschungsergebnis, dass die Termvertei-
lung innerhalb eines Dokumentes dessen Inhalt widerspiegelt sowie dessen Häufigkeit
dessen Signifikanz repräsentiert [28]. Die Repräsentation der Dokumente im Vektor-
raum-Model ist de facto Standard im Bereich des Dokumenten-Clusterings.
Das Vektorraum-Modell ist die am häufigsten benutzte Form der Dokumentenrepräsen-
tation. In diesem werden die Dokumente als Vektoren dargestellt, dessen Attribute die
Terme und die Werte ihre Gewichtung repräsentieren. Diese werden zur weiteren Ver-
arbeitung in einer globalen Term-Dokumentenmatrix (TDM) gespeichert. Die Initial-
gewichtung entspricht in dieser Arbeit der absoluten Häufigkeit eines Terms in einem
Dokument.
3 Grundlagen 11
Abbildung 2: Term-Dokumentenmatrix
Das in der Literatur am häufigsten eingesetzte Maß zur Termgewichtung ist das
Termfrequenz-inverse Dokumentenfrequenz (TF-IDF) Maß. Mehrere Alternativen sind
dazu vorhanden, wobei die Termfrequenz-Inverse Korpusfrequenz (TF-ICF) mit Aus-
blick auf die Umsetzung auf die GPU eine geeignete Wahl darstellen kann, da der IDF
Term abhängig von der zu analysierenden Gesamtkollektion ist. ICF erreicht gegenüber
IDF eine Reduzierung der Komplexität von zu , bei einer ähnlichen Qualität
[16] [29]. Vereinfacht erläutert, approximiert ICF die Termverteilung der Gesamtkol-
lektion mittels Untermengen dieser, weshalb nicht mehr die ganze Kollektion in den
begrenzten Speicher der Grafikkarte geladen werden muss. Somit ist eine höhere An-
zahl an Dokumenten klassifizierbar.
Für diese Arbeit reicht der vorhandene Grafikspeicher aus, weshalb das genauere TF-
IDF Maß verwendet wird. Dieses besteht aus zwei Komponenten, der Termfrequenz
und der inversen Dokumentenfrequenz, welche in dem folgenden Abschnitt erläutert
werden.
3.2.1 TF-IDF-Maß
Die Termfrequenz (TF) beschreibt die relative Häufigkeit eines Terms in einem Doku-
ment und ist in Formel 1 definiert. Folglich besitzen Terme mit einer höheren Frequenz
eine höhere Gewichtung.
ist die Häufigkeit des Terms im Dokument
ist die Anzahl aller Terme im Dokument
Formel 1: Termfrequenz
Die inverse Dokumentenfrequenz (IDF) beschreibt die Gewichtung eines Terms in Re-
lation zur Gesamtkollektion. Definiert in Formel 2 wird eine hohe Termgewichtung
durch eine niedrige Anzahl an Dokumenten, die diesen Term beinhalten, realisiert.
documents
term…,0
termm-1,0
term0,0
term…,…
termm,…
term0,…
term…,|D|-1
termm-1,|D|-1
term0,|D|-1
terms
3 Grundlagen 12
ist die Anzahl aller Dokumente der zu untersuchenden Kollektion
ist die Anzahl aller Dokumente, welche Term beinhalten
Formel 2: inverse Dokumentenfrequenz
Das TF-IDF-Maß besteht, wie in Formel 3 dargestellt, aus der multiplikativen Zusam-
mensetzung der Termfrequenz mit der inversen Dokumentenfrequenz.
Formel 3: TF-IDF Maß
3.3 Ähnlichkeitsanalyse
Auf Basis der Dokumentenvektoren, hier der TDM, wird nun eine Ähnlichkeitsanalyse
durchgeführt. Hierzu werden paarweise die Korrelationskoeffizienten der Dokumente
mit einem ausgewählten Ähnlichkeitsmaß bestimmt.
3.3.1 Ähnlichkeitsmaß
Wichtige Koeffizienten im IR sind der Kosinus, Dice- sowie Jaccardkoeffizient. In die-
ser Arbeit wird der Kosinuskoeffizient verwendet und in diesem Abschnitt vorgestellt.
Der Kosinus zweier Vektoren, Formel 4, gibt den Winkel zwischen diesen wieder. Ha-
ben beide Vektoren dieselbe Ausrichtung, beträgt der Winkel zwischen Ihnen 0, wes-
halb sie eine Ähnlichkeit von 1 besitzen.
Formel 4: Kosinus-Ähnlichkeitsmaß zweier Vektoren
Für normierte Vektoren, d.h. die Vektoren besitzen die Einheitslänge nach Euklidischer
Norm, ist die Kosinus-Formel mit dem Skalarprodukt zweier Vektoren identisch und es
ergibt sich Formel 5. Durch diese Normalisierung werden die Terme unabhängig von
der Länge eines einzelnen Dokumentes gewichtet.
Formel 5: Skalarprodukt-Ähnlichkeitsmaß zweier normierter Vektoren
3 Grundlagen 13
3.3.2 Dreiecksmatrix
Ergebnis dieser Ähnlichkeitsanalyse ist die Dokumenten-Ähnlichkeits-Dreiecksmatrix
(DDM), deren Diagonale die Eigenähnlichkeit der Dokumente repräsentiert und somit
ignoriert werden kann. Die Anzahl der Paare, die Initialcluster, kann aus der Formel für
Dreieckszahlen hergeleitet werden (s. Formel 10).
3.4 Clusteranalyse
Ziel der Clusteranalyse ist es, eine vollständige Segmentierung aller Objekte eines zu
analysierenden Datenbestandes anhand ihrer Ähnlichkeiten zueinander zu erreichen. Für
diesen Vorgang muss ein geeignetes Ähnlichkeitsmaß gewählt werden. Dabei sollen
sich ähnliche Objekte eines Datenbestandes in einem Cluster zusammengefasst werden,
wobei sich unähnliche Objekte in unterschiedlichen Clustern befinden sollen. Folgend
verdeutlicht Abbildung 3 diesen Vorgang:
Abbildung 3: Zuweisung der Datenpunkte zu drei farblich gekennzeichneten Cluster
Zur Erreichung dieses Zieles haben sich in der Vergangenheit zwei Hauptkategorien der
Clusterverfahren etabliert. Diese sind das partitionierende sowie das hierarchische Ver-
fahren, welches einen höheren Rechenaufwand benötigt. Das hierarchisch agglomerati-
ve Verfahren wird in dieser Arbeit vorgestellt und verwendet.
3.4.1 Clusterdefinition
In der disjunkten Clusteranalyse, welche in dieser Ausarbeitung behandelt wird, ist ein
Cluster einer Datenobjektmenge wie folgt definiert:
definiert eine gewählte Korrelationsfunktion
Formel 6: Clusterdefinition
3 Grundlagen 14
3.4.2 Clusterverfahren
3.4.3 Partitionierende Verfahren
Partitionierende Clusterverfahren ordnen die Objekte eines zu analysierenden Datenbe-
standes iterativ einer fest vorgegebenen Anzahl an Ergebnisclustern zu. In jeder Iterati-
on wird versucht, gemäß dem verwendeten Distanzmaß die Ergebnisclusterzugehörig-
keit der Objekte zu optimieren. Sind keine weiteren Optimierungen mehr möglich, ter-
miniert das Verfahren. Ein offensichtlicher Nachteil dieser Verfahren ist die notwendige
Festlegung der Ergebniscluster, welche somit für ein zufriedenstellendes Ergebnis opti-
mal gewählt werden müssen. Der bekannteste Vertreter der partitionierenden Verfahren
ist das K-Means-Verfahren, welches eine Komplexität von besitzt.
3.4.4 Hierarchische Verfahren
Die Festlegung der Ergebniscluster bei partitionierenden Clusterverfahren ist bei hierar-
chischen Verfahren nicht nötig. Diese verfolgen entweder eine top-down (divisive) oder
eine bottom-up (agglomerative) Strategie, welches in diesem Abschnitt erklärt wird.
Aufgrund ihrer teilenden oder zusammenfügenden Iterationsschritte können diese Ver-
fahren jederzeit ohne Einbußen in der Clusterqualität gestoppt werden.
Agglomerative Verfahren (HAC)
Das bottom-up-Verfahren startet mit der Einteilung aller Objekte in einzelne Initialclus-
ter und fügt diese in jeder Iteration anhand der ähnlichsten Elemente zusammen. Dies
wird solange durchgeführt, bis alle Initialcluster in einem großen Cluster zusammenge-
fügt worden sind.
Agglomerative Clustermethoden (HACM)
Hierarchisch agglomerative Clustermethoden werden in zwei Arten unterteilt, die geo-
metrischen Methoden, wie die Zentroid- oder Wardmethode sowie den grafenorientier-
ten Methoden wie Average-Linkage, Single-Linkage und Complete-Linkage.
• Average Linkage: Die Distanz eines Clusters gegenüber einem anderen soll
gleich der durchschnittlichen Distanz jedes Elementes dieses Clusters gegenüber
jedem Element des anderen Clusters sein.
Folgend sind die notwendigen Rekursionsschritte der Methoden aufgelistet:
a. Findung des Maximums
Der Cluster dessen Elemente die höchste Ähnlichkeit vorweisen, wird gesucht
(Komplexität von ).
b. Update der Nachbarn
3 Grundlagen 15
Alle Cluster werden durchlaufen und mittels einer gewählten Clustermethode
werden die Nachbarn des Maxima Clusters aktualisiert (Komplexität von ).
Diese drei vorgestellten Verfahren sind stark mit der Erstellung eines gewichteten, mi-
nimalen Spannbaumes verbunden und sind zeitlich in lösbar [30].
3.5 Ergebnispräsentation
Die Ergebnispräsentation einer Clusteranalyse kann, in Abhängigkeit des Anwendungs-
gebietes, eine hohe Bedeutung für den Benutzer besitzen. Das Ergebnis wird meistens
als Dendogramm oder vereinfacht als Baumstruktur präsentiert. So wäre es dem Benut-
zer im Falle einer Such- oder Empfehlungsanwendung sehr schnell möglich, weitere,
für ihn relevante Informationen zu erhalten oder abzurufen.
3.6 General Purpose Computation on Graphics Processing
Unit (GPGPU)
Die Verwendung eines Grafikprozessors für allgemeine Berechnungen wird mittels
GPGPU abgekürzt. Vor der Einführung von programmierbaren GPGPU-Schnittstellen
um das Jahr 2006 (CTM der Firma AMD) wurden die vorhandenen Grafikschnittstellen
zweckentfremdet. Die neu eingeführten GPGPU-Schnittstellen ermöglichten Entwick-
lern einen nun freien Zugriff auf den Videospeicher und nativen Befehlssatz der GPUs.
Die beiden größten Grafikkartenhersteller AMD und Nvidia sind beide unter Führung
der Khronos Group Mitglied der OpenCL-Arbeitsgruppe. Diese führt die Weiterent-
wicklung ihrer plattformunabhängigen GPGPU-Schnittstelle OpenCL voran.
Momentan konkurriert AMD mit ihrer auf OpenCL basierenden ATI-Stream-
Entwicklungsumgebung gegen Nvidias Cuda Framework [31].
Aufgrund der sehr ähnlichen Grafikkartenarchitekturen beider Hersteller basieren beide
auf der Hochsprache C und beide besitzen eine gemeinsame Schnittmenge mit den rele-
vantesten Funktionen. Aus diesem Grund sind OpenCL-fähige GPU-Programme, soge-
nannte GPU-Kernel, auch auf GPUs von Nvidia lauffähig. Jedoch besitzen OpenCL-
Kernel meistens eine niedrigere Performanz gegenüber nativen Cuda Implementierun-
gen [32] [33]. Zudem ist Nvidia auch aufgrund von erhöhten Marketinganstrengungen,
insbesondere zu Beginn der GPGPU-Entwicklung mit Cuda in der Wissenschaft und
Wirtschaft sehr viel stärker vertreten und wird stetig weiterentwickelt [34].
Zum Verständnis der in Kapitel 4 vorgestellten Implementierungsdetails bedarf es zu-
nächst eines Überblicks über den Aufbau aktueller Grafikkartenhardware am Beispiel
von Nvidia-Grafikkarten basierend auf der aktuellen Fermi-Architektur. Anschließend
3 Grundlagen 16
erfolgt ein Einblick in die aktuelle Grafikprogrammierung anhand der Cuda-
Technologie.
3.6.1 Grafikkarten-Architektur
Aktuelle Grafikkarten besitzen eine skalierbare, parallele SIMD-Architektur. SIMD
steht für „Single Instruction Multiple Data―, was ins Deutsche übersetzt so viel wie „Ei-
ne Instruktion für Mehrere Daten― heißt. In früheren Architekturen besaßen die einzel-
nen Vertex-, Pixel- und Geometrieshader aufgrund ihrer unterschiedlich benötigten Be-
fehlssätze unterschiedliche Implementierungen in der GPU-Hardware. Mit notwendig
erweitertem Funktionsumfang dieser Shadereinheiten näherten diese sich immer mehr
einander an, weshalb der Ansatz der Universalshader verwirklicht wurde. Somit erfolgt
keine Unterteilung der Shader in unterschiedliche Typen mehr, weshalb alle denselben
Funktionsumfang besitzen. Dies spiegelt sich in einer Vereinfachung der Grafikkarten-
architektur wider.
Aktuelle Grafikkarten besitzen eine beliebige Anzahl an Streaming-Multiprozessoren
(SM), welche aus mehreren Hardwarekernen bestehen [2] (siehe Abbildung 15 und 16
im Anhang). Jeder dieser Kerne besitzt eine vollständige Integer-Arithmetik- (ALU)
sowie Fließkommazahlen-Einheit (FPU). Kerne eines SM teilen sich einen Register-
sowie gemeinsamen Speicher, engl. Shared Memory (SM) und L1 Cache. Die Skalier-
barkeit wird durch die Anzahl der SM und deren Kerne erreicht.
3.6.2 Compute Unified Device Architecture (Cuda)
Cuda, die von Nvidia entwickelte Architektur zur parallelen Berechnung auf GPUs,
besteht aus mehreren Abstraktionsebenen (s. Abbildung 28 im Anhang). Die unterste ist
die anwendungssprachunabhängige Driver-API mit höchstmöglicher Kontrolle des
Entwicklers, darüber liegt die Runtime-API, welche es ermöglicht Cuda-Kernel mit in
das C-Programm zu integrieren und einen Emulationsmodus besitzt. Beide APIs sind
untereinander austauschbar. Die letzte Ebene beinhaltet die der verfügbaren Cuda-
Bibliotheken, welche stetig weiterentwickelt werden. Diese können je nach Anwen-
dungsziel den Programmieraufwand sehr stark reduzieren, da der Entwickler für allge-
meine Aufgaben keine eigenen Cuda-Kernel mehr schreiben muss. Nvidia liefert für
ihre Cuda-spezifischen C-Erweiterungen und Beschränkungen einen eigenen Compiler
namens Nvcc, welcher einen nativen C-Compiler ergänzt, mit.
3.6.3 Grundlegender Arbeitsvorgang
Der wiederkehrende grundlegende Arbeitsvorgang von Cuda sieht, wie in Abbildung 4
illustriert und in diesem Abschnitt referenziert, folgendermaßen aus: Zuerst müssen die
Daten in den Videospeicher der Grafikkarte allokiert und, falls notwendig, kopiert wer-
3 Grundlagen 17
den (1). Dann instruiert die CPU die GPU (2) den Kernel in den einzelnen Kernen paral-
lel auszuführen (3). Anschließend werden die Ergebnisdaten aus dem Videospeicher in
den Hauptspeicher zurück kopiert (4). In der zeitlichen Evaluierung müssen alle vier
Schritte, und nicht nur die alleinige Ausführung des Kernels, betrachtet werden.
Abbildung 4: Arbeitsvorgang von Cuda
Seit Cuda 4.0 und dem Rechenmodell 2.0 kann die GPU bei expliziter Nutzung auch auf
den Hauptspeicher der CPU zugreifen, was bei einer einmaligen Nutzung der Daten den
Vorgang beschleunigen kann [3].
3.6.4 Kernelausführung
Die parallelen GPU-Routinen, die Kernel, können nur durch die CPU gestartet und
grundsätzlich nur sequentiell auf der GPU ausgeführt werden. Seitdem Grafikkarten der
neuesten Generation das Cuda-Rechenmodell 2.0 unterstützen, können diese auch semi-
parallel ausgeführt werden. Es ist dabei die Aufgabe des Entwicklers, Race-Conditions
zu vermeiden. Cuda implementiert zur Ausführung der Kernel eine hierarchische Pro-
grammierstruktur, welche aus einem Gitter (engl. Grid) pro Kernel besteht und in Ab-
bildung 5 verdeutlicht wird. Dieses Gitter besteht aus mehreren Blöcken, welche wiede-
rum aus mehreren Threads bestehen. Blöcke eines Gitters und Threads eines Blocks
sind bis zu dreidimensional angeordnet, jedoch in ihrer jeweiligen Anzahl an Blöcken
oder Threads limitiert. Die Anzahl der Blöcke je Gitterdimension ist bis zum Rechen-
modell 2.0 auf 65535 limitiert. Die Anzahl der Threads pro Block liegt abhängig vom
Rechenmodell zwischen 512 und 1024. Die Anzahl an zu nutzenden Blöcken und
Threads müssen der Grafikkarte explizit beim Start des Kernels übergeben werden. Um
Main
Memory
GPU
Memory
CPU
GPC
SM SM
2
3
4
1
3 Grundlagen 18
eine hohe Auslastung der Grafikkarte zu gewährleisten, bedürfen diese Werte einer vo-
rangehenden sorgfältigen Überlegung. Weiterhin müssen die Kernel-Algorithmen an
diese Limitationen skalierbar angepasst werden, um eine hohe Performanz und Kompa-
tibilität erreichen zu können. Ein SM führt konsekutive Threads eines Blockes immer in
einer Gruppe von 32 parallelen Threads, einem Warp, aus und es befinden sich maximal
8 Blöcke in einem SM (bis Rechenmodell 2.0). [3].
Abbildung 5: Kernelausführung © Nvidia
Grundlegende Kontrollmechanismen dieser Struktur sind die Indizes der Blöcke und
Threads. Weiterhin können Threads eines Blockes miteinander synchronisiert werden.
Eine globale Synchronisierung aller Blöcke ist grundlegend nur über die Terminierung
eines Kernels realisierbar, und nicht im Sinne einer effektiven parallelen Ausführung.
3.6.5 Speichermodell
Eine effiziente Nutzung der unterschiedlichen Speicher der GPU ist mit von höchster
Priorität zur Erreichung einer hohen Performanz. Wie in Abbildung 6 zu erkennen, be-
sitzt jeder Thread eigene Register und einen eigenen lokalen Speicher. Threads eines
Blockes teilen sich einen gemeinsamen Speicher (SMEM) und jeder Thread besitzt Zu-
griff auf den globalen Video-, Textur- und Konstantenspeicher, welche in einem Cache
zwischengespeichert werden [3]. Die Pfeile verdeutlichen die Schreib- bzw. Leserechte
eines Threads. Jeder dieser Speicher besitzt unterschiedliche Vor- und Nachteile. Der
globale und lokale Videospeicher, das RAM, besitzt die größte Kapazität, jedoch auch
die größte Latenz, mit ungefähr 400-600 Taktzyklen für jede Lese- oder Schreiboperati-
on (abhängig von der Grafikkarte). Hingegen ist die Anzahl aller Register per SM be-
GPUCPU
Kernel
1
Kernel
2
Serial
Code
Serial
Code
Grid 1
Block
(0, 0)
Block
(1, 0)
Block
(2, 0)
Block
(0, 1)
Block
(1, 1)
Block
(2, 1)
Grid 2
Block (1,1)
Thread
(0,0)
Thread
(1,0)
Thread
(2,0)
Thread
(3,0)
Thread
(0,1)
Thread
(1,1)
Thread
(2,1)
Thread
(3,1)
Thread
(0,2)
Thread
(1,2)
Thread
(2,2)
Thread
(3,2)
3 Grundlagen 19
grenzt und bei Überschreitung dieses Limits werden Variablen in den langsamen loka-
len Speicher ausgeladen. Register und der gemeinsame Speicher sind auf dem Chip lo-
kalisiert und benötigen für eine Lese- oder Schreiboperation nur ein bis vier Taktzyklen.
Abbildung 6: Cuda-Speichermodell © Nvidia
Speicherzugriff wird generell in zwei einzelne Speicherzugriffe für jeden halben Warp
(16 Threads) aufgeteilt, um mögliche Konflikte zu vermeiden. Bei Zugriff zum globalen
Videospeicher wird direkt ein konsekutiver Speicherbereich (16-bit) für die Threads
eines halben Warp geladen. Somit können bei einer abgleichenden2
Speicherindexierung
im Kernel die Speicherzugriffe mehrerer Threads zu einem Zugriff verschmolzen wer-
den, was zu einer erhöhten Performanz führt und von hoher Bedeutung für den Entwick-
ler ist.
2
Ein Thread eines halben Warp mit Index i muss auf das Element an Index i zugreifen.
GPU
Global
Memory
Constant
Memory
Texture
Memory
Block (0,1)
Shared Memory
Thread(0, 0)
Registers
Local
Memory
Thread(1, 0)
Registers
Local
Memory
Block (0,1)
Shared Memory
Thread(0, 0)
Registers
Local
Memory
Thread(1, 0)
Registers
Local
Memory
CPU
4 Eingeschlagener Realisierungsweg 20
4 Eingeschlagener Realisierungsweg
Die entwickelte Clusteranwendung wurde in der Plattform-unabhängigen und Objekt-
orientierten Programmiersprache Java (Version 1.6) geschrieben und als 64-Bit Anwen-
dung konzipiert. Cuda wurde in der aktuellsten Version 4.0 verwendet. Da der Cuda-
Code auf der Sprache C basiert, kann er nur in dieser und mit der Runtime API direkt
geschrieben werden, weshalb eine Sprachverbindung für Java benötigt wird [35]. Da
das Nvidia Entwickler-SDK keinen nativen C-Compiler mitliefert, wurde zur Kompilie-
rung des Cuda-Programmcode der 64-Bit C-Compiler der Visual Studio 2008 Entwick-
lungsumgebung von Microsoft verwendet.
Um eine qualitative Aussage in der Evaluierung zu erreichen, wurden zwei, an die je-
weilige Architektur angepasst, möglichst gleichwertige Implementierungen der Cluster-
analyse erstellt. Dies bedeutet auch, falls möglich, die Nutzung aller CPU-Kerne in der
CPU-Implementierung. Diese beiden Pfade implementieren zu großen Teilen dieselben
Programmierinterfaces in Form von abstrakten Klassen, um einen identischen Ablauf
des Clusterprozesses zu gewährleisten und zur Vermeidung von redundanten Codetei-
len.
Nach anfänglicher Implementierung der einzelnen Schritte der Textvorverarbeitung auf
die GPU realisierte ich, dass diese den zeitlichen Rahmen dieser Bachelorarbeit über-
steigen würde und entschied mich, die komplette Textvorverarbeitung inklusive Be-
rechnung der Termfrequenz auf der CPU durchzuführen. Da diese Schritte noch unab-
hängig von der eingesetzten Kollektion sind, bedarf es, je Dokument nur einer einmali-
gen Durchführung dieser Schritte. Demzufolge haben diese im eingeschlagenen Reali-
sierungsweg eine niedrigere Priorität und werden in Abschnitt 4.2 nur kurz behandelt.
Konzeptschritte
Folgende Schritte werden in diesem Kapitel für beide Implementierungen erläutert:
1. Dokumentenrepräsentation
a. IDF-Berechnung
b. Normalisierung
2. Ähnlichkeitsanalyse
3. Clusteranalyse
a. Findung des Maximums
b. Update der Nachbarn
4 Eingeschlagener Realisierungsweg 21
4.1 Allgemeine Implementierungsdetails
Zum Verständnis der in diesem Kapitel vorgestellten Algorithmen bedarf es zuerst der
Vorstellung der Clusterimplementierung. Ein Cluster wurde als Binärknoten implemen-
tiert, welcher zwei Cluster oder Dokumente als Kindsknoten besitzt (Abbildung 7). Do-
kumente als Kindsknoten sind mit einem Blatt des binären Baumes gleichzusetzen, wel-
cher von beiden Varianten aufgespannt und anschließend mittels einer Benutzeroberflä-
che angezeigt wird.
Abbildung 7: Cluster-Implementierung
4.1.1 Ähnlichkeitsanalyse
Für eine effektive Datenspeicherung wird die Ähnlichkeitsmatrix (DDM) mittels For-
mel 11 in eine eindimensionale Liste konvertiert. Abbildung 8 und Abbildung 9 ver-
deutlichen diesen Vorgang. Die Referenzen der beiden Kinder eines Clusters werden
mittels der konstanten Spalten- und Reihenindizes realisiert:
Abbildung 8: Ähnlichkeitsmatrix von fünf Dokumenten
Abbildung 9: Eindimensionale Form der Ähnlichkeitsmatrix aus Abbildung 8 mit Do-
kumenten-Reihen und Zeilenreferenzen
parent
left child
/ row
right child
/ column
4 53
0
7 8
1 2
0
1
2
3
4
43210DD
6 9
0 1 2 4 53 7 86 9idx
1 2 2 3 33 4 44 4row
0 0 1 1 20 1 20 3col
4 Eingeschlagener Realisierungsweg 22
4.1.2 Update der Nachbarn
Abbildung 10 verdeutlicht den in dieser Arbeit entwickelten Algorithmus zur Aktuali-
sierung der Cluster. In dem iterativen Updateschritt der Clusteranalyse werden die Clus-
ter der eindimensionalen DDD bis auf den Maxima Cluster (rot) durchlaufen und falls
sich ein Cluster in Kindsrelation (dunkelrot) mit dem Maxima-Cluster befindet (grün),
wird das jeweilige Kind dieser Relation des Clusters durch den Maxima-Cluster ersetzt.
Diese Relation führt dazu, dass immer zwei Cluster, die Nachbarn, zu einem neuen
Cluster zusammengefügt werden, wie der braune bidirektionale Pfeil verdeutlicht. Der
Wert dieses neuen Clusters wird durch das eingesetzte Distanzmaß, hier Average-
Linkage, bestimmt. Die Kinder des neuen Clusters werden durch den Nachbarn mit mi-
nimalem Index bestimmt. Orange identifiziert Cluster, welche sich in keiner Kindsrela-
tion mit dem Maxima-Cluster befinden und direkt in die Ausgabe kopiert werden. So
wird in jeder Rekursion die DDM zur nächstkleineren Dreieckszahl hin verkleinert.
Dieser Vorgang wird in zwei Schritte unterteilt. Zuerst werden die neuen Werte und
Referenzen der zusammengefügten Cluster, mittels mehrerer CPU-Threads (CPU, s.
Abschnitt 4.2.3) oder der Grafikkarte (GPU, s. Abschnitt 4.3.7) parallel berechnet. An-
schließend wird durch einen weiteren CPU-Thread der Clusterbaum asynchron aktuali-
siert. Da sich die Anzahl der Cluster und somit das Wertearray in jeder Iteration zur
nächstkleineren Dreieckszahl hin verkleinert und nur die Werte dieser neuen Cluster
berechnet werden, benötigt die Aktualisierung des Clusterbaumes auf der CPU die Re-
ferenz zu dem jeweiligen originalen Cluster (Nachbar mit minimalen Index) sowie die
Kindsposition des Maxima-Clusters. Weitere detaillierte Informationen zu diesem Algo-
rithmus befinden sich auf dem mitgelieferten Datenträger [36].
Abbildung 10: Durchführung eines Clusteranalyse-Rekursionsschrittes
0.938 0 0 1.057 0.539 0 0.662 0.3470.274 0.000
1 0 2 0 2 1 3 0 3 1 3 2 4 0 4 1 4 2 4 3
0
2 1
0.274 0.000
4 1 4 2
0.738
1
0
2
0.505
41.05
3 0
1.05
3 0
1.05
3 0
0 00222
10 2 86 7
max pos
orig ref
index 0 1 2 3 4 5 876 9
4 Eingeschlagener Realisierungsweg 23
4.2 CPU
Die komplette Textvorverarbeitung der Dokumente wird auf der CPU ausgeführt und
bedarf keiner detaillierten Beschreibung. Jedes Dokument wird sequentiell eingelesen
und mittels einer Hash-Tabelle werden die Dokumentenvektoren mit TF-Werten erstellt.
Dabei werden die von nun an ungenutzten Textvariablen einzelner Dokumente manuell
freigegeben, um den Hauptspeicher direkt zu entlasten.
Der in dieser Arbeit verwendete Algorithmus zum Stemming von englischsprachigen
Texten ist der Porter-Algorithmus von Martin Porter [37].
Jeder der in den folgenden Unterabschnitten beschriebenen Schritte nutzt, falls vorhan-
den, mehrere Kerne der CPU aus. Die maximale Anzahl an Threads ist äquivalent zu
der Anzahl an CPU-Threads.
4.2.1 Dokumentenrepräsentation
IDF-Berechnung
Die nebenläufige TF-IDF-Berechnung arbeitet pro Thread auf einer Dokumentenpartiti-
on der Kollektion. Vor dem Aufruf der TF-IDF-Methode durch die erstellten Threads
fügt der erstellende Hauptthread alle eindeutigen Terme der Dokumentenvektoren in
einer globalen Hashtabelle zusammen. Dabei berechnet er gleichzeitig die logarithmier-
te Dokumentenhäufigkeit (DF) der Terme. Dies macht eine redundante Berechnung
dessen in den Threads obsolet und führt zu einer Verkürzung der Laufzeit der TF-IDF-
Berechnung um den Faktor 5.
Normalisierung
Die Implementierung der Normalisierung ist analog zur TF-IDF-Berechnung parallel
umgesetzt. Hierbei normalisiert ein Thread die Dokumentenvektoren seiner Partition an
Dokumenten.
4.2.2 Ähnlichkeitsanalyse
Die Ähnlichkeitsanalyse wird auch mittels mehrerer Threads durchgeführt. Da die An-
zahl der zu berechnenden Paare mit Durchlauf der Dokumente abnimmt, bedarf es einer
ungefähr gleichmäßigen Verteilung der Dokumente über die Threads. Somit berechnet
ein Thread die Ähnlichkeit für seine Dokumentenpaarmenge mit Formel 7, welche
anschließend mittels Abbildung 11 visualisiert wird.
4 Eingeschlagener Realisierungsweg 24
Formel 7: Dokumentenpaare, deren Ähnlichkeit ein Thread bestimmt
Abbildung 11: Visualisierung der CPU-Threadausnutzung der Ähnlichkeitsanalyse mit
farblich gekennzeichneten Threads und folgenden Parametern: |T|=3; |D|=5; p=2
Das Ergebnis der einzelnen Threads, die Initialcluster, wird in dynamischen Listen vom
Typ ArrayList gespeichert und anschließend vom Hauptthread zusammengefügt.
Daraus ergibt sich, wie in Abbildung 12 verdeutlicht, eine komprimierte Version der
TDM in Form einer eindimensionalen Liste mit der in Formel 10 definierten Länge.
Abbildung 12: Schaubild der aus Abbildung 11 komprimierten DDMs
4.2.3 Clusteranalyse
Auf Basis dieser eindimensionalen Ähnlichkeitsmatrix erfolgt in diesem Abschnitt die
hierarchisch agglomerative Clusteranalyse auf der CPU. Folgend werden die beiden
Teilschritte der Iteration erläutert sowie deren Ablauf in Abbildung 13 präsentiert.
Die Berechnung der neuen Clusterwerte wurde nicht direkt auf dem CPU-Clusterbaum
durchgeführt, da dies die Komplexität des Algorithmus sehr stark erhöhen würde (vgl.
Code 8: Cluster-Aktualisierungsalgorithmus auf dem Cluster-Binärbaum, Codezeile:
32).
0
1
2
3
4
43210DD
0
1
2
1
0
T
4 Eingeschlagener Realisierungsweg 25
Abbildung 13: Ablaufdiagramm der CPU-Clusteranalyse
Findung des Maximums
Die Findung des Clusters in der eindimensionalen DDM, dessen Kinder die höchste
Ähnlichkeit aufweisen wird nebenläufig ausgeführt. Zum Einsatz kommt ein paralleler
Reduktionsalgorithmus, in dem jeder Thread in seinem Teil der DDM den lokalen Ma-
xima-Cluster findet. Nach Terminierung der Threads durchsucht der initiierende Haupt-
thread die lokalen Maxima nach dem globalen Maxima-Cluster.
Update der Nachbarn
Auf Basis eines primitiven float Arrays wird die Berechnung der neuen Clusterwerte
unter Benutzung der Reihen- und Spaltenindizes als Kindsreferenzen durchgeführt. An-
schließend spannt ein weiterer CPU-Thread asynchron den CPU-Clusterbaum weiter
auf (s. 4.1.2). Die beiden verwendeten Klassen Idx2D und El2D und sind identisch
mit den in der GPU-Variante verwendeten C-Strukturen (s. Anhang C: Erweiterter Cu-
da-Code).
Java-Implementierung
float[] clusterValues; //global input cluster values1
int clusterMaxIndex; //global input cluster maximum value2
3
float[] clusterValuesNew; //output new cluster values4
int[] clusterLinIdxRefs; //output original cluster references5
int[] clusterCopyFlags; //cluster max position and copy flags6
7
//maximum cluster childs / row and column indices8
Idx2D clusterMax = new Idx2D(indicesRow[clusterMaxIndex],9
indicesCol[clusterMaxIndex]);10
11
//cluster12
El2D cluster = null;13
//relative cluster14
CPU
Update Thread Main Thread
get max index
calc cluster values
update cpu cluster tree
while nCluster > 1
cpu sync barrier
4 Eingeschlagener Realisierungsweg 26
El2D clusterRel = null;15
16
int copy = -1, outLinIdx = -1, minRefIdx = -1;17
18
//Threads loops over the number of elements he is responsible of19
for(int i = 0; i < clusterValues.length; i++)20
{21
if(i != clusterMaxIndex)22
{23
//set cluster and relative cluster24
cluster = new ElFloat2D(indicesRow[i], indicesCol[i],25
clusterValues[i]);26
clusterRel = new ElFloat2D(cluster.row, cluster.column,27
cluster.value);28
29
//0 = direct copy of cluster, no relative30
//1 = cluster max will be merged as right child, 2 = left31
copy = 0;32
33
//find relative cluster34
if(cluster.row == clusterMax.column)35
{36
copy = 1;37
clusterRel.set(clusterMax.row, cluster.column);38
}39
else if(cluster.row == clusterMax.row)40
{41
copy = 1;42
if(clusterMax.column > cluster.column)43
clusterRel.set(clusterMax.column, cluster.column);44
else45
clusterRel.set(cluster.column, clusterMax.column);46
}47
else if(cluster.column == clusterMax.column)48
{49
copy = 2;50
if(cluster.row > clusterMax.row)51
clusterRel.set(cluster.row, clusterMax.row);52
else53
clusterRel.set(clusterMax.row, cluster.row);54
}55
else if(cluster.column == clusterMax.row)56
{57
copy = 2;58
clusterRel.set(cluster.row, clusterMax.column);59
}60
61
//merge neighbors at their minimum index and calculate new value62
if(copy != 0)63
{64
clusterRel.value = clusterValues[getMatLinIdx(clusterRel.row,65
clusterRel.column)];66
cluster.row = Math.min(cluster.row, clusterRel.row);67
cluster.column = Math.min(cluster.column, clusterRel.column);68
cluster.value = 0.5f * (cluster.value + clusterRel.value);69
}70
71
//Update Row and Column Indices by reducing them with one72
//if they are larger then their cluster max counterparts73
if(cluster.row >= clusterMax.row)74
{75
cluster.row--; // = max(0, cluster.row - 1);76
//non-copy clusters dont need column decrease77
4 Eingeschlagener Realisierungsweg 27
if(copy == 078
&& cluster.column > clusterMax.row)79
cluster.column = Math.max(0, cluster.column - 1);80
}81
82
minRefIdx = Math.min(i, getMatLinIdx(clusterRel.row,83
clusterRel.column));84
85
//output at minimum index86
if(minRefIdx == i)87
{88
//Get output linear index (s. Formel 11)89
outLinIdx = Util.getMatLinIdx(cluster.row, cluster.column);90
91
clusterValuesNew[outLinIdx] = cluster.value;92
clusterLinIdxRefs[outLinIdx] = minRefIdx;93
clusterCopyFlags[outLinIdx] = copy;94
}95
}96
}97
Code 1: Java-Code zur Speicherallokation und Kopierung einer Matrix
4.3 GPU
Bei der Implementierung der einzelnen Schritte auf die GPU wurde auf die Gewährleis-
tung einer höchstmöglichen Kompatibilität geachtet, weshalb alle implementierten Ker-
nel auf dem Cuda-Rechenmodell 1.0 lauffähig sind. Weiterhin wurde auf die maximale
Skalierbarkeit der einzelnen Cuda-Kernel geachtet. Falls die Anzahl der Blöcke in den
einzelnen Kerneln das Limit des vorzeichenlosen 32-Bit Integer-Datentyp3
überschrei-
ten würde, müsste für die Indizierungen und Längenangaben auf 64-Bit gewechselt
werden. Da die Kernel bei den untersuchten Kollektionsgrößen dieses Limit nicht errei-
chen werden, wurde darauf verzichtet. Alle Implementierungsskizzen in diesem Ab-
schnitt benutzen für die Speicherbereiche dieselben Farben wie in Abbildung 6: Cuda-
Speichermodell © Nvidia.
4.3.1 Ressourcenausnutzung
Aufgrund der in Abschnitt 3.6.4 und 3.6.5 vorgestellten Limitationen bedarf es einiger
Vorüberlegung bezüglich der Anzahl an Blocks und dessen Threads, um die Grafikkar-
ten optimal auszulasten, da diese zur Kompilierungszeit vorhanden sein müssen. Alle
implementierten grundlegenden Kernel benutzen dieselbe Anzahl an Threads pro Block.
Jedoch benötigen Kernel, die eine parallele Reduktion durchführen eine Anzahl an
Threads gleich einer Zweierpotenz (siehe Abschnitt 4.3.4). Die optimale Anzahl an
Threads kann unterschiedlich ermittelt werden, ist jedoch von dem unterstützten Cuda-
Rechenmodell der Grafikkarte abhängig. Da keiner der entwickelten Kernel das mini-
3
Maximalwert des vorzeichenlosen Integer-Datentyp ist 4.294.967.294
4 Eingeschlagener Realisierungsweg 28
malste Limit (Rechenmodell 1.0) an nutzbaren Registern pro SM übersteigt4
, können in
dieser Arbeit die optimalen Daten für ein Cuda-Rechenmodell dynamisch mithilfe der
abrufbaren Grafikkarteninformationen wie folgt berechnet werden [38]:
Formel 8: Berechnung der optimalen Blockgröße
Die optimale Blockgröße für Kernel, die eine parallele Reduktion durchführen, wird auf
der nächsthöheren Zweierpotenz festgelegt. Diese beiden Blockgrößen führen zu einer
vollständigen Auslastung der Grafikkarte. Für das niedrigste Rechenmodell 1.0, ergibt
sich die optimale Blockgröße wie folgt:
Definition 1: Optimale Blockgrößen für das Cuda-Rechenmodell 1.0
Des Weiteren sollten Arraygrößen zur Nutzung des gemeinsamen Speichers der Threads
eines Blockes zur Kompilierzeit als Konstanten definiert werden. Diese sollten Vielfa-
che von 16 sein und wurden in dieser Arbeit abhängig von der Größe des gemeinsamen
Speichers festgelegt [3].
4.3.2 JCuda
Die verwendete Java-Bindung für Cuda ist JCuda, von Marco Hütter in der Version
0.4.0-beta1 für die aktuelle Cuda-Version 4.0 [35]. Diese besteht zum größten Teil aus
einer direkten Portierung der Originalen C Version, besitzt aber Java-bedingte Limita-
tionen. So unterstützt Cuda einige C++ sprachspezifische Elemente, wie Strukturen oder
Templates, welche in der aktuellsten Version der Java-Bindung jedoch noch nicht unter-
stützt werden. Deshalb müssen alle Algorithmen auf primitiven Datentypen basieren.
Weiterhin ist darauf zu achten, dass sich die in beiden Sprachen vorhandenen primitiven
Datentypen in ihrer Bitrepräsentation voneinander unterscheiden können.
4.3.3 Hilfsklassen zur dynamischen Speicherallokation und Kopierung
Da der Videospeicher der eigenständigen Grafikkarte nicht denselben Speicherbereich
des Hauptspeichers besitzt, benötigt es einen vermehrten Aufwand, um Daten im Vi-
4
Für das Rechenmodell 1.0 sind 10 Register pro Thread optimal. Zwei der implementierten Kernel be-
nutzten bis zu zwei zusätzliche Register, welche mittels des Nvcc-Befehls „-maxregcount=10― für das
Rechenmodell 1.0 auf 10 reduziert werden.
4 Eingeschlagener Realisierungsweg 29
deospeicher zu speichern. Speicheroperationen und Transaktionen zwischen dem Host
(CPU) und dem Device (GPU) müssen implizit aufgerufen werden. Grundlegende Me-
thoden zur Speicherallokation, Speicherkopierung und Speichersetzung in Cuda basie-
ren auf ihren nativen C Implementierungen. Die zwei in dieser Arbeit wichtigsten wer-
den in folgender Tabelle 1 vorgestellt. Hierbei sind „dst―, „src― und „devPtr― Pointer
und „size― gibt die Größe des zu bearbeitenden Speicherbereichs in Byte an. Kopiervor-
gänge in Cuda werden mittels einer Richtungsvorgabe, „cudaMemcpyKind‖, ausgeführt.
Art C Cuda Runtime API
Allokation malloc(size) cudaMalloc(devPtr, size)
Kopierung memcpy(dst, src, size) cudaMemcpy(dst, src, size, cuda-
MemcpyKind)
Tabelle 1: Cuda-Methoden zur Speicheroperation und Transaktion [39].
Eine einfache Speicherallokation eines Host-Arrays auf das Device und dessen spätere
Rückkopierung sind mit einem erheblichen Programmieraufwand verbunden, weshalb
zuerst Hilfsklassen für die obig aufgelisteten Methoden für ein- und zweidimensionale
Arrays von primitiven Datentypen (Byte, Integer, Float) erstellt wurden. Da Java
keine generischen Klassen von primitiven Datentypen unterstützt, benötigte jeder ver-
wendete Datentyp seine eigenen Hilfsklassen.
Der nachfolgende Codeausschnitt zeigt die manuelle Speicherallokation und Kopierung
eines zweidimensionalen Arrays vom Datentyp Float von dem Hostspeicher in den
Videospeicher der Grafikkarte (Device) mittels der Java-Bindung für Cuda. Dieser ver-
deutlicht den notwendigen Programmieraufwand um Daten in den Videospeicher zu
kopieren und in den GPU-Kernels nutzen zu können.
Java-Implementierung
import static jcuda.runtime.cudaMemcpyKind.cudaMemcpyHostToDevice;1
import static jcuda.runtime.cudaMemcpyKind.cudaMemcpyDeviceToHost;2
import jcuda.Pointer, jcuda.Sizeof, jcuda.runtime.JCuda;3
4
//a two dimensional non-empty Array to copy to device5
float[][] mat;6
//device Row Pointers7
Pointer[] arr_d = new Pointer[mat.length];8
//device Pointer9
Pointer data_d = new Pointer();10
int size = mat[0].length * Sizeof.FLOAT;11
12
//Allocate and copy each row of the matrix13
for (int i = 0; i < mat.length; i++)14
{15
arr_d[i] = new Pointer();16
JCuda.cudaMalloc(arr_d[i], size);17
JCuda.cudaMemcpy(arr_d[i],18
Pointer.to(mat[i]),19
4 Eingeschlagener Realisierungsweg 30
size,20
cudaMemcpyHostToDevice);21
}22
//Allocate and copy the Pointer of the Matrix Datatype23
int sizeOfArrD = mat.length * Sizeof.POINTER;24
JCuda.cudaMalloc(data_d, sizeOfArrD);25
JCuda.cudaMemcpy(data_d,26
Pointer.to(arr_d),27
sizeOfArrD,28
cudaMemcpyHostToDevice);29
Code 2: Java-Code zur Speicherallokation und Kopierung einer Matrix
4.3.4 Parallele Reduktion
In vielen Schritten des Dokumenten-Clustering muss eine Menge auf ein einzelnes
Element reduziert werden oder ein einzelner Wert aus dieser berechnet werden. Klassi-
scher Vertreter ist die Findung des Clusterpaares mit der höchsten Ähnlichkeit. Ein sehr
effizienter Ansatz für dieses Problem ist die parallele Reduktion [40] [41]. Die Imple-
mentierung dieses Ansatzes auf der GPU unterscheidet sich aber grundlegend von der
schon genutzten CPU-Variante, da C keine dynamischen Listen/ Arrays kennt und auf
der GPU sehr viel mehr Threads verwendet werden. Die parallele Reduktion auf der
GPU basiert auf folgendem Prinzip, welche an einem Beispiel in Abbildung 14 mit dem
Additions-Operator veranschaulicht wird und anschließend in Cuda generisch imple-
mentiert wird.
Die Anzahl der genutzten Threads ist gleich der Hälfte der Größe des Eingabearrays,
weshalb jeder Thread zwei Elemente miteinander vergleicht und das Resultat dieses
Vergleiches in das Array an seinem Index schreibt. Die Arraygröße muss somit ein
Vielfaches von zwei sein. Nach jeder Iteration werden alle Threads miteinander syn-
chronisiert und dieser Vorgang wird solange durchgeführt, bis sich das gewünschte
Element an der ersten Stelle des Arrays befindet.
Abbildung 14: Parallele Reduktion auf der GPU
0 1 2 4 5 6 73
3 4 1 0 8 2 6 5
Threads
Daten 2 11 7 4 9 0 10
5 15 8 4 17 2 16 9 2 11 7 4 9 0 10
4
10
22 17 24 13 9 2 10 5 2 11 7 4 9 0 10 10
46 30 10 5 9 2 10 5 2 11 7 4 9 0 10 10
76 11 10 5 9 2 10 5 2 11 7 4 9 0 10 10
Iteration: 1
Iteration: 2
Iteration: 3
Iteration: 4
4 Eingeschlagener Realisierungsweg 31
Dieser Algorithmus wird innerhalb eines Blockes durchgeführt, da nur auf dieser Ebene
die Threads miteinander effizient synchronisiert werden können. Vor der Ausführung
der Rekursion kopieren die Threads gemeinsam die Daten aus dem globalen in ihren
gemeinsamen Speicher, da dieser für die vielen Lese- und Schreibzugriffe der einzelnen
Iterationen eine sehr viel geringere Latenz aufweist.
Da die Threadanzahl pro Block limitiert, ist kann der Operator nur auf die zweifache
Blockgröße an Elementen angewendet werden. Eine höhere Anzahl benötigt somit meh-
rere Blöcke, welche jeweils ihr lokales Resultat berechnen und anschließend mittels
weiterer Kernel-Aufrufe auf das globale Element reduziert werden.
Cuda-Implementierung
/**1
* @param in T* input arraypointer2
* @param out T single output variable3
*/4
template <typename T>5
__device__ void reductionAdd(T* in,6
T &out)7
{8
if(blockDim.x >= 1024 && threadIdx.x < 512)9
in[threadIdx.x] += in[threadIdx.x + 512];10
__syncthreads();11
12
if(blockDim.x >= 512 && threadIdx.x < 256)13
in[threadIdx.x] += in[threadIdx.x + 256];14
__syncthreads();15
16
if(blockDim.x >= 256 && threadIdx.x < 128)17
in[threadIdx.x] += in[threadIdx.x + 128];18
__syncthreads();19
20
if(blockDim.x >= 128 && threadIdx.x < 64)21
in[threadIdx.x] += in[threadIdx.x + 64];22
__syncthreads();23
24
//unroll last warp no sync needed25
if(threadIdx.x < 32)26
{27
if(blockDim.x >= 64) in[threadIdx.x] += in[threadIdx.x + 32];28
if(blockDim.x >= 32) in[threadIdx.x] += in[threadIdx.x + 16];29
if(blockDim.x >= 16) in[threadIdx.x] += in[threadIdx.x + 8];30
if(blockDim.x >= 8) in[threadIdx.x] += in[threadIdx.x + 4];31
if(blockDim.x >= 4) in[threadIdx.x] += in[threadIdx.x + 2];32
if(blockDim.x >= 2) in[threadIdx.x] += in[threadIdx.x + 1];33
34
//set final value35
if(threadIdx.x == 0) out += in[0];36
}37
}38
Code 3: Cuda-Code für eine blockabhängige, generische, additive parallele Reduktion
auf einem Array im gemeinsamen Speicher (SMEM)
4 Eingeschlagener Realisierungsweg 32
4.3.5 Dokumentenrepräsentation
Da die Erstellung der Dokumentenvektoren auf der CPU mittels dynamischen Hash-
Tabellen realisiert wird, bedarf es einer Konvertierung dieser in die eigentliche zweidi-
mensionale Term-Dokument-Matrix (TDMs) mittels primitiven Float Arrays um die-
se zur weiteren Benutzung auf die Grafikkarte kopieren zu können. Die eigentlichen
Terme der TDM sind für die nachfolgenden Berechnungen irrelevant und werden bei
der Konvertierung ignoriert.
IDF-Berechnung
Die TF-IDF-Berechnung wird auf der Term-Dokumenten-Matrix (TDM) mit TF-
Werten durchgeführt. Die IDF-Berechnung benötigt die Anzahl der Dokumente, in de-
nen jeder Term vorhanden ist. Diese Zähloperation könnte mittels atomarer Operationen
vereinfacht realisiert werden. Jedoch benötigen atomare Operationen im langsamen glo-
balen Speicher mindestens das Cuda-Rechenmodell 1.1 und im SMEM sogar 1.2 [3],
weshalb diese Zähloperation pro Term mittels einer parallelen Reduktion durchgeführt
wird. Ein weiteres Argument ist die notwendige interne Serialisierung der atomaren
Operationen. Die IDF-Berechnung auf der GPU wird in Abbildung 15 visualisiert und
in darauffolgendem Codesegment implementiert. Ein Block ist für einen Term, d.h. eine
Reihe der TDM verantwortlich. Somit kann die parallele Reduktion auf dem SMEM
realisiert werden.
Abbildung 15: Cuda TF-IDF-Berechnung eines Blockes mit = Spal-
ten/Dokumenten-Anzahl, =Threadanzahl,
thread doc/row
tile loop
RowBlockIndex=b
doc tile: pdoc tile: …doc tile: 1
eb,… eb, |T|-1 eb, |D|-1-|T| eb, … eb, |D|-1eb, i*|T|
eb, (i+1)*
|T|-1)
eb, …eb, 0
TDM
O0 O… Otones
count
IDF
0 … |T|-1Threadsi
1. collect
data save
count
2. update count with
sum of ones using
parallel reduction
3. calculate IDF value
4. update elements
4 Eingeschlagener Realisierungsweg 33
Die Threads eines Blockes sind für die einzelnen Spalten, d.h. Dokumente verantwort-
lich. Da mehr Dokumente als Threads pro Block vorhanden sind, ist jeder Thread für
mehrere Dokumente verantwortlich, was mittels einer Schleife implementiert wurde
(Codeblock ab Zeile: 46). Jeder Thread prüft, ob die Werte seiner Dokumente bezüglich
des Blockterms größer als Null sind und schreibt, falls ja, eine Eins in das SMEM-
Hilfsarray (s. Abbildung 15: 1, Codezeilen: 56-69). Die Threads synchronisieren sich
und mittels der additiven parallelen Reduktion werden die Einsen in dem Hilfsarray
gezählt (2, Codezeile: 73). Nach einer weiteren Synchronisierung kann ein Thread den
IDF-Wert für diesen Term/ Block berechnen (3, Codezeile: 81). Zum Schluss aktuali-
siert jeder Thread seine Elemente mittels dieses IDF-Wertes (4, Codeblock ab Zeile:
87).
Cuda-Implementierung
/*1
* @param inOutTDM_g float** Term-Document Matrix2
* @inTermCount_s int Heigth of the Matrix equals the number of terms3
* @inDocCount_s int Width of the Matrixe equals number of documents4
* @inDocTileCount_s int Tile loop number to load terms equals to5
(s. Formel 12 mit Parametern (heigth, blockDim.x * 2))6
* @inOutputDocTileCount_s int Output document tile count equals to7
(s. Formel 12 mit Parametern (heigth, blockDim.x))8
*/9
__global__ void calcTfIdf(float** inOutTDM_g,10
const unsigned int inTermCount_s,11
const unsigned int inDocCount_s,12
const unsigned int inDocTileCount_s,13
const unsigned int inOutputDocTileCount_s)14
{15
const unsigned int blockId = blockIdx.y * gridDim.x + blockIdx.x16
+ gridDim.x * gridDim.y * blockIdx.z; //3D -> long long int17
18
if(blockId >= inTermCount_s)19
return;20
21
__shared__ unsigned int tfFlags_s[BLOCK_SIZE_POW2];22
23
//idf and termCount for the current term24
__shared__ unsigned int termCount_s;25
__shared__ float idf_s;26
27
//init shared memory28
if(threadIdx.x < BLOCK_SIZE_POW2)29
{30
tfFlags_s[threadIdx.x] = 0;31
if(threadIdx.x == 0)32
{33
idf_s = 0.0f;34
termCount_s = 0;35
}36
}37
__syncthreads();38
39
unsigned int idx1 = 0, idx2 = 0;40
41
//loop over the tiles requirered to count the non-zero elements42
for(int i = 0; i < inDocTileCount_s; i++)43
4 Eingeschlagener Realisierungsweg 34
{44
idx1 = i * BLOCK_SIZE_POW2_DOUBLE + threadIdx.x;45
idx2 = idx1 + blockDim.x;46
47
/*48
* Perform global parallel reduction step checking 2 values49
* load values into shared memory50
* save a 1 if input value is greater then 051
*/52
if(idx1 < inDocCount_s)53
{54
if(idx2 < inDocCount_s)55
{56
tfFlags_s[threadIdx.x] =57
(int)(inOutTDM_g[blockId][idx1] > 0.0f)58
+ (int)(inOutTDM_g[blockId][idx2] > 0.0f);59
}60
else61
tfFlags_s[threadIdx.x] =62
(int)(inOutTDM_g[blockId][idx1] > 0.0f);63
}64
else65
tfFlags_s[threadIdx.x] = 0;66
__syncthreads();67
68
//Do Reduction with to count the non-zero elements69
reductionAdd_ui(tfFlags_s, termCount_s);70
71
//sync tile loop72
__syncthreads();73
}74
75
//calculate shared idf value76
if(threadIdx.x == 0)77
idf_s = log10f(inDocCount_s) - log10f(termCount_s);78
__syncthreads();79
80
float tf = 0.0f;81
82
//loop over the documents this thread is responsible of83
for(int b = 0; b < inOutputDocTileCount_s; b++)84
{85
//coalesced global memory indexing86
idx1 = threadIdx.x * inOutputDocTileCount_s + b;87
if(idx1 >= inDocCount_s)88
break;89
//reduce global memory write operations90
tf = inOutMatTermDoc_g[blockId][idx1];91
if(tf > 0.0f)92
inOutMatTermDoc_g[blockId][idx1] = tf * idf_s;93
}94
}95
Code 4: Cuda-Code zur Berechnung der TF-IDF-Werte
4 Eingeschlagener Realisierungsweg 35
Normalisierung
Die Normalisierung der Dokumentenvektoren in der TDM erfolgt spaltenweise. Die
Normalisierung eines Vektors benötigt dessen Länge, welche abhängig von den einzel-
nen Werten ist. Anhand dieser Überlegungen wird die Cuda-Gitter-Struktur wie folgt
verwendet: Ein Block normalisiert einen Dokumenten-Spaltenvektor der TDM. Da es
mehr Terme in der TDM als mögliche maximale Threads pro Block gibt, ist jeder
Thread für eine Teilmenge der Terme verantwortlich, welche er mithilfe einer Schleife
(siehe Abbildung 16 Punkt 1, Codeblock ab Zeile: 42) durchläuft. Zuerst berechnen die
Threads partitionsweise die Quadrate ihrer Elemente (1, Codezeilen: 53-66), um dann
mittels paralleler Reduktion gemeinsam die Summe derer zu berechnen (2, Codezeile:
70). Daraufhin synchronisieren sich alle Threads und ein Thread berechnet die Rezipro-
ke der Wurzel dieser Summe (3, Codezeile: 80). Nach einer weiteren Synchronisierung
berechnet jeder Thread für seine Elemente mittels einer Multiplikation die normierten
Werte (4, nicht skizziert, Codeblock ab Zeile: 87).
Abbildung 16: Cuda-Zeilenvektor-Normalisierungsschema eines Blockes mit = Zei-
len/ Term-Anzahl, =Threadanzahl,
thread column tile loop
P0
P…
Pt
products
ColumnBlockIndex=b
elementtile:p
em-1-|T|, b
e…, b
em-1, b
elementtile:…
ei*|T|, b
e(i+1)*|T|-1 , b
e…, b
elementtile:1
e…, b
e|T|-1, b
e0, bsumrcpRoot
2. update sum with
sum of products using
parallel reduction
1. collect data
calculate products
3. calculate
reciprocal
square root
4. update
elements
TDM
0
…
|T|-1
Threadsi
4 Eingeschlagener Realisierungsweg 36
Cuda-Implementierung
/*1
* @param inOutMat_g float** Matrix2
* @param inWidth_s int width of the Matrix3
* @param inHeight_s int height of the Matrix4
* @param inTileCount_s int Column Tile Count5
(s. Formel 12 mit Parametern (heigth, blockDim.x * 2))6
* @inOutputTileCount_s int Output column tile count7
(s. Formel 12 mit Parametern (heigth, blockDim.x))8
*/9
__global__ void normColumn(float** inOutMat_g,10
const unsigned int inWidth_s,11
const unsigned int inHeight_s,12
const unsigned int inTileCount_s,13
const unsigned int inOutputTileCount_s)14
{15
const unsigned int blockId = blockIdx.y * gridDim.x + blockIdx.x16
+ gridDim.x * gridDim.y * blockIdx.z; //3D -> long long int17
18
if (blockId >= inWidth_s)19
return;20
21
__shared__ float dotSum_s;22
__shared__ float values_s[BLOCK_SIZE_POW2];23
24
//initialize shared memory25
if (threadIdx.x < BLOCK_SIZE_POW2)26
{27
values_s[threadIdx.x] = 0;28
if (threadIdx.x == 0)29
dotSum_s = 0.0f;30
}31
__syncthreads();32
33
unsigned int idx1 = 0, idx2 = 0;34
float value1 = 0.0f, value2 = 0.0f;35
36
//loop over the tiles requirered to compute the dotSum element37
for (int i = 0; i < inTileCount_s; i++)38
{39
idx1 = i * BLOCK_SIZE_POW2_DOUBLE + threadIdx.x;40
idx2 = idx1 + blockDim.x;41
42
/*43
* load values into shared memory44
* calculate product of 2 values already45
* at this stage so that normal reduction46
* steps result only in addition47
*/48
if (idx1 < inHeight_s)49
{50
value1 = inOutMat_g[idx1][blockId];51
value1 *= value1;52
if (idx2 < inHeigth_s)53
{54
valueTemp2 = inOutMat_g[idx2][blockId];55
values_s[threadIdx.x] = value1 + value2 * value2;56
}57
else58
values_s[threadIdx.x] = value1;59
}60
4 Eingeschlagener Realisierungsweg 37
else61
values_s[threadIdx.x] = 0.0f;62
__syncthreads();63
64
reductionAdd_f(values_s, dotSum_s); //do add reduction65
66
//sync tile loop67
__syncthreads();68
}69
70
//root is undefined if dotSum_s == 0.0f71
if (dotSum_s > 0.0f)72
{73
//calculate the root74
if (threadIdx.x == 0)75
dotSum_s = rsqrtf(dotSum_s);76
__syncthreads();77
78
float value = 0.0f;79
//output as many row cells this threads is responsible of80
for (int b = 0; b < inOutputTileCount_s; b++)81
{82
//coalesced memory access83
idx1 = threadIdx.x * inOutputTileCount_s + b;84
if (idx1 >= inHeight_s)85
break;86
value = inOutMat_g[idx1][blockId];87
//reduce global memory write operations88
if (value > 0.0f)89
inOutMat_g[idx1][blockId] = value * dotSum_s;90
}91
}92
}93
Code 5: Cuda-Code zur Zeilenvektor-Normalisierung
4.3.6 Ähnlichkeitsanalyse
Die Umsetzung der Ähnlichkeitsanalyse in Cuda folgt folgendem, in Abbildung 17 vi-
sualisierten und diesem Abschnitt referenzierten Prinzip. Jeder Block hält ein Dokument
und die einzelnen Threads dieses Blockes berechnen die Ähnlichkeiten aller anderen
Dokumente zu diesem Dokument und speichern diese an die entsprechende Stelle des
eindimensionalen Ausgabearrays. Ein Block kann jedoch nur einen Teil des Dokumen-
ten-Spaltenvektors in den gemeinsamen Speicher seiner Threads laden, da dieser be-
grenzt ist (s. oben). Weiterhin muss jeder Thread aufgrund der limitierten Threadanzahl
eines Blockes die Ähnlichkeit mehrerer Dokumente zu dem Block-Dokument berech-
nen. Aufgrund dieser beiden Limitationen muss der gemeinsame Thread-Ladevorgang
des Block-Dokumententeiles (1, Codezeilen: 38-47) und die Berechnung der paarweisen
euklidischen Ähnlichkeiten in einer verschachtelten Schleife durchgeführt werden (2,
Term-Schleife ab Zeile: 33; Dokumentenschleife ab Zeile: 50). Der Ähnlichkeitswert
eines Dokumentenpaares wird in das Ausgabearrays an den Index aus Formel 11 ge-
schrieben (3)(s. 4.1.1). Nach Berechnung dieser Werte auf der GPU werden diese in den
Hauptspeicher kopiert und mehrere CPU-Threads erstellen die Initialcluster.
4 Eingeschlagener Realisierungsweg 38
Abbildung 17: Cuda-Schema zur Berechnung der paarweisen euklidischen Ähnlichkei-
ten der Dokumentenvektoren in der TDM eines Blockes mit =Threadanzahl
Cuda-Implementierung
/*1
* @param inTDM_g float** Term-Document Matrix2
* @param inTermCount_s Number of Terms/Rows3
* @param inTermTileCount_s number of term tiles we need4
(s. Formel 12 mit Parametern (inTermCount_s, TILE_SIZE))5
* @param inDocCount_s Number of Documents/Columns6
* @param inDocTileCount_s Number of document tiles we need7
(s. Formel 12 mit Parametern (inDocCount_s, blockDim.x))8
* @param inClusterCount_s length of outDDM_g (s. Formel 10)9
* @param outDDM_g output cluster similarities array10
*/11
__global__ void calcSimTermMax(const float** inTDM_g,12
const unsigned int inTermCount_s,13
const unsigned int inTermTileCount_s,14
const unsigned int inDocCount_s,15
const unsigned int inDocTileCount_s,16
const unsigned int inClusterCount_s,17
float* outDDM_g)18
{19
const unsigned int blockId = blockIdx.y * gridDim.x + blockIdx.x20
+ gridDim.x * gridDim.y * blockIdx.z; //3D -> long long int21
22
if(blockId >= inDocCount_s)23
return;24
25
__shared__ float docBlockValues_s[TILE_SIZE];26
27
float dot = 0.0f;28
unsigned int rowStartIdx = 0, rowIdx = 0, linIdx = 0, colIdx = 0;29
int i = 0, k = 0;30
31
//iterate over the term tiles32
for(int j = 0; j < inTermTileCount_s; j++)33
{34
TDM
blockb
term tile
threadi doc tile loop
threaditermtile
loop
elementtile:…
e…, …
e(i+1)*
|T|-1 …
ei*|T|,
…
elementtile:…
e…, …
e(i+1)*
|T|-1 …
ei*|T|,
…
elementtile:…
e…, …
e(i+1)*
|T|-1 …
ei*|T|,
…
elementtile:…
e…, …
e…, …
e…, …
elementtile:…
e…, b
e…, b
e…, b
ssp
ssp
ssp
elementtile:…
e…, …
e…, …
e…, …
elementtile:…
e…, …
e…, …
e…, …
1
2
DDM
add add add
3 spp:sum of
pairwise products
4 Eingeschlagener Realisierungsweg 39
rowStartIdx = j * TILE_SIZE;35
36
//load the current term tile for the block-document37
if(threadIdx.x < TILE_SIZE)38
{39
rowIdx = rowStartIdx + threadIdx.x;40
if(rowIdx < inTermCount_s)41
docBlockValues_s[threadIdx.x] = inTDM_g[rowIdx][blockId];42
//catched in loop below!43
else44
docBlockValues_s[threadIdx.x] = 0.0f;45
}46
__syncthreads();47
48
//calculate the dot product for the documents of this thread49
for(i = 0; i < inDocTileCount_s; i++)50
{51
//coalesced52
colIdx = threadIdx.x * inDocTileCount_s + i;53
//triangular matrix54
if(colIdx >= blockId)55
break;56
//reset dot for every new document57
dot = 0.0f;58
//loop over values in shared block-document tile59
for(k = 0; k < TILE_SIZE; k++)60
{61
rowIdx = rowStartIdx + k;62
if(rowIdx >= inTermCount_s)63
break;64
dot += docBlockValues_s[k] * inTDM_g[rowIdx][colIdx];65
}66
//calculate linear output index (s. Formel 11)67
linIdx = getTriMatLinIdx(blockId, colIdx);68
//reduce global read and write operations69
if(dot >= 0.0f)70
outDDM[linIdx] += dot;71
}72
73
//sync term tile loop74
__syncthreads();75
}76
}77
Code 6: Cuda-Code zur Berechnung der paarweisen Ähnlichkeiten der Dokumente
4 Eingeschlagener Realisierungsweg 40
4.3.7 Clusteranalyse
Auf Basis der eindimensionalen Ähnlichkeitsmatrix, welche sich im globalen Video-
speicher der Grafikkarte befindet, erfolgt in diesem Abschnitt die hierarchisch agglome-
rative Clusteranalyse mit dem Average-Linkage Distanzmaß auf der GPU. Folgend
werden die beiden Teilschritte erläutert sowie deren asynchroner Ablauf in Abbildung
18 verdeutlicht:
Abbildung 18: UML-Sequenzdiagramm der GPU-Clusteranalyse
Der CPU-Haupt-Thread startet den GPU-Kernel zur Findung des Index des höchsten
Ähnlichkeitswertes und wartet auf dessen Ergebnis. Den Index des Maximums übergibt
dieser Thread zusammen mit den aktuellen Ähnlichkeitswerten der Cluster als Parame-
ter an den GPU-Kernel, welcher die neuen Werte der Cluster und deren originale Clus-
terreferenzen (Indizes) in seine Ausgabearrays speichert. Die Länge dieser Ausgabear-
rays ist eine Dreieckszahl kleiner als die des Werte-Eingabearrays. Mittels dieser Daten
aktualisiert ein weiterer CPU-Thread asynchron den CPU-Clusterbaum, was eine vo-
rangehende Synchronisierung beider CPU-Threads benötigt.
Findung des Maximums
Mittels der in Abschnitt 4.3.4 vorgestellten parallelen Reduktion wird auf der GPU der
Index des höchsten Wertes in dem Cluster-Wertearray gesucht. Dabei wird zuerst ein
Kernel ausgeführt, welcher pro Block den Index und dessen Wert berechnet und aus-
gibt. Sofern mehr als ein Block verwendet wurde, erfolgt die Ausführung des rekursiven
GPU-Reduktionskernel, welcher solange wiederholt ausgeführt wird, bis der Index mit-
tels eines einzelnen Blockes berechnet werden kann (s. Code 6). Die beiden eingesetz-
ten Kernel ähneln der in Code 3 vorgestellten Implementierung und benötigen keiner
weiteren Vorstellung.
CPU GPU
Update Thread Main Thread GPU
get max index
return max index
calc cluster values
return values
update cpu cluster tree
while nCluster > 1
cpu sync barrier
4 Eingeschlagener Realisierungsweg 41
Java-Implementierung
//create two empty values and indices output helper arrays on device1
private static CudaFloat1D outMaxValues_hd, out2MaxValues_hd;2
private static CudaInt1D outMaxIndices_hd, out2MaxIndices_hd;3
4
/**5
* Returns the index of the given device float value array6
* @param array_hd CudaFloat1D Float Array on the device7
* @return int maximum index8
*/9
public static int getMaxIdx1D(CudaFloat1D inValues_hd)10
{11
//calculate number of necessary blocks12
int blockCount = p(inValues_hd.length, BLOCK_SIZE_POW2_DOUBLE);13
14
//start first gpu reduction phase15
GPU_Kernel_FirstReduction.Start(blockCount,16
BLOCK_SIZE_POW2,17
params { inValues_hd,18
inValues_hd.length,19
outMaxValues_hd,20
outMaxIndices_hd })21
22
23
//reduce on gpu until we only have one more block24
while(blockCount > 1)25
{26
//reduce block count27
int length = blockCount;28
//calculate number of necessary blocks29
blockCount = p(blockCount, BLOCK_SIZE_POW2_DOUBLE);30
31
//start first reduction phase32
GPU_Kernel_LoopReduction.Start(blockCount,33
BLOCK_SIZE_POW2,34
params { outMaxValues_hd,35
outMaxIndices_hd,36
length,37
out2MaxValues_hd,38
out2MaxIndices_hd })39
40
//swap in and output data for loop41
outMaxIndices_hd = out2MaxIndices_hd;42
outMaxValues_hd = out2MaxValues_hd;43
}44
45
return maxIndices_hd.getResults()[0];46
}47
Code 6: Java-Pseudocode zur Ausführung der GPU-Kernel zur Berechnung des Index
des höchsten Wertes eines Arrays auf der GPU.
Update der Nachbarn
Folgend wird der GPU-Kernel zur Berechnung der neuen Clusterwerte vorgestellt, wel-
cher analog dem Konzept der CPU-Implementierung in Abschnitt 4.2.3 folgt. Der Cuda-
Code für verwendete Strukturen und Funktionen befindet sich im Anhang (s. Seite 53).
4 Eingeschlagener Realisierungsweg 42
Cuda-Implementierung
/*1
* Note: cluster maximum in smem leads to errors2
* @param inVal_g float* input cluster value array3
* @param inIdxRow_s int* input Row indices array4
* @param inCount_s int number of elements [triangular number]5
* @param inMaxIdx_s int index of the maximum Element in the given6
input value array7
* @param outValues_g float* output value array8
* @param outLinIdxRef_g int* output linear index references array9
* @param outMaxPos_g int* output maximum cluster position10
0 = no relation11
1 = left (row) child is maximum cluster left or right child12
2 = right (column) is maximum cluster left or right child13
*/14
__global__ void cluster(const float* inValues_g,15
const unsigned int* inIdxRow_g,16
const unsigned int inCount_s,17
const unsigned int inMaxIdx_s,18
float* outValues_g,19
unsigned int* outLinIdxRef_g,20
unsigned int* outMaxPos_g)21
{22
//three dimensional grid needs 64-bit integer for all lin. indices23
const unsigned int blockId = blockIdx.y * gridDim.x + blockIdx.x24
+ gridDim.x * gridDim.y * blockIdx.z;25
const unsigned int tId = blockId * blockDim.x + threadIdx.x;26
27
//maximum cluster is ignored28
if(tId >= inCount_s || tId == inMaxIdx_s)29
return;30
31
//read row indices to calculate column indices32
Idx2D clusterMax = Idx2D(inIdxRow_g[inMaxIdx_s], 0);33
clusterMax.column = getTriMatCol(inMaxIdx_s, clusterMax.row);34
35
ElFloat2D cluster = ElFloat2D(inIdxRow_g[tId], 0, inValues_g[tId]);36
cluster.column = getTriMatCol(tId, cluster.row);37
38
//relative cluster39
ElFloat2D clusterRel = ElFloat2D(cluster.row, cluster.column,40
cluster.value);41
42
//0 = direct copy of cluster, no relative43
//1 = cluster max will be merged right, 2 = left44
unsigned int copy = 0;45
46
//find relative cluster47
if(cluster.row == clusterMax.column)48
{49
copy = 1;50
clusterRel.set(clusterMax.row, cluster.column);51
}52
else if(cluster.row == clusterMax.row)53
{54
copy = 1;55
if(clusterMax.column > cluster.column)56
clusterRel.set(clusterMax.column, cluster.column);57
else58
clusterRel.set(cluster.column, clusterMax.column);59
}60
4 Eingeschlagener Realisierungsweg 43
else if(cluster.column == clusterMax.column)61
{62
copy = 2;63
if(cluster.row > clusterMax.row)64
clusterRel.set(cluster.row, clusterMax.row);65
else66
clusterRel.set(clusterMax.row, cluster.row);67
}68
else if(cluster.column == clusterMax.row)69
{70
copy = 2;71
clusterRel.set(cluster.row, clusterMax.column);72
}73
74
//merge neighbors at their minimum index and calculate new value75
if(copy != 0)76
{77
clusterRel.value = inValues_g[getMatLinIdx(clusterRel.row,78
clusterRel.column)]; //(s. Formel 11)79
cluster.row = min(cluster.row, clusterRel.row);80
cluster.column = min(cluster.column, clusterRel.column);81
cluster.value = 0.5f * (cluster.value + clusterRel.value);82
}83
84
//Update Row and Column Indices by reducing them with one85
//if they are larger then their cluster max counterparts86
if(cluster.row >= clusterMax.row)87
{88
cluster.row--;89
//non-copy clusters dont need column decrease90
if(copy == 0 && cluster.column > clusterMax.row)91
cluster.column = max(0, cluster.column - 1);92
}93
94
//get minimum reference index (s. Formel 11)95
const unsigned int minRefIdx = min(tId,96
getMatLinIdx(clusterRel.row, clusterRel.column));97
98
//output at minimum index99
if(minRefIdx == tId)100
{101
//Get output linear index (s. Formel 11)102
const unsigned int outLinIdx = getMatLinIdx(cluster.row,103
cluster.column);104
105
outValues_g[outLinIdx] = cluster.value;106
outLinIdxRef_g[outLinIdx] = minRefIdx;107
outMaxPos_g[outLinIdx] = copy;108
}109
}110
Code 7: Cuda-Code zur Berechnung der neuen Clusterwerte
5 Evaluation 44
5 Evaluation
In diesem Kapitel wird die Performanz der CPU- und GPU-Varianten der einzelnen, in
Kapitel 4 definierten Schritte der vollständigen hierarchischen Dokumenten-Cluster-
analyse miteinander verglichen. Die Textvorverarbeitung der Dokumente sowie die Be-
rechnung der Termfrequenzen werden in beiden Varianten auf der CPU durchgeführt
und deshalb nicht in dieser Arbeit evaluiert (s. Einleitung von Kapitel 4).
Als Dokumentengrundlage dient eine Kollektion von Kunden-Buchrezensionen des
Internetversandhändlers Amazon, welche im XML-Format vorliegen. Dabei werden nur
Dokumente betrachtet, welche auch einen Text in dem entsprechenden XML-Knoten
beinhalten. Im nächsten Abschnitt wird kurz die eingesetzte Hardware vorgestellt und
anschließend, im darauffolgenden Abschnitt, erfolgt eine tabellarische sowie graphische
Auswertung der Versuchsergebnisse für Ausschnitte der Kollektion mit unterschiedli-
cher Dokumentenanzahl. Die Ergebnisse werden in der in dieser Arbeit entwickelten
Anwendung als Baumstruktur präsentiert (s. Abbildung 29).
5.1 Eingesetzte Hardware
Die zur Evaluierung verwendete Hardware besteht aus einem Intel Core i7 CPU, mit 4
physikalischen Kernen, welche mittels der Hyperthreadingtechnologie in 8 Threads auf-
geteilt werden, einem 6 Gigabyte großen Hauptspeicher und einer Nvidia Geforce GTX
480 Grafikkarte mit 1.5 Gigabyte Videospeicher und 480 Kernen, welche maximal
24.576 Threads parallel ausführen können [3]. Das eingesetzte Betriebssystem ist Win-
dows 7 in der 64-bit Architektur.
5.2 Ergebnisse
Wie in Abschnitt 4.3.5 erläutert wurde, muss die auf der CPU erstellte Term-
Dokumentenmatrix (mit den Termfrequenzen) in der GPU-Variante in den globalen
Videospeicher der Grafikkarte kopiert werden, weshalb der dafür zeitlich benötigte
Aufwand mit in die zeitliche Evaluation einbezogen wird. Die zusammengefügten Eva-
luationsergebnisse der einzelnen Schritte auf der CPU (blau) und GPU (rot) werden in
nachfolgender Tabelle 2 sowie in Abbildung 19 und Abbildung 20 präsentiert.
Die Durchführung der CPU-Variante mit 4.096 Dokumenten benötigt während der Aus-
führung der Clusteranalyse fast die kompletten 6 Gigabyte des Hauptspeichers. Die
Analysierung noch größerer Dokumentenbestände würde sehr wahrscheinlich eine Aus-
lagerung temporärer Daten auf den langsamen Festplattenspeicher zur Folge haben und
zu einer Verringerung der Performanz führen.
5 Evaluation 45
Fall |D| |T| GPU COPY CPU GPU CPU GPU
I 128 10.123 0,485 3,842 0,640 0,2965 0,0494
II 256 19.816 0,882 49,945 1,282 0,9845 0,0253
III 512 22.456 1,023 107,339 2,324 0,9336 0,0202
IV 1.024 27.255 1,297 310,287 8,972 1,1118 0,0321
V 2.048 37.255 2,500 900,960 69,079 1,1808 0,0905
VI 4.096 59.013 5,266 5.450,165 619,017 2,2548 0,2561
TDM TDM-Elementkosten*10^5
Gesamt [1-3]
Tabelle 2: Gesamtergebnisse in Sekunden der Evaluation, wobei die Anzahl der
Dokumente und die Anzahl der Terme in der TDM repräsentiert.
0
1.000
2.000
3.000
4.000
5.000
128 256 512 1.024 2.048 4.096
Zeitins
Dokumentenanzahl
Gesamt [1-3]
Abbildung 19: Performanz der hierarchi-
schen Dokumenten-Clusteranalyse
0
10
20
30
40
50
128 256 512 1.024 2.048 4.096
Beschleunigungsfaktor
Dokumentenanzahl
Gesamt Beschleunigungsfaktor
Abbildung 20: GPU-Beschleunigungsfaktor
gegenüber der CPU
0,0000
0,5000
1,0000
1,5000
2,0000
128 256 512 1.024 2.048 4.096
Zeitkostenins
Dokumentenanzahl
Zeitkosten pro TDM-Element *10^5
Abbildung 21: Zeitliche Kosten pro Element der TDM
Aus Abbildung 21 kann der zeitliche Aufwand der gesamten Clusteranalyse in Abhän-
gigkeit von der Anzahl der Elemente in den jeweiligen TDMs abgelesen werden. Auf-
grund der sehr hohen Anzahl an leichten Threads steigt dieser Aufwand auf der GPU
gegenüber der CPU sehr viel langsamer an.
Die Durchführung der kompletten Clusteranalyse auf der GPU ergab eine bedeutsame
Beschleunigung des Prozesses um das bis zu 50-fache bei einer Kollektionsgröße von
bis zu 4.096 Dokumenten.
5 Evaluation 46
Die zeitlichen Evaluierungsergebnisse der einzelnen Schritte werden in folgender Ta-
belle sowie den nachfolgenden Abbildungen vorgestellt. Die benötigte Zeit zur Berech-
nung der Reihen- und Spaltenindizes der eindimensionalen DDM ist für die untersuch-
ten Fälle beider Varianten ungefähr identisch (nicht skizziert). Die Testfälle I – IV wur-
den jeweils viermal und die Testfälle V und VI jeweils zweimal für jede Variante
durchgeführt, von denen für jeden Schritt der Mittelwert berechnet wurde und in Tabel-
le 3 präsentiert wird.
Fall CPU GPU CPU GPU CPU GPU CPU GPU
I 0,009 0,002 0,001 0,003 3,804 0,060 0,028 0,089
II 0,030 0,003 0,004 0,009 49,827 0,163 0,083 0,224
III 0,044 0,003 0,006 0,021 106,727 0,345 0,560 0,930
IV 0,094 0,007 0,008 0,047 304,951 2,148 5,226 5,465
V 0,149 0,015 0,016 0,235 856,020 22,407 44,766 43,914
VI 0,406 0,063 0,063 1,516 5.018,852 287,360 430,829 324,781
3. Clusteranalyse1. a. IDF Berechnung 1. b. Normalisierung 2. Ähnlichkeitsanalyse
Tabelle 3: Performanz-Ergebnisse der einzelnen Schritte in Sekunden
0,0
0,1
0,2
0,3
0,4
128 256 512 1.024 2.048 4.096
Zeitins
Dokumentenanzahl
IDF Berechnung
Abbildung 22: Performanz der IDF-
Berechnung
0,0
0,5
1,0
1,5
128 256 512 1.024 2.048 4.096
Zeitins
Dokumentenanzahl
Normalisierung
Abbildung 23: Performanz der Dokumen-
tenvektoren-Normalisierung
Die bessere Performanz der Normalisierung von den Dokumentenvektoren auf der CPU
erfolgt aufgrund der Tatsache, dass in der CPU-Implementierung jedes Dokument nur
seine eigenen Terme enthält und die GPU die spaltenweise Normalisierung dieser Vek-
toren auf der kompletten hochdimensionalen, nur spärlich besetzten Term-Dokumenten-
Matrix durchführen muss.
5 Evaluation 47
0
1.000
2.000
3.000
4.000
5.000
128 256 512 1.024 2.048 4.096
Zeitins
Dokumentenanzahl
Ähnlichkeitsanalyse
Abbildung 24: Performanz der Ähnlich-
keitsanalyse
0
100
200
300
400
128 256 512 1.024 2.048 4.096
Zeitins
Dokumentenanzahl
Clusteranalyse
Abbildung 25: Performanz der hierarchi-
schen Clusteranalyse
Die Ähnlichkeitsanalyse ist der zeitintensivste Schritt der hierarchischen Dokumenten-
Clusteranalyse und ausschlaggebend für die Gesamtperformanz. Die Analyse benötigt
auf der CPU bis zu 300-mal mehr Zeit als auf der GPU. Auf der GPU skaliert diese sehr
viel besser, da die größere Anzahl an Threads pro Block sehr viel weniger Dokumente
je Thread laden muss und die Threads eines Blockes sehr effizient zusammen arbeiten
können. In der CPU-Variante müssen die paarweisen Ähnlichkeitswerte der Dokumente
mittels einer zweifach verschachtelten Schleife realisiert werden, wohingegen auf der
GPU die Threads eines Blockes gemeinsam das Dokument der äußeren Schleife laden
und somit die äußere Schleife durch die Blockstruktur von Cuda ersetzt wird.
Ab ungefähr 1.024 Dokumenten erreicht die Durchführung der Clusteranalyse auf der
GPU die Performanz der CPU und übersteigt diese für alle Weiteren größeren Aus-
schnitte der Kollektion.
6 Zusammenfassung und Ausblick 48
6 Zusammenfassung und Ausblick
6.1 Zusammenfassung
GPGPU-fähige Grafikkarten bieten sich für rechenaufwendige mathematische Probleme
an, als kostengünstige Co-Prozessoren der Hauptprozessoren diese Berechnungen um
das Vielfache zu beschleunigen. Sorgfältig an die jeweils aktuelle Grafikkartenarchitek-
tur angepasste parallele und skalierbare Algorithmen sind notwendig, um die theoreti-
sche Performanz der GPUs vollständig ausnutzen zu können. Weiterhin ist ein gut über-
legter synchronisierter Arbeitsablauf zwischen der CPU und GPU nötig, um die jeweili-
gen unterschiedlichen Ressourcen optimal ausnutzen zu können.
Die hier erarbeiteten Lösungsschritte5
zur Durchführung einer hierarchischen Dokumen-
ten-Clusteranalyse für beliebig große Kollektionen erreichten auf der GPU eine weitaus
höhere Performanz gegenüber den gleichwertigen parallelen CPU-Implementierungen.
Die GPU-Variante erreichte eine 5 - 50-fache Beschleunigung gegenüber ihrem CPU-
Pendant. Weitere mögliche Optimierungen beider Varianten werden im Ausblick vorge-
stellt.
6.2 Ausblick
Mehrere optimierende Beobachtungen wurden während der Ausarbeitung dieser Arbeit
gemacht und werden in diesem Abschnitt vorgestellt:
Nach Durchführung der Ähnlichkeitsanalyse sind die Ähnlichkeitswerte der Initialclus-
ter sehr klein, welche sich mit zunehmender Kollektionsgröße weiterhin verkleinern. Da
der Datentyp Float nur eine Präzision von 8 Stellen besitzt und somit anhand der vo-
rangehenden Nullstellen der Ähnlichkeitswerte viel Präzision ungenutzt verloren geht,
könnten diese Werte mit einer von der Kollektionsgröße abhängigen Zehnerpotenz mul-
tipliziert werden. Bei der Nutzung des Average-Linkage-Distanzmaß mit sehr großen
Kollektionen wird ein Verlust der Präzision unumgänglich, weshalb ein Datentyp mit
einer erhöhten Anzahl an signifikanten Stellen, wie Double, verwendet werden sollte.
Dabei ist darauf zu achten, dass ältere Grafikkarten, wie die G80-Reihe von Nvidia,
doppelte Fließkommazahlen nicht unterstützen und automatisch auf einfache Fließ-
kommazahlen umstellen (bis Rechenmodell 1.3) oder weniger Recheneinheiten für die-
se besitzen [3].
In der GPU-Variante ist die Kopierung der Ähnlichkeitswerte der Initialcluster (DDM)
von dem Videospeicher der Grafikkarte in den Hauptspeicher bei Durchführung von
5
Ausgenommen der Normalisierung der Dokumentenvektoren
6 Zusammenfassung und Ausblick 49
mindestens einer Iteration der Clusteranalyse nicht notwendig, da die Initialcluster auch
nach Durchführung der ersten Iteration erstellt werden können. Somit kann diese teure
Kopier-Transaktion vermieden werden.
Die Ähnlichkeitsanalyse kann in der CPU-Variante weiterhin direkt den Index des ers-
ten Maxima-Clusters finden, da die Initialcluster in der Analyse erstellt werden. Analog
kann die Findung in der GPU-Variante pro Block durchgeführt werden.
Mit der zukünftigen möglichen Unterstützung von C-Strukturen und Templates in der
Cuda-Bindung für Java könnte der Java- und Cuda-Quellcode, unabhängig von der Per-
formanz weiter optimiert werden. Um eine Skalierbarkeit über mehrere GPUs und
Computer hinweg zu gewährleisten, sollte das eingesetzte TF-IDF-Maß durch ein von
globalem Wissen der Gesamtkollektion unabhängiges Maß (siehe TF-ICF-Maß in Ab-
schnitt 3.2) eingesetzt werden. Cuda bietet die Möglichkeit, Arrays mittels eines soge-
nannten Abstands (engl. pitch) in Größe einer Zweierpotenz zu allokieren, was zu einer
Optimierung des Speicherzugriffes führt [3] (s. Abschnitt 3.6.5). Des Weiteren ist eine
Untersuchung über die mögliche Nutzung von Sparse-Matrizen6
auf der GPU notwen-
dig, um den Speicherbedarf der Term-Dokumentenmatrix zu minimieren und die Nor-
malisierung der Dokumentenvektoren, Berechnung der TF-IDF-Werte und die Ähnlich-
keitsanalyse zu beschleunigen. Die parallelen Reduktionsalgorithmen auf der GPU soll-
ten weiter optimiert werden, da Mark Harris in seinem optimierten parallelen Redukti-
ons-Algorithmus mehr als zwei Elemente in dem ersten Reduktionsschritt (vgl. 4.3.4)
miteinander vergleicht und somit eine höhere Performanz erreicht [41].
Um qualitativere Schlussfolgerungen in der Evaluation ziehen zu können, bedarf es ei-
ner weiteren Untersuchung von größeren Ausschnitten der Kollektion, da die CPU-
Variante den Hauptspeicher vermehrt auslasten wird und ab einer gewissen Anzahl an
Dokumenten benötigte Daten in den langsamen virtuellen Arbeitsspeicher auf der Fest-
platte auslagern werden muss.
Weiterführende Arbeiten zu diesem Thema sollten die Möglichkeit untersuchen, die
Erstellung und Aktualisierung der binären Cluster-Baumstruktur komplett auf die GPU
auszulagern.
Mit der fortschreitenden Entwicklung der Grafikkarten wird es möglich, immer größere
Datenbestände hierarchisch zu clustern und den Anwendern diverser Applikationen die
Ergebnisse ihres Clusterkontextes, wie Suchanfragen, schneller anzubieten. Mit der re-
sultierenden zunehmenden Geschwindigkeit des Clustering wird die mögliche Echtzeit-
nutzung für Bestände mit geringer Anzahl an Daten an Bedeutung zunehmen.
6
Datentypen für spärlich besetzte Arrays oder Matrizen
Anhang A: Grafikkartenarchitektur und Cuda 50
Anhang A: Grafikkartenarchitektur und Cuda
Abbildung 26: Schematische Zeichnung einer Streaming-Multiprozessoreinheit der
Nvidia Fermi-Grafikkartenarchitektur © Nvidia
SM
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
Core
SFU
SFU
SFU
SFU
SFU
SFU
SFU
SFU
LD/ST
LD/ST
LD/ST
LD/ST
LD/ST
LD/ST
LD/ST
LD/ST
LD/ST
LD/ST
LD/ST
LD/ST
LD/ST
LD/ST
LD/ST
LD/ST
Register File (32,768 x 32-bit)
Dispatch Unit Dispatch Unit
Warp Scheduler
Dispatch Unit Dispatch Unit
Warp Scheduler
Instruction Cache
64 KB Shared Memory / L1 Cache
Uniform Cache
Uniform Cache
Tex Tex Tex Tex Tex Tex Tex Tex
Texture Cache
PolyMorph Engine
Vertex Fetch Tesselator Viewport Transform
Attribute Setup Stream Output
CUDA Core
FP
Unit
INT
Unit
Operand Collector
Result Queue
Dispatch Port
Anhang A: Grafikkartenarchitektur und Cuda 51
Abbildung 27: Aufbau der Nvidia Fermi-Grafikkartenarchitektur, deutlich sind die SM
Einheiten aus Abbildung 26 zu erkennen. © Nvidia
Abbildung 28: Cuda-API-Ebenen © Nvidia
GPC
SM
PolyMorphEngine
Raster Engine
SM
PolyMorphEngine
SM
PolyMorphEngine
SM
PolyMorphEngine
MemoryControllerMemoryControllerMemoryController
MemoryControllerMemoryControllerMemoryController
GPC
SM
PolyMorphEngine
Raster Engine
SM
PolyMorphEngine
SM
PolyMorphEngine
SM
PolyMorphEngine
GPC
SM
PolyMorphEngine
Raster Engine
SM
PolyMorphEngine
SM
PolyMorphEngine
SM
PolyMorphEngine
GPC
SM
PolyMorphEngine
Raster Engine
SM
PolyMorphEngine
SM
PolyMorphEngine
SM
PolyMorphEngine
L2 Cache
GigaThread Engine
Host Interface
GPU
CPU
Application
CUDA Libraries
CUDA Runtime
CUDA Driver
Anhang B: Formeln 52
Anhang B: Formeln
Formel 9: Formel für die -te Dreieckszahl (Null-basierte Indizierung)
ist die Anzahl aller Dokumente der zu untersuchenden Kollektion
Formel 10: Anzahl der Ähnlichkeitspaare
Formel 11: Eindimensionale Index-Berechnung einer Zweidimensionalen Dreiecksmat-
rix ohne Diagonalbelegung
Formel 12: Berechnung der Partitionsgröße eines Threads
Anhang C: Erweiterter Cuda-Code 53
Anhang C: Erweiterter Cuda-Code
/*
* Matrix 2D-Element Indices Struct containing
* a row and column field with datatype int
*/
struct __align__(16) Idx2D
{
unsigned int row;
unsigned int column;
__device__ Idx2D()
{
this->set(0, 0);
}
__device__ Idx2D(unsigned int row,
unsigned int column)
{
this->set(row, column);
}
__device__ void set(unsigned int row,
unsigned int column)
{
this->row = row;
this->column = column;
}
};
/*
* represents a element from
* a two dimensional float matrix
*/
struct __align__(16) ElFloat2D : Idx2D
{
float value;
__device__ ElFloat2D(unsigned int row,
unsigned int column,
float value)
{
Idx2D::set(row, column);
this->value = value;
}
__device__ void set(unsigned int row,
unsigned int column)
{
Idx2D::set(row, column);
}
__device__ void set(unsigned int row,
unsigned int column,
float value)
{
this->set(row, column);
this->value = value;
}
};
/**
* (s. Formel 10)
* @param n int
* @return int
Anhang C: Erweiterter Cuda-Code 54
*/
__device__ unsigned int getTriNum(const unsigned int n)
{
return (unsigned int)((n - 1) * n * 0.5f); //is cast safe!
}
/*
* (s. Formel 11)
* @param row int Row of the Element
* @param column int Column of the Element
* @return int
*/
__device__ unsigned int getTriMatLinIdx(const unsigned int row,
const unsigned int column)
{
return getTriNum(row) + column;
}
/*
* Retrieves the Column Index of the given linear Index
* @param linIdx int Linear 0 based Index
* @param row int Row Index
* @return int column
*/
__device__ unsigned int getTriMatCol(const unsigned int linIdx,
const unsigned int row)
{
return linIdx - getTriNum(row);
}
ba_thesis
ba_thesis
ba_thesis
ba_thesis
ba_thesis
ba_thesis
ba_thesis
ba_thesis
ba_thesis

Weitere ähnliche Inhalte

Andere mochten auch

EU-Bürger sorgen sich um gesunde Meere.
EU-Bürger sorgen sich um gesunde Meere.EU-Bürger sorgen sich um gesunde Meere.
EU-Bürger sorgen sich um gesunde Meere.WWF Deutschland
 
Auftakt Socialbar Bremen
Auftakt Socialbar BremenAuftakt Socialbar Bremen
Auftakt Socialbar Bremenclhanken
 
DNX Workshop ★ Live life to the fullest! Was es heißt, deine Träume zu verwir...
DNX Workshop ★ Live life to the fullest! Was es heißt, deine Träume zu verwir...DNX Workshop ★ Live life to the fullest! Was es heißt, deine Träume zu verwir...
DNX Workshop ★ Live life to the fullest! Was es heißt, deine Träume zu verwir...DNX
 
Chateaux syndic
Chateaux syndicChateaux syndic
Chateaux syndicdesintocx
 
Comment ne pas oublier les acquis de la
Comment ne pas oublier les acquis de laComment ne pas oublier les acquis de la
Comment ne pas oublier les acquis de laMarion Gauthier
 
GAIS Celebrates End of the First Unit 2008
GAIS Celebrates End of the First Unit 2008GAIS Celebrates End of the First Unit 2008
GAIS Celebrates End of the First Unit 2008GAIS
 
Présentation netmessage grand manitou
Présentation netmessage grand manitouPrésentation netmessage grand manitou
Présentation netmessage grand manitoupisrael
 
Présentation de l'accessibilité numérique par Sébastien Huillet (Tribu And Co)
Présentation de l'accessibilité numérique par Sébastien Huillet (Tribu And Co)Présentation de l'accessibilité numérique par Sébastien Huillet (Tribu And Co)
Présentation de l'accessibilité numérique par Sébastien Huillet (Tribu And Co)TribuAndCo
 
FFTélécoms - Etude Roland Berger : Les opérateurs télécoms, partenaires de la...
FFTélécoms - Etude Roland Berger : Les opérateurs télécoms, partenaires de la...FFTélécoms - Etude Roland Berger : Les opérateurs télécoms, partenaires de la...
FFTélécoms - Etude Roland Berger : Les opérateurs télécoms, partenaires de la...Fédération Française des Télécoms
 
Simulations- und spielbasierte Kompetenzmessung
Simulations- und spielbasierte KompetenzmessungSimulations- und spielbasierte Kompetenzmessung
Simulations- und spielbasierte KompetenzmessungKarsten D. Wolf
 
C&PDUEDC
C&PDUEDCC&PDUEDC
C&PDUEDClelc9b
 

Andere mochten auch (17)

EU-Bürger sorgen sich um gesunde Meere.
EU-Bürger sorgen sich um gesunde Meere.EU-Bürger sorgen sich um gesunde Meere.
EU-Bürger sorgen sich um gesunde Meere.
 
Journée olympique au ddc
Journée olympique au ddcJournée olympique au ddc
Journée olympique au ddc
 
Multi Vit Plus
Multi Vit PlusMulti Vit Plus
Multi Vit Plus
 
Auftakt Socialbar Bremen
Auftakt Socialbar BremenAuftakt Socialbar Bremen
Auftakt Socialbar Bremen
 
Fotosinvernalespreciosas
FotosinvernalespreciosasFotosinvernalespreciosas
Fotosinvernalespreciosas
 
DNX Workshop ★ Live life to the fullest! Was es heißt, deine Träume zu verwir...
DNX Workshop ★ Live life to the fullest! Was es heißt, deine Träume zu verwir...DNX Workshop ★ Live life to the fullest! Was es heißt, deine Träume zu verwir...
DNX Workshop ★ Live life to the fullest! Was es heißt, deine Träume zu verwir...
 
Chateaux syndic
Chateaux syndicChateaux syndic
Chateaux syndic
 
Dms
DmsDms
Dms
 
Comment ne pas oublier les acquis de la
Comment ne pas oublier les acquis de laComment ne pas oublier les acquis de la
Comment ne pas oublier les acquis de la
 
Recommandations déontologiques SVA+
Recommandations déontologiques SVA+Recommandations déontologiques SVA+
Recommandations déontologiques SVA+
 
URL en Flickr
URL en FlickrURL en Flickr
URL en Flickr
 
GAIS Celebrates End of the First Unit 2008
GAIS Celebrates End of the First Unit 2008GAIS Celebrates End of the First Unit 2008
GAIS Celebrates End of the First Unit 2008
 
Présentation netmessage grand manitou
Présentation netmessage grand manitouPrésentation netmessage grand manitou
Présentation netmessage grand manitou
 
Présentation de l'accessibilité numérique par Sébastien Huillet (Tribu And Co)
Présentation de l'accessibilité numérique par Sébastien Huillet (Tribu And Co)Présentation de l'accessibilité numérique par Sébastien Huillet (Tribu And Co)
Présentation de l'accessibilité numérique par Sébastien Huillet (Tribu And Co)
 
FFTélécoms - Etude Roland Berger : Les opérateurs télécoms, partenaires de la...
FFTélécoms - Etude Roland Berger : Les opérateurs télécoms, partenaires de la...FFTélécoms - Etude Roland Berger : Les opérateurs télécoms, partenaires de la...
FFTélécoms - Etude Roland Berger : Les opérateurs télécoms, partenaires de la...
 
Simulations- und spielbasierte Kompetenzmessung
Simulations- und spielbasierte KompetenzmessungSimulations- und spielbasierte Kompetenzmessung
Simulations- und spielbasierte Kompetenzmessung
 
C&PDUEDC
C&PDUEDCC&PDUEDC
C&PDUEDC
 

Ähnlich wie ba_thesis

Blockchain-based access right management for private data in decentralized cl...
Blockchain-based access right management for private data in decentralized cl...Blockchain-based access right management for private data in decentralized cl...
Blockchain-based access right management for private data in decentralized cl...ArtemEger
 
Multicore Parallele Programmierung Kng617
Multicore Parallele Programmierung Kng617Multicore Parallele Programmierung Kng617
Multicore Parallele Programmierung Kng617guest465f28
 
Multicore Parallele Programmierung Kng617
Multicore Parallele Programmierung Kng617Multicore Parallele Programmierung Kng617
Multicore Parallele Programmierung Kng617guest465f28
 
Analyse wissenschaftlicher Publikationen
Analyse wissenschaftlicher PublikationenAnalyse wissenschaftlicher Publikationen
Analyse wissenschaftlicher Publikationenadrianwilke
 
04_Whitepaper_Perspektive_Daten_WEB-2.pdf
04_Whitepaper_Perspektive_Daten_WEB-2.pdf04_Whitepaper_Perspektive_Daten_WEB-2.pdf
04_Whitepaper_Perspektive_Daten_WEB-2.pdfMILTONHARVEYSANCHEZ
 
Masterarbeit Roland Kahlert
Masterarbeit Roland KahlertMasterarbeit Roland Kahlert
Masterarbeit Roland KahlertRoland Kahlert
 
Cross Platform Development und Cross Compiling mit PhoneGap
Cross Platform Development und Cross Compiling mit PhoneGapCross Platform Development und Cross Compiling mit PhoneGap
Cross Platform Development und Cross Compiling mit PhoneGapSarah Steffen
 
Christian Fuchs Semanticweb
Christian Fuchs SemanticwebChristian Fuchs Semanticweb
Christian Fuchs Semanticwebguest79cf81
 
Generische Datenintegration zur semantischen Diagnoseunterstützung im Projekt...
Generische Datenintegration zur semantischen Diagnoseunterstützung im Projekt...Generische Datenintegration zur semantischen Diagnoseunterstützung im Projekt...
Generische Datenintegration zur semantischen Diagnoseunterstützung im Projekt...Florian Stegmaier
 
2013-10-10 Nutzung von COinS und Open Data Services im eigenen Katalog
2013-10-10 Nutzung von COinS und Open Data Services im eigenen Katalog2013-10-10 Nutzung von COinS und Open Data Services im eigenen Katalog
2013-10-10 Nutzung von COinS und Open Data Services im eigenen Katalogkostaedt
 
Rbu amanox big_data_intro_infrastruktur
Rbu amanox big_data_intro_infrastrukturRbu amanox big_data_intro_infrastruktur
Rbu amanox big_data_intro_infrastrukturRene Burgener
 
Cloud Storage unter Berücksichtigung der Risiken von großen Datensammlungen a...
Cloud Storage unter Berücksichtigung der Risiken von großen Datensammlungen a...Cloud Storage unter Berücksichtigung der Risiken von großen Datensammlungen a...
Cloud Storage unter Berücksichtigung der Risiken von großen Datensammlungen a...Sarah Steffen
 
Smile2 Office auf dem iPad
Smile2 Office auf dem iPadSmile2 Office auf dem iPad
Smile2 Office auf dem iPadjekel & team
 
Web-2.0-Forschung der KWARC-Gruppe
Web-2.0-Forschung der KWARC-GruppeWeb-2.0-Forschung der KWARC-Gruppe
Web-2.0-Forschung der KWARC-GruppeChristoph Lange
 

Ähnlich wie ba_thesis (20)

Blockchain-based access right management for private data in decentralized cl...
Blockchain-based access right management for private data in decentralized cl...Blockchain-based access right management for private data in decentralized cl...
Blockchain-based access right management for private data in decentralized cl...
 
Multicore Parallele Programmierung Kng617
Multicore Parallele Programmierung Kng617Multicore Parallele Programmierung Kng617
Multicore Parallele Programmierung Kng617
 
Multicore Parallele Programmierung Kng617
Multicore Parallele Programmierung Kng617Multicore Parallele Programmierung Kng617
Multicore Parallele Programmierung Kng617
 
Xm b
Xm bXm b
Xm b
 
Die Loesung - Turbo iXtractor -
Die Loesung - Turbo iXtractor -Die Loesung - Turbo iXtractor -
Die Loesung - Turbo iXtractor -
 
Analyse wissenschaftlicher Publikationen
Analyse wissenschaftlicher PublikationenAnalyse wissenschaftlicher Publikationen
Analyse wissenschaftlicher Publikationen
 
04_Whitepaper_Perspektive_Daten_WEB-2.pdf
04_Whitepaper_Perspektive_Daten_WEB-2.pdf04_Whitepaper_Perspektive_Daten_WEB-2.pdf
04_Whitepaper_Perspektive_Daten_WEB-2.pdf
 
Pr O St Doku
Pr O St DokuPr O St Doku
Pr O St Doku
 
Masterarbeit Roland Kahlert
Masterarbeit Roland KahlertMasterarbeit Roland Kahlert
Masterarbeit Roland Kahlert
 
Cross Platform Development und Cross Compiling mit PhoneGap
Cross Platform Development und Cross Compiling mit PhoneGapCross Platform Development und Cross Compiling mit PhoneGap
Cross Platform Development und Cross Compiling mit PhoneGap
 
Christian Fuchs Semanticweb
Christian Fuchs SemanticwebChristian Fuchs Semanticweb
Christian Fuchs Semanticweb
 
Generische Datenintegration zur semantischen Diagnoseunterstützung im Projekt...
Generische Datenintegration zur semantischen Diagnoseunterstützung im Projekt...Generische Datenintegration zur semantischen Diagnoseunterstützung im Projekt...
Generische Datenintegration zur semantischen Diagnoseunterstützung im Projekt...
 
2013-10-10 Nutzung von COinS und Open Data Services im eigenen Katalog
2013-10-10 Nutzung von COinS und Open Data Services im eigenen Katalog2013-10-10 Nutzung von COinS und Open Data Services im eigenen Katalog
2013-10-10 Nutzung von COinS und Open Data Services im eigenen Katalog
 
Rbu amanox big_data_intro_infrastruktur
Rbu amanox big_data_intro_infrastrukturRbu amanox big_data_intro_infrastruktur
Rbu amanox big_data_intro_infrastruktur
 
Zeitreihen in Apache Cassandra
Zeitreihen in Apache CassandraZeitreihen in Apache Cassandra
Zeitreihen in Apache Cassandra
 
Cloud Storage unter Berücksichtigung der Risiken von großen Datensammlungen a...
Cloud Storage unter Berücksichtigung der Risiken von großen Datensammlungen a...Cloud Storage unter Berücksichtigung der Risiken von großen Datensammlungen a...
Cloud Storage unter Berücksichtigung der Risiken von großen Datensammlungen a...
 
Abschlussbericht des Projekts Viprof
Abschlussbericht des Projekts ViprofAbschlussbericht des Projekts Viprof
Abschlussbericht des Projekts Viprof
 
Smile2 Office auf dem iPad
Smile2 Office auf dem iPadSmile2 Office auf dem iPad
Smile2 Office auf dem iPad
 
Web-2.0-Forschung der KWARC-Gruppe
Web-2.0-Forschung der KWARC-GruppeWeb-2.0-Forschung der KWARC-Gruppe
Web-2.0-Forschung der KWARC-Gruppe
 
Aus dem Alltag einer Datenkuratorin
Aus dem Alltag einer DatenkuratorinAus dem Alltag einer Datenkuratorin
Aus dem Alltag einer Datenkuratorin
 

ba_thesis

  • 1. Bachelorarbeit Effizienteres Dokumenten-Clustering durch Grafikprozessoren Marco Janc Betreuer: Prof. Dr. – Ing. Norbert Fuhr Dipl, -Inform. Marc Lechtenfeld Universität Duisburg-Essen Fakultät für Ingenieurwissenschaften Abteilung Informatik und angewandte Kognitionswissenschaft Fachgebiet Informationssysteme 47084 Duisburg
  • 2. Inhaltsverzeichnis 2 Inhaltsverzeichnis Inhaltsverzeichnis ........................................................................................................... 2 Einleitung......................................................................................................................... 4 1.1 Motivation ............................................................................................................. 4 1.2 Aufgabenstellung................................................................................................... 5 1.3 Aufbau der Arbeit.................................................................................................. 6 2 Verwandte Arbeiten ............................................................................................ 7 2.1 Textvorverarbeitung .............................................................................................. 7 2.2 Clusteranalyse........................................................................................................ 7 2.2.1 Shaderprogramme.................................................................................................. 7 2.2.2 GPGPU.................................................................................................................. 8 3 Grundlagen .......................................................................................................... 9 3.1 Textvorverarbeitung .............................................................................................. 9 3.1.1 Entfernung von Stoppwörtern ............................................................................... 9 3.1.2 Reduzierung auf Wortstämme (Stemming)......................................................... 10 3.2 Dokumentenrepräsentation.................................................................................. 10 3.2.1 TF-IDF-Maß........................................................................................................ 11 3.3 Ähnlichkeitsanalyse............................................................................................. 12 3.3.1 Ähnlichkeitsmaß.................................................................................................. 12 3.3.2 Dreiecksmatrix .................................................................................................... 13 3.4 Clusteranalyse...................................................................................................... 13 3.4.1 Clusterdefinition.................................................................................................. 13 3.4.2 Clusterverfahren .................................................................................................. 14 3.4.3 Partitionierende Verfahren .................................................................................. 14 3.4.4 Hierarchische Verfahren...................................................................................... 14 3.5 Ergebnispräsentation ........................................................................................... 15 3.6 General Purpose Computation on Graphics Processing Unit (GPGPU)............. 15 3.6.1 Grafikkarten-Architektur..................................................................................... 16 3.6.2 Compute Unified Device Architecture (Cuda).................................................... 16 3.6.3 Grundlegender Arbeitsvorgang ........................................................................... 16 3.6.4 Kernelausführung ................................................................................................ 17 3.6.5 Speichermodell.................................................................................................... 18 4 Eingeschlagener Realisierungsweg .................................................................. 20 4.1 Allgemeine Implementierungsdetails.................................................................. 21 4.1.1 Ähnlichkeitsanalyse............................................................................................. 21
  • 3. Inhaltsverzeichnis 3 4.1.2 Update der Nachbarn........................................................................................... 22 4.2 CPU ..................................................................................................................... 23 4.2.1 Dokumentenrepräsentation.................................................................................. 23 4.2.2 Ähnlichkeitsanalyse............................................................................................. 23 4.2.3 Clusteranalyse...................................................................................................... 24 4.3 GPU ..................................................................................................................... 27 4.3.1 Ressourcenausnutzung ........................................................................................ 27 4.3.2 JCuda................................................................................................................... 28 4.3.3 Hilfsklassen zur dynamischen Speicherallokation und Kopierung..................... 28 4.3.4 Parallele Reduktion ............................................................................................. 30 4.3.5 Dokumentenrepräsentation.................................................................................. 32 4.3.6 Ähnlichkeitsanalyse............................................................................................. 37 4.3.7 Clusteranalyse...................................................................................................... 40 5 Evaluation .......................................................................................................... 44 5.1 Eingesetzte Hardware.......................................................................................... 44 5.2 Ergebnisse............................................................................................................ 44 6 Zusammenfassung und Ausblick ..................................................................... 48 6.1 Zusammenfassung ............................................................................................... 48 6.2 Ausblick............................................................................................................... 48 Anhang A: Grafikkartenarchitektur und Cuda ........................................................ 50 Anhang B: Formeln ...................................................................................................... 52 Anhang C: Erweiterter Cuda-Code............................................................................ 53 Anhang D: Java CPU-Clusterupdate-Algorithmus auf der Clusterliste/ Cluster-Binärbaum............................................................................................ 55 Anhang E: Anwendung ................................................................................................ 56 Abbildungsverzeichnis.................................................................................................. 57 Tabellenverzeichnis ...................................................................................................... 58 Abkürzungsverzeichnis ................................................................................................ 59 Literaturverzeichnis ..................................................................................................... 60
  • 4. Einleitung 4 Einleitung Die Wiederbeschaffung von relevanten Informationen, engl. „Information Retrieval― (IR), gewinnt aufgrund der enormen Masse an Informationen, die heute für jeden Men- schen vor allem durch das Internet verfügbar sind, immer stärker an Bedeutung. Der Mensch ist auf seiner Suche zeitlich nicht mehr in der Lage alle Informationen seines Suchkontextes selbst nach den für ihn relevanten Informationen zu durchsuchen. Eine Clusteranalyse kann bei diesem Problem hilfreich sein. Dieses analysiert nach zumeist statistisch-mathematischen Verfahren einen vorgegebenen Datenbestand zur Erkennung neuer Muster und Relationen, wie die Ähnlichkeit der Daten untereinander. Für kleinere Datenbestände wird dies vereinfacht schon durchgeführt. Ein Beispiel dafür sind Kon- textempfehlungen aufgrund von Schlagwörtern. Die Analysierung des Informationsin- haltes von sehr großen Datenbeständen, in unserem Falle Text-Dokumente, ist mit ei- nem zu großen Rechenaufwand verbunden, um sie alleinig auf dem Hauptprozessor durchführen zu können. 1.1 Motivation Aufgrund physikalischer Grenzen endete um das Jahre 2005 das Rennen namhafter Hauptprozessor-(CPU) Hersteller den CPU mit der höchsten Taktfrequenz herzustellen. Um jedoch weiterhin die Computer der Kunden beschleunigen zu können, erfolgte ein Paradigmenwechsel in der Geschichte der Prozessorentwicklung. Anstelle von immer höheren Taktfrequenzen wurden fortan mehrere Kerne in einem Prozessor zu sogenann- ten Mehrkernprozessoren verbaut. Damit eine einzelne Anwendung davon optimal pro- fitieren kann, muss sie explizit auf Mehrkernsysteme hin optimiert werden. Der ak- tuellste Mehrkernprozessor der Firma Intel besitzt jedoch erst 6 physische Kerne [1], eine aktuelle Nvidia Grafikkarte jedoch mehr als 500 [2]. Seit dem Beginn der Videospielindustrie ist diese bestrebt, immer realitätsgetreuere virtuelle Umgebungen, insbesondere im Bereich der Visualisierung, zu erschaffen. Dies hat einen steigenden mathematischen Rechenaufwand zur Folge, weshalb die Grafikkar- tenhersteller immer schnellere Grafikkarten produzieren. Aktuelle Grafikprozessoren (GPU) besitzen eine skalierbare parallele SIMD Rechnerarchitektur und mit ihrer star- ken Verbreitung in Desktop Computern, Videospielkonsolen, Workstations und mitt- lerweile sogar in Rechenzentren sollte jeder Anwendungsentwickler parallelisierbare Aufgaben, falls möglich, auf die GPU auslagern [2]. Bei den Grafikkarten ist eine dauerhafte Erhöhung des Taktes aufgrund ihrer skalierba- ren Rechnerarchitektur nicht von höchster Priorität, sondern die Anzahl der internen
  • 5. Einleitung 5 Stream-Prozessoren sowie der Speicherdurchsatz und die Größe des Videospeichers sind von höherer Bedeutung. Zudem ermöglichen die meisten Mainboards den Einbau von mehreren Grafikkarten, wovon Entwickler profitieren können. Wie die folgende Abbildung 1 verdeutlicht, ist ein aktueller Prozessor einer aktuellen Grafikkarte bei Fließkommazahlenberechnungen mit einfacher Präzision weit unterlegen. Die Nutzung dieser möglichen Rechenkraft der GPU sollte die notwendigen Schritte einer Dokumen- ten-Clusteranalyse, welche meistens aus gleichförmigen und voneinander unabhängigen Prozeduren bestehen, beschleunigen. Dies wird in dieser Arbeit untersucht und gegen- über einer gleichwertigen CPU-Variante in Kapitel 5 evaluiert. Woodcrest Harpertown Bloomfield Westmere 7800 GTX 8800 GTX GTX 285 GTX 480 GTX 580 0 200 400 600 800 1000 1200 1400 1600 1800 Intel CPU Nvidia GPU Abbildung 1: CPU und GPU Performanz-Vergleich in Gigaflops bei Berechnungen mit Fließkommazahlen mit einfacher Präzision (SP) [3] 1.2 Aufgabenstellung In dieser Arbeit wird die Möglichkeit untersucht, den Vorgang des Dokumenten Cluste- ring unter Zunahme einer auf parallele Berechnungen hin optimierten Hardware zu be- schleunigen. Ein Ziel dieser Abschlussarbeit ist es, einen Rahmen zur Eignung von ak- tuellen GPUs zur hierarchischen Clusteranalyse aufzustellen. In diesem Prozess sollen die zu definierenden Schritte dieser auf ihre Parallelisierbarkeit und Adaption auf GPUs überprüft und falls möglich umgesetzt werden. Weiterhin soll die Ausführung möglicher Schritte auf der GPU eine bessere Performanz gegenüber der CPU bieten. Um die Performanz einer GPU mit einer gleichwertigen1 CPU vergleichen zu können, sollte die CPU-Variante, falls möglich, auch parallelisiert werden, um alle Kerne der CPU nutzen zu können. Weiterhin sollten beide für qualitati- 1 Gleichwertig bedeutet in diesem Kontext, dass die zu vergleichende CPU aus demselben Jahr und dem- selben Verbrauchermarkt wie die GPU stammt
  • 6. Einleitung 6 ve Evaluationsergebnisse, ähnliche jedoch für die Architektur optimale und angepasste Algorithmen benutzen. 1.3 Aufbau der Arbeit Nach dieser Einleitung folgt die Festlegung der zu realisierenden Ziele dieser Arbeit. In Kapitel 2 werden verschiedene wissenschaftliche Arbeiten im Bereich des Dokumenten- Clustering im Kontext der Grafikkartenprogrammierung (GPGPU) vorgestellt. Darauf- hin folgt in dem nächsten Kapitel eine detaillierte Beschreibung der notwendigen Grundlagen zu den Themen Dokumenten-Clustering und der allgemeinen Programmie- rung auf Grafikkarten. Der schließlich eingeschlagene Realisierungsweg und Details der Implementierung werden in Kapitel 4 vorgestellt. Schließlich erfolgt im nächsten Kapi- tel die Evaluierung der Ergebnisse dieser Arbeit. Diese schließt mit einer Zusammenfas- sung und dem Ausblick.
  • 7. 2 Verwandte Arbeiten 7 2 Verwandte Arbeiten Literatur zum Dokumenten-Clustering und speziell dem hierarchischen ist in Bibliothe- ken und dem Internet sehr leicht auffindbar und in vielfältiger Form vorhanden [4] [5] [6]. Ziele dieser wissenschaftlichen Arbeiten sind die Verbesserung der Qualität der Clusterverfahren [7], Analysen und Vergleiche vorhandener Verfahren [8], Erhöhung der Performanz mit besonderem Fokus auf die parallele Ausführung der Clusteralgo- rithmen [9] [10] [11] [12] [13] [14]. Diese Arbeit konzentriert sich auf die effiziente parallele Ausführung des Dokumenten- Clustering auf Grafikkarten, weshalb in diesem Kapitel Arbeiten, die die Grafikkarte als Co-Prozessor für das hierarchische Dokumenten-Clustering verwenden, vorgestellt werden. Alle hier vorgestellten GPU-Arbeiten erreichten eine viel höhere Performanz gegenüber ihrer jeweiligen CPU-Variante. 2.1 Textvorverarbeitung Die Konvertierung der Text-Dokumente in eine statistisch-mathematisch repräsentative Form ist für die eigentliche Clusteranalyse von hoher Bedeutung. Y. Zhang et al. wid- men sich diesem Thema und erstellten in ihrer Arbeit einen Algorithmus zur Beschleu- nigung des Text Mining mittels GPUs [15] [16]. Die einzelnen Schritte der Textvorver- arbeitung sowie Repräsentierung der Dokumente im Vektorraum-Modell wurden kom- plett auf der GPU realisiert. Aufgabe der CPU ist es, die Aufgaben der GPU zu syn- chronisieren, und einen asynchronen dokumentenweisen Eingabestrom an die GPU zu gewährleisten. Zur Realisierung des Vektorraum-Modells im Speicher der Grafikkarte erstellten sie eine eigene, eindimensionale Hash-Tabellen-Datenstruktur. Mittels ihrer Implementierung erreichten sie eine Verkürzung der Laufzeit um das bis zu 30-fache. 2.2 Clusteranalyse Bis zu diesem Zeitpunkt ist erst eine kleine Anzahl an Arbeiten über das Dokumenten- Clustering mithilfe der GPU verfasst worden [17] [18] [19] [20], von denen sich ein kleiner Teil mit dem hierarchischen Verfahren beschäftigt [21] [22] [23] [24]. 2.2.1 Shaderprogramme Vor der Entwicklung von Schnittstellen zur Programmierung auf Grafikkarten wurde die parallele SIMD-Rechnerarchitektur der Grafikkarten mithilfe von Pixel-Shader- Programmen ausgenutzt. Diese waren grundsätzlich für die Pixel-basierte Berechnung der Oberflächeneigenschaften von 3D-Objekten einer virtuellen Szene vorgesehen. Die
  • 8. 2 Verwandte Arbeiten 8 Ergebnisse der entwickelten Algorithmen wurden in den Framebuffer geschrieben und konnten somit gespeichert und ausgewertet werden. Arbeiten über das Dokumenten- Clustering mit Verwendung dieser Technik realisierten meistens das partitionierende K- Means-Clusterverfahren [17] [19]. 2.2.2 GPGPU D. Chang et al. erarbeiteten in dem Jahre 2008 einen CUDA-Algorithmus zur parallelen Berechnung der paarweisen euklidischen Distanzen von Datenpunkten [22], welchen D. Chang und M. Ouyang mit M. Kantardzic ein Jahr später als Basis zur Berechnung der paarweisen Pearson-Korrelation verwenden [25]. Anschließend führten sie eine hierar- chische Single-Linkage Clusteranalyse auf Basis der zweidimensionalen Pearson- Korrelation-Dreiecksmatrix durch. Die Ähnlichkeitsmatrix-Dreiecksmatrix in der obigen Arbeit besitzt zwei Dimensionen, jedoch ist sie nur spärlich besetzt. A. Shalom et al. optimierten diese zu einem eindi- mensionalen Array mit Zeilen- und Spaltenindizes zur Erreichung einer höheren Per- formanz [21] und untersuchten die Performanz in Abhängigkeit von unterschiedlichen Cuda-Blockgrößen [26].
  • 9. 3 Grundlagen 9 3 Grundlagen Die notwendigen Schritte einer vollständigen Dokumenten-Clusteranalyse sind folgend aufgelistet und werden anschließend detailliert beschrieben: 1. Textvorverarbeitung 2. Dokumentenrepräsentation 3. Ähnlichkeitsanalyse 4. Clusteranalyse 5. Ergebnispräsentation Die Komplexität der einzelnen Schritte ist von mehreren Faktoren abhängig, wie der Art der Dokumente, dem jeweiligen Ziel der Analyse und den damit ausgesuchten Cluster- verfahren. 3.1 Textvorverarbeitung Bevor die Dokumente in eine maschinenlesbare Repräsentation konvertiert werden (s. Abschnitt 3.2) können, sind einige textvorverarbeitende Schritte, welche in diesem Ab- schnitt vorgestellt werden, nötig. Die Sprachdimension der zu analysierenden Kollektion sowie deren Sprachen sind für die Textvorverarbeitung von hoher Bedeutung. Bei der Indizierung der einzelnen Wort- terme müssen bei multilingualen Kollektionen komplexere Verfahren angewendet wer- den, außerdem die Parametrisierung der Stoppwortentfernung und des Stemming von der Sprache abhängig [13] [27]. In Textdokumenten wie Büchern oder Artikeln besitzen Wörter gegenüber Sonderzei- chen oder numerischen Werten einen sehr hohen relevanten Informationsgehalt, wes- halb zuerst alle nichtalphabetischen Zeichen entfernt werden. Um einzelne Wörter mit- einander vergleichen zu können, müssen ihre Buchstaben anschließend in dieselbe Stel- lung konvertiert werden. Daraufhin folgt die Tokenisierung, in welcher der Text in seine einzelnen Wortterme aufgeteilt wird. Die lexikalische Bedeutung aufeinanderfolgenden von Worten und ganzen Haupt- wie Nebensätzen gehen durch diesen Ansatz verloren. 3.1.1 Entfernung von Stoppwörtern Die natürliche Sprache und deren Texte enthalten eine große Anzahl von lexikalisch irrelevanten Worten. Diese sogenannten Stoppwörter verbinden Textteile meistens se- mantisch sowie syntaktisch miteinander. Klassische Beispiele für Stoppwörter sind Konjunktionen, Artikel und Präpositionen. Da diese Wörter zum Textinhalt sehr wenig
  • 10. 3 Grundlagen 10 beitragen, aber gleichzeitig mit einer hohen Frequenz vorhanden sind, werden sie in diesem Schritt entfernt. Zur Identifizierung und Entfernung dieser werden Listen ver- wendet, in denen alle Stoppwörter einer Sprache vorhanden sind. 3.1.2 Reduzierung auf Wortstämme (Stemming) In der natürlichen Sprache kann sich die Grundbedeutung vieler Wörter sehr ähnlich bis sogar gleich sein. Diese Wörter mit unterschiedlichen morphologischen Varianten eines gemeinsamen Terms werden beim Stemming zu einem Wortstamm zusammengefasst. Im Gegensatz zur Lemmatisierung muss dieser in der Sprache jedoch nicht als selbst- ständiges Wort existieren. Dieses Verfahren entfernt Flexionsendungen und Derivati- onssuffixe der Worte. Je nach Algorithmus können auch Kompositionen in ihre einzel- nen Terme zerlegt werden. Das Stemming-Verfahren kann in zwei Kategorien unterteilt werden, die linguistische und die nichtlinguistische Kategorie. Die nichtlinguistischen Verfahren basieren meist auf großen Tabellen, in welchen jedem Term ein jeweiliger Wortstamm zugeordnet ist. Dagegen basiert das linguistische Verfahren auf dem sprachspezifischen Wissen über Suffixe, Ableitungen etc., weshalb es für unterschiedli- che Sprachen verschiedene Implementierungen gibt. Letzteres wird beim Dokumenten- Clustering meistens angewendet, da hier keine große Wortstammtabelle nötig ist, da die Findung eines Terms in dieser mit einem hohen Suchaufwand verbunden ist. Mittels dieses Vorgangs ist es nicht mehr nötig alle möglichen Terme einer Kollektion eindeu- tig speichern zu müssen, was zu einer Reduzierung der Termmenge um bis zu 50% führt. 3.2 Dokumentenrepräsentation Ziel der Dokumentenrepräsentation der Dokumente einer Kollektion ist es, Textinhalte durch Merkmale wider zu spiegeln. Häufig genutzte Kandidaten für eine Merkmalsaus- prägung sind Metadaten über das Dokument, einzelne Bereiche dessen sowie die ein- zelnen Dokumententerme. Diese basieren in der Regel auf statistisch-mathematischen Verfahren. Schon 1957 kam H. Luhn zu dem Forschungsergebnis, dass die Termvertei- lung innerhalb eines Dokumentes dessen Inhalt widerspiegelt sowie dessen Häufigkeit dessen Signifikanz repräsentiert [28]. Die Repräsentation der Dokumente im Vektor- raum-Model ist de facto Standard im Bereich des Dokumenten-Clusterings. Das Vektorraum-Modell ist die am häufigsten benutzte Form der Dokumentenrepräsen- tation. In diesem werden die Dokumente als Vektoren dargestellt, dessen Attribute die Terme und die Werte ihre Gewichtung repräsentieren. Diese werden zur weiteren Ver- arbeitung in einer globalen Term-Dokumentenmatrix (TDM) gespeichert. Die Initial- gewichtung entspricht in dieser Arbeit der absoluten Häufigkeit eines Terms in einem Dokument.
  • 11. 3 Grundlagen 11 Abbildung 2: Term-Dokumentenmatrix Das in der Literatur am häufigsten eingesetzte Maß zur Termgewichtung ist das Termfrequenz-inverse Dokumentenfrequenz (TF-IDF) Maß. Mehrere Alternativen sind dazu vorhanden, wobei die Termfrequenz-Inverse Korpusfrequenz (TF-ICF) mit Aus- blick auf die Umsetzung auf die GPU eine geeignete Wahl darstellen kann, da der IDF Term abhängig von der zu analysierenden Gesamtkollektion ist. ICF erreicht gegenüber IDF eine Reduzierung der Komplexität von zu , bei einer ähnlichen Qualität [16] [29]. Vereinfacht erläutert, approximiert ICF die Termverteilung der Gesamtkol- lektion mittels Untermengen dieser, weshalb nicht mehr die ganze Kollektion in den begrenzten Speicher der Grafikkarte geladen werden muss. Somit ist eine höhere An- zahl an Dokumenten klassifizierbar. Für diese Arbeit reicht der vorhandene Grafikspeicher aus, weshalb das genauere TF- IDF Maß verwendet wird. Dieses besteht aus zwei Komponenten, der Termfrequenz und der inversen Dokumentenfrequenz, welche in dem folgenden Abschnitt erläutert werden. 3.2.1 TF-IDF-Maß Die Termfrequenz (TF) beschreibt die relative Häufigkeit eines Terms in einem Doku- ment und ist in Formel 1 definiert. Folglich besitzen Terme mit einer höheren Frequenz eine höhere Gewichtung. ist die Häufigkeit des Terms im Dokument ist die Anzahl aller Terme im Dokument Formel 1: Termfrequenz Die inverse Dokumentenfrequenz (IDF) beschreibt die Gewichtung eines Terms in Re- lation zur Gesamtkollektion. Definiert in Formel 2 wird eine hohe Termgewichtung durch eine niedrige Anzahl an Dokumenten, die diesen Term beinhalten, realisiert. documents term…,0 termm-1,0 term0,0 term…,… termm,… term0,… term…,|D|-1 termm-1,|D|-1 term0,|D|-1 terms
  • 12. 3 Grundlagen 12 ist die Anzahl aller Dokumente der zu untersuchenden Kollektion ist die Anzahl aller Dokumente, welche Term beinhalten Formel 2: inverse Dokumentenfrequenz Das TF-IDF-Maß besteht, wie in Formel 3 dargestellt, aus der multiplikativen Zusam- mensetzung der Termfrequenz mit der inversen Dokumentenfrequenz. Formel 3: TF-IDF Maß 3.3 Ähnlichkeitsanalyse Auf Basis der Dokumentenvektoren, hier der TDM, wird nun eine Ähnlichkeitsanalyse durchgeführt. Hierzu werden paarweise die Korrelationskoeffizienten der Dokumente mit einem ausgewählten Ähnlichkeitsmaß bestimmt. 3.3.1 Ähnlichkeitsmaß Wichtige Koeffizienten im IR sind der Kosinus, Dice- sowie Jaccardkoeffizient. In die- ser Arbeit wird der Kosinuskoeffizient verwendet und in diesem Abschnitt vorgestellt. Der Kosinus zweier Vektoren, Formel 4, gibt den Winkel zwischen diesen wieder. Ha- ben beide Vektoren dieselbe Ausrichtung, beträgt der Winkel zwischen Ihnen 0, wes- halb sie eine Ähnlichkeit von 1 besitzen. Formel 4: Kosinus-Ähnlichkeitsmaß zweier Vektoren Für normierte Vektoren, d.h. die Vektoren besitzen die Einheitslänge nach Euklidischer Norm, ist die Kosinus-Formel mit dem Skalarprodukt zweier Vektoren identisch und es ergibt sich Formel 5. Durch diese Normalisierung werden die Terme unabhängig von der Länge eines einzelnen Dokumentes gewichtet. Formel 5: Skalarprodukt-Ähnlichkeitsmaß zweier normierter Vektoren
  • 13. 3 Grundlagen 13 3.3.2 Dreiecksmatrix Ergebnis dieser Ähnlichkeitsanalyse ist die Dokumenten-Ähnlichkeits-Dreiecksmatrix (DDM), deren Diagonale die Eigenähnlichkeit der Dokumente repräsentiert und somit ignoriert werden kann. Die Anzahl der Paare, die Initialcluster, kann aus der Formel für Dreieckszahlen hergeleitet werden (s. Formel 10). 3.4 Clusteranalyse Ziel der Clusteranalyse ist es, eine vollständige Segmentierung aller Objekte eines zu analysierenden Datenbestandes anhand ihrer Ähnlichkeiten zueinander zu erreichen. Für diesen Vorgang muss ein geeignetes Ähnlichkeitsmaß gewählt werden. Dabei sollen sich ähnliche Objekte eines Datenbestandes in einem Cluster zusammengefasst werden, wobei sich unähnliche Objekte in unterschiedlichen Clustern befinden sollen. Folgend verdeutlicht Abbildung 3 diesen Vorgang: Abbildung 3: Zuweisung der Datenpunkte zu drei farblich gekennzeichneten Cluster Zur Erreichung dieses Zieles haben sich in der Vergangenheit zwei Hauptkategorien der Clusterverfahren etabliert. Diese sind das partitionierende sowie das hierarchische Ver- fahren, welches einen höheren Rechenaufwand benötigt. Das hierarchisch agglomerati- ve Verfahren wird in dieser Arbeit vorgestellt und verwendet. 3.4.1 Clusterdefinition In der disjunkten Clusteranalyse, welche in dieser Ausarbeitung behandelt wird, ist ein Cluster einer Datenobjektmenge wie folgt definiert: definiert eine gewählte Korrelationsfunktion Formel 6: Clusterdefinition
  • 14. 3 Grundlagen 14 3.4.2 Clusterverfahren 3.4.3 Partitionierende Verfahren Partitionierende Clusterverfahren ordnen die Objekte eines zu analysierenden Datenbe- standes iterativ einer fest vorgegebenen Anzahl an Ergebnisclustern zu. In jeder Iterati- on wird versucht, gemäß dem verwendeten Distanzmaß die Ergebnisclusterzugehörig- keit der Objekte zu optimieren. Sind keine weiteren Optimierungen mehr möglich, ter- miniert das Verfahren. Ein offensichtlicher Nachteil dieser Verfahren ist die notwendige Festlegung der Ergebniscluster, welche somit für ein zufriedenstellendes Ergebnis opti- mal gewählt werden müssen. Der bekannteste Vertreter der partitionierenden Verfahren ist das K-Means-Verfahren, welches eine Komplexität von besitzt. 3.4.4 Hierarchische Verfahren Die Festlegung der Ergebniscluster bei partitionierenden Clusterverfahren ist bei hierar- chischen Verfahren nicht nötig. Diese verfolgen entweder eine top-down (divisive) oder eine bottom-up (agglomerative) Strategie, welches in diesem Abschnitt erklärt wird. Aufgrund ihrer teilenden oder zusammenfügenden Iterationsschritte können diese Ver- fahren jederzeit ohne Einbußen in der Clusterqualität gestoppt werden. Agglomerative Verfahren (HAC) Das bottom-up-Verfahren startet mit der Einteilung aller Objekte in einzelne Initialclus- ter und fügt diese in jeder Iteration anhand der ähnlichsten Elemente zusammen. Dies wird solange durchgeführt, bis alle Initialcluster in einem großen Cluster zusammenge- fügt worden sind. Agglomerative Clustermethoden (HACM) Hierarchisch agglomerative Clustermethoden werden in zwei Arten unterteilt, die geo- metrischen Methoden, wie die Zentroid- oder Wardmethode sowie den grafenorientier- ten Methoden wie Average-Linkage, Single-Linkage und Complete-Linkage. • Average Linkage: Die Distanz eines Clusters gegenüber einem anderen soll gleich der durchschnittlichen Distanz jedes Elementes dieses Clusters gegenüber jedem Element des anderen Clusters sein. Folgend sind die notwendigen Rekursionsschritte der Methoden aufgelistet: a. Findung des Maximums Der Cluster dessen Elemente die höchste Ähnlichkeit vorweisen, wird gesucht (Komplexität von ). b. Update der Nachbarn
  • 15. 3 Grundlagen 15 Alle Cluster werden durchlaufen und mittels einer gewählten Clustermethode werden die Nachbarn des Maxima Clusters aktualisiert (Komplexität von ). Diese drei vorgestellten Verfahren sind stark mit der Erstellung eines gewichteten, mi- nimalen Spannbaumes verbunden und sind zeitlich in lösbar [30]. 3.5 Ergebnispräsentation Die Ergebnispräsentation einer Clusteranalyse kann, in Abhängigkeit des Anwendungs- gebietes, eine hohe Bedeutung für den Benutzer besitzen. Das Ergebnis wird meistens als Dendogramm oder vereinfacht als Baumstruktur präsentiert. So wäre es dem Benut- zer im Falle einer Such- oder Empfehlungsanwendung sehr schnell möglich, weitere, für ihn relevante Informationen zu erhalten oder abzurufen. 3.6 General Purpose Computation on Graphics Processing Unit (GPGPU) Die Verwendung eines Grafikprozessors für allgemeine Berechnungen wird mittels GPGPU abgekürzt. Vor der Einführung von programmierbaren GPGPU-Schnittstellen um das Jahr 2006 (CTM der Firma AMD) wurden die vorhandenen Grafikschnittstellen zweckentfremdet. Die neu eingeführten GPGPU-Schnittstellen ermöglichten Entwick- lern einen nun freien Zugriff auf den Videospeicher und nativen Befehlssatz der GPUs. Die beiden größten Grafikkartenhersteller AMD und Nvidia sind beide unter Führung der Khronos Group Mitglied der OpenCL-Arbeitsgruppe. Diese führt die Weiterent- wicklung ihrer plattformunabhängigen GPGPU-Schnittstelle OpenCL voran. Momentan konkurriert AMD mit ihrer auf OpenCL basierenden ATI-Stream- Entwicklungsumgebung gegen Nvidias Cuda Framework [31]. Aufgrund der sehr ähnlichen Grafikkartenarchitekturen beider Hersteller basieren beide auf der Hochsprache C und beide besitzen eine gemeinsame Schnittmenge mit den rele- vantesten Funktionen. Aus diesem Grund sind OpenCL-fähige GPU-Programme, soge- nannte GPU-Kernel, auch auf GPUs von Nvidia lauffähig. Jedoch besitzen OpenCL- Kernel meistens eine niedrigere Performanz gegenüber nativen Cuda Implementierun- gen [32] [33]. Zudem ist Nvidia auch aufgrund von erhöhten Marketinganstrengungen, insbesondere zu Beginn der GPGPU-Entwicklung mit Cuda in der Wissenschaft und Wirtschaft sehr viel stärker vertreten und wird stetig weiterentwickelt [34]. Zum Verständnis der in Kapitel 4 vorgestellten Implementierungsdetails bedarf es zu- nächst eines Überblicks über den Aufbau aktueller Grafikkartenhardware am Beispiel von Nvidia-Grafikkarten basierend auf der aktuellen Fermi-Architektur. Anschließend
  • 16. 3 Grundlagen 16 erfolgt ein Einblick in die aktuelle Grafikprogrammierung anhand der Cuda- Technologie. 3.6.1 Grafikkarten-Architektur Aktuelle Grafikkarten besitzen eine skalierbare, parallele SIMD-Architektur. SIMD steht für „Single Instruction Multiple Data―, was ins Deutsche übersetzt so viel wie „Ei- ne Instruktion für Mehrere Daten― heißt. In früheren Architekturen besaßen die einzel- nen Vertex-, Pixel- und Geometrieshader aufgrund ihrer unterschiedlich benötigten Be- fehlssätze unterschiedliche Implementierungen in der GPU-Hardware. Mit notwendig erweitertem Funktionsumfang dieser Shadereinheiten näherten diese sich immer mehr einander an, weshalb der Ansatz der Universalshader verwirklicht wurde. Somit erfolgt keine Unterteilung der Shader in unterschiedliche Typen mehr, weshalb alle denselben Funktionsumfang besitzen. Dies spiegelt sich in einer Vereinfachung der Grafikkarten- architektur wider. Aktuelle Grafikkarten besitzen eine beliebige Anzahl an Streaming-Multiprozessoren (SM), welche aus mehreren Hardwarekernen bestehen [2] (siehe Abbildung 15 und 16 im Anhang). Jeder dieser Kerne besitzt eine vollständige Integer-Arithmetik- (ALU) sowie Fließkommazahlen-Einheit (FPU). Kerne eines SM teilen sich einen Register- sowie gemeinsamen Speicher, engl. Shared Memory (SM) und L1 Cache. Die Skalier- barkeit wird durch die Anzahl der SM und deren Kerne erreicht. 3.6.2 Compute Unified Device Architecture (Cuda) Cuda, die von Nvidia entwickelte Architektur zur parallelen Berechnung auf GPUs, besteht aus mehreren Abstraktionsebenen (s. Abbildung 28 im Anhang). Die unterste ist die anwendungssprachunabhängige Driver-API mit höchstmöglicher Kontrolle des Entwicklers, darüber liegt die Runtime-API, welche es ermöglicht Cuda-Kernel mit in das C-Programm zu integrieren und einen Emulationsmodus besitzt. Beide APIs sind untereinander austauschbar. Die letzte Ebene beinhaltet die der verfügbaren Cuda- Bibliotheken, welche stetig weiterentwickelt werden. Diese können je nach Anwen- dungsziel den Programmieraufwand sehr stark reduzieren, da der Entwickler für allge- meine Aufgaben keine eigenen Cuda-Kernel mehr schreiben muss. Nvidia liefert für ihre Cuda-spezifischen C-Erweiterungen und Beschränkungen einen eigenen Compiler namens Nvcc, welcher einen nativen C-Compiler ergänzt, mit. 3.6.3 Grundlegender Arbeitsvorgang Der wiederkehrende grundlegende Arbeitsvorgang von Cuda sieht, wie in Abbildung 4 illustriert und in diesem Abschnitt referenziert, folgendermaßen aus: Zuerst müssen die Daten in den Videospeicher der Grafikkarte allokiert und, falls notwendig, kopiert wer-
  • 17. 3 Grundlagen 17 den (1). Dann instruiert die CPU die GPU (2) den Kernel in den einzelnen Kernen paral- lel auszuführen (3). Anschließend werden die Ergebnisdaten aus dem Videospeicher in den Hauptspeicher zurück kopiert (4). In der zeitlichen Evaluierung müssen alle vier Schritte, und nicht nur die alleinige Ausführung des Kernels, betrachtet werden. Abbildung 4: Arbeitsvorgang von Cuda Seit Cuda 4.0 und dem Rechenmodell 2.0 kann die GPU bei expliziter Nutzung auch auf den Hauptspeicher der CPU zugreifen, was bei einer einmaligen Nutzung der Daten den Vorgang beschleunigen kann [3]. 3.6.4 Kernelausführung Die parallelen GPU-Routinen, die Kernel, können nur durch die CPU gestartet und grundsätzlich nur sequentiell auf der GPU ausgeführt werden. Seitdem Grafikkarten der neuesten Generation das Cuda-Rechenmodell 2.0 unterstützen, können diese auch semi- parallel ausgeführt werden. Es ist dabei die Aufgabe des Entwicklers, Race-Conditions zu vermeiden. Cuda implementiert zur Ausführung der Kernel eine hierarchische Pro- grammierstruktur, welche aus einem Gitter (engl. Grid) pro Kernel besteht und in Ab- bildung 5 verdeutlicht wird. Dieses Gitter besteht aus mehreren Blöcken, welche wiede- rum aus mehreren Threads bestehen. Blöcke eines Gitters und Threads eines Blocks sind bis zu dreidimensional angeordnet, jedoch in ihrer jeweiligen Anzahl an Blöcken oder Threads limitiert. Die Anzahl der Blöcke je Gitterdimension ist bis zum Rechen- modell 2.0 auf 65535 limitiert. Die Anzahl der Threads pro Block liegt abhängig vom Rechenmodell zwischen 512 und 1024. Die Anzahl an zu nutzenden Blöcken und Threads müssen der Grafikkarte explizit beim Start des Kernels übergeben werden. Um Main Memory GPU Memory CPU GPC SM SM 2 3 4 1
  • 18. 3 Grundlagen 18 eine hohe Auslastung der Grafikkarte zu gewährleisten, bedürfen diese Werte einer vo- rangehenden sorgfältigen Überlegung. Weiterhin müssen die Kernel-Algorithmen an diese Limitationen skalierbar angepasst werden, um eine hohe Performanz und Kompa- tibilität erreichen zu können. Ein SM führt konsekutive Threads eines Blockes immer in einer Gruppe von 32 parallelen Threads, einem Warp, aus und es befinden sich maximal 8 Blöcke in einem SM (bis Rechenmodell 2.0). [3]. Abbildung 5: Kernelausführung © Nvidia Grundlegende Kontrollmechanismen dieser Struktur sind die Indizes der Blöcke und Threads. Weiterhin können Threads eines Blockes miteinander synchronisiert werden. Eine globale Synchronisierung aller Blöcke ist grundlegend nur über die Terminierung eines Kernels realisierbar, und nicht im Sinne einer effektiven parallelen Ausführung. 3.6.5 Speichermodell Eine effiziente Nutzung der unterschiedlichen Speicher der GPU ist mit von höchster Priorität zur Erreichung einer hohen Performanz. Wie in Abbildung 6 zu erkennen, be- sitzt jeder Thread eigene Register und einen eigenen lokalen Speicher. Threads eines Blockes teilen sich einen gemeinsamen Speicher (SMEM) und jeder Thread besitzt Zu- griff auf den globalen Video-, Textur- und Konstantenspeicher, welche in einem Cache zwischengespeichert werden [3]. Die Pfeile verdeutlichen die Schreib- bzw. Leserechte eines Threads. Jeder dieser Speicher besitzt unterschiedliche Vor- und Nachteile. Der globale und lokale Videospeicher, das RAM, besitzt die größte Kapazität, jedoch auch die größte Latenz, mit ungefähr 400-600 Taktzyklen für jede Lese- oder Schreiboperati- on (abhängig von der Grafikkarte). Hingegen ist die Anzahl aller Register per SM be- GPUCPU Kernel 1 Kernel 2 Serial Code Serial Code Grid 1 Block (0, 0) Block (1, 0) Block (2, 0) Block (0, 1) Block (1, 1) Block (2, 1) Grid 2 Block (1,1) Thread (0,0) Thread (1,0) Thread (2,0) Thread (3,0) Thread (0,1) Thread (1,1) Thread (2,1) Thread (3,1) Thread (0,2) Thread (1,2) Thread (2,2) Thread (3,2)
  • 19. 3 Grundlagen 19 grenzt und bei Überschreitung dieses Limits werden Variablen in den langsamen loka- len Speicher ausgeladen. Register und der gemeinsame Speicher sind auf dem Chip lo- kalisiert und benötigen für eine Lese- oder Schreiboperation nur ein bis vier Taktzyklen. Abbildung 6: Cuda-Speichermodell © Nvidia Speicherzugriff wird generell in zwei einzelne Speicherzugriffe für jeden halben Warp (16 Threads) aufgeteilt, um mögliche Konflikte zu vermeiden. Bei Zugriff zum globalen Videospeicher wird direkt ein konsekutiver Speicherbereich (16-bit) für die Threads eines halben Warp geladen. Somit können bei einer abgleichenden2 Speicherindexierung im Kernel die Speicherzugriffe mehrerer Threads zu einem Zugriff verschmolzen wer- den, was zu einer erhöhten Performanz führt und von hoher Bedeutung für den Entwick- ler ist. 2 Ein Thread eines halben Warp mit Index i muss auf das Element an Index i zugreifen. GPU Global Memory Constant Memory Texture Memory Block (0,1) Shared Memory Thread(0, 0) Registers Local Memory Thread(1, 0) Registers Local Memory Block (0,1) Shared Memory Thread(0, 0) Registers Local Memory Thread(1, 0) Registers Local Memory CPU
  • 20. 4 Eingeschlagener Realisierungsweg 20 4 Eingeschlagener Realisierungsweg Die entwickelte Clusteranwendung wurde in der Plattform-unabhängigen und Objekt- orientierten Programmiersprache Java (Version 1.6) geschrieben und als 64-Bit Anwen- dung konzipiert. Cuda wurde in der aktuellsten Version 4.0 verwendet. Da der Cuda- Code auf der Sprache C basiert, kann er nur in dieser und mit der Runtime API direkt geschrieben werden, weshalb eine Sprachverbindung für Java benötigt wird [35]. Da das Nvidia Entwickler-SDK keinen nativen C-Compiler mitliefert, wurde zur Kompilie- rung des Cuda-Programmcode der 64-Bit C-Compiler der Visual Studio 2008 Entwick- lungsumgebung von Microsoft verwendet. Um eine qualitative Aussage in der Evaluierung zu erreichen, wurden zwei, an die je- weilige Architektur angepasst, möglichst gleichwertige Implementierungen der Cluster- analyse erstellt. Dies bedeutet auch, falls möglich, die Nutzung aller CPU-Kerne in der CPU-Implementierung. Diese beiden Pfade implementieren zu großen Teilen dieselben Programmierinterfaces in Form von abstrakten Klassen, um einen identischen Ablauf des Clusterprozesses zu gewährleisten und zur Vermeidung von redundanten Codetei- len. Nach anfänglicher Implementierung der einzelnen Schritte der Textvorverarbeitung auf die GPU realisierte ich, dass diese den zeitlichen Rahmen dieser Bachelorarbeit über- steigen würde und entschied mich, die komplette Textvorverarbeitung inklusive Be- rechnung der Termfrequenz auf der CPU durchzuführen. Da diese Schritte noch unab- hängig von der eingesetzten Kollektion sind, bedarf es, je Dokument nur einer einmali- gen Durchführung dieser Schritte. Demzufolge haben diese im eingeschlagenen Reali- sierungsweg eine niedrigere Priorität und werden in Abschnitt 4.2 nur kurz behandelt. Konzeptschritte Folgende Schritte werden in diesem Kapitel für beide Implementierungen erläutert: 1. Dokumentenrepräsentation a. IDF-Berechnung b. Normalisierung 2. Ähnlichkeitsanalyse 3. Clusteranalyse a. Findung des Maximums b. Update der Nachbarn
  • 21. 4 Eingeschlagener Realisierungsweg 21 4.1 Allgemeine Implementierungsdetails Zum Verständnis der in diesem Kapitel vorgestellten Algorithmen bedarf es zuerst der Vorstellung der Clusterimplementierung. Ein Cluster wurde als Binärknoten implemen- tiert, welcher zwei Cluster oder Dokumente als Kindsknoten besitzt (Abbildung 7). Do- kumente als Kindsknoten sind mit einem Blatt des binären Baumes gleichzusetzen, wel- cher von beiden Varianten aufgespannt und anschließend mittels einer Benutzeroberflä- che angezeigt wird. Abbildung 7: Cluster-Implementierung 4.1.1 Ähnlichkeitsanalyse Für eine effektive Datenspeicherung wird die Ähnlichkeitsmatrix (DDM) mittels For- mel 11 in eine eindimensionale Liste konvertiert. Abbildung 8 und Abbildung 9 ver- deutlichen diesen Vorgang. Die Referenzen der beiden Kinder eines Clusters werden mittels der konstanten Spalten- und Reihenindizes realisiert: Abbildung 8: Ähnlichkeitsmatrix von fünf Dokumenten Abbildung 9: Eindimensionale Form der Ähnlichkeitsmatrix aus Abbildung 8 mit Do- kumenten-Reihen und Zeilenreferenzen parent left child / row right child / column 4 53 0 7 8 1 2 0 1 2 3 4 43210DD 6 9 0 1 2 4 53 7 86 9idx 1 2 2 3 33 4 44 4row 0 0 1 1 20 1 20 3col
  • 22. 4 Eingeschlagener Realisierungsweg 22 4.1.2 Update der Nachbarn Abbildung 10 verdeutlicht den in dieser Arbeit entwickelten Algorithmus zur Aktuali- sierung der Cluster. In dem iterativen Updateschritt der Clusteranalyse werden die Clus- ter der eindimensionalen DDD bis auf den Maxima Cluster (rot) durchlaufen und falls sich ein Cluster in Kindsrelation (dunkelrot) mit dem Maxima-Cluster befindet (grün), wird das jeweilige Kind dieser Relation des Clusters durch den Maxima-Cluster ersetzt. Diese Relation führt dazu, dass immer zwei Cluster, die Nachbarn, zu einem neuen Cluster zusammengefügt werden, wie der braune bidirektionale Pfeil verdeutlicht. Der Wert dieses neuen Clusters wird durch das eingesetzte Distanzmaß, hier Average- Linkage, bestimmt. Die Kinder des neuen Clusters werden durch den Nachbarn mit mi- nimalem Index bestimmt. Orange identifiziert Cluster, welche sich in keiner Kindsrela- tion mit dem Maxima-Cluster befinden und direkt in die Ausgabe kopiert werden. So wird in jeder Rekursion die DDM zur nächstkleineren Dreieckszahl hin verkleinert. Dieser Vorgang wird in zwei Schritte unterteilt. Zuerst werden die neuen Werte und Referenzen der zusammengefügten Cluster, mittels mehrerer CPU-Threads (CPU, s. Abschnitt 4.2.3) oder der Grafikkarte (GPU, s. Abschnitt 4.3.7) parallel berechnet. An- schließend wird durch einen weiteren CPU-Thread der Clusterbaum asynchron aktuali- siert. Da sich die Anzahl der Cluster und somit das Wertearray in jeder Iteration zur nächstkleineren Dreieckszahl hin verkleinert und nur die Werte dieser neuen Cluster berechnet werden, benötigt die Aktualisierung des Clusterbaumes auf der CPU die Re- ferenz zu dem jeweiligen originalen Cluster (Nachbar mit minimalen Index) sowie die Kindsposition des Maxima-Clusters. Weitere detaillierte Informationen zu diesem Algo- rithmus befinden sich auf dem mitgelieferten Datenträger [36]. Abbildung 10: Durchführung eines Clusteranalyse-Rekursionsschrittes 0.938 0 0 1.057 0.539 0 0.662 0.3470.274 0.000 1 0 2 0 2 1 3 0 3 1 3 2 4 0 4 1 4 2 4 3 0 2 1 0.274 0.000 4 1 4 2 0.738 1 0 2 0.505 41.05 3 0 1.05 3 0 1.05 3 0 0 00222 10 2 86 7 max pos orig ref index 0 1 2 3 4 5 876 9
  • 23. 4 Eingeschlagener Realisierungsweg 23 4.2 CPU Die komplette Textvorverarbeitung der Dokumente wird auf der CPU ausgeführt und bedarf keiner detaillierten Beschreibung. Jedes Dokument wird sequentiell eingelesen und mittels einer Hash-Tabelle werden die Dokumentenvektoren mit TF-Werten erstellt. Dabei werden die von nun an ungenutzten Textvariablen einzelner Dokumente manuell freigegeben, um den Hauptspeicher direkt zu entlasten. Der in dieser Arbeit verwendete Algorithmus zum Stemming von englischsprachigen Texten ist der Porter-Algorithmus von Martin Porter [37]. Jeder der in den folgenden Unterabschnitten beschriebenen Schritte nutzt, falls vorhan- den, mehrere Kerne der CPU aus. Die maximale Anzahl an Threads ist äquivalent zu der Anzahl an CPU-Threads. 4.2.1 Dokumentenrepräsentation IDF-Berechnung Die nebenläufige TF-IDF-Berechnung arbeitet pro Thread auf einer Dokumentenpartiti- on der Kollektion. Vor dem Aufruf der TF-IDF-Methode durch die erstellten Threads fügt der erstellende Hauptthread alle eindeutigen Terme der Dokumentenvektoren in einer globalen Hashtabelle zusammen. Dabei berechnet er gleichzeitig die logarithmier- te Dokumentenhäufigkeit (DF) der Terme. Dies macht eine redundante Berechnung dessen in den Threads obsolet und führt zu einer Verkürzung der Laufzeit der TF-IDF- Berechnung um den Faktor 5. Normalisierung Die Implementierung der Normalisierung ist analog zur TF-IDF-Berechnung parallel umgesetzt. Hierbei normalisiert ein Thread die Dokumentenvektoren seiner Partition an Dokumenten. 4.2.2 Ähnlichkeitsanalyse Die Ähnlichkeitsanalyse wird auch mittels mehrerer Threads durchgeführt. Da die An- zahl der zu berechnenden Paare mit Durchlauf der Dokumente abnimmt, bedarf es einer ungefähr gleichmäßigen Verteilung der Dokumente über die Threads. Somit berechnet ein Thread die Ähnlichkeit für seine Dokumentenpaarmenge mit Formel 7, welche anschließend mittels Abbildung 11 visualisiert wird.
  • 24. 4 Eingeschlagener Realisierungsweg 24 Formel 7: Dokumentenpaare, deren Ähnlichkeit ein Thread bestimmt Abbildung 11: Visualisierung der CPU-Threadausnutzung der Ähnlichkeitsanalyse mit farblich gekennzeichneten Threads und folgenden Parametern: |T|=3; |D|=5; p=2 Das Ergebnis der einzelnen Threads, die Initialcluster, wird in dynamischen Listen vom Typ ArrayList gespeichert und anschließend vom Hauptthread zusammengefügt. Daraus ergibt sich, wie in Abbildung 12 verdeutlicht, eine komprimierte Version der TDM in Form einer eindimensionalen Liste mit der in Formel 10 definierten Länge. Abbildung 12: Schaubild der aus Abbildung 11 komprimierten DDMs 4.2.3 Clusteranalyse Auf Basis dieser eindimensionalen Ähnlichkeitsmatrix erfolgt in diesem Abschnitt die hierarchisch agglomerative Clusteranalyse auf der CPU. Folgend werden die beiden Teilschritte der Iteration erläutert sowie deren Ablauf in Abbildung 13 präsentiert. Die Berechnung der neuen Clusterwerte wurde nicht direkt auf dem CPU-Clusterbaum durchgeführt, da dies die Komplexität des Algorithmus sehr stark erhöhen würde (vgl. Code 8: Cluster-Aktualisierungsalgorithmus auf dem Cluster-Binärbaum, Codezeile: 32). 0 1 2 3 4 43210DD 0 1 2 1 0 T
  • 25. 4 Eingeschlagener Realisierungsweg 25 Abbildung 13: Ablaufdiagramm der CPU-Clusteranalyse Findung des Maximums Die Findung des Clusters in der eindimensionalen DDM, dessen Kinder die höchste Ähnlichkeit aufweisen wird nebenläufig ausgeführt. Zum Einsatz kommt ein paralleler Reduktionsalgorithmus, in dem jeder Thread in seinem Teil der DDM den lokalen Ma- xima-Cluster findet. Nach Terminierung der Threads durchsucht der initiierende Haupt- thread die lokalen Maxima nach dem globalen Maxima-Cluster. Update der Nachbarn Auf Basis eines primitiven float Arrays wird die Berechnung der neuen Clusterwerte unter Benutzung der Reihen- und Spaltenindizes als Kindsreferenzen durchgeführt. An- schließend spannt ein weiterer CPU-Thread asynchron den CPU-Clusterbaum weiter auf (s. 4.1.2). Die beiden verwendeten Klassen Idx2D und El2D und sind identisch mit den in der GPU-Variante verwendeten C-Strukturen (s. Anhang C: Erweiterter Cu- da-Code). Java-Implementierung float[] clusterValues; //global input cluster values1 int clusterMaxIndex; //global input cluster maximum value2 3 float[] clusterValuesNew; //output new cluster values4 int[] clusterLinIdxRefs; //output original cluster references5 int[] clusterCopyFlags; //cluster max position and copy flags6 7 //maximum cluster childs / row and column indices8 Idx2D clusterMax = new Idx2D(indicesRow[clusterMaxIndex],9 indicesCol[clusterMaxIndex]);10 11 //cluster12 El2D cluster = null;13 //relative cluster14 CPU Update Thread Main Thread get max index calc cluster values update cpu cluster tree while nCluster > 1 cpu sync barrier
  • 26. 4 Eingeschlagener Realisierungsweg 26 El2D clusterRel = null;15 16 int copy = -1, outLinIdx = -1, minRefIdx = -1;17 18 //Threads loops over the number of elements he is responsible of19 for(int i = 0; i < clusterValues.length; i++)20 {21 if(i != clusterMaxIndex)22 {23 //set cluster and relative cluster24 cluster = new ElFloat2D(indicesRow[i], indicesCol[i],25 clusterValues[i]);26 clusterRel = new ElFloat2D(cluster.row, cluster.column,27 cluster.value);28 29 //0 = direct copy of cluster, no relative30 //1 = cluster max will be merged as right child, 2 = left31 copy = 0;32 33 //find relative cluster34 if(cluster.row == clusterMax.column)35 {36 copy = 1;37 clusterRel.set(clusterMax.row, cluster.column);38 }39 else if(cluster.row == clusterMax.row)40 {41 copy = 1;42 if(clusterMax.column > cluster.column)43 clusterRel.set(clusterMax.column, cluster.column);44 else45 clusterRel.set(cluster.column, clusterMax.column);46 }47 else if(cluster.column == clusterMax.column)48 {49 copy = 2;50 if(cluster.row > clusterMax.row)51 clusterRel.set(cluster.row, clusterMax.row);52 else53 clusterRel.set(clusterMax.row, cluster.row);54 }55 else if(cluster.column == clusterMax.row)56 {57 copy = 2;58 clusterRel.set(cluster.row, clusterMax.column);59 }60 61 //merge neighbors at their minimum index and calculate new value62 if(copy != 0)63 {64 clusterRel.value = clusterValues[getMatLinIdx(clusterRel.row,65 clusterRel.column)];66 cluster.row = Math.min(cluster.row, clusterRel.row);67 cluster.column = Math.min(cluster.column, clusterRel.column);68 cluster.value = 0.5f * (cluster.value + clusterRel.value);69 }70 71 //Update Row and Column Indices by reducing them with one72 //if they are larger then their cluster max counterparts73 if(cluster.row >= clusterMax.row)74 {75 cluster.row--; // = max(0, cluster.row - 1);76 //non-copy clusters dont need column decrease77
  • 27. 4 Eingeschlagener Realisierungsweg 27 if(copy == 078 && cluster.column > clusterMax.row)79 cluster.column = Math.max(0, cluster.column - 1);80 }81 82 minRefIdx = Math.min(i, getMatLinIdx(clusterRel.row,83 clusterRel.column));84 85 //output at minimum index86 if(minRefIdx == i)87 {88 //Get output linear index (s. Formel 11)89 outLinIdx = Util.getMatLinIdx(cluster.row, cluster.column);90 91 clusterValuesNew[outLinIdx] = cluster.value;92 clusterLinIdxRefs[outLinIdx] = minRefIdx;93 clusterCopyFlags[outLinIdx] = copy;94 }95 }96 }97 Code 1: Java-Code zur Speicherallokation und Kopierung einer Matrix 4.3 GPU Bei der Implementierung der einzelnen Schritte auf die GPU wurde auf die Gewährleis- tung einer höchstmöglichen Kompatibilität geachtet, weshalb alle implementierten Ker- nel auf dem Cuda-Rechenmodell 1.0 lauffähig sind. Weiterhin wurde auf die maximale Skalierbarkeit der einzelnen Cuda-Kernel geachtet. Falls die Anzahl der Blöcke in den einzelnen Kerneln das Limit des vorzeichenlosen 32-Bit Integer-Datentyp3 überschrei- ten würde, müsste für die Indizierungen und Längenangaben auf 64-Bit gewechselt werden. Da die Kernel bei den untersuchten Kollektionsgrößen dieses Limit nicht errei- chen werden, wurde darauf verzichtet. Alle Implementierungsskizzen in diesem Ab- schnitt benutzen für die Speicherbereiche dieselben Farben wie in Abbildung 6: Cuda- Speichermodell © Nvidia. 4.3.1 Ressourcenausnutzung Aufgrund der in Abschnitt 3.6.4 und 3.6.5 vorgestellten Limitationen bedarf es einiger Vorüberlegung bezüglich der Anzahl an Blocks und dessen Threads, um die Grafikkar- ten optimal auszulasten, da diese zur Kompilierungszeit vorhanden sein müssen. Alle implementierten grundlegenden Kernel benutzen dieselbe Anzahl an Threads pro Block. Jedoch benötigen Kernel, die eine parallele Reduktion durchführen eine Anzahl an Threads gleich einer Zweierpotenz (siehe Abschnitt 4.3.4). Die optimale Anzahl an Threads kann unterschiedlich ermittelt werden, ist jedoch von dem unterstützten Cuda- Rechenmodell der Grafikkarte abhängig. Da keiner der entwickelten Kernel das mini- 3 Maximalwert des vorzeichenlosen Integer-Datentyp ist 4.294.967.294
  • 28. 4 Eingeschlagener Realisierungsweg 28 malste Limit (Rechenmodell 1.0) an nutzbaren Registern pro SM übersteigt4 , können in dieser Arbeit die optimalen Daten für ein Cuda-Rechenmodell dynamisch mithilfe der abrufbaren Grafikkarteninformationen wie folgt berechnet werden [38]: Formel 8: Berechnung der optimalen Blockgröße Die optimale Blockgröße für Kernel, die eine parallele Reduktion durchführen, wird auf der nächsthöheren Zweierpotenz festgelegt. Diese beiden Blockgrößen führen zu einer vollständigen Auslastung der Grafikkarte. Für das niedrigste Rechenmodell 1.0, ergibt sich die optimale Blockgröße wie folgt: Definition 1: Optimale Blockgrößen für das Cuda-Rechenmodell 1.0 Des Weiteren sollten Arraygrößen zur Nutzung des gemeinsamen Speichers der Threads eines Blockes zur Kompilierzeit als Konstanten definiert werden. Diese sollten Vielfa- che von 16 sein und wurden in dieser Arbeit abhängig von der Größe des gemeinsamen Speichers festgelegt [3]. 4.3.2 JCuda Die verwendete Java-Bindung für Cuda ist JCuda, von Marco Hütter in der Version 0.4.0-beta1 für die aktuelle Cuda-Version 4.0 [35]. Diese besteht zum größten Teil aus einer direkten Portierung der Originalen C Version, besitzt aber Java-bedingte Limita- tionen. So unterstützt Cuda einige C++ sprachspezifische Elemente, wie Strukturen oder Templates, welche in der aktuellsten Version der Java-Bindung jedoch noch nicht unter- stützt werden. Deshalb müssen alle Algorithmen auf primitiven Datentypen basieren. Weiterhin ist darauf zu achten, dass sich die in beiden Sprachen vorhandenen primitiven Datentypen in ihrer Bitrepräsentation voneinander unterscheiden können. 4.3.3 Hilfsklassen zur dynamischen Speicherallokation und Kopierung Da der Videospeicher der eigenständigen Grafikkarte nicht denselben Speicherbereich des Hauptspeichers besitzt, benötigt es einen vermehrten Aufwand, um Daten im Vi- 4 Für das Rechenmodell 1.0 sind 10 Register pro Thread optimal. Zwei der implementierten Kernel be- nutzten bis zu zwei zusätzliche Register, welche mittels des Nvcc-Befehls „-maxregcount=10― für das Rechenmodell 1.0 auf 10 reduziert werden.
  • 29. 4 Eingeschlagener Realisierungsweg 29 deospeicher zu speichern. Speicheroperationen und Transaktionen zwischen dem Host (CPU) und dem Device (GPU) müssen implizit aufgerufen werden. Grundlegende Me- thoden zur Speicherallokation, Speicherkopierung und Speichersetzung in Cuda basie- ren auf ihren nativen C Implementierungen. Die zwei in dieser Arbeit wichtigsten wer- den in folgender Tabelle 1 vorgestellt. Hierbei sind „dst―, „src― und „devPtr― Pointer und „size― gibt die Größe des zu bearbeitenden Speicherbereichs in Byte an. Kopiervor- gänge in Cuda werden mittels einer Richtungsvorgabe, „cudaMemcpyKind‖, ausgeführt. Art C Cuda Runtime API Allokation malloc(size) cudaMalloc(devPtr, size) Kopierung memcpy(dst, src, size) cudaMemcpy(dst, src, size, cuda- MemcpyKind) Tabelle 1: Cuda-Methoden zur Speicheroperation und Transaktion [39]. Eine einfache Speicherallokation eines Host-Arrays auf das Device und dessen spätere Rückkopierung sind mit einem erheblichen Programmieraufwand verbunden, weshalb zuerst Hilfsklassen für die obig aufgelisteten Methoden für ein- und zweidimensionale Arrays von primitiven Datentypen (Byte, Integer, Float) erstellt wurden. Da Java keine generischen Klassen von primitiven Datentypen unterstützt, benötigte jeder ver- wendete Datentyp seine eigenen Hilfsklassen. Der nachfolgende Codeausschnitt zeigt die manuelle Speicherallokation und Kopierung eines zweidimensionalen Arrays vom Datentyp Float von dem Hostspeicher in den Videospeicher der Grafikkarte (Device) mittels der Java-Bindung für Cuda. Dieser ver- deutlicht den notwendigen Programmieraufwand um Daten in den Videospeicher zu kopieren und in den GPU-Kernels nutzen zu können. Java-Implementierung import static jcuda.runtime.cudaMemcpyKind.cudaMemcpyHostToDevice;1 import static jcuda.runtime.cudaMemcpyKind.cudaMemcpyDeviceToHost;2 import jcuda.Pointer, jcuda.Sizeof, jcuda.runtime.JCuda;3 4 //a two dimensional non-empty Array to copy to device5 float[][] mat;6 //device Row Pointers7 Pointer[] arr_d = new Pointer[mat.length];8 //device Pointer9 Pointer data_d = new Pointer();10 int size = mat[0].length * Sizeof.FLOAT;11 12 //Allocate and copy each row of the matrix13 for (int i = 0; i < mat.length; i++)14 {15 arr_d[i] = new Pointer();16 JCuda.cudaMalloc(arr_d[i], size);17 JCuda.cudaMemcpy(arr_d[i],18 Pointer.to(mat[i]),19
  • 30. 4 Eingeschlagener Realisierungsweg 30 size,20 cudaMemcpyHostToDevice);21 }22 //Allocate and copy the Pointer of the Matrix Datatype23 int sizeOfArrD = mat.length * Sizeof.POINTER;24 JCuda.cudaMalloc(data_d, sizeOfArrD);25 JCuda.cudaMemcpy(data_d,26 Pointer.to(arr_d),27 sizeOfArrD,28 cudaMemcpyHostToDevice);29 Code 2: Java-Code zur Speicherallokation und Kopierung einer Matrix 4.3.4 Parallele Reduktion In vielen Schritten des Dokumenten-Clustering muss eine Menge auf ein einzelnes Element reduziert werden oder ein einzelner Wert aus dieser berechnet werden. Klassi- scher Vertreter ist die Findung des Clusterpaares mit der höchsten Ähnlichkeit. Ein sehr effizienter Ansatz für dieses Problem ist die parallele Reduktion [40] [41]. Die Imple- mentierung dieses Ansatzes auf der GPU unterscheidet sich aber grundlegend von der schon genutzten CPU-Variante, da C keine dynamischen Listen/ Arrays kennt und auf der GPU sehr viel mehr Threads verwendet werden. Die parallele Reduktion auf der GPU basiert auf folgendem Prinzip, welche an einem Beispiel in Abbildung 14 mit dem Additions-Operator veranschaulicht wird und anschließend in Cuda generisch imple- mentiert wird. Die Anzahl der genutzten Threads ist gleich der Hälfte der Größe des Eingabearrays, weshalb jeder Thread zwei Elemente miteinander vergleicht und das Resultat dieses Vergleiches in das Array an seinem Index schreibt. Die Arraygröße muss somit ein Vielfaches von zwei sein. Nach jeder Iteration werden alle Threads miteinander syn- chronisiert und dieser Vorgang wird solange durchgeführt, bis sich das gewünschte Element an der ersten Stelle des Arrays befindet. Abbildung 14: Parallele Reduktion auf der GPU 0 1 2 4 5 6 73 3 4 1 0 8 2 6 5 Threads Daten 2 11 7 4 9 0 10 5 15 8 4 17 2 16 9 2 11 7 4 9 0 10 4 10 22 17 24 13 9 2 10 5 2 11 7 4 9 0 10 10 46 30 10 5 9 2 10 5 2 11 7 4 9 0 10 10 76 11 10 5 9 2 10 5 2 11 7 4 9 0 10 10 Iteration: 1 Iteration: 2 Iteration: 3 Iteration: 4
  • 31. 4 Eingeschlagener Realisierungsweg 31 Dieser Algorithmus wird innerhalb eines Blockes durchgeführt, da nur auf dieser Ebene die Threads miteinander effizient synchronisiert werden können. Vor der Ausführung der Rekursion kopieren die Threads gemeinsam die Daten aus dem globalen in ihren gemeinsamen Speicher, da dieser für die vielen Lese- und Schreibzugriffe der einzelnen Iterationen eine sehr viel geringere Latenz aufweist. Da die Threadanzahl pro Block limitiert, ist kann der Operator nur auf die zweifache Blockgröße an Elementen angewendet werden. Eine höhere Anzahl benötigt somit meh- rere Blöcke, welche jeweils ihr lokales Resultat berechnen und anschließend mittels weiterer Kernel-Aufrufe auf das globale Element reduziert werden. Cuda-Implementierung /**1 * @param in T* input arraypointer2 * @param out T single output variable3 */4 template <typename T>5 __device__ void reductionAdd(T* in,6 T &out)7 {8 if(blockDim.x >= 1024 && threadIdx.x < 512)9 in[threadIdx.x] += in[threadIdx.x + 512];10 __syncthreads();11 12 if(blockDim.x >= 512 && threadIdx.x < 256)13 in[threadIdx.x] += in[threadIdx.x + 256];14 __syncthreads();15 16 if(blockDim.x >= 256 && threadIdx.x < 128)17 in[threadIdx.x] += in[threadIdx.x + 128];18 __syncthreads();19 20 if(blockDim.x >= 128 && threadIdx.x < 64)21 in[threadIdx.x] += in[threadIdx.x + 64];22 __syncthreads();23 24 //unroll last warp no sync needed25 if(threadIdx.x < 32)26 {27 if(blockDim.x >= 64) in[threadIdx.x] += in[threadIdx.x + 32];28 if(blockDim.x >= 32) in[threadIdx.x] += in[threadIdx.x + 16];29 if(blockDim.x >= 16) in[threadIdx.x] += in[threadIdx.x + 8];30 if(blockDim.x >= 8) in[threadIdx.x] += in[threadIdx.x + 4];31 if(blockDim.x >= 4) in[threadIdx.x] += in[threadIdx.x + 2];32 if(blockDim.x >= 2) in[threadIdx.x] += in[threadIdx.x + 1];33 34 //set final value35 if(threadIdx.x == 0) out += in[0];36 }37 }38 Code 3: Cuda-Code für eine blockabhängige, generische, additive parallele Reduktion auf einem Array im gemeinsamen Speicher (SMEM)
  • 32. 4 Eingeschlagener Realisierungsweg 32 4.3.5 Dokumentenrepräsentation Da die Erstellung der Dokumentenvektoren auf der CPU mittels dynamischen Hash- Tabellen realisiert wird, bedarf es einer Konvertierung dieser in die eigentliche zweidi- mensionale Term-Dokument-Matrix (TDMs) mittels primitiven Float Arrays um die- se zur weiteren Benutzung auf die Grafikkarte kopieren zu können. Die eigentlichen Terme der TDM sind für die nachfolgenden Berechnungen irrelevant und werden bei der Konvertierung ignoriert. IDF-Berechnung Die TF-IDF-Berechnung wird auf der Term-Dokumenten-Matrix (TDM) mit TF- Werten durchgeführt. Die IDF-Berechnung benötigt die Anzahl der Dokumente, in de- nen jeder Term vorhanden ist. Diese Zähloperation könnte mittels atomarer Operationen vereinfacht realisiert werden. Jedoch benötigen atomare Operationen im langsamen glo- balen Speicher mindestens das Cuda-Rechenmodell 1.1 und im SMEM sogar 1.2 [3], weshalb diese Zähloperation pro Term mittels einer parallelen Reduktion durchgeführt wird. Ein weiteres Argument ist die notwendige interne Serialisierung der atomaren Operationen. Die IDF-Berechnung auf der GPU wird in Abbildung 15 visualisiert und in darauffolgendem Codesegment implementiert. Ein Block ist für einen Term, d.h. eine Reihe der TDM verantwortlich. Somit kann die parallele Reduktion auf dem SMEM realisiert werden. Abbildung 15: Cuda TF-IDF-Berechnung eines Blockes mit = Spal- ten/Dokumenten-Anzahl, =Threadanzahl, thread doc/row tile loop RowBlockIndex=b doc tile: pdoc tile: …doc tile: 1 eb,… eb, |T|-1 eb, |D|-1-|T| eb, … eb, |D|-1eb, i*|T| eb, (i+1)* |T|-1) eb, …eb, 0 TDM O0 O… Otones count IDF 0 … |T|-1Threadsi 1. collect data save count 2. update count with sum of ones using parallel reduction 3. calculate IDF value 4. update elements
  • 33. 4 Eingeschlagener Realisierungsweg 33 Die Threads eines Blockes sind für die einzelnen Spalten, d.h. Dokumente verantwort- lich. Da mehr Dokumente als Threads pro Block vorhanden sind, ist jeder Thread für mehrere Dokumente verantwortlich, was mittels einer Schleife implementiert wurde (Codeblock ab Zeile: 46). Jeder Thread prüft, ob die Werte seiner Dokumente bezüglich des Blockterms größer als Null sind und schreibt, falls ja, eine Eins in das SMEM- Hilfsarray (s. Abbildung 15: 1, Codezeilen: 56-69). Die Threads synchronisieren sich und mittels der additiven parallelen Reduktion werden die Einsen in dem Hilfsarray gezählt (2, Codezeile: 73). Nach einer weiteren Synchronisierung kann ein Thread den IDF-Wert für diesen Term/ Block berechnen (3, Codezeile: 81). Zum Schluss aktuali- siert jeder Thread seine Elemente mittels dieses IDF-Wertes (4, Codeblock ab Zeile: 87). Cuda-Implementierung /*1 * @param inOutTDM_g float** Term-Document Matrix2 * @inTermCount_s int Heigth of the Matrix equals the number of terms3 * @inDocCount_s int Width of the Matrixe equals number of documents4 * @inDocTileCount_s int Tile loop number to load terms equals to5 (s. Formel 12 mit Parametern (heigth, blockDim.x * 2))6 * @inOutputDocTileCount_s int Output document tile count equals to7 (s. Formel 12 mit Parametern (heigth, blockDim.x))8 */9 __global__ void calcTfIdf(float** inOutTDM_g,10 const unsigned int inTermCount_s,11 const unsigned int inDocCount_s,12 const unsigned int inDocTileCount_s,13 const unsigned int inOutputDocTileCount_s)14 {15 const unsigned int blockId = blockIdx.y * gridDim.x + blockIdx.x16 + gridDim.x * gridDim.y * blockIdx.z; //3D -> long long int17 18 if(blockId >= inTermCount_s)19 return;20 21 __shared__ unsigned int tfFlags_s[BLOCK_SIZE_POW2];22 23 //idf and termCount for the current term24 __shared__ unsigned int termCount_s;25 __shared__ float idf_s;26 27 //init shared memory28 if(threadIdx.x < BLOCK_SIZE_POW2)29 {30 tfFlags_s[threadIdx.x] = 0;31 if(threadIdx.x == 0)32 {33 idf_s = 0.0f;34 termCount_s = 0;35 }36 }37 __syncthreads();38 39 unsigned int idx1 = 0, idx2 = 0;40 41 //loop over the tiles requirered to count the non-zero elements42 for(int i = 0; i < inDocTileCount_s; i++)43
  • 34. 4 Eingeschlagener Realisierungsweg 34 {44 idx1 = i * BLOCK_SIZE_POW2_DOUBLE + threadIdx.x;45 idx2 = idx1 + blockDim.x;46 47 /*48 * Perform global parallel reduction step checking 2 values49 * load values into shared memory50 * save a 1 if input value is greater then 051 */52 if(idx1 < inDocCount_s)53 {54 if(idx2 < inDocCount_s)55 {56 tfFlags_s[threadIdx.x] =57 (int)(inOutTDM_g[blockId][idx1] > 0.0f)58 + (int)(inOutTDM_g[blockId][idx2] > 0.0f);59 }60 else61 tfFlags_s[threadIdx.x] =62 (int)(inOutTDM_g[blockId][idx1] > 0.0f);63 }64 else65 tfFlags_s[threadIdx.x] = 0;66 __syncthreads();67 68 //Do Reduction with to count the non-zero elements69 reductionAdd_ui(tfFlags_s, termCount_s);70 71 //sync tile loop72 __syncthreads();73 }74 75 //calculate shared idf value76 if(threadIdx.x == 0)77 idf_s = log10f(inDocCount_s) - log10f(termCount_s);78 __syncthreads();79 80 float tf = 0.0f;81 82 //loop over the documents this thread is responsible of83 for(int b = 0; b < inOutputDocTileCount_s; b++)84 {85 //coalesced global memory indexing86 idx1 = threadIdx.x * inOutputDocTileCount_s + b;87 if(idx1 >= inDocCount_s)88 break;89 //reduce global memory write operations90 tf = inOutMatTermDoc_g[blockId][idx1];91 if(tf > 0.0f)92 inOutMatTermDoc_g[blockId][idx1] = tf * idf_s;93 }94 }95 Code 4: Cuda-Code zur Berechnung der TF-IDF-Werte
  • 35. 4 Eingeschlagener Realisierungsweg 35 Normalisierung Die Normalisierung der Dokumentenvektoren in der TDM erfolgt spaltenweise. Die Normalisierung eines Vektors benötigt dessen Länge, welche abhängig von den einzel- nen Werten ist. Anhand dieser Überlegungen wird die Cuda-Gitter-Struktur wie folgt verwendet: Ein Block normalisiert einen Dokumenten-Spaltenvektor der TDM. Da es mehr Terme in der TDM als mögliche maximale Threads pro Block gibt, ist jeder Thread für eine Teilmenge der Terme verantwortlich, welche er mithilfe einer Schleife (siehe Abbildung 16 Punkt 1, Codeblock ab Zeile: 42) durchläuft. Zuerst berechnen die Threads partitionsweise die Quadrate ihrer Elemente (1, Codezeilen: 53-66), um dann mittels paralleler Reduktion gemeinsam die Summe derer zu berechnen (2, Codezeile: 70). Daraufhin synchronisieren sich alle Threads und ein Thread berechnet die Rezipro- ke der Wurzel dieser Summe (3, Codezeile: 80). Nach einer weiteren Synchronisierung berechnet jeder Thread für seine Elemente mittels einer Multiplikation die normierten Werte (4, nicht skizziert, Codeblock ab Zeile: 87). Abbildung 16: Cuda-Zeilenvektor-Normalisierungsschema eines Blockes mit = Zei- len/ Term-Anzahl, =Threadanzahl, thread column tile loop P0 P… Pt products ColumnBlockIndex=b elementtile:p em-1-|T|, b e…, b em-1, b elementtile:… ei*|T|, b e(i+1)*|T|-1 , b e…, b elementtile:1 e…, b e|T|-1, b e0, bsumrcpRoot 2. update sum with sum of products using parallel reduction 1. collect data calculate products 3. calculate reciprocal square root 4. update elements TDM 0 … |T|-1 Threadsi
  • 36. 4 Eingeschlagener Realisierungsweg 36 Cuda-Implementierung /*1 * @param inOutMat_g float** Matrix2 * @param inWidth_s int width of the Matrix3 * @param inHeight_s int height of the Matrix4 * @param inTileCount_s int Column Tile Count5 (s. Formel 12 mit Parametern (heigth, blockDim.x * 2))6 * @inOutputTileCount_s int Output column tile count7 (s. Formel 12 mit Parametern (heigth, blockDim.x))8 */9 __global__ void normColumn(float** inOutMat_g,10 const unsigned int inWidth_s,11 const unsigned int inHeight_s,12 const unsigned int inTileCount_s,13 const unsigned int inOutputTileCount_s)14 {15 const unsigned int blockId = blockIdx.y * gridDim.x + blockIdx.x16 + gridDim.x * gridDim.y * blockIdx.z; //3D -> long long int17 18 if (blockId >= inWidth_s)19 return;20 21 __shared__ float dotSum_s;22 __shared__ float values_s[BLOCK_SIZE_POW2];23 24 //initialize shared memory25 if (threadIdx.x < BLOCK_SIZE_POW2)26 {27 values_s[threadIdx.x] = 0;28 if (threadIdx.x == 0)29 dotSum_s = 0.0f;30 }31 __syncthreads();32 33 unsigned int idx1 = 0, idx2 = 0;34 float value1 = 0.0f, value2 = 0.0f;35 36 //loop over the tiles requirered to compute the dotSum element37 for (int i = 0; i < inTileCount_s; i++)38 {39 idx1 = i * BLOCK_SIZE_POW2_DOUBLE + threadIdx.x;40 idx2 = idx1 + blockDim.x;41 42 /*43 * load values into shared memory44 * calculate product of 2 values already45 * at this stage so that normal reduction46 * steps result only in addition47 */48 if (idx1 < inHeight_s)49 {50 value1 = inOutMat_g[idx1][blockId];51 value1 *= value1;52 if (idx2 < inHeigth_s)53 {54 valueTemp2 = inOutMat_g[idx2][blockId];55 values_s[threadIdx.x] = value1 + value2 * value2;56 }57 else58 values_s[threadIdx.x] = value1;59 }60
  • 37. 4 Eingeschlagener Realisierungsweg 37 else61 values_s[threadIdx.x] = 0.0f;62 __syncthreads();63 64 reductionAdd_f(values_s, dotSum_s); //do add reduction65 66 //sync tile loop67 __syncthreads();68 }69 70 //root is undefined if dotSum_s == 0.0f71 if (dotSum_s > 0.0f)72 {73 //calculate the root74 if (threadIdx.x == 0)75 dotSum_s = rsqrtf(dotSum_s);76 __syncthreads();77 78 float value = 0.0f;79 //output as many row cells this threads is responsible of80 for (int b = 0; b < inOutputTileCount_s; b++)81 {82 //coalesced memory access83 idx1 = threadIdx.x * inOutputTileCount_s + b;84 if (idx1 >= inHeight_s)85 break;86 value = inOutMat_g[idx1][blockId];87 //reduce global memory write operations88 if (value > 0.0f)89 inOutMat_g[idx1][blockId] = value * dotSum_s;90 }91 }92 }93 Code 5: Cuda-Code zur Zeilenvektor-Normalisierung 4.3.6 Ähnlichkeitsanalyse Die Umsetzung der Ähnlichkeitsanalyse in Cuda folgt folgendem, in Abbildung 17 vi- sualisierten und diesem Abschnitt referenzierten Prinzip. Jeder Block hält ein Dokument und die einzelnen Threads dieses Blockes berechnen die Ähnlichkeiten aller anderen Dokumente zu diesem Dokument und speichern diese an die entsprechende Stelle des eindimensionalen Ausgabearrays. Ein Block kann jedoch nur einen Teil des Dokumen- ten-Spaltenvektors in den gemeinsamen Speicher seiner Threads laden, da dieser be- grenzt ist (s. oben). Weiterhin muss jeder Thread aufgrund der limitierten Threadanzahl eines Blockes die Ähnlichkeit mehrerer Dokumente zu dem Block-Dokument berech- nen. Aufgrund dieser beiden Limitationen muss der gemeinsame Thread-Ladevorgang des Block-Dokumententeiles (1, Codezeilen: 38-47) und die Berechnung der paarweisen euklidischen Ähnlichkeiten in einer verschachtelten Schleife durchgeführt werden (2, Term-Schleife ab Zeile: 33; Dokumentenschleife ab Zeile: 50). Der Ähnlichkeitswert eines Dokumentenpaares wird in das Ausgabearrays an den Index aus Formel 11 ge- schrieben (3)(s. 4.1.1). Nach Berechnung dieser Werte auf der GPU werden diese in den Hauptspeicher kopiert und mehrere CPU-Threads erstellen die Initialcluster.
  • 38. 4 Eingeschlagener Realisierungsweg 38 Abbildung 17: Cuda-Schema zur Berechnung der paarweisen euklidischen Ähnlichkei- ten der Dokumentenvektoren in der TDM eines Blockes mit =Threadanzahl Cuda-Implementierung /*1 * @param inTDM_g float** Term-Document Matrix2 * @param inTermCount_s Number of Terms/Rows3 * @param inTermTileCount_s number of term tiles we need4 (s. Formel 12 mit Parametern (inTermCount_s, TILE_SIZE))5 * @param inDocCount_s Number of Documents/Columns6 * @param inDocTileCount_s Number of document tiles we need7 (s. Formel 12 mit Parametern (inDocCount_s, blockDim.x))8 * @param inClusterCount_s length of outDDM_g (s. Formel 10)9 * @param outDDM_g output cluster similarities array10 */11 __global__ void calcSimTermMax(const float** inTDM_g,12 const unsigned int inTermCount_s,13 const unsigned int inTermTileCount_s,14 const unsigned int inDocCount_s,15 const unsigned int inDocTileCount_s,16 const unsigned int inClusterCount_s,17 float* outDDM_g)18 {19 const unsigned int blockId = blockIdx.y * gridDim.x + blockIdx.x20 + gridDim.x * gridDim.y * blockIdx.z; //3D -> long long int21 22 if(blockId >= inDocCount_s)23 return;24 25 __shared__ float docBlockValues_s[TILE_SIZE];26 27 float dot = 0.0f;28 unsigned int rowStartIdx = 0, rowIdx = 0, linIdx = 0, colIdx = 0;29 int i = 0, k = 0;30 31 //iterate over the term tiles32 for(int j = 0; j < inTermTileCount_s; j++)33 {34 TDM blockb term tile threadi doc tile loop threaditermtile loop elementtile:… e…, … e(i+1)* |T|-1 … ei*|T|, … elementtile:… e…, … e(i+1)* |T|-1 … ei*|T|, … elementtile:… e…, … e(i+1)* |T|-1 … ei*|T|, … elementtile:… e…, … e…, … e…, … elementtile:… e…, b e…, b e…, b ssp ssp ssp elementtile:… e…, … e…, … e…, … elementtile:… e…, … e…, … e…, … 1 2 DDM add add add 3 spp:sum of pairwise products
  • 39. 4 Eingeschlagener Realisierungsweg 39 rowStartIdx = j * TILE_SIZE;35 36 //load the current term tile for the block-document37 if(threadIdx.x < TILE_SIZE)38 {39 rowIdx = rowStartIdx + threadIdx.x;40 if(rowIdx < inTermCount_s)41 docBlockValues_s[threadIdx.x] = inTDM_g[rowIdx][blockId];42 //catched in loop below!43 else44 docBlockValues_s[threadIdx.x] = 0.0f;45 }46 __syncthreads();47 48 //calculate the dot product for the documents of this thread49 for(i = 0; i < inDocTileCount_s; i++)50 {51 //coalesced52 colIdx = threadIdx.x * inDocTileCount_s + i;53 //triangular matrix54 if(colIdx >= blockId)55 break;56 //reset dot for every new document57 dot = 0.0f;58 //loop over values in shared block-document tile59 for(k = 0; k < TILE_SIZE; k++)60 {61 rowIdx = rowStartIdx + k;62 if(rowIdx >= inTermCount_s)63 break;64 dot += docBlockValues_s[k] * inTDM_g[rowIdx][colIdx];65 }66 //calculate linear output index (s. Formel 11)67 linIdx = getTriMatLinIdx(blockId, colIdx);68 //reduce global read and write operations69 if(dot >= 0.0f)70 outDDM[linIdx] += dot;71 }72 73 //sync term tile loop74 __syncthreads();75 }76 }77 Code 6: Cuda-Code zur Berechnung der paarweisen Ähnlichkeiten der Dokumente
  • 40. 4 Eingeschlagener Realisierungsweg 40 4.3.7 Clusteranalyse Auf Basis der eindimensionalen Ähnlichkeitsmatrix, welche sich im globalen Video- speicher der Grafikkarte befindet, erfolgt in diesem Abschnitt die hierarchisch agglome- rative Clusteranalyse mit dem Average-Linkage Distanzmaß auf der GPU. Folgend werden die beiden Teilschritte erläutert sowie deren asynchroner Ablauf in Abbildung 18 verdeutlicht: Abbildung 18: UML-Sequenzdiagramm der GPU-Clusteranalyse Der CPU-Haupt-Thread startet den GPU-Kernel zur Findung des Index des höchsten Ähnlichkeitswertes und wartet auf dessen Ergebnis. Den Index des Maximums übergibt dieser Thread zusammen mit den aktuellen Ähnlichkeitswerten der Cluster als Parame- ter an den GPU-Kernel, welcher die neuen Werte der Cluster und deren originale Clus- terreferenzen (Indizes) in seine Ausgabearrays speichert. Die Länge dieser Ausgabear- rays ist eine Dreieckszahl kleiner als die des Werte-Eingabearrays. Mittels dieser Daten aktualisiert ein weiterer CPU-Thread asynchron den CPU-Clusterbaum, was eine vo- rangehende Synchronisierung beider CPU-Threads benötigt. Findung des Maximums Mittels der in Abschnitt 4.3.4 vorgestellten parallelen Reduktion wird auf der GPU der Index des höchsten Wertes in dem Cluster-Wertearray gesucht. Dabei wird zuerst ein Kernel ausgeführt, welcher pro Block den Index und dessen Wert berechnet und aus- gibt. Sofern mehr als ein Block verwendet wurde, erfolgt die Ausführung des rekursiven GPU-Reduktionskernel, welcher solange wiederholt ausgeführt wird, bis der Index mit- tels eines einzelnen Blockes berechnet werden kann (s. Code 6). Die beiden eingesetz- ten Kernel ähneln der in Code 3 vorgestellten Implementierung und benötigen keiner weiteren Vorstellung. CPU GPU Update Thread Main Thread GPU get max index return max index calc cluster values return values update cpu cluster tree while nCluster > 1 cpu sync barrier
  • 41. 4 Eingeschlagener Realisierungsweg 41 Java-Implementierung //create two empty values and indices output helper arrays on device1 private static CudaFloat1D outMaxValues_hd, out2MaxValues_hd;2 private static CudaInt1D outMaxIndices_hd, out2MaxIndices_hd;3 4 /**5 * Returns the index of the given device float value array6 * @param array_hd CudaFloat1D Float Array on the device7 * @return int maximum index8 */9 public static int getMaxIdx1D(CudaFloat1D inValues_hd)10 {11 //calculate number of necessary blocks12 int blockCount = p(inValues_hd.length, BLOCK_SIZE_POW2_DOUBLE);13 14 //start first gpu reduction phase15 GPU_Kernel_FirstReduction.Start(blockCount,16 BLOCK_SIZE_POW2,17 params { inValues_hd,18 inValues_hd.length,19 outMaxValues_hd,20 outMaxIndices_hd })21 22 23 //reduce on gpu until we only have one more block24 while(blockCount > 1)25 {26 //reduce block count27 int length = blockCount;28 //calculate number of necessary blocks29 blockCount = p(blockCount, BLOCK_SIZE_POW2_DOUBLE);30 31 //start first reduction phase32 GPU_Kernel_LoopReduction.Start(blockCount,33 BLOCK_SIZE_POW2,34 params { outMaxValues_hd,35 outMaxIndices_hd,36 length,37 out2MaxValues_hd,38 out2MaxIndices_hd })39 40 //swap in and output data for loop41 outMaxIndices_hd = out2MaxIndices_hd;42 outMaxValues_hd = out2MaxValues_hd;43 }44 45 return maxIndices_hd.getResults()[0];46 }47 Code 6: Java-Pseudocode zur Ausführung der GPU-Kernel zur Berechnung des Index des höchsten Wertes eines Arrays auf der GPU. Update der Nachbarn Folgend wird der GPU-Kernel zur Berechnung der neuen Clusterwerte vorgestellt, wel- cher analog dem Konzept der CPU-Implementierung in Abschnitt 4.2.3 folgt. Der Cuda- Code für verwendete Strukturen und Funktionen befindet sich im Anhang (s. Seite 53).
  • 42. 4 Eingeschlagener Realisierungsweg 42 Cuda-Implementierung /*1 * Note: cluster maximum in smem leads to errors2 * @param inVal_g float* input cluster value array3 * @param inIdxRow_s int* input Row indices array4 * @param inCount_s int number of elements [triangular number]5 * @param inMaxIdx_s int index of the maximum Element in the given6 input value array7 * @param outValues_g float* output value array8 * @param outLinIdxRef_g int* output linear index references array9 * @param outMaxPos_g int* output maximum cluster position10 0 = no relation11 1 = left (row) child is maximum cluster left or right child12 2 = right (column) is maximum cluster left or right child13 */14 __global__ void cluster(const float* inValues_g,15 const unsigned int* inIdxRow_g,16 const unsigned int inCount_s,17 const unsigned int inMaxIdx_s,18 float* outValues_g,19 unsigned int* outLinIdxRef_g,20 unsigned int* outMaxPos_g)21 {22 //three dimensional grid needs 64-bit integer for all lin. indices23 const unsigned int blockId = blockIdx.y * gridDim.x + blockIdx.x24 + gridDim.x * gridDim.y * blockIdx.z;25 const unsigned int tId = blockId * blockDim.x + threadIdx.x;26 27 //maximum cluster is ignored28 if(tId >= inCount_s || tId == inMaxIdx_s)29 return;30 31 //read row indices to calculate column indices32 Idx2D clusterMax = Idx2D(inIdxRow_g[inMaxIdx_s], 0);33 clusterMax.column = getTriMatCol(inMaxIdx_s, clusterMax.row);34 35 ElFloat2D cluster = ElFloat2D(inIdxRow_g[tId], 0, inValues_g[tId]);36 cluster.column = getTriMatCol(tId, cluster.row);37 38 //relative cluster39 ElFloat2D clusterRel = ElFloat2D(cluster.row, cluster.column,40 cluster.value);41 42 //0 = direct copy of cluster, no relative43 //1 = cluster max will be merged right, 2 = left44 unsigned int copy = 0;45 46 //find relative cluster47 if(cluster.row == clusterMax.column)48 {49 copy = 1;50 clusterRel.set(clusterMax.row, cluster.column);51 }52 else if(cluster.row == clusterMax.row)53 {54 copy = 1;55 if(clusterMax.column > cluster.column)56 clusterRel.set(clusterMax.column, cluster.column);57 else58 clusterRel.set(cluster.column, clusterMax.column);59 }60
  • 43. 4 Eingeschlagener Realisierungsweg 43 else if(cluster.column == clusterMax.column)61 {62 copy = 2;63 if(cluster.row > clusterMax.row)64 clusterRel.set(cluster.row, clusterMax.row);65 else66 clusterRel.set(clusterMax.row, cluster.row);67 }68 else if(cluster.column == clusterMax.row)69 {70 copy = 2;71 clusterRel.set(cluster.row, clusterMax.column);72 }73 74 //merge neighbors at their minimum index and calculate new value75 if(copy != 0)76 {77 clusterRel.value = inValues_g[getMatLinIdx(clusterRel.row,78 clusterRel.column)]; //(s. Formel 11)79 cluster.row = min(cluster.row, clusterRel.row);80 cluster.column = min(cluster.column, clusterRel.column);81 cluster.value = 0.5f * (cluster.value + clusterRel.value);82 }83 84 //Update Row and Column Indices by reducing them with one85 //if they are larger then their cluster max counterparts86 if(cluster.row >= clusterMax.row)87 {88 cluster.row--;89 //non-copy clusters dont need column decrease90 if(copy == 0 && cluster.column > clusterMax.row)91 cluster.column = max(0, cluster.column - 1);92 }93 94 //get minimum reference index (s. Formel 11)95 const unsigned int minRefIdx = min(tId,96 getMatLinIdx(clusterRel.row, clusterRel.column));97 98 //output at minimum index99 if(minRefIdx == tId)100 {101 //Get output linear index (s. Formel 11)102 const unsigned int outLinIdx = getMatLinIdx(cluster.row,103 cluster.column);104 105 outValues_g[outLinIdx] = cluster.value;106 outLinIdxRef_g[outLinIdx] = minRefIdx;107 outMaxPos_g[outLinIdx] = copy;108 }109 }110 Code 7: Cuda-Code zur Berechnung der neuen Clusterwerte
  • 44. 5 Evaluation 44 5 Evaluation In diesem Kapitel wird die Performanz der CPU- und GPU-Varianten der einzelnen, in Kapitel 4 definierten Schritte der vollständigen hierarchischen Dokumenten-Cluster- analyse miteinander verglichen. Die Textvorverarbeitung der Dokumente sowie die Be- rechnung der Termfrequenzen werden in beiden Varianten auf der CPU durchgeführt und deshalb nicht in dieser Arbeit evaluiert (s. Einleitung von Kapitel 4). Als Dokumentengrundlage dient eine Kollektion von Kunden-Buchrezensionen des Internetversandhändlers Amazon, welche im XML-Format vorliegen. Dabei werden nur Dokumente betrachtet, welche auch einen Text in dem entsprechenden XML-Knoten beinhalten. Im nächsten Abschnitt wird kurz die eingesetzte Hardware vorgestellt und anschließend, im darauffolgenden Abschnitt, erfolgt eine tabellarische sowie graphische Auswertung der Versuchsergebnisse für Ausschnitte der Kollektion mit unterschiedli- cher Dokumentenanzahl. Die Ergebnisse werden in der in dieser Arbeit entwickelten Anwendung als Baumstruktur präsentiert (s. Abbildung 29). 5.1 Eingesetzte Hardware Die zur Evaluierung verwendete Hardware besteht aus einem Intel Core i7 CPU, mit 4 physikalischen Kernen, welche mittels der Hyperthreadingtechnologie in 8 Threads auf- geteilt werden, einem 6 Gigabyte großen Hauptspeicher und einer Nvidia Geforce GTX 480 Grafikkarte mit 1.5 Gigabyte Videospeicher und 480 Kernen, welche maximal 24.576 Threads parallel ausführen können [3]. Das eingesetzte Betriebssystem ist Win- dows 7 in der 64-bit Architektur. 5.2 Ergebnisse Wie in Abschnitt 4.3.5 erläutert wurde, muss die auf der CPU erstellte Term- Dokumentenmatrix (mit den Termfrequenzen) in der GPU-Variante in den globalen Videospeicher der Grafikkarte kopiert werden, weshalb der dafür zeitlich benötigte Aufwand mit in die zeitliche Evaluation einbezogen wird. Die zusammengefügten Eva- luationsergebnisse der einzelnen Schritte auf der CPU (blau) und GPU (rot) werden in nachfolgender Tabelle 2 sowie in Abbildung 19 und Abbildung 20 präsentiert. Die Durchführung der CPU-Variante mit 4.096 Dokumenten benötigt während der Aus- führung der Clusteranalyse fast die kompletten 6 Gigabyte des Hauptspeichers. Die Analysierung noch größerer Dokumentenbestände würde sehr wahrscheinlich eine Aus- lagerung temporärer Daten auf den langsamen Festplattenspeicher zur Folge haben und zu einer Verringerung der Performanz führen.
  • 45. 5 Evaluation 45 Fall |D| |T| GPU COPY CPU GPU CPU GPU I 128 10.123 0,485 3,842 0,640 0,2965 0,0494 II 256 19.816 0,882 49,945 1,282 0,9845 0,0253 III 512 22.456 1,023 107,339 2,324 0,9336 0,0202 IV 1.024 27.255 1,297 310,287 8,972 1,1118 0,0321 V 2.048 37.255 2,500 900,960 69,079 1,1808 0,0905 VI 4.096 59.013 5,266 5.450,165 619,017 2,2548 0,2561 TDM TDM-Elementkosten*10^5 Gesamt [1-3] Tabelle 2: Gesamtergebnisse in Sekunden der Evaluation, wobei die Anzahl der Dokumente und die Anzahl der Terme in der TDM repräsentiert. 0 1.000 2.000 3.000 4.000 5.000 128 256 512 1.024 2.048 4.096 Zeitins Dokumentenanzahl Gesamt [1-3] Abbildung 19: Performanz der hierarchi- schen Dokumenten-Clusteranalyse 0 10 20 30 40 50 128 256 512 1.024 2.048 4.096 Beschleunigungsfaktor Dokumentenanzahl Gesamt Beschleunigungsfaktor Abbildung 20: GPU-Beschleunigungsfaktor gegenüber der CPU 0,0000 0,5000 1,0000 1,5000 2,0000 128 256 512 1.024 2.048 4.096 Zeitkostenins Dokumentenanzahl Zeitkosten pro TDM-Element *10^5 Abbildung 21: Zeitliche Kosten pro Element der TDM Aus Abbildung 21 kann der zeitliche Aufwand der gesamten Clusteranalyse in Abhän- gigkeit von der Anzahl der Elemente in den jeweiligen TDMs abgelesen werden. Auf- grund der sehr hohen Anzahl an leichten Threads steigt dieser Aufwand auf der GPU gegenüber der CPU sehr viel langsamer an. Die Durchführung der kompletten Clusteranalyse auf der GPU ergab eine bedeutsame Beschleunigung des Prozesses um das bis zu 50-fache bei einer Kollektionsgröße von bis zu 4.096 Dokumenten.
  • 46. 5 Evaluation 46 Die zeitlichen Evaluierungsergebnisse der einzelnen Schritte werden in folgender Ta- belle sowie den nachfolgenden Abbildungen vorgestellt. Die benötigte Zeit zur Berech- nung der Reihen- und Spaltenindizes der eindimensionalen DDM ist für die untersuch- ten Fälle beider Varianten ungefähr identisch (nicht skizziert). Die Testfälle I – IV wur- den jeweils viermal und die Testfälle V und VI jeweils zweimal für jede Variante durchgeführt, von denen für jeden Schritt der Mittelwert berechnet wurde und in Tabel- le 3 präsentiert wird. Fall CPU GPU CPU GPU CPU GPU CPU GPU I 0,009 0,002 0,001 0,003 3,804 0,060 0,028 0,089 II 0,030 0,003 0,004 0,009 49,827 0,163 0,083 0,224 III 0,044 0,003 0,006 0,021 106,727 0,345 0,560 0,930 IV 0,094 0,007 0,008 0,047 304,951 2,148 5,226 5,465 V 0,149 0,015 0,016 0,235 856,020 22,407 44,766 43,914 VI 0,406 0,063 0,063 1,516 5.018,852 287,360 430,829 324,781 3. Clusteranalyse1. a. IDF Berechnung 1. b. Normalisierung 2. Ähnlichkeitsanalyse Tabelle 3: Performanz-Ergebnisse der einzelnen Schritte in Sekunden 0,0 0,1 0,2 0,3 0,4 128 256 512 1.024 2.048 4.096 Zeitins Dokumentenanzahl IDF Berechnung Abbildung 22: Performanz der IDF- Berechnung 0,0 0,5 1,0 1,5 128 256 512 1.024 2.048 4.096 Zeitins Dokumentenanzahl Normalisierung Abbildung 23: Performanz der Dokumen- tenvektoren-Normalisierung Die bessere Performanz der Normalisierung von den Dokumentenvektoren auf der CPU erfolgt aufgrund der Tatsache, dass in der CPU-Implementierung jedes Dokument nur seine eigenen Terme enthält und die GPU die spaltenweise Normalisierung dieser Vek- toren auf der kompletten hochdimensionalen, nur spärlich besetzten Term-Dokumenten- Matrix durchführen muss.
  • 47. 5 Evaluation 47 0 1.000 2.000 3.000 4.000 5.000 128 256 512 1.024 2.048 4.096 Zeitins Dokumentenanzahl Ähnlichkeitsanalyse Abbildung 24: Performanz der Ähnlich- keitsanalyse 0 100 200 300 400 128 256 512 1.024 2.048 4.096 Zeitins Dokumentenanzahl Clusteranalyse Abbildung 25: Performanz der hierarchi- schen Clusteranalyse Die Ähnlichkeitsanalyse ist der zeitintensivste Schritt der hierarchischen Dokumenten- Clusteranalyse und ausschlaggebend für die Gesamtperformanz. Die Analyse benötigt auf der CPU bis zu 300-mal mehr Zeit als auf der GPU. Auf der GPU skaliert diese sehr viel besser, da die größere Anzahl an Threads pro Block sehr viel weniger Dokumente je Thread laden muss und die Threads eines Blockes sehr effizient zusammen arbeiten können. In der CPU-Variante müssen die paarweisen Ähnlichkeitswerte der Dokumente mittels einer zweifach verschachtelten Schleife realisiert werden, wohingegen auf der GPU die Threads eines Blockes gemeinsam das Dokument der äußeren Schleife laden und somit die äußere Schleife durch die Blockstruktur von Cuda ersetzt wird. Ab ungefähr 1.024 Dokumenten erreicht die Durchführung der Clusteranalyse auf der GPU die Performanz der CPU und übersteigt diese für alle Weiteren größeren Aus- schnitte der Kollektion.
  • 48. 6 Zusammenfassung und Ausblick 48 6 Zusammenfassung und Ausblick 6.1 Zusammenfassung GPGPU-fähige Grafikkarten bieten sich für rechenaufwendige mathematische Probleme an, als kostengünstige Co-Prozessoren der Hauptprozessoren diese Berechnungen um das Vielfache zu beschleunigen. Sorgfältig an die jeweils aktuelle Grafikkartenarchitek- tur angepasste parallele und skalierbare Algorithmen sind notwendig, um die theoreti- sche Performanz der GPUs vollständig ausnutzen zu können. Weiterhin ist ein gut über- legter synchronisierter Arbeitsablauf zwischen der CPU und GPU nötig, um die jeweili- gen unterschiedlichen Ressourcen optimal ausnutzen zu können. Die hier erarbeiteten Lösungsschritte5 zur Durchführung einer hierarchischen Dokumen- ten-Clusteranalyse für beliebig große Kollektionen erreichten auf der GPU eine weitaus höhere Performanz gegenüber den gleichwertigen parallelen CPU-Implementierungen. Die GPU-Variante erreichte eine 5 - 50-fache Beschleunigung gegenüber ihrem CPU- Pendant. Weitere mögliche Optimierungen beider Varianten werden im Ausblick vorge- stellt. 6.2 Ausblick Mehrere optimierende Beobachtungen wurden während der Ausarbeitung dieser Arbeit gemacht und werden in diesem Abschnitt vorgestellt: Nach Durchführung der Ähnlichkeitsanalyse sind die Ähnlichkeitswerte der Initialclus- ter sehr klein, welche sich mit zunehmender Kollektionsgröße weiterhin verkleinern. Da der Datentyp Float nur eine Präzision von 8 Stellen besitzt und somit anhand der vo- rangehenden Nullstellen der Ähnlichkeitswerte viel Präzision ungenutzt verloren geht, könnten diese Werte mit einer von der Kollektionsgröße abhängigen Zehnerpotenz mul- tipliziert werden. Bei der Nutzung des Average-Linkage-Distanzmaß mit sehr großen Kollektionen wird ein Verlust der Präzision unumgänglich, weshalb ein Datentyp mit einer erhöhten Anzahl an signifikanten Stellen, wie Double, verwendet werden sollte. Dabei ist darauf zu achten, dass ältere Grafikkarten, wie die G80-Reihe von Nvidia, doppelte Fließkommazahlen nicht unterstützen und automatisch auf einfache Fließ- kommazahlen umstellen (bis Rechenmodell 1.3) oder weniger Recheneinheiten für die- se besitzen [3]. In der GPU-Variante ist die Kopierung der Ähnlichkeitswerte der Initialcluster (DDM) von dem Videospeicher der Grafikkarte in den Hauptspeicher bei Durchführung von 5 Ausgenommen der Normalisierung der Dokumentenvektoren
  • 49. 6 Zusammenfassung und Ausblick 49 mindestens einer Iteration der Clusteranalyse nicht notwendig, da die Initialcluster auch nach Durchführung der ersten Iteration erstellt werden können. Somit kann diese teure Kopier-Transaktion vermieden werden. Die Ähnlichkeitsanalyse kann in der CPU-Variante weiterhin direkt den Index des ers- ten Maxima-Clusters finden, da die Initialcluster in der Analyse erstellt werden. Analog kann die Findung in der GPU-Variante pro Block durchgeführt werden. Mit der zukünftigen möglichen Unterstützung von C-Strukturen und Templates in der Cuda-Bindung für Java könnte der Java- und Cuda-Quellcode, unabhängig von der Per- formanz weiter optimiert werden. Um eine Skalierbarkeit über mehrere GPUs und Computer hinweg zu gewährleisten, sollte das eingesetzte TF-IDF-Maß durch ein von globalem Wissen der Gesamtkollektion unabhängiges Maß (siehe TF-ICF-Maß in Ab- schnitt 3.2) eingesetzt werden. Cuda bietet die Möglichkeit, Arrays mittels eines soge- nannten Abstands (engl. pitch) in Größe einer Zweierpotenz zu allokieren, was zu einer Optimierung des Speicherzugriffes führt [3] (s. Abschnitt 3.6.5). Des Weiteren ist eine Untersuchung über die mögliche Nutzung von Sparse-Matrizen6 auf der GPU notwen- dig, um den Speicherbedarf der Term-Dokumentenmatrix zu minimieren und die Nor- malisierung der Dokumentenvektoren, Berechnung der TF-IDF-Werte und die Ähnlich- keitsanalyse zu beschleunigen. Die parallelen Reduktionsalgorithmen auf der GPU soll- ten weiter optimiert werden, da Mark Harris in seinem optimierten parallelen Redukti- ons-Algorithmus mehr als zwei Elemente in dem ersten Reduktionsschritt (vgl. 4.3.4) miteinander vergleicht und somit eine höhere Performanz erreicht [41]. Um qualitativere Schlussfolgerungen in der Evaluation ziehen zu können, bedarf es ei- ner weiteren Untersuchung von größeren Ausschnitten der Kollektion, da die CPU- Variante den Hauptspeicher vermehrt auslasten wird und ab einer gewissen Anzahl an Dokumenten benötigte Daten in den langsamen virtuellen Arbeitsspeicher auf der Fest- platte auslagern werden muss. Weiterführende Arbeiten zu diesem Thema sollten die Möglichkeit untersuchen, die Erstellung und Aktualisierung der binären Cluster-Baumstruktur komplett auf die GPU auszulagern. Mit der fortschreitenden Entwicklung der Grafikkarten wird es möglich, immer größere Datenbestände hierarchisch zu clustern und den Anwendern diverser Applikationen die Ergebnisse ihres Clusterkontextes, wie Suchanfragen, schneller anzubieten. Mit der re- sultierenden zunehmenden Geschwindigkeit des Clustering wird die mögliche Echtzeit- nutzung für Bestände mit geringer Anzahl an Daten an Bedeutung zunehmen. 6 Datentypen für spärlich besetzte Arrays oder Matrizen
  • 50. Anhang A: Grafikkartenarchitektur und Cuda 50 Anhang A: Grafikkartenarchitektur und Cuda Abbildung 26: Schematische Zeichnung einer Streaming-Multiprozessoreinheit der Nvidia Fermi-Grafikkartenarchitektur © Nvidia SM Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core Core SFU SFU SFU SFU SFU SFU SFU SFU LD/ST LD/ST LD/ST LD/ST LD/ST LD/ST LD/ST LD/ST LD/ST LD/ST LD/ST LD/ST LD/ST LD/ST LD/ST LD/ST Register File (32,768 x 32-bit) Dispatch Unit Dispatch Unit Warp Scheduler Dispatch Unit Dispatch Unit Warp Scheduler Instruction Cache 64 KB Shared Memory / L1 Cache Uniform Cache Uniform Cache Tex Tex Tex Tex Tex Tex Tex Tex Texture Cache PolyMorph Engine Vertex Fetch Tesselator Viewport Transform Attribute Setup Stream Output CUDA Core FP Unit INT Unit Operand Collector Result Queue Dispatch Port
  • 51. Anhang A: Grafikkartenarchitektur und Cuda 51 Abbildung 27: Aufbau der Nvidia Fermi-Grafikkartenarchitektur, deutlich sind die SM Einheiten aus Abbildung 26 zu erkennen. © Nvidia Abbildung 28: Cuda-API-Ebenen © Nvidia GPC SM PolyMorphEngine Raster Engine SM PolyMorphEngine SM PolyMorphEngine SM PolyMorphEngine MemoryControllerMemoryControllerMemoryController MemoryControllerMemoryControllerMemoryController GPC SM PolyMorphEngine Raster Engine SM PolyMorphEngine SM PolyMorphEngine SM PolyMorphEngine GPC SM PolyMorphEngine Raster Engine SM PolyMorphEngine SM PolyMorphEngine SM PolyMorphEngine GPC SM PolyMorphEngine Raster Engine SM PolyMorphEngine SM PolyMorphEngine SM PolyMorphEngine L2 Cache GigaThread Engine Host Interface GPU CPU Application CUDA Libraries CUDA Runtime CUDA Driver
  • 52. Anhang B: Formeln 52 Anhang B: Formeln Formel 9: Formel für die -te Dreieckszahl (Null-basierte Indizierung) ist die Anzahl aller Dokumente der zu untersuchenden Kollektion Formel 10: Anzahl der Ähnlichkeitspaare Formel 11: Eindimensionale Index-Berechnung einer Zweidimensionalen Dreiecksmat- rix ohne Diagonalbelegung Formel 12: Berechnung der Partitionsgröße eines Threads
  • 53. Anhang C: Erweiterter Cuda-Code 53 Anhang C: Erweiterter Cuda-Code /* * Matrix 2D-Element Indices Struct containing * a row and column field with datatype int */ struct __align__(16) Idx2D { unsigned int row; unsigned int column; __device__ Idx2D() { this->set(0, 0); } __device__ Idx2D(unsigned int row, unsigned int column) { this->set(row, column); } __device__ void set(unsigned int row, unsigned int column) { this->row = row; this->column = column; } }; /* * represents a element from * a two dimensional float matrix */ struct __align__(16) ElFloat2D : Idx2D { float value; __device__ ElFloat2D(unsigned int row, unsigned int column, float value) { Idx2D::set(row, column); this->value = value; } __device__ void set(unsigned int row, unsigned int column) { Idx2D::set(row, column); } __device__ void set(unsigned int row, unsigned int column, float value) { this->set(row, column); this->value = value; } }; /** * (s. Formel 10) * @param n int * @return int
  • 54. Anhang C: Erweiterter Cuda-Code 54 */ __device__ unsigned int getTriNum(const unsigned int n) { return (unsigned int)((n - 1) * n * 0.5f); //is cast safe! } /* * (s. Formel 11) * @param row int Row of the Element * @param column int Column of the Element * @return int */ __device__ unsigned int getTriMatLinIdx(const unsigned int row, const unsigned int column) { return getTriNum(row) + column; } /* * Retrieves the Column Index of the given linear Index * @param linIdx int Linear 0 based Index * @param row int Row Index * @return int column */ __device__ unsigned int getTriMatCol(const unsigned int linIdx, const unsigned int row) { return linIdx - getTriNum(row); }