Bachelorarbeit
Effizienteres Dokumenten-Clustering durch
Grafikprozessoren
Marco Janc
Betreuer:
Prof. Dr. – Ing. Norbert F...
Inhaltsverzeichnis 2
Inhaltsverzeichnis
Inhaltsverzeichnis ..................................................................
Inhaltsverzeichnis 3
4.1.2 Update der Nachbarn...............................................................................
Einleitung 4
Einleitung
Die Wiederbeschaffung von relevanten Informationen, engl. „Information Retrieval―
(IR), gewinnt au...
Einleitung 5
Stream-Prozessoren sowie der Speicherdurchsatz und die Größe des Videospeichers
sind von höherer Bedeutung. Z...
Einleitung 6
ve Evaluationsergebnisse, ähnliche jedoch für die Architektur optimale und angepasste
Algorithmen benutzen.
1...
2 Verwandte Arbeiten 7
2 Verwandte Arbeiten
Literatur zum Dokumenten-Clustering und speziell dem hierarchischen ist in Bib...
2 Verwandte Arbeiten 8
Ergebnisse der entwickelten Algorithmen wurden in den Framebuffer geschrieben und
konnten somit ges...
3 Grundlagen 9
3 Grundlagen
Die notwendigen Schritte einer vollständigen Dokumenten-Clusteranalyse sind folgend
aufgeliste...
3 Grundlagen 10
beitragen, aber gleichzeitig mit einer hohen Frequenz vorhanden sind, werden sie in
diesem Schritt entfern...
3 Grundlagen 11
Abbildung 2: Term-Dokumentenmatrix
Das in der Literatur am häufigsten eingesetzte Maß zur Termgewichtung i...
3 Grundlagen 12
ist die Anzahl aller Dokumente der zu untersuchenden Kollektion
ist die Anzahl aller Dokumente, welche Ter...
3 Grundlagen 13
3.3.2 Dreiecksmatrix
Ergebnis dieser Ähnlichkeitsanalyse ist die Dokumenten-Ähnlichkeits-Dreiecksmatrix
(D...
3 Grundlagen 14
3.4.2 Clusterverfahren
3.4.3 Partitionierende Verfahren
Partitionierende Clusterverfahren ordnen die Objek...
3 Grundlagen 15
Alle Cluster werden durchlaufen und mittels einer gewählten Clustermethode
werden die Nachbarn des Maxima ...
3 Grundlagen 16
erfolgt ein Einblick in die aktuelle Grafikprogrammierung anhand der Cuda-
Technologie.
3.6.1 Grafikkarten...
3 Grundlagen 17
den (1). Dann instruiert die CPU die GPU (2) den Kernel in den einzelnen Kernen paral-
lel auszuführen (3)...
3 Grundlagen 18
eine hohe Auslastung der Grafikkarte zu gewährleisten, bedürfen diese Werte einer vo-
rangehenden sorgfält...
3 Grundlagen 19
grenzt und bei Überschreitung dieses Limits werden Variablen in den langsamen loka-
len Speicher ausgelade...
4 Eingeschlagener Realisierungsweg 20
4 Eingeschlagener Realisierungsweg
Die entwickelte Clusteranwendung wurde in der Pla...
4 Eingeschlagener Realisierungsweg 21
4.1 Allgemeine Implementierungsdetails
Zum Verständnis der in diesem Kapitel vorgest...
4 Eingeschlagener Realisierungsweg 22
4.1.2 Update der Nachbarn
Abbildung 10 verdeutlicht den in dieser Arbeit entwickelte...
4 Eingeschlagener Realisierungsweg 23
4.2 CPU
Die komplette Textvorverarbeitung der Dokumente wird auf der CPU ausgeführt ...
4 Eingeschlagener Realisierungsweg 24
Formel 7: Dokumentenpaare, deren Ähnlichkeit ein Thread bestimmt
Abbildung 11: Visua...
4 Eingeschlagener Realisierungsweg 25
Abbildung 13: Ablaufdiagramm der CPU-Clusteranalyse
Findung des Maximums
Die Findung...
4 Eingeschlagener Realisierungsweg 26
El2D clusterRel = null;15
16
int copy = -1, outLinIdx = -1, minRefIdx = -1;17
18
//T...
4 Eingeschlagener Realisierungsweg 27
if(copy == 078
&& cluster.column > clusterMax.row)79
cluster.column = Math.max(0, cl...
4 Eingeschlagener Realisierungsweg 28
malste Limit (Rechenmodell 1.0) an nutzbaren Registern pro SM übersteigt4
, können i...
4 Eingeschlagener Realisierungsweg 29
deospeicher zu speichern. Speicheroperationen und Transaktionen zwischen dem Host
(C...
4 Eingeschlagener Realisierungsweg 30
size,20
cudaMemcpyHostToDevice);21
}22
//Allocate and copy the Pointer of the Matrix...
4 Eingeschlagener Realisierungsweg 31
Dieser Algorithmus wird innerhalb eines Blockes durchgeführt, da nur auf dieser Eben...
4 Eingeschlagener Realisierungsweg 32
4.3.5 Dokumentenrepräsentation
Da die Erstellung der Dokumentenvektoren auf der CPU ...
4 Eingeschlagener Realisierungsweg 33
Die Threads eines Blockes sind für die einzelnen Spalten, d.h. Dokumente verantwort-...
4 Eingeschlagener Realisierungsweg 34
{44
idx1 = i * BLOCK_SIZE_POW2_DOUBLE + threadIdx.x;45
idx2 = idx1 + blockDim.x;46
4...
4 Eingeschlagener Realisierungsweg 35
Normalisierung
Die Normalisierung der Dokumentenvektoren in der TDM erfolgt spaltenw...
4 Eingeschlagener Realisierungsweg 36
Cuda-Implementierung
/*1
* @param inOutMat_g float** Matrix2
* @param inWidth_s int ...
4 Eingeschlagener Realisierungsweg 37
else61
values_s[threadIdx.x] = 0.0f;62
__syncthreads();63
64
reductionAdd_f(values_s...
4 Eingeschlagener Realisierungsweg 38
Abbildung 17: Cuda-Schema zur Berechnung der paarweisen euklidischen Ähnlichkei-
ten...
4 Eingeschlagener Realisierungsweg 39
rowStartIdx = j * TILE_SIZE;35
36
//load the current term tile for the block-documen...
4 Eingeschlagener Realisierungsweg 40
4.3.7 Clusteranalyse
Auf Basis der eindimensionalen Ähnlichkeitsmatrix, welche sich ...
4 Eingeschlagener Realisierungsweg 41
Java-Implementierung
//create two empty values and indices output helper arrays on d...
4 Eingeschlagener Realisierungsweg 42
Cuda-Implementierung
/*1
* Note: cluster maximum in smem leads to errors2
* @param i...
4 Eingeschlagener Realisierungsweg 43
else if(cluster.column == clusterMax.column)61
{62
copy = 2;63
if(cluster.row > clus...
5 Evaluation 44
5 Evaluation
In diesem Kapitel wird die Performanz der CPU- und GPU-Varianten der einzelnen, in
Kapitel 4 ...
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,...
5 Evaluation 46
Die zeitlichen Evaluierungsergebnisse der einzelnen Schritte werden in folgender Ta-
belle sowie den nachf...
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...
6 Zusammenfassung und Ausblick 48
6 Zusammenfassung und Ausblick
6.1 Zusammenfassung
GPGPU-fähige Grafikkarten bieten sich...
6 Zusammenfassung und Ausblick 49
mindestens einer Iteration der Clusteranalyse nicht notwendig, da die Initialcluster auc...
Anhang A: Grafikkartenarchitektur und Cuda 50
Anhang A: Grafikkartenarchitektur und Cuda
Abbildung 26: Schematische Zeichn...
Anhang A: Grafikkartenarchitektur und Cuda 51
Abbildung 27: Aufbau der Nvidia Fermi-Grafikkartenarchitektur, deutlich sind...
Anhang B: Formeln 52
Anhang B: Formeln
Formel 9: Formel für die -te Dreieckszahl (Null-basierte Indizierung)
ist die Anzah...
Anhang C: Erweiterter Cuda-Code 53
Anhang C: Erweiterter Cuda-Code
/*
* Matrix 2D-Element Indices Struct containing
* a ro...
Anhang C: Erweiterter Cuda-Code 54
*/
__device__ unsigned int getTriNum(const unsigned int n)
{
return (unsigned int)((n -...
ba_thesis
ba_thesis
ba_thesis
ba_thesis
ba_thesis
ba_thesis
ba_thesis
ba_thesis
ba_thesis
Nächste SlideShare
Wird geladen in …5
×

ba_thesis

178 Aufrufe

Veröffentlicht am

0 Kommentare
0 Gefällt mir
Statistik
Notizen
  • Als Erste(r) kommentieren

  • Gehören Sie zu den Ersten, denen das gefällt!

Keine Downloads
Aufrufe
Aufrufe insgesamt
178
Auf SlideShare
0
Aus Einbettungen
0
Anzahl an Einbettungen
6
Aktionen
Geteilt
0
Downloads
3
Kommentare
0
Gefällt mir
0
Einbettungen 0
Keine Einbettungen

Keine Notizen für die Folie

ba_thesis

  1. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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); }

×