Fakultät für Elektrotechnik und Informatik Institut für Praktische Informatik Fachgebiet Datenbanken und Informationssysteme Evaluation von ausgewählten Matching-Algorithmen für attributierte Graphen Bachelorarbeit im Studiengang Informatik Simon Wingert - Prüfer: Prof. Dr. Udo Lipeck Zweitprüfer: Dr. Hans Hermann Brüggemann Betreuer: M.Sc. Oliver Pabst 9. Juli 2015 Inhaltsverzeichnis 1 Einleitung 5 1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.2 Überblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 2 Grundlagen 8 2.1 Graphen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Levenshtein-Distanz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.3 Stochastik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.4 Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.4.1 Kernel für Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.4.2 Gute Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 3 Algorithmen 3.1 8 15 VF2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.1.1 Komplexität . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 Fehlerkorrigierende Subgraphisomorphismen . . . . . . . . . . . . . . . . 20 3.2.1 Edit-Graphen und Subgraphisomorphismen . . . . . . . . . . . . 21 3.2.2 Die Dekomposition . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3.2.3 Der Algorithmus . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.2.4 Komplexität . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 3.3 Diskrete Relaxation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 3.4 SMKernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 3.4.1 36 3.2 Komplexität . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 4 Implementierung 4.1 37 Externe Bibliotheken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 4.1.1 Die JGraphT-Bibliothek . . . . . . . . . . . . . . . . . . . . . . . 37 4.1.2 Andere Hilfsfunktionen . . . . . . . . . . . . . . . . . . . . . . . . 39 Allgemeine Klassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 4.2.1 Knoten- und Kantenklassen . . . . . . . . . . . . . . . . . . . . . 39 4.2.2 Die GraphMatcher-Klasse . . . . . . . . . . . . . . . . . . . . . . 41 4.3 VF2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 4.4 Fehlerkorrigierende Subgraphisomorphismen . . . . . . . . . . . . . . . . 45 4.5 Diskrete Relaxation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 4.6 SMKernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 4.2 5 Evaluation 5.1 5.2 58 Synthetische Graphen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 5.1.1 VF2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 5.1.2 Fehlerkorrigierende Subgraphisomorphismen . . . . . . . . . . . . 60 5.1.3 Diskrete Relaxation . . . . . . . . . . . . . . . . . . . . . . . . . . 63 5.1.4 SMKernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 Reale Daten einer Bibliotheksdatenbank . . . . . . . . . . . . . . . . . . 70 5.2.1 VF2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 5.2.2 Fehlerkorrigierende Subgraphisomorphismen . . . . . . . . . . . . 72 5.2.3 Diskrete Relaxation . . . . . . . . . . . . . . . . . . . . . . . . . . 74 5.2.4 SMKernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 6 Fazit & Ausblick 77 4 Kapitel 1 Einleitung 1.1 Motivation Graphen sind eine vielseitig einsetzbare Datenstruktur. Mit ihnen lassen sich Aufgabenstellungen in vielen Anwendungsfeldern der Informatik modellieren. In der Bioinformatik beispielsweise werden Proteine als Graphen dargestellt. Um die Funktion von Proteinen im Organismus zu verstehen, gibt es den Ansatz, sie mit Proteinen zu vergleichen, deren Funktion bereits bekannt ist. Die Idee ist, dass Proteine, die eine ähnliche Molekülstruktur besitzen, auch eine ähnliche Funktion besitzen. Mit Graph-Algorithmen werden in Datenbanken deshalb die Graph-Repräsentationen von Proteinen untersucht [BOS+ 05]. Graphen werden auch in der Bildverarbeitung eingesetzt. Straßenverläufe auf Satellitenbildern werden als Graph modelliert und anschließend mit den Straßenverläufen verglichen, die aus vorliegenden Kartendaten bekannt sind. So kann zum Beispiel veraltetes oder ungenaues Kartenmaterial erkannt werden [WH97]. Wie diese Beispiele zeigen, ist eine wiederkehrende Aufgabe in den Anwendungen der Vergleich von zwei Objekten. Wenn die Informationen in geeigneter Weise als Graph repräsentiert werden, so kann das Problem durch eine Form von Graph Matching bearbeitet werden. Graph Matching meint das Finden von Übereinstimmungen zwischen den Knoten und Kanten zweier Graphen unter Berücksichtigung von bestimmten Bedingungen, so dass ähnliche Strukturen in einem auf den anderen Graphen abgebildet werden [FPV14]. In dieser Arbeit werden Algorithmen zum Graph Matching auf attributierten Graphen betrachtet. Die ersten Arbeiten zum Graph Matching können in zwei Klassen eingeteilt werden: exaktes und inexaktes Graph Matching. Während Ersteres eine strenge Übereinstimmung der Strukturen der zu vergleichenden Graphen fordert, kann bei Letzterem ein 5 Matching der Graphen auch bei einer gewissen strukturellen Unterschiedlichkeit realisiert werden [CFSV04a]. Mit inexaktem Matching verbindet sich zum einen die Hoffnung in einer schnelleren Laufzeit nicht unbedingt optimale, aber ausreichend gute Ergebnisse zu erzielen. Zum anderen ist es dadurch motiviert, dass die Daten des Graphen durch Messungenauigkeiten „verunreinigt“ sein könnten oder sich der Standard zum Erfassen von Daten unterscheidet (z.B. das Ausschreiben oder Abkürzen des Vornamens), so dass ein präzises Matching nie zum Ziel führen würde. In der vorliegenden Arbeit beschäftigen wir uns aus diesen Gründen mit zwei bekannten Verfahren aus beiden Bereichen: der VF2-Algorithmus [CFSV04b] ist ein Ansatz für das exakte Matching und der Algorithmus aus [MB98] erlaubt inexaktes Matching. Beiden Algorithmen ist gemein, dass sie die gesamte Information aus der Struktur des Graphen und die Information der Knoten- bzw. Kantenattribute benutzen, um ein Matching zu identifizieren. Um auch einen Algorithmus kennenzulernen, der dem oben formulierten Ziel der schnelleren Laufzeit näherkommt, wollen wir zudem einen Ansatz [WH97] vorstellen, bei dem bewusst Information über die Gesamtstruktur des Graphen ungenutzt bleibt, um ein inexaktes Matching zu finden. Wir wollen sowohl die Geschwindigkeit der Algorithmen untersuchen als auch insbesondere für den letzten Algorithmus die Qualität der gefundenen Matchings. Formal präzise ausgedrückt untersuchen wir in der vorliegenden Arbeit Algorithmen, die Subgraphisomorphismen zwischen Graphen finden. Da dieses Problem NP-vollständig ist, wurden in den letzten zehn Jahren auch Algorithmen entwickelt, die kein Matching in obigem Sinne erlauben, aber dennoch den Vergleich von Graphen ermöglichen. Sie berechnen sogenannte Graph Kernel, d.h. eine Maßzahl der Ähnlichkeit von zwei Graphen. Vereinfacht gesagt bilden Graph Kernel die Ähnlichkeit zweier Graphen als reelle Zahl ab[FPV14]. In dieser Arbeit wird deshalb auch ein Algorithmus [KM12] zum Berechnen eines Graph Kernels vorgestellt und die Aussagekraft der berechneten reellen Zahl im Hinblick auf die Identifikation von Subgraphisomorphismen zwischen Graphen überprüft. 1.2 Überblick Wir wollen in dieser Arbeit einen exakten und zwei inexakte Graph Matching-Algorithmen vorstellen und vergleichen sowie einen Algorithmus, der einen Graph-Kernel berechnet. In Kapitel 2 werden wir die formalen Grundlagen vorstellen und grundlegende Definitionen vorgeben, mit denen wir die Algorithmen beschreiben. In Kapitel 3 stellen wir die vier Algorithmen vor. In Abschnitt 3.1 nehmen wir zunächst einen exakten Algorithmus für das Graph Matching in den Blick: Der VF2-Algorithmus [CFSV04b] durchsucht systematisch den Suchraum möglicher Graph Matchings. Im Anschluss werden zwei inexakte Algorithmen gezeigt. In Abschnitt 3.2 lernen wir einen 6 Algorithmus [MB98] kennen, der die Unterschiedlichkeit von Graphen in Änderungsoperationen misst, um einen Graphen in einen anderen zu transformieren, und anhand dieser zwischen den ähnlichsten ein Matching konstruiert. Schließlich geht Abschnitt 3.3 auf einen Relaxationsalgorithmus [WH97] ein. Relaxation ist die Optimierung einer globalen Zielfunktion, die die Güte des Matchings beschreibt[WH96]. Der Algorithmus [KM12] in Abschnitt 3.4 berechnet einen Graph Kernel. Die Implementierung der Algorithmen erfolgt in der Programmiersprache Java und wird in Kapitel 4 vorgestellt. Zuerst wird gezeigt, welche Klassen Graphen und beschriftete Knoten und Kanten darstellen sowie welche externen Bibliotheken verwendet werden. Im Anschluss daran geht es mit Rückgriff auf die theoretisch besprochenen Algorithmen um deren Implementierung. Einige Details der Implementierungen sind erst mit dem Wissen um die Anwendung der Algorithmen in der Evaluierung zu verstehen. Kapitel 5 hat die Evaluierung zum Thema. Wir evaluieren die Algorithmen an einem synthetischen Datensatz und Daten aus einer Bibliotheksdatenbank. In Kapitel 6 ziehen wir ein Fazit über die Eignung der Algorithmen für das Graph Matching auf attributierten Graphen. 7 Kapitel 2 Grundlagen 2.1 Graphen Die vorgestellten Algorithmen arbeiten auf attributierten ungerichteten und gerichteten Graphen, deren Knoten und Kanten beschriftet sind. Die Knoten- und Kantenbeschriftungen seien in den Mengen LV und LE enthalten. Ein attributierter gerichteter Graph ist ein 4-Tupel G = (V, E, α, β), wobei V die Menge der Knoten, E ⊆ V × V die Menge der Kanten, α : V → LV eine Funktion, die jedem Knoten ein Label aus der Menge LV zuweist, und β : E → LE eine Funktion, die jeder Kante ein Label aus der Menge LE zuweist, ist. Auch wenn wir im Folgenden nur von Graphen sprechen, sind in der Regel attributierte Graphen gemeint, sofern nicht explizit anders angegeben. Die Menge der Vorgängerknoten eines Knoten v im Graphen G1 bezeichnet Pred(G, v) = {u ∈ V |(u, v) ∈ E}, analog die Menge der Nachfolgerknoten Succ(G, v) = {u ∈ V |(v, u) ∈ E}. Wir nennen die Menge eines Knoten j und seiner Nachfolger seine Super-Clique[WH97]: Cj = {j} ∪ Succ(G, j) und schreiben Rj = (j, i1 , . . . , i|Cj |−1 ) für die n-stellige Relation, die Knoten j mit seinen Nachfolgern bildet. Seien G1 = (V1 , E1 , α1 , β1 ) und G2 = (V2 , E2 , α2 , β2 ) zwei Graphen, sodass V1 ∩ V2 = ∅, und E 0 ⊆ (V1 × V2 ) ∪ (V2 × V1 ) eine Kantenmenge mit einer Beschriftungsfunktion β 0 : E 0 → LE sind. Dann heißt G = (V, E, α, β) Vereinigung von G1 und G2 [MB98] hinsichtlich der die beiden Teilgraphen verbindenden Kantenmenge E 0 , wenn 8 • V = V1 ∪ V2 • E = E1 ∪ E2 ∪ E 0 α (v) wenn v ∈ V 1 1 • α(v) = α2 (v) wenn v ∈ V2 β1 (e) wenn e ∈ E1 • β(e) = β2 (e) wenn e ∈ E2 0 β (e) wenn e ∈ E 0 Wir schreiben vereinfachend G = G1 ∪E 0 G2 . Wenn G = G1 ∪E 0 G2 gilt, definieren wir zur Schreibvereinfachung die Subtraktion auf Graphen als G1 = G − G2 bzw. G2 = G − G1 . Zu einem gegebenen Graph G = (V, E, α, β) nennen wir einen Graph GS = (VS , ES , αS , β S ) Subgraph, wenn gilt VS ⊆ V und ES ⊆ E ∩ (VS × VS ) und αS sowie β S auf VS bzw. ES beschränkte Funktionen α und β sind, d.h. für v ∈ VS : αS (v) = α(v) bzw. für e ∈ ES : β S (e) = β(e). Wir bemerken also, dass der Subgraph nicht alle Kanten besitzen muss, die im Graphen G zwischen den entsprechenden Knoten aus VS vorhanden sind. Wenn hingegen ES = E ∩ (VS × VS ) gilt, heißt der Subgraph knoten-induziert. Wir schreiben dann auch G[VS ] = GS ⊆ G. Damit können wir die oben eingeführte Subtraktion G1 = G − G2 definieren als einen durch die Knotenmenge V \ V2 induzierten Subgraph auf G: G1 = G − G2 = G[V \ V2 ]. Wir nennen eine Bijektion f : V1 → V2 einen Graphisomorphismus von Graph G1 = (V1 , E1 , α1 , β 1 ) zu Graph G2 = (V2 , E2 , α2 , β 2 ), wenn sie • die Nachbarschaftsbeziehung der Knoten beibehält, d.h. für alle u, v ∈ V1 : (u, v) ∈ E1 ⇔ (f (u), f (v)) ∈ E2 , • die Knotenbeschriftungen beibehält, d.h. für alle v ∈ V1 gilt α1 (v) = α2 (f (v)), • die Kantenbeschriftungen beibehält: Sei ψf : V12 → V22 die von f implizierte Zuordnung der Kanten, sodass gilt ψf ( (u, v) ) = ( f (u), f (v) ). Dann meint Beibehaltung der Kantenbeschriftung, dass für alle Kanten e1 ∈ E1 gilt β1 (e1 ) = β2 (ψf (e1 )). Davon zu unterscheiden ist ein Subgraphisomorphismus, d.h. eine injektive Funktion f : V1 → V2 von G1 zu G2 , zu der es einen Subgraph S ⊆ G2 gibt, sodass f ein Graphisomorphismus von G1 zu S ist. Weil wir fordern, dass ein Graphisomorphismus die Nachbarschaftsbeziehung der Knoten erhält, ist der entstehende Subgraph knoten-induziert. Wir bezeichnen einen Isomorphismus zwischen zwei Graphen auch als Matching der Graphen. Mit (v1 , v2 ) ∈ f bezeichnen wir die Zuordnung von Knoten v1 ∈ G1 zu Knoten v2 ∈ S im Matching f . Zur Schreibvereinfachung führen wir zusätzlich v1 ∈ f bzw. v2 ∈ f ein und meinen damit, dass Knoten v1 ∈ G1 bzw. Knoten v2 ∈ G2 durch das Matching f zu einem Knoten des anderen Graphen zugeordnet wurde. Von der Komplexität des Problems, zwischen zwei Graphen einen Graphisomorphismus zu finden, ist unbekannt, ob es NP-vollständig ist. Einen Subgraphisomorphismus zwischen zwei Graphen zu finden, ist die Verallgemeinerung des Graphisomorphismus- 9 problems, weil ein Isomorphismus als ein Subgraphisomorphismus zwischen Graphen gleicher Knotenzahl interpretiert werden kann. Das Problem, einen Subgraphisomorphismus zu finden, ist NP-vollständig [GJ79]. 2.2 Levenshtein-Distanz Die Levenshtein-Distanz zweier Strings s1 , s2 gibt an, mit wievielen Einfüge-, Lösch- oder Änderungsoperationen ein String in den anderen überführt werden kann. Wir berechnen die Distanz gemäß Algorithmus 2.1 nach [MRS08]. Es wird davon ausgegangen, dass die Strings als Arrays vorliegen. Schrittweise wird dann eine Matrix m konstruiert, deren Eintrag (i, j) nach Beendigung des Algorithmus angibt, wie groß die Distanz von den ersten i Buchstaben des Strings s1 zu den ersten j Buchstaben von s2 ist. Algorithmus 2.1 Levenshtein-Distanz, lev(s1 , s2 ) Eingabe: Strings s1 , s2 Ausgabe: Edit-Distanz zwischen den Strings 1: Initialisiere Matrix m[i, j] = 0 für 0 ≤ i ≤ |s1 | und 0 ≤ j ≤ |s2 | 2: for (i = 0; i ≤ |s1 |; i + +) do 3: m[i, 0] = i 4: for (j = 0; j ≤ |s2 |; j + +) do 5: m[0, j] = j 6: for (i = 1; i ≤ |s1 |; i + +) do 7: for (j = 1; j ≤ |sn2 |; j + +) do o 8: m[i, j] = min m[i − 1, j − 1] + 1s1 [i]6=s2 [j] , m[i − 1, j], m[i, j − 1] 9: return m[i, j] Dabei ist 1s1 [i]6=s2 [j] gleich 1, falls s1 [i] 6= s2 [j] und sonst 0. Für die folgenden Algorithmen werden wir ein Maß für die Ähnlichkeit von Knoten- und Kantenbeschriftungen benötigen. Dieses Maß levsim definieren wir über die LevenshteinDistanz und einen Schwellwert t ∈ [0, 1]. Zwei Strings s1 , s2 sind ähnlich, d.h. s1 ≈ s2 , falls levsim (s1 , s2 ) = 1 − lev(s1 , s2 ) max{|s1 | , |s2 | , 1} 10 und s1 ≈ s2 ⇔ levsim (s1 , s2 ) ≥ t 2.3 Stochastik Sei (Ω, F, P ) ein Wahrscheinlichkeitsraum, wobei Ω die nichtleere Ergebnismenge, F eine Menge von Teilmengen von Ω, die wir Ereignisse nennen, und P : F → [0, 1] das Wahrscheinlichkeitsmaß. Dann ist für zwei Ereignisse A, B ∈ F die bedingte Wahrscheinlichkeit P (A ∩ B) , P (A | B) = P (B) falls P (B) > 0. Wir können dies als die Wahrscheinlichkeit für das Eintreten von Ereignis A interpretieren, wenn wir wissen, dass Ereignis B eingetreten ist. Wir nennen die Wahrscheinlichkeit deshalb auch a-posteriori. Der Satz von Bayes drückt eine bedingte Wahrscheinlichkeit mit Hilfe einer anderen aus: P (B|A) P (A) . P (A|B) = P (B) Der Satz von der totalen Wahrscheinlichkeit hilft, die Wahrscheinlichkeit für ein Ereignis B mithilfe von bedingten Wahrscheinlichkeiten disjunkter Ereignisse Ai auszudrücken: P P (B) = N i=1 P (B | Ai )P (Ai ). Wir sprechen von einer gedächtnislosen Verteilung, wenn die bisherigen Ereignisse Ai den Ausgang des nächsten Ereignisses Ai+1 nicht beeinflussen. Dann gilt P (Ai+1 | Ai , . . . , A0 ) = P (Ai+1 ). Obige Definitionen sind die Basis für eine Methode unbekannte Parameter einer Verteilung zu schätzen, wenn wir lediglich einen Ausschnitt aus der Grundgesamtheit beobachten konnten. Wir schätzen einen unbekannten Parameter p einer bekannten Beobachtung x mit der Maximum-a-posteriori-Methode(MAP)[KS99], indem wir den Wert für p wählen, der die Wahrscheinlichkeit P (p|x) maximiert. Mit dem Satz von Bayes können wir schreiben: P (x|p)P (p) . p = arg max P (p|x) = arg max p p P (x) Zum weiteren Verständnis der später erklärten Algorithmen benötigen wir noch den Begriff der stochastischen Unabhängigkeit. Wir nennen zwei Ereignisse unabhängig, wenn gilt: P (A ∩ B) = P (A)P (B). Wir verstehen darunter, dass sich die Wahrscheinlichkeit für das Eintreten eines Ereignisses nicht ändert, wenn das andere Ereignis eintritt oder nicht eintritt. 2.4 Kernel Der folgende Abschnitt basiert auf [SS01] und soll einen eher intuitiven Zugang zu dem Thema Kernel und deren Anwendung bieten. Wir interpretieren Kernel als eine Ähnlichkeitsfunktion für beliebige Objekte aus einer Menge X . Für die Menge der Graphen 11 G wäre ein Kernel also eine Funktion k, die je zwei Graphen eine reelle Zahl als Maß für deren Ähnlichkeit zuordnet. Sei also X eine nicht-leere Menge. Um die Ähnlichkeit von Objekten x1 , x2 ∈ X messen zu können, benötigen wir eine Funktion k : X 2 → R. Wir nennen diese Funktion einen Kernel. Wir verlangen außerdem, dass k symmetrisch (k(x1 , x2 ) = k(x2 , x1 )) ist. Die Levenshtein-Distanz ist in diesem Sinne ein Kernel über der Menge der Zeichenketten. Auch das Skalarprodukt im Rn , das durch hx1 , x2 i = ni=1 [x1 ]i [x2 ]i definiert ist, wobei [x1 ]i die i-te Komponente des Vektors bezeichnet, ist ein Kernel. Nicht nur im Rn können durch ein Skalarprodukt Objekte der euklidischen Geometrie wie Winkel und Längen definiert werden, sondern ganz allgemein kann in Skalarprodukträumen H, d.h. Vektorräumen mit Skalarprodukt, die geometrische Anschauung dieser Objekte formal bezeichnet werden. Für unsere Zwecke genügt das Verständnis von H als Skalarproduktraum, obwohl wir der Vollständigkeit halber erwähnen, dass sogar noch ein wenig mehr gefordert wird: Genau genommen soll H ein sogenannter Hilbert-Raum sein. Die Details der Unterscheidung von Skalarproduktraum und Hilbert-Raum sind in dieser Arbeit nicht wichtig. P Einige Algorithmen des maschinellen Lernens wie zum Beispiel Support-Vektor-Maschinen gehen davon aus, dass die Daten in einem Hilbertraum H, dem sogenannten FeatureSpace, vorliegen. Mit anderen Worten: Wenn wir für Daten wie Graphen oder Zeichenketten eine geeignete Abbildung ϕ : X → H finden, können wir die Algorithmen des maschinellen Lernens für diese Art von Objekten zugänglich machen. Wir nennen für x1 ∈ X seinen Feature-Vektor die Auswertung der Abbildung ϕ(x1 ). Eine wichtige Eigenschaft von Kerneln ist ihre positive Definitheit, denn wie sich herausstellt, gilt für sogenannte positiv definite Kernel k, dass k(x1 , x2 ) = hϕ(x1 ), ϕ(x2 )i, wobei x1 , x2 ∈ X sind und ϕ wie oben definiert ist. Mit einem positiv definiten Kernel lässt sich das Skalarprodukt der Feature-Vektoren in (irgend-)einem Feature-Space berechnen und also lassen sich Methoden des maschinellen Lernens für Objekte wie Graphen einsetzen. Um uns dem Begriff der positiven Definitheit zu nähern, benötigen wir zunächst einige Definitionen. Für Objekte x1 , . . . , xm ∈ X und einen Kernel k definieren wir die GramMatrix als Kij = k(xi , xj ). Weiter nennen wir eine Matrix K positiv (semi-)definit (psd), wenn für alle ci ∈ R gilt, dass m X ci cj Kij ≥ 0 i,j=1 Wir nennen einen Kernel k positiv (semi-)definit, wenn für alle m ∈ N und x1 , . . . , xm ∈ X die zugehörige Gram-Matrix positiv definit ist. 12 Wir wollen nun eine Idee davon vermitteln, wodurch Kernel k und Abbildung ϕ zusammenhängen [Mur12] und abschließend ein Beispiel für einen Kernel für Strings geben. Zu einer positiv definiten Gram-Matrix kann die eindeutige Cholesky-Zerlegung K = LT DL gefunden werden, wobei D die Diagonalmatrix der Eigenwerte λi > 0 von K ist und die Spalten von L die Eigenvektoren von K sind. Dann können die Elemente Kij wie folgt berechnet werden: 1 1 Kij = (D 2 L·,i )T (D 2 L·,j ) Dabei sei zur Schreibvereinfachung L·,i der i-te Spaltenvektor der Matrix L. Mit ϕ(xi ) = 1 D 2 L·,i können wir auch schreiben Kij = ϕ(xi )T ϕ(xj ) = hϕ(xi ), ϕ(xj )i Wir sehen nun, dass ein Kernel das Skalarprodukt von Feature-Vektoren in einem Feature-Space ist, die durch die Eigenvektoren aus L definiert werden. Durch die Funktion ϕ ist also eine Abbildung aus X nach H gefunden. Es stellt sich heraus, dass die explizite Kenntnis der Berechnungsvorschrift für die Funktion ϕ in vielen Anwendungen nicht notwendig ist, häufig reicht es, das Skalarprodukt hϕ(xi ), ϕ(xj )i = k(xi , xj ) zu berechnen [Mur12]. Um die Werte des Kernels als Ähnlichkeitsmaß interpretieren zu können, normalisieren wir den Kernel, d.h. wir schränken seinen Wertebereich auf [0, 1] ein [SS01]. Der Kernel wird normalisiert, indem wir für die Feature-Vektoren ϕ(xi ), ϕ(xj ) ∈ H die KosinusÄhnlichkeit berechnen: cos(θ) = k(xi , xj ) hϕ(xi ), ϕ(xj )i e =q =: k(x i , xj ) ||ϕ(xi )|| ||ϕ(xj )|| k(xi , xi ) k(xj , xj ) Der Wert kann anschaulich als Winkel θ zwischen den Feature-Vektoren interpretiert e werden. Wir nennen k(x i , xj ) den normalisierten Kernel. 2.4.1 Kernel für Strings Die Levenshtein-Distanz ist nicht postiv definit [CHM04]. Wir können aber einen positiv definiten Kernel für Strings s1 , s2 definieren: k(s1 , s2 ) = 1 falls s1 = s2 . 0 sonst Kernel, die wie oben definiert werden, heißen auch Dirac- oder Delta-Kernel [Bor07]. Einen anderen Kernel für Strings erhalten wir, indem wir die Ähnlichkeit der Strings als die Anzahl von in beiden Strings vorkommenden Substrings definieren [LSS+ 02]. Dabei beachten wir nicht nur die Substrings, die zusammenhängend in den Strings vorkommen. 13 Mit einer geringeren Gewichtung gehen auch die Substrings in die Kernelberechnung ein, die nur mit Lücken zu finden sind. In den beiden Strings s1 =„cat“ und s2 =„cart“ kommt zum Beispiel der Substring u1 =„ca“ vor, aber auch (mit Lücken) der Substring u2 =„cat“. Das Vorkommen eines Substrings bewerten wir mit der Länge seines Vorkommens in dem jeweiligen String. Für einen Parameter λ ∈ (0, 1) ist der Wert von u1 in s1 bzw. in s2 gegeben durch λ2 . Der Wert von u2 in s1 ist λ3 , wohingegen er in s2 gegeben ist durch λ4 . n Wir bilden einen String s ∈ Σ∗ nun in einen Feature Space R|Σ | ab. Jeder Substring u ∈ Σn definiert dann eine Komponente des Feature Vektors wie folgt λLänge des Vorkommens von u in s X ϕu (s) = Vorkommen von u in s n| Durch komponentenweise Multiplikation der in den Feature Space R|Σ Strings erhalten wir den String Subsequence Kernel: k(s1 , s2 ) = X abgebildeten ϕu (s1 )ϕu (s2 ) u∈Σn 2.4.2 Gute Kernel Es stellt sich die Frage, welche Eigenschaften ein Kernel für Graphen erfüllen sollte. In [Bor07] wird gefordert, dass ein guter Kernel • • • • positiv definit ist nicht nur für bestimmte Graphen zu berechnen ist effizient zu berechnen ist ausdrucksstark ist. Die Kernelfunktion k = 0 ist zum Beispiel positiv definit und für alle Graphen effizient zu berechnen, aber nicht ausdrucksstark in dem Sinne, dass Aussagen über die Ähnlichkeit von Graphen ermöglicht werden. Der in der vorliegenden Arbeit vorgestellte Kernel ist positiv definit und grundsätzlich ausdrucksstark. Allerdings werden wir sehen, dass er nur auf attributierte Graphen beschränkt ist und auch nicht effizient zu berechnen ist. 14 Kapitel 3 Algorithmen In diesem Kapitel stellen wir vier Algorithmen zum Graph Matching vor. Der erste Algorithmus „VF2“ gehört zu den exakten Graph-Matching-Algorithmen. Der zweite Algorithmus, der einen sogenannten fehlerkorrigierenden Subgraphisomorphismus findet, und der dritte Relaxationsalgorithmus gehören zu den inexakten Graph-MatchingAlgorithmen. Der zuletzt vorgestellte Algorithmus berechnet einen Graphkernel zwischen zwei Graphen, d.h. eine reelle Zahl, und ist kein Graph-Matching-Algorithmus im engeren Sinne. Wir wollen an dieser Stelle eine Übersicht über die Ausgaben der verschiedenen Algorithmen geben, wenn wir diese auf zwei Graphen G1 und G2 mit Knotenmengen V1 bzw. V2 anwenden. 1. Durch den Algorithmus VF2 wird ein Subgraphisomorphismus zwischen den Graphen berechnet, d.h. eine bijektive Funktion f : V1 → S für S ⊆ V2 . Falls ein solcher nicht existiert, wird keine Knotenzuordnung ausgegeben. 2. Einen fehlerkorrigierenden Subgraphisomorphismus zu finden, meint vereinfacht gesagt, einen Subgraphisomorphismus zwischen einem in Struktur und Beschriftungen veränderten Graphen G1 und dem originalen Graphen G2 zu finden. Der Algorithmus kennt dazu eine Reihe von sogenannten Grapheditoperationen, die alle gewisse Kosten besitzen. Er bestimmt dann diejenigen kostengünstigsten Operationen, mit denen Graph G1 so zu einem Graph G01 mit Knoten V10 verändert wird, dass daraus eine bijektive Funktion f : V10 → S für S ⊆ V2 bestimmt wird. Der Algorithmus findet diejenigen fehlerkorrigierenden Subgraphisomorphismen (1) (2) (m) von Graphen aus einer Menge von Graphen B = {G1 , G1 , . . . , G1 } zu einem Graphen G2 , die am kostengünstigsten sind. Das bedeutet, dass nur von den Sub(i) graphen S (i) ⊆ G1 ∈ B ein solches Matching gefunden wird, zu denen die dazugehörigen Kosten verglichen mit den anderen möglichen Subgraphen von Graphen aus B minimal sind. 3. Der Relaxationsalgorithmus findet eine Knotenzuordnung f : V1 → V2 . Relaxation 15 heißt die iterative Optimierung der Zielfunktion bzw. Knotenzuordnung f anhand eines Gütekriteriums. Im Allgemeinen wird nicht das globale Maximum des Gütekriteriums gefunden. Auch ist die in diesem Verfahren gefundene Knotenzuordnung im Allgemeinen nicht injektiv [Wil96]. 4. Der im letzten Algorithmus berechnete normalisierte Graphkernel ist eine reelle Zahl c ∈ [0, 1]. Aus dem Graphkernel kann keine Knotenzuordnung rekonstruiert werden, wir können den Graphkernel lediglich als Ähnlichkeitsmaß zwischen zwei Graphen interpretieren. 3.1 VF2 Der VF2-Algorithmus [CFSV04b] findet sowohl Graph- als auch Subgraphisomorphismen zwischen zwei Graphen G1 und G2 . Er findet strukturell exakte Matches, d.h. nur solche, in denen alle Knoten und Kanten aus G1 im Graph G2 vorkommen. Es ist möglich, mit dem Algorithmus auch Matches zwischen zwei Graphen zu finden, wenn ihre Kanten- und Knotenbeschriftungen nicht exakt identisch, sondern ähnlich sind. Der Algorithmus konstruiert rekursiv ein partielles Matching f : V1 → V2 und erweitert dieses sechs Regeln folgend schrittweise um je eine Zuordnung (mj , ni ), bis alle Knoten von G1 erfasst sind. Anfangs ist das partielle Matching leer. In jedem Schritt wird zunächst eine Menge P an möglichen Zuordnungen der Form (mj , ni ) berechnet. Aus dieser Menge wird anschließend anhand von sechs Regeln eine Zuordnung gesucht, sodass das partielle Matching erweitert um die Zuordnung ein gültiges partielles Matching ergibt. Rekursiv wird dann die nächste Zuordnung gesucht. Andernfalls wird mittels Backtracking einen Schritt zurückgegangen und eine andere Zuordnung aus P ausgewählt. Algorithmus 3.1 VF2 Eingabe: Graphen G1 = (V1 , E1 , α1 , β1 ) und G2 = (V2 , E2 , α2 , β2 ) Ausgabe: ein Subgraphisomorphismus f : V1 → V2 oder ∅ function Match(partielles Matching f, G1 , G2 ) if f enthält alle Knoten aus G1 then return f else Berechne die Menge möglicher Zuordnungen P bezüglich ≺ und f for all (mj , ni ) ∈ P do if alle 6 Regeln evaluieren zu wahr then Berechne neues partielles Matching f 0 ← f ∪ {(mj , ni )} Match(f 0 , G1 , G2 ) 10: end function 1: 2: 3: 4: 5: 6: 7: 8: 9: 16 Algorithmus 3.1 gibt einen Überblick über den Ablauf des Algorithmus. Im Folgenden sollen die Details zur Bildung von der Menge P möglicher Zuordnungen und die sechs Regeln zur Evaluierung möglicher Zuordnungen erläutert werden. Für die Bildung von P wird die Menge der Knoten in G1 einer totalen Ordnung (mit ≺ bezeichnet) unterworfen. Nur für den gemäß ≺ kleinsten nicht in P vorkommenden Knoten mj ∈ V1 werden in einem Schritt mögliche Zuordnungen (mj , ni ) gesucht. Durch diese Vorgehensweise wird der vom Algorithmus betrachtete Suchraum strukturiert, was möglich ist, weil es für das eventuell letztlich gefundene Matching unerheblich ist, welche Zuordnung (mj , ni ) zuerst hinzugefügt wurde. Die Menge P wird aus den Mengen der Knoten bestimmt, die noch nicht im partiellen Matching f enthalten sind. Dadurch garantiert der Algorithmus die Bildung eines injektiven Matchings. Es gibt drei Möglichkeiten, wie die Menge P gebildet werden kann: Wir bezeichnen die jeweiligen Mengen mit Pi . Erstens werden alle Kombinationen von Knoten der Graphen gebildet, die von den Knoten des partiellen Matchings aus f in G1 oder G2 über eine Kante erreicht werden. Seien T1out bzw. T2out die Mengen der Knoten von Graph G1 bzw. G2 , die noch nicht im Matching enthalten sind und von einem Knoten im partiellen Matching aus über eine Kante erreicht werden. Tiout = {v | v ∈ Vi ∧ v ∈ / f ∧ ∃u ∈ f (v ∈ Succ(Gi , u))} Dann ergibt sich P1 = {(mj , ni ) | mj ∈ T1out ∧ ni ∈ T2out ∧ ¬∃mp ∈ V1 ( mp ∈ P1 ∧ mp ≺ mj )} Falls P1 leer ist, werden zweitens Kombinationen von Knoten aus G1 und G2 gebildet, von denen aus eine Kante in das partielle Matching hinein führt. Zu diesem Zweck definieren wir analog T1in und T2in . Tiin = {v | v ∈ Vi ∧ v ∈ / f ∧ ∃u ∈ f (v ∈ P red(Gi , u))} und daraus folgend P2 = {(mj , ni ) | mj ∈ T1in ∧ ni ∈ T2in ∧ ¬∃mp ∈ V1 ( mp ∈ P2 ∧ mp ≺ mj )} Falls es auch keine solchen Kombinationen gibt, werden drittens alle Kombinationen von Knoten gebildet, die nicht im partiellen Matching sind. P3 = {(mj , ni ) | mj ∈ / f ∧ ni ∈ / f ∧ ¬∃mp ∈ V1 ( mp ∈ P3 ∧ mp ≺ mj )} Durch die Anwendung von Regeln auf die möglichen Zuordnungen aus P wird gleichzeitig die Gültigkeit des Matchings und dessen Erweiterbarkeit überprüft. Eine Regel überprüft die Ähnlichkeit der Knoten- und Kantenbeschriftungen, die anderen fünf Regeln die strukturelle Eignung, das partielle Matching um die mögliche Zuordnung (mj , ni ) zu erweitern. 17 Für die Knotenbeschriftungen und die Beschriftungen von allen ein- und ausgehenden Kanten muss gelten, dass sie ähnlich sind: α1 (mj ) ≈ α2 (ni ) ∧ ∀(m0 , n0 ) ∈ f (ni , n0 ) ∈ E2 ⇒ β 2 ((ni , n0 )) ≈ β 1 ((mj , m0 )) ∧ (R1) (n0 , ni ) ∈ E2 ⇒ β 2 ((n0 , ni )) ≈ β 1 ((m0 , mj )) Die Definition der Ähnlichkeit (≈) ist von der Anwendung abhängig. In dieser Arbeit werden Ähnlichkeiten für Beschriftungen mit Strings und Zahlen definiert (siehe dazu 4.2.1). Man merkt, dass durch die erste Regel nur Kantenbeschriftungen von Vorgängern und Nachfolgern von ni ∈ G2 überprüft werden. Dass keine Kanten in G1 vorhanden sind, die keine entsprechende Kante in G2 besitzen, stellen wir erst mit den nächsten beiden Regeln sicher. Die ersten beiden strukturellen Regeln überprüfen, dass zu allen bereits im Matching enthaltenen Vorgängern bzw. Nachfolgern vom zuzuordnenden Knoten (ni , mj ) auch Vorgänger bzw. Nachfolger im anderen Graphen existieren, die einander jeweils durch das partielle Matching zugeordnet sind. ∀n0 ∈ f ∩ Pred(G1 , ni ) ∃m0 ∈ Pred(G2 , mj ) (n0 , m0 ) ∈ f ∀m0 ∈ f ∩ Pred(G2 , mj ) ∃n0 ∈ Pred(G1 , ni ) (n0 , m0 ) ∈ f ∧ (R2) Für Nachfolger: ∀n0 ∈ f ∩ Succ(G1 , ni ) ∃m0 ∈ Succ(G2 , mj ) (n0 , m0 ) ∈ f ∀m0 ∈ f ∩ Succ(G2 , mj ) ∃n0 ∈ Succ(G1 , ni ) (n0 , m0 ) ∈ f ∧ (R3) Zur Veranschaulichung betrachten wir Abbildung 3.1. Sei das partielle Matching zu diesem Zeitpunkt durch die Kombinationen (mk , nk ) für k ∈ {1, . . . , 6} gegeben. Die mögliche Kombination (mj , ni ) verletzt (R1), weil für den zugeordneten Vorgänger m1 von mj der Knoten n1 kein Vorgänger von ni ist. Analog ist (R2) verletzt, wenn wir die bereits festgelegte Zuordnung (m5 , n5 ) evaluieren. m1 m4 mj m2 m3 n1 m5 n2 m6 n4 ni n3 (a) Graph G1 n5 n6 (b) Graph G2 Abbildung 3.1: Die Regeln (R2) und (R3) 18 Die drei bereits beschriebenen Regeln sind ausreichend, um einen Subgraphisomorphismus zwischen zwei Graphen zu finden, sofern f eine vollständige Abbildung. Der Algorithmus benutzt drei weitere Regeln, um ungültige partielle Matchings möglichst früh zu erkennen. Weil alle Knoten von Graph G1 in einem gültigen Matching vorhanden sein müssen, darf es nicht mehr Vorgänger oder Nachfolger von mj ∈ V1 geben, die zugleich auch in T1in oder T1out sind, als es Vorgänger oder Nachfolger von ni ∈ V2 aus T2in oder T2out gibt. Die beiden daraus resultierenden Regeln prüfen also in Voraussicht, ob es schon wegen der Anzahl verbleibender Knoten im Umfeld des möglichen neuen partiellen Matchings zu Konflikten kommen kann. Succ(G2 , ni ) ∩ T2in ≥ Succ(G1 , mj ) ∩ T1in Pred(G2 , ni ) ∩ T2in ≥ Pred(G1 , mj ) ∩ T1in Succ(G2 , ni ) ∩ T2out ≥ Succ(G1 , mj ) ∩ T1out Pred(G2 , ni ) ∩ T2out ≥ Pred(G1 , mj ) ∩ T1out ∧ (R4) ∧ (R5) Wir wollen zum Verständnis der Regel beispielhaft Abbildung 3.2 betrachten, die Ausschnitte aus Graphen zeigt. Im Matching seien bereits die Knotenkombinationen (m1 , n1 ), (m2 , n2 ) enthalten, sodass die Knoten ti bzw. t0i zu den Mengen T1in , T1out , T2in , T2out gehören. Gleichzeitig sind ti bzw. t0i Nachfolger von ni bzw. mj . Also ist |Succ(G2 , ni ) ∩ T2out | = 3 ≥ |Succ(G1 , mj ) ∩ T1out | = 2 und (R4) damit erfüllt. (R5) ist verletzt, denn für die zwei Knoten t3 , t4 ∈ Succ(G1 , mj ) ∩ T1in existiert nur ein möglicher Partner in G2 : der Knoten t04 . t01 t1 m1 n1 t2 t02 ni mj m2 n2 t3 t03 t4 t04 (a) Ausschnitt aus Graph G1 (b) Ausschnitt aus Graph G2 Abbildung 3.2: Die Regeln (R4) und (R5) Mit Hilfe der letzten Regel wird sichergestellt, dass es nicht mehr Vorgänger oder Nachfolger zu mj ∈ V1 gibt, die nicht direkt aus dem Umfeld des partiellen Matchings (d.h. v∈ / T1in ∪ T1out = T1 ) kommen. Diese Regel schaut quasi zwei Schritte des Algorithmus 19 voraus, da der Konflikt mit diesen Nachbarknoten des hinzuzufügenden Knotens im Vergleich zu (R4) und (R5) erst nach Erweiterung mit einer weiteren Zuordnung entstehen könnte. |Succ(G2 , ni ) \ (T2 ∪ {n|n ∈ f })| ≥ |Succ(G1 , mj ) \ (T1 ∪ {m|m ∈ f })| ∧ (R6) |Pred(G2 , ni ) \ (T1 ∪ {n|n ∈ f })| ≥ |Pred(G1 , mj ) \ (T2 ∪ {m|m ∈ f })| Im Beispiel von Abbildung 3.3 seien t1 , t2 , t01 nicht in T1 bzw. T2 und nicht im partiellen Matching enthalten. Dann ist (R6) verletzt und kein Subgraphisomorphismus von G1 nach G2 möglich. t1 mj ni t2 (a) Ausschnitt aus Graph G1 t01 (b) Ausschnitt aus Graph G2 Abbildung 3.3: Die Regel (R6) Falls ein Graphisomorphismus gefunden werden soll, sollte in den Regeln 3, 4 und 5 das ≥ durch ein = ersetzt werden. 3.1.1 Komplexität Der Suchraum des Algorithmus ist im schlimmsten Fall dadurch charakterisiert, dass die Knoten aus V1 mit allen noch nicht im partiellen Matching vorhandenen Knoten aus V2 zu möglichen Kombinationen verbunden werden. Der erste Knoten v1 ∈ V1 kann auf alle Knoten aus V2 abgebildet werden. Diese erste Knotenzuordnung kann dann mit allen möglichen Zuordnungen des zweiten Knotens v2 ∈ V1 auf (|V2 | − 1) Knoten aus V2 kombiniert werden. Es ergeben sich |V2 | · (|V2 | − 1) · . . . · (|V2 | − |V1 |) viele Kombinationen. Die Laufzeit zum Durchlaufen des Suchraums kann also mit O(|V2 |!) abgeschätzt werden. In jedem Schritt müssen außerdem die Regeln evaluiert und der Folgezustand gebildet werden, was in linearer Laufzeit möglich ist. Insgesamt erhalten wir damit als Abschätzung eine exponentielle Laufzeit O(|V2 |! |V2 |). 3.2 Fehlerkorrigierende Subgraphisomorphismen Im Folgenden wird zunächst ein Distanzmaß zwischen zwei Graphen ähnlich der Levenshtein-Distanz definiert. Es wird angegeben, mit welcher Anzahl von Einfüge- oder 20 Löschoperationen von Knoten und Kanten bzw. der Änderung von deren Beschriftung ein Graph in einen anderen überführt werden kann. Damit wird es möglich sein, auch zwischen strukturell unterschiedlichen Graphen Subgraphisomorphismen zu definieren: Wir erlauben, dass ein Graph erst durch eine minimale Anzahl von Operationen in einen zum ersten Graph isomorphen Graphen umgewandelt wird. Insofern ist der im Anschluss dargestellte Algorithmus [MB98] in der Lage, nicht nur zwischen ähnlich beschrifteten Graphen Subgraphisomorphismen zu erkennen, sondern auch zwischen strukturell ähnlichen. 3.2.1 Edit-Graphen und Subgraphisomorphismen Zu einem Graphen G = (V, E, α, β) bezeichnet eine Grapheditoperation δ: • • • • • α(v) → lv , v ∈ V, lv ∈ LV : die Änderung der Beschriftung von Knoten v zu lv β(e) → le , e ∈ E, le ∈ LE : die Änderung der Beschriftung von Kante e zu le v → $, v ∈ V : das Löschen von Knoten v e → $, e ∈ E: das Löschen von Kante e $ → (u, v), u, v ∈ V : das Einfügen einer Kante zwischen den Knoten u, v Der Edit-Graph δ(G) = (Vδ , Eδ , αδ , βδ ) zu einer Operation δ besteht aus: V − {v} wenn δ = (v → $) sonst • Vδ = V E ∪ {e} wenn δ = ($ → e) • Eδ = E − {e} wenn δ = (e → $) E ∩ (Vδ × Vδ ) sonst • αδ (v) = • βδ (e) = l v α(v) l wenn δ = (α(v) → lv ) sonst wenn δ = (β(e) → le ) β(e) sonst e Durch eine Folge von Grapheditoperationen ∆ = (δ1 , . . . , δk ) kann ein Graph in einen anderen Graphen ∆(G) = δk (. . . δ1 (G) . . . ) umgeformt werden, der auch als Edit-Graph bezeichnet wird. Wir bemerken, dass das Hinzufügen von Knoten (δ = $ → v) nicht notwendig ist, wenn es darum geht, einen Subgraphisomorphismus von ∆(G1 ) nach G2 zu finden. Wenn mit jeder Operation δ bestimmte Kosten C(δ) verbunden sind, so sind die Kosten von ∆, C(∆), die Summe der Kosten der einzelnen Operationen. In dieser Arbeit werden Kostenfunktionen für Strings und Zahlen als Knoten- bzw. Kantenbeschriftungen implementiert (siehe dazu 4.4). 21 Wir wollen von diesen Definitionen ausgehend nun den Subgraphisomorphismus zwischen einem Edit-Graphen und einem anderen Graphen definieren. Ein fehlerkorrigierender Subgraphisomorphismus zwischen G1 und G2 ist ein 2-Tupel f = (∆, f∆ ), sodass f∆ ein Subgraphisomorphismus zwischen ∆(G1 ) und G2 ist. Weil im Allgemeinen mehr als eine Folge an Operationen ∆ existiert, um zwischen zwei Graphen G1 , G2 einen fehlerkorrigierenden Subgraphisomorphismus zu finden, definieren wir als Subgraphdistanz d(G1 , G2 ) die minimalen Kosten der Operationen aller möglichen fehlerkorrigierenden Subgraphisomorphismen. Das heißt d(G1 , G2 ) = min{C(∆)|f = (∆, f∆ ) ist fehlerkorrigierender Subgraphisomorphismus}. ∆ 3.2.2 Die Dekomposition Der hier vorgestellte Algorithmus [MB98] findet den kostengünstigsten fehlerkorrigierenden Subgraphisomorphismus von einem editierten Graphen aus einer Menge B = (1) (n) {G1 , . . . , G1 } zu einem Graphen G2 . Diese Anordnung ist dadurch motiviert, dass in vielen Anwendungen nicht nur für einen Graphen G1 ein inexaktes Matching zu einem Graphen G2 gesucht wird, sondern für eine ganze Graphdatenbank mit einer Menge an Graphen dieses inexakte Matching zu bestimmen ist. Daher muss die Menge B in einem Vorverarbeitungsschritt zuerst in eine andere Darstellung - die sogenannte Dekomposition - transformiert werden, die dann Ausgangspunkt des Matchingprozesses ist. Eine Dekomposition definiert eine rekursive Zerlegung der Graphen aus B in kleinere Subgraphen. Jedes 4-Tupel (G, G0 , G00 , E 0 ) der Dekompositionsmenge zerlegt den Graph G in die Teilgraphen G0 und G00 und die Menge der verbindenden Kanten E 0 (vgl. Definition Vereinigung von Graphen in Abschnitt 2.1). (1) (n) Sei B = {G1 , . . . , G1 } eine Menge von Graphen. Eine Dekomposition (in Anlehnung an [MB98]) D(B) ist eine Menge von 4-Tupeln (G, G0 , G00 , E 0 ), wobei G, G0 ⊂ G, G00 ⊂ G Graphen sind E 0 eine Knotenmenge ist, sodass G = G0 ∪E 0 G00 (i) (i) für jedes G1 ∈ B ein 4-Tupel (G1 , G0 , G00 , E 0 ) ∈ D(B) existiert zu jedem (G, G0 , G00 , E 0 ) ∈ D(B) existiert kein anderes (Ĝ, Ĝ0 , Ĝ00 , Ê 0 ) ∈ D(B) mit G = Ĝ 5. für jedes 4-Tupel (G, G0 , G00 , E 0 ) ∈ D(B) gilt • wenn G0 mehr als einen Knoten besitzt, existiert (G0 , . . . , . . . , . . . ) ∈ D(B) • wenn G00 mehr als einen Knoten besitzt, existiert (G00 , . . . , . . . , . . . ) ∈ D(B) • wenn G0 aus einem Knoten besteht, existiert (G0 , ∅, ∅, ∅) ∈ D(B) • wenn G00 aus einem Knoten besteht, existiert (G00 , ∅, ∅, ∅) ∈ D(B) 1. 2. 3. 4. Durch Bedingung 4 ist sichergestellt, dass die Zerlegung an sich eindeutig ist; Bedingung 22 3 sorgt dafür, dass alle Graphen der Menge B erfasst sind. Weil Bedingung 5 besagt, dass für jedes 4-Tupel (G, G0 , G00 , E 0 ), dessen Graph G aus mehr als einem Knoten besteht, zwei „nachfolgende“ 4-Tupel für G0 und G00 existieren, können wir eine Dekomposition als gerichteten Graphen darstellen. Von jedem 4-Tupel, das „Nachfolger“ in diesem Sinne besitzt, gehen zwei Kanten zu den „nachfolgenden“ 4-Tupeln aus. Ein Beispiel für eine Dekomposition von zwei Graphen ist in Abbildung 3.4 gegeben. Wir erkennen nun, inwiefern die Dekomposition gemeinsame Subgraphen nur ein Mal berücksichtigt: Der Graph g2 ist in zwei Subgraphen zerlegt worden und „teilt“ sich den Subgraphen s2 mit dem Graphen g1 . s3 c s4 a s5 b (s2 , s4 , s5 , {e1 }) a a e1 b b c b a e2 (a) Graph g1 c a c b b (s1 , s3 , s2 , {e2 }) a b a b e3 a e4 b (g1 , s2 , s5 , {e3 , e4 }) e5 a b b (g2 , s1 , s2 , {e5 }) (b) Graph g2 (c) Dekomposition Abbildung 3.4: Dekomposition von zwei Graphen Für eine Menge von Graphen B gibt es aber mehr als nur eine Möglichkeit, eine Dekomposition zu konstruieren. Der nachfolgend vorgestellte Algorithmus zur Erzeugung der Dekomposition ist in [Mes95] beschrieben. 23 Algorithmus 3.2 Decompose in Anlehnung an [Mes95] Eingabe: Graph G, ursprüngliche Dekomposition D̄(B), veränderte Dekomposition D(B) 1: Finde den größten Subgraph Smax ⊆ G, zu dem ein 4-Tupel (Smax , G0 , G00 , E 0 ) ∈ D̄(B) existiert 2: if Smax ist isomorph zu G then 3: return . G ist bereits komplett in D̄(B) zerlegt 4: if kein Smax wurde gefunden then 5: Wähle zufällig einen Subgraph Smax ⊆ G 6: if G besteht aus einem Knoten then 7: D(B) = D(B) ∪ {(G, ∅, ∅, ∅)} 8: return 9: else 10: Decompose(Smax , D̄(B), D(B)) 11: Decompose(G − Smax , D̄(B), D(B)) 12: Füge das 4-Tupel (G, Smax , G − Smax , E) zu D(B) hinzu, wobei E die Menge der Kanten zwischen Smax und G − Smax ist 13: return Im Algorithmus 3.2 unterscheiden wir zwei Dekompositionen D(B), D̄(B). Die Dekompsosition D̄(B) wird nicht verändert, es ist die ursprüngliche Dekomposition, die zum ersten Aufrufzeitpunkt des Algorithmus vorliegt. Wir benötigen sie, um den maximalen Subgraph Smax zu finden. In der Dekompostion D(B) werden die Veränderungen durch den Algorithmus vorgenommen: Zu Beginn des Algorithmus gilt D(B) = D̄(B), nach Abschluss des Algorithmus ist der Graph G in der Dekomposition D(B) zerlegt. Wir konstruieren die neue Dekomposition rekursiv. Der Algorithmus kennt zwei Abbruchkriterien: Entweder wir enden mit einem Graphen, der schon komplett in der Dekomposition D̄(B) enthalten ist (Zeile 3), oder wir treffen auf einen Graphen, der nur aus einem Knoten besteht (Zeile 8). Diese Graphen können nicht weiter zerlegt werden. Nachdem im Algorithmus also sichergestellt ist, dass Smax in D̄(B) vorhanden ist, zerlegen wir G − Smax und fügen anschließend das 4-Tupel zur Dekomposition hinzu. Zwei Punkte verbleiben, die zu erklären sind. Wir bilden einen zufälligen Subgraph Smax ⊆ G (Zeile 5), indem wir eine Hälfte der Knotenmenge V von G auswählen und den induzierten Subgraph bilden. Der „größte“ Subgraph (Zeile 1) ist der (im Allgemeinen nicht eindeutige) Subgraph, zu dem es keinen anderen Subgraphen gibt, der mehr Knoten hat. Wir können ihn mit jedem exakten Graph-Matching-Algorithmus bestimmen. 24 3.2.3 Der Algorithmus Seien nun also eine Dekomposition D(B) und ein Graph G2 gegeben. Der Algorithmus findet dann die kostengünstigsten fehlerkorrigierenden Subgraphisomorphismen von Graphen aus B zu G2 . Vereinfachend gesagt werden von den Graphen aus der Dekomposition schrittweise fehlerkorrigierende Subgraphisomorphismen zu dem Graph G2 konstruiert. Wenn für zwei Graphen S1 , S2 ein solcher fehlerkorrigierender Subgraphisomorphismus gefunden wurde und ein 4-Tupel (S, S1 , S2 , E) ∈ D(B) existiert, so werden diese fehlerkorrigierende Subgraphisomorphismen nach Möglichkeit kombiniert und ein fehlerkorrigierender Subgraphisomorphismus von S zu G2 gebildet. Die ersten fehlerkorrigierenden Subgraphisomorphismen werden von Graphen Si gebildet, die genau einen Knoten enthalten. Allgemein sind mehrere fehlerkorrigierende Subgraphisomorphismen zu einem Graph G2 denkbar, die mit Kosten verbunden sind (Entfernen des Knotens, Umbenennung), oder ein Subgraphisomorphismus ohne Fehlerkorrektur, falls die Beschriftungen gleich sind. Wir speichern für jeden Graphen zwei Listen von fehlerkorrigierenden Subgraphisomorphismen: open(Si ) und closed(Si ). Einen neu gebildeten fehlerkorrigierenden Subgraphisomorphismus speichern wir zuerst in open(Si ). Wenn wir ihn dann mit anderen fehlerkorrigierenden Subgraphisomorphismen wie oben beschrieben zu kombinieren versucht haben, speichern wir ihn in closed(Si ). Um die kostengünstigste Lösung zu erhalten, wird im Algorithmus immer der fehlerkorrigierende Subgraphisomorphismus mit den geringsten Kosten als nächstes betrachtet. Zur Schreibvereinfachung definieren wir daher auch die Kosten einer Liste als die Kosten des kostengünstigsten Subgraphisomorphismus. Formal: Die Kosten einer Liste sind C open(S) = min ∞ f {C(f )|f ∈ open(S)} falls open(S) nicht leer sonst Die Kosten für closed(S) definieren wir analog. Der sich ergebende Suchraum wird auch bei einer geeigneten Kostenfunktion schnell sehr groß, wenn alle möglichen sich neu ergebenden fehlerkorrigierenden Subgraphisomorphismen betrachtet würden. Um die Suche effizient zu gestalten und weniger vielversprechend wirkende fehlerkorrigierende Subgraphisomorphismen nicht weiter untersuchen zu müssen, betrachten wir bei der Auswahl des kostengünstigsten fehlerkorrigierenden Subgraphisomorphismus außerdem eine Heuristik h(S). Wir wählen als Ausgangspunkt für die Bildung von Kombinationen von fehlerkorrigierenden Subgraphsiomorphismen dann also denjenigen fehlerkorrigierenden Subgraphsiomorphismus S, für den C(S)+h(S) minimal in D(B) ist. Die hier verwendete Heuristik prüft, ob alle in einem fehlerkorrigierendem Subgraphisomorphismus aufeinander abgebildeten Knoten ähnlich sind. Sei also S = (∆, S∆ ) ein 25 fehlerkorrigierender Subgraphisomorphismus und eine Funktion ≈ : V → {true, false} ein Ähnlichkeitsmaß für Knoten. Dann ist h(S) = 0 falls für alle (v1 → v2 ) ∈ ∆ gilt: v1 ≈ v2 ∞ sonst Der Algorithmus konstruiert fehlerkorrigierende Subgraphisomorphismen, bis für einen Graphen aus B ein solcher gefunden wurde. Anschließend werden nur noch fehlerkorrigierende Subgraphisomorphismen gesucht, die höchstens genauso „teuer“ sind. Für manche Graphen aus B werden also eventuell keine fehlerkorrigierende Subgraphisomorphismen gefunden, für manche eventuell mehrere. Damit ergibt sich folgender Algorithmus: Algorithmus 3.3 ErrorCorrectingSubgraph Eingabe: Graph G2 = (V2 , E2 , α2 , β2 ), Menge an Graphen B, Dekomposition D(B) Ausgabe: Für die Graphen aus B, für die die kostengünstigsten fehlerkorrigierenden Subgraphisomorphismen existieren: dieses inexakte Matching Parameter: ein Schwellwert T = ∞, eine Kostenfunktion für Grapheditoperationen C(δ), eine Heuristik für Subgraphisomorphismen h(S) 1: for all S ∈ D(B) mit |VS | = 1, {v} = VS do 2: Setze open(S) = VertexMatching(v, αS (v), G2 ) 3: while Es gibt ein f1 ∈ open(S1 ), sodass C(open(S1 )) + h(S) ≤ T minimal in D(B) und C(f1 ) minimal in open(S1 ) do 4: Entferne f1 aus open(S1 ) und setze closed(S1 ) = closed(S1 ) ∪ {f1 } 5: if S1 ist ein Graph aus der Menge B then 6: Setze T = C(f1 ) 7: for all (S, S1 , S2 , E) ∈ D(B) oder (S, S2 , S1 , E) ∈ D(B) do 8: for all f2 ∈ closed(S2 ) do 9: f = Combine(S1 , S2 , E, G2 , f1 , f2 ) 10: if f ist nicht leer then 11: Setze open(S) = open(S) ∪ {f } (1) (n) 12: return für jeden Graphen Gi ∈ B = {G1 , . . . , G1 }, für den ein Matching gefunden wurde: closed(Gi ) Zwei Teilprobleme müssen wir noch genauer betrachten, die im Algorithmus 3.3 durch Unterprogrammaufrufe VertexMatching und Combine angezeigt sind. VertexMatching realisiert das Finden von fehlerkorrigierenden Subgraphisomorphismen von Graphen mit einem Knoten auf Graph G2 und Combine versucht zwei fehlerkorrigierende Subgraphisomorphismen zu S1 , S2 so zu kombinieren, dass ein neuer fehlerkorrigierender Subgraphisomorphismus von einem Graph S gefunden wird. Die Aufgabe des Algorithmus 3.4 ist es, alle möglichen fehlerkorrigierenden Subgraphi- 26 somorphismen von einem Graphen S, der einen Knoten v mit Beschriftung l besitzt, auf G2 zu finden. Zu diesem Zweck werden die Knoten des Graphs v2 ∈ G2 einzeln betrachtet: Der fehlerkorrigierende Subgraphisomorphismus ergibt sich aus der Änderung der Knotenbeschriftung und einer Zuordnung f∆ (v) = v2 . Außerdem muss die mögliche Löschung des Knotens v in Betracht gezogen werden und in die Menge möglicher fehlerkorrigierender Subgraphisomorphismen einbezogen werden. Algorithmus 3.4 VertexMatching Eingabe: Knoten v, Beschriftung l, Graph G2 = (V2 , E2 , α2 , β2 ) Ausgabe: Menge fehlerkorrigierender Subgraphisomorphismen F 1: F = ∅ . Menge möglicher fehlerkorrigierender Subgraphisomorphismen 2: for all v2 ∈ V2 do 3: F = F ∪ {∆, f∆ } mit ∆ = (l → α2 (v2 )), f∆ (v) = v2 4: F = F ∪ {(v → $), ∅} . Löschung des Knotens v 5: return F Sei (S, S1 , S2 , E) ∈ D(B). Im Folgenden zeigen wir, wie es möglich ist, aus zwei fehlerkorrigierenden Subgraphisomorphismen f1 = (∆1 , f∆1 ), f2 = (∆2 , f∆2 ), die zu den Teilgraphen S1 bzw. S2 gehören, einen fehlerkorrigierenden Subgraphisomorphismus von S zu G2 zu konstruieren. In Algorithmus 3.5 wird zuerst geprüft, dass die Knoten aus S1 und S2 durch f∆1 bzw. f∆2 auf verschiedene Knoten in G2 abgebildet werden. Ansonsten kann kein gültiger fehlerkorrigierender Subgraphisomorphismus erzeugt werden. Weil auf die Operationen von ∆1 bzw. ∆2 nur auf S1 bzw. S2 definiert sind, können die Grapheditoperationen für den fehlerkorrigierenden Subgraphisomorphismus von ∆(S1 ∪E S2 ) zu G2 durch Vereinigung von ∆1 und ∆2 und noch zu bestimmenden Operationen ∆E auf der Menge der Kanten E zwischen den Teilgraphen gebildet werden, d.h. ∆ = ∆1 ∪ ∆2 ∪ ∆E . Wir betrachten bei der Bildung von ∆E erst v ∈ S1 , w ∈ S2 , dann w ∈ S1 , v ∈ S2 und unterscheiden drei mögliche Fälle: 1. Wenn für e = (v, w) ∈ E eine Kante e2 = f∆1 (v), f∆2 (w) ∈ E2 existiert und β((v, w)) 6= β2 (f ∆1 (v), f∆2 (w)), dann ergänze ∆E um (β(e) → β (e )) falls Änderung der Beschriftung günstiger 2 2 die Operation (e → $) ∪ ($ → e2 ) falls Löschen und Einfügen günstiger 2. Wenn für e = (v, w) ∈ E keine Kante e2 = f∆1 (v), f∆2 (w) ∈ E2 existiert, dann ergänze ∆E um die Operation der Kante) (e → $) (Löschen 3. Wenn für eine Kante e2 = f∆1 (v), f∆2 (w) ∈ E2 keine Kante e = (v, w) ∈ E existiert, dann ergänze ∆E um die Operation ($ → e2 ) (Einfügen der Kante) 27 Algorithmus 3.5 Combine Eingabe: Teilgraphen S1 , S2 , Kantenmenge E, Graph G2 Fehlerkorrigierende Subgraphisomorphismen f1 = (∆1 , f∆1 ), f2 = (∆2 , f∆2 ) mit ∆1 (S1 ) = (V∆1 , E∆1 , α∆1 , β∆1 ) und ∆2 (S2 ) = (V∆2 , E∆2 , α∆2 , β∆2 ) Ausgabe: ein fehlerkorrigierender Subgraphisomorphismus f = (∆, f∆ ) oder ∅ 1: if f∆1 (V∆1 ) ∩ f∆2 (V∆2 ) 6= ∅ then 2: return ∅ 3: Erzeuge den fehlerkorrigierenden Subgraphisomorphismus f = (∆, f∆ ) von S1 ∪E S2 nach G2 mit f f∆ (v) = wenn v ∈ V∆1 f∆2 (v) wenn v ∈ V∆2 ∆1 (v) und ∆ = ∆1 ∪ ∆2 ∪ ∆E , wobei ∆E die Grapheditoperationen für E sind (siehe Text) 4: return f 3.2.4 Komplexität Die Analyse der Laufzeit orientiert sich an [Mes95], weicht von dieser aber ab, weil wir in dieser Arbeit nicht die dort gewählte Heuristik einsetzen. Wir nehmen an, dass (1) (L) die Graphen B = {G1 , . . . , G1 } in folgende Dekomposition zerlegt wurden: Jeder (i) Graph wird in den einen gemeinsamen Subgraph S und den einzigartigen Graphen G1 − S = Si zerlegt. Es gibt also L einzigartige Subgraphen. Der gemeinsame Subgraph S kann eventuell der leere Graph sein, falls die Graphen keinen gemeinsamen Subgraphen besitzen. Der gemeinsame Subgraph besitze n1 Knoten und die durchschnittliche Anzahl an Knoten der einzigartigen Graphen Si sei im Mittel n2 , sodass n = n1 + n2 gilt. Anschließend werden die sich ergebenden Subgraphen jeweils so zerlegt, dass in jedem Schritt der Zerlegung ein Graph Sv , der aus einem Knoten besteht, „abgetrennt“ wird: Aus einem Subgraph Si ergibt sich folglich das 4-Tupel (Si , Sv , Si − Sv , E). Si − Sv wird dann weiter zerlegt. Sei die Anzahl an Knoten in Graph G2 mit m bezeichnet. Wir betrachten zuerst die Si(1) tuation, in der nur ein Graph G1 in der Dekomposition vorhanden ist. Wir können die Anzahl fehlerkorrigierender Subgraphisomorphismen von (irgend-)einem Subgraphen S (1) mit k Knoten nach G2 schätzen durch O(mk ). Weil G1 in O(n) viele Subgraphen mit unterschiedlicher Knotenanzahl k = 1, . . . , n zerlegt wurde, ist die Anzahl aller fehlerkorrigierender Subgraphisomorphismen beschränkt durch O(mn n). Wenn wir außerdem die Bildung der Kombinationen von je zwei fehlerkorrigierenden Subgraphisomorphismen zu einem neuen berücksichtigen, bei der wir für alle Knoten des einen Subgraphen je alle Knoten des anderen Subgraphen durchlaufen (vgl. Zeile 1 und Bildung von ∆E in Zeile 3 in Algorithmus 3.5), ergibt sich insgesamt O(mn n2 ). 28 Wenn wir nun mehrere Graphen in der Dekomposition betrachten, können wir also folgern, dass wir für den gemeinsamen Subgraph die fehlerkorrigierenden Subgraphisomorphismen in O(mn1 n21 ) berechnen. Für die L einzigartigen Subgraphen der Dekomposition ist die Laufzeit durch O(Lmn2 n22 ) beschränkt. Zuletzt müssen die fehlerkorrigierenden Subgraphisomorphismen von den Graphen aus der Menge B zum Graph G2 durch Kombination gebildet werden, was durch O(Lmn n1 n2 ) beschränkt ist. Insgesamt ist die Laufzeit des Algorithmus damit also exponentiell in der mittleren Größe der Graphen aus B beschränkt: O(mn1 n21 + Lmn2 n22 + Lmn n1 n2 ). 3.3 Diskrete Relaxation Im Folgenden stellen wir einen Relaxationsalgorithmus zum Graph Matching vor. Dieser optimiert iterativ eine globale Zielfunktion, die die sogenannte Konsistenz des Matchings, also deren Güte, beschreibt. Der vorgestellte Ansatz[WH97] zählt zu den diskreten Relaxationsalgorithmen, d.h. in jedem Iterationsschritt wird jedem Knoten eines Graphs ein einziger anderer Knoten des anderen Graphs zugeordnet. Der hier vorgestellte Ansatz basiert darauf, mithilfe von bayesischer Stochastik ein Maß der Konsistenz eines Graph Matchings zu definieren und dieses mithilfe diskreter Relaxation zu optimieren. Das gefundene Matching ist im Allgemeinen kein Subgraphisomorphismus [Wil96]. Stattdessen ordnet der Algorithmus „ähnliche“ Knoten zweier Graphen einander zu. Formal wird eine Funktion f : V1 → V2 gefunden, die Knoten aus V1 auf Knoten aus V2 abbildet. Seien also zwei attributierte Graphen G1 = (V1 , E1 , α1 , β1 ) und G2 = (V2 , E2 , α2 , β2 ) gegeben. Wir bezeichnen die Beschriftungen der Graphen mit A1 = {x1v |v ∈ V1 : α1 (v1 ) = x1v } und A2 = {x2v |v ∈ V2 : α2 (v2 ) = x2v }. Der Algorithmus nutzt die Information von Kantenbeschriftungen nicht. Die A-posteriori-Wahrscheinlichkeit, d.h. die Wahrscheinlichkeit eines unbekannten Matchings f bei „Beobachtung“ der Beschriftungen A1 , A2 ist die Zielfunktion der Maximuma-posteriori-Methode. Mit dem Satz von Bayes ergibt sich formal: P (f |A1 , A2 ) = P (A1 , A2 |f ) · P (f ) P (A1 , A2 ) Dabei ist P (A1 , A2 |f ) die bedingte Wahrscheinlichkeit der Beschriftungen für ein Matching, P (f ) die unbedingte Wahrscheinlichkeit eines Matchings und P (A1 , A2 ) die gemeinsame Wahrscheinlichkeit der beiden Beschriftungsmengen. Wenn wir weiter annehmen, dass für ein Matching die verschiedenen Paare der Knotenbeschriftungen stochas- 29 tisch unabhängig voneinander sind und den Satz von Bayes anwenden, so ergibt sich: P (A1 , A2 |f ) Unabhängigkeit = Y Bayes P (x1u , x2v |u, v) = Y (u,v)∈f (u,v)∈f P (u, v|x1u , x2v ) P (x1u , x2v ) P (u, v) Wir drücken die Gesamtwahrscheinlichkeit also durch die Wahrscheinlichkeit des Matchings zweier Knoten P (u, v), der Wahrscheinlichkeit des Auftretens von zwei Beschriftungen P (x1u , x2v ) und der Wahrscheinlichkeit des Matchings zweier Knoten unter der Bedingung, dass ihre Knotenbeschriftungen bekannt sind, P (u, v|x1u , x2v ) aus. Um die Zielfunktion gemäß Maximum-a-posteriori-Methode zu maximieren, werden die Wahrscheinlichkeiten P (A1 , A2 ) und P (x1u , x2v ) nicht betrachtet, weil sie nicht abhängig von dem Matching sind; sie müssen nicht gesondert berechnet werden. Die Wahrscheinlichkeiten P (u, v|x1u , x2v ) und P (u, v) sind abhängig vom Kontext der Anwendung[WH97]. Die in dieser Arbeit betrachteten Implementierungen werden in 4.6 vorgestellt. Im Weiteren soll nun die Entwicklung eines Modells für P (f ) nachvollzogen werden. Um die Güte eines Matchings zu beurteilen und P (f ) zu berechnen, wird ein Kriterium entwickelt, das auf der einen Seite nicht die gesamte Topologie des Graphen in den Blick nimmt, auf der anderen Seite aber die topologische Information auch nicht vollkommen außer Acht lässt. Wir betrachten daher strukturelle „Constraints“ von Graph G2 , die vorgeben, welche Matchings von einem Knoten u ∈ V1 auf einen Knoten v ∈ V2 denkbar sind und nutzen die Super-Cliquen von Graph G2 aus. Weil benachbarte SuperCliquen miteinander in Beziehung stehen, verbindet sich mit dem Ansatz die Idee, dass sich die Information von dem Matching einer Super-Clique durch Iteration während der Relaxation auf den restlichen Graph auswirkt [Wil96]. Wenn wir einen Knoten u ∈ V1 betrachten, dann sei Cu seine Super-Clique und Ru = (u, u1 , . . . , u|Cu |−1 ) die Relation auf den Knoten der Super-Clique. Die abgebildete Super Clique ist dann Γu = f (u), f (u1 ), . . . , f (u|Cu |−1 ) . Wir bezeichnen die Menge aller Super-Cliquen von G2 , die mögliche Matchings von u auf irgendein v definieren, als Dictionary für u: Θu . Die in Θu enthaltenen Super-Cliquen bilden Relationen Sv = (v, v1 , . . . ). Wir bestimmen ein Dictionary unter Berücksichtigung von Größenunterschieden in der Knotenanzahl der Super-Cliquen, weil wir davon ausgehen, dass in Anwendungen Knoten fehlen oder Knoten, die nicht zugeordnet werden können, vorhanden sind. Für ein u ∈ V1 wird Θu bestimmt, indem wir die Super-Cliquen von Graph G2 betrachten und die kleinere Super-Clique mit sogenannten „dummy“ Knoten auffüllen. Wenn also Succ(G2 , v) < Succ(G1 , u) gilt, so wird die Super-Clique von v zuerst mit „dummy“ Knoten aufgefüllt, ansonsten wird die Super-Clique von u aufgefüllt. Wir betrachten die Anzahl der maximalen Knotendifferenz zwischen Super-Cliquen als Parameter für den Algorithmus. Anschließend werden die äußeren Knoten um den Knoten v permutiert, 30 sodass alle möglichen Relationen Sv erzeugt werden (siehe Abbildung 3.5). {u, u1 , u2 , u3 } abgebildet aufΘu u1 u3 u2 u v1 v (a) Super-Clique Cu in Graph G1 { {v, v1 , $, $}, {v, $, v1 , $}, {v, $, $, v1 } } (b) Super-Clique Cv in Graph G2 (c) Dictionary Θu {u, u1 , $} abgebildet auf Θu = u u1 v1 v v2 (d) Super-Clique Cu in Graph G1 (e) Super-Clique Cv in Graph G2 { {v, v1 , v2 }, {v, v2 , v1 } } (f) Dictionary Θu Abbildung 3.5: Beispiele von Super-Cliquen und Dictionaries Algorithmus 3.6 ComposeSuperClique Eingabe: Relation einer Super-Clique Ru , Graph G2 = (V2 , E2 ), Knotendifferenz d Ausgabe: Dictionary Θu 1: for all v ∈ V2 do 2: Bilde Relation der Super-Clique Sv = (v, v1 , . . . , vn ) 3: if ||Sv | − |Ru || ≤ d then 4: Fülle die kleinere Relation mit „dummy“ Knoten $ auf, sodass |Sv | = |Ru | 5: Permutiere alle Knoten außer v in Sv und füge die Permutation zu Θu hinzu 6: return Θu Wir merken zur Bildung der Permutationen an, dass eventuell vorkommende „dummy“ Knoten $ nicht unterscheidbare Elemente sind. Das bedeutet, dass mathematisch präzise Permutationen mit Wiederholung gebildet werden(vgl. dazu Abbildung 3.5c). Der Wert für die Wahrscheinlichkeit P (f ) wird definiert als arithmetisches Mittel aller 1 P P (Γu ). Für eine Super-Clique Ru abgebildeten Super-Cliquen, d.h. P (f ) = |V1 | u∈V1 wissen wir aber, dass alle möglichen Matchings in Θu enthalten sind, und folgern also mit dem Satz von der totalen Wahrscheinlichkeit P (Γu ) = X P (Γu | Sv ) · P (Sv ) Sv ∈Θu Für die Wahrscheinlichkeit P (Sv ), dass eine Relation aus dem Dictionary richtig ist, neh- 31 men wir eine Gleichverteilung an, d.h. P (Sv ) = |Θ1u | . Im Folgenden soll nun ein Modell für die Wahrscheinlichkeit P (Γu | Sv ) entwickelt werden, die beschreibt, dass eine abgebildete Super-Clique Γu durch die richtige Abbildung f entstanden ist unter der Bedingung, dass Sv die „wahre“ Relation ist. Durch die Annahme, dass beim Matching Fehler mit einer Wahrscheinlichkeit auftreten können, die einer gedächtnislosen Verteilung folgen, können wir den Ausdruck als Produkt der einzelnen bedingten Wahrscheinlichkeiten schreiben: P (Γu | Sv ) = |S Qv | P (f (uk ) | vk ). k=1 Unser Modell für fehlerhafte Matchings sieht zwei verschiedene Gründe vor: Zum einen können Fehler durch falsche Zuordnung einzelner Knoten zueinander im Matchingprozess entstehen, zum anderen können sie entstehen, wenn die Graphen strukturelle Unterschiede aufweisen. Wir nehmen an, dass die Wahrscheinlichkeit fehlerhafter Zuweisungen im Matchingprozess Pe einer gleichverteilten, gedächtnislosen Verteilung folgt. Fehler durch Strukturunterschiede treten annahmegemäß mit einer gleichverteilten Wahrscheinlichkeit Pφ auf. Das bedeutet, wenn wir einen Knoten u ∈ V1 auf einen „dummy“ Knoten oder einen „dummy“ Knoten auf einen Knoten v ∈ V2 abbilden, so passiert das mit Wahrscheinlichkeit Pφ . Es ergibt sich: Pφ wenn uk oder vk „dummy“ Knoten P (f (uk ) | vk ) = (1 − Pφ )(1 − Pe ) wenn f (uk ) = vk (1 − Pφ )Pe wenn f (uk ) 6= vk Für den Wert von Pe wird in der Implementierung zu [WH97] der Wert 0.3 vorgeschlagen. Wir folgen diesem Vorschlag. Zu Pφ wird in dem Artikel angemerkt, dass der Parameter vor allem aus formalen Gründen eingeführt wird. Wenn wir eine abgebildete Super-Clique Γu mit einer Super-Clique Sv aus dem Dictionary Θu vergleichen, müssen wir also bestimmen: • Die Anzahl der größeren der beiden Cliquen C = max{|Ru | , |Sv |} • Die Anzahl an „dummy“ Knoten, mit denen Sv oder Ru aufgefüllt wude, ψ(Γu , Sv ) = ||Ru | − |Sv || • Die Anzahl der Unterschiede zwischen nicht-„dummy“ Knoten, die auf nicht-„dummy“ Knoten aus Sv abgebildet werden, die wir H(Γu , Sv ) nennen Mit einigen Umformungen erhalten wir den folgenden Ausdruck 1 X KΓ exp − ke H(Γu , Sv ) + kφ ψ(Γu , Sv ) P (Γu ) = |Θu | Sv ∈Θu u C wobei KΓu = (1 − Pφ )(1 − Pe ) , ke = ln (1 − Pφ )(1 − Pe ) 1 − Pe und kφ = ln . Pe Pφ Die Konsistenz des Matchings für eine abgebildete Super-Clique P (Γu ) wird damit also als Summe über die Inhalte der Dictionaries bestimmt. Weil die Unterschiedlichkeit 32 der abgebildeten Super-Clique Γu auf mögliche Dictionary-Einträge Sv als Exponenten H(Γu , Sv ) bzw. ψ(Γu , Sv ) in die Berechnung eingehen, werden viele Unterschiede zu einer kleinen Maßzahl und große Ähnlichkeit zu einer großen Maßzahl führen. Die Vorschrift für die Relaxation lautet damit f = arg max f Y (u,v)∈f P (u, v|x1u , x2v ) X P (Γu ) P (u, v) u∈V1 Wir können das Matching f aber auch knotenweise optimieren, da eine Zuordnung f (u) = v den Wert P (f ) nur im Umfeld von Knoten u beeinflusst. f (u) = arg max v∈V2 P (u, v|x1u , x2v ) X P (Γu ) P (u, v) u∈Ru Algorithmus 3.7 DiskreteRelaxation Eingabe: Graph G1 = (V1 , E1 , α1 , β1 ), Graph G2 = (V2 , E2 , α2 , β2 ) Ausgabe: Knotenabbildung f : V1 → V2 Parameter: maximale Schrittzahl I, Anzahl der Knotendifferenz von Super-Cliquen d 1: for all u ∈ V1 do 2: Bilde Super-Clique Ru 3: Setze Θu = composeSuperClique(Ru , G2 , d) P (u, v|x1u , x2v ) 4: Finde initiales Matching f (u) = arg max v∈V2 P (u, v) 5: while f wurde verändert und i ≤ I do 6: for all u ∈ V1 do P (u, v|x1u , x2v ) P 7: Setze f (u) = arg max P (Γu ) v∈V2 P (u, v) u∈Ru 8: i←i+1 9: return f 3.4 SMKernel Der im folgenden vorgestellte Algorithmus aus [KM12] berechnet einen Kernel auf Graphen. Die Kernelfunktion wird über alle zwischen den Graphen existierenden Subgraphisomorphismen von G1 nach G2 und von G2 nach G1 definiert. Die Berechnung dieser Art von Kerneln auf Graphen ist NP-vollständig, aber nach [Bor07] ist sie sehr ausdrucksstark, sodass sich die Frage stellt, ob es eine Möglichkeit gibt, die Information des Kernels für Anwendungen nutzbar zu machen, in denen Subgraphisomorphismen gesucht werden. 33 Gegeben die beiden Graphen G1 und G2 bezeichnen wir mit B(G1 , G2 ) die Menge aller Bijektionen zwischen Knotenmengen V10 ⊆ V1 und V20 ⊆ V2 . Seien weiter λ : B(G1 , G2 ) → R+ eine Gewichtungsfunktion und KV eine Kernelfunktion für Knoten und KE eine Kernelfunktion für Kanten. Dann ist der Subgraph Matching Kernel die Funktion ksm (G1 , G2 ) = X f ∈B(G1 ,G2 ) Y λ(f ) Y KV (v, f (v)) v∈V10 KE (e, ψf (e)). e∈V10 ×V20 In [KM12] wird gezeigt, dass dieser Kernel positiv semi-definit ist. Wir erhalten als Spezialfall davon einen anderen Kernel, wenn wir die Kernel-Funktionen KV für Knoten und KE für Kanten als Dirac-Kernel wie folgt wählen 1 wenn α1 (v1 ) = α2 (v2 ) KV (v1 , v2 ) = 0 sonst 1 KE (e1 , e2 ) = wenn e1 ∈ E1 ∧ e2 ∈ E2 ∧β1 (e1 ) = β2 (e2 ) oder e1 ∈ / E1 ∧ e2 ∈ / E2 0 sonst Wir erkennen, dass durch die Wahl der Kernelfunktionen alle Bijektionen, die kein Subgraphisomorphismus sind, nicht zur Summe beitragen. Anstatt über die Bijektionen aus B(G1 , G2 ) zu summieren, können wir nun durch die Wahl dieser Kernelfunktionen über die Isomorphismen zwischen den Graphen summieren, die von Teilknotenmengen V10 ⊆ V1 und V20 ⊆ V2 (ein sogenannter gemeinsamer Subgraphisomorphismus) induziert werden, und erhalten einen weiteren Kernel. Sei also I(G1 , G2 ) diese Menge aller von den Teilknotenmengen induzierten Isomorphismen zwischen G1 [V10 ] und G2 [V20 ]. Für eine Gewichtungsfunktion λ : I(G1 , G2 ) → R+ ist der Common Subgraph Isomorphism (CSI) Kernel definiert als X kcsi (G1 , G2 ) = λ(f ) f ∈I(G1 ,G2 ) Nach [Lev73] gibt es eine Beziehung zwischen den gemeinsamen Subgraphisomorphismen und den Cliquen im sogenannten Produktgraph von zwei Graphen , die die Berechnung des CSI Kernels und des Subgraph Matching Kernels ermöglicht. Der (modulare) Produktgraph GP = (VP , EP ) zweier Graphen G1 , G2 ist definiert durch VP = {(v1 , v2 ) ∈ V1 × V2 |α1 (v1 ) = α2 (v2 )} und EP besitzt Kanten von (u1 , u2 ) ∈ VP zu (v1 , v2 ) ∈ VP , falls gilt u1 6= v1 ∧ u2 6= v2 und entweder e1 = (u1 , v1 ) ∈ E1 ∧ e2 = (u2 , v2 ) ∈ E2 ∧ β1 (e1 ) = β2 (e2 ) (c-Kante) oder e1 ∈ / E1 ∧ e2 ∈ / E2 (d-Kante). Die Unterscheidung von c-Kanten und d-Kanten wird bei der Kernelberechnung von maßgeblicher Bedeutung sein, wenn es um die Geschwindigkeit geht (siehe dazu 4.6). Ein Beispiel für einen Produktgraphen ist in Abbildung 3.6 gegeben. Gleiche Knoten der Ausgangsgraphen werden zu einem Knoten im Produktgraphen und Kanten verbinden die Knoten entsprechend obiger Vorschrift. Dabei sind d-Kanten gestrichelt und c-Kanten durchgängig gezeichnet. 34 1 N O 1 2 C 3 O (a) G1 C N 2 (3, 1) (2, 2) 4 3 5 C C (1, 3) 6 O (3, 6) (b) G2 (2, 4) (2, 5) (c) Produktgraph von G1 und G2 Abbildung 3.6: Zwei Graphen 3.6a, 3.6b und ihr Produktgraph 3.6c Das Ergebnis von [Lev73] ist, dass jeder Clique C in GP ein gemeinsamer Subgraphisomorphismus f ∈ I(G1 , G2 ) zugeordnet werden kann. Deshalb kann der CSI Kernel durch Bestimmung der Cliquen im Produktgraph berechnet werden. Dieses Resultat kann durch Einführung des gewichteten Produktgraphs so erweitert werden, dass auch die Berechnung des Subgraph Matching Kernels möglich wird. Für zwei Kernelfunktionen KV auf Knoten und KE auf Kanten ist der gewichtete Produktgraph GP = (VP , EP , αP , βP ) ein 4-Tupel VP = {(v1 , v2 ) ∈ V1 × V2 | KV (v1 , v2 ) > 0} EP = {( (u1 , u2 ), (v1 , v2 ) ) ∈ VP × VP | u1 6= v1 ∧ u2 6= v2 ∧ KE ( (u1 , v1 ), (u2 , v2 ) ) > 0} αP (v) = KV (v1 , v2 ) ∀(v1 , v2 ) ∈ VP βP (e) = KE ( (u1 , v1 ), (u2 , v2 ) ) ∀e ∈ EP Die Knoten und Kanten des gewichteten Produktgraphs sind also mit den Werten des Kernels auf den Knoten der Einzelgraphen bzw. des Kernels auf den Kanten der Einzelgraphen beschriftet. Wenn der Kernel KE auf den Kanten ein Dirac-Kernel ist, so können auch im gewichteten Produktgraph d-Kanten und c-Kanten unterschieden werden. Im Folgenden meinen wir den gewichteten Produktgraphen, wenn wir vom Produktgraphen sprechen, soweit nicht anders angegeben. e Um den normalisierten Kernel k(G 1 , G2 ) zu berechnen, müssen neben dem Wert von k(G1 , G2 ) auch die Werte der Kernel von k(G1 , G1 ) und k(G2 , G2 ) bekannt sein. Alle drei Kernel werden durch die identische Funktion SMKernel rekurisv berechnet, die systematisch die Cliquen eines gewichteten Produktgraphs aufzählt. In [KM12] wird zur Verbesserung der Laufzeit des Algorithmus vorgeschlagen, die Rekursion bei Erreichen einer bestimmten maximalen Größe kmax der Cliquen im Produktgraph abzubrechen. Damit können also auch nur Subgraphen bis zur Größe kmax in die Berechnung des Kernels miteinbezogen werden. Der so berechnete Kernel verbleibt positiv definit, weil er äquivalent zu dem allgemeinen Subgraph Matching Kernel mit einer entsprechend gewählten Gewichtungsfunktion λ ist. Es ergibt sich folgender Algorithmus, um den Subgraph Matching Kernel zu berechnen. 35 Algorithmus 3.8 SubgraphMatchingKernel Eingabe: Graphen G1 = (V1 , E1 , α1 , β1 ) und G2 = (V2 , E2 , α2 , β2 ) e Ausgabe: der normalisierte Graphkernel k(G 1 , G2 ) ∈ [0, 1] Parameter: Kernelfunktionen für Knoten KV und Kanten KE und max. Rekursionstiefe d, Gewichtungsfunktion λ 1: Erstelle gewichteten Produktgraph GP = (VP , EP , αP , βP ) aus G1 , G2 2: k(G1 , G2 ) = SMKernel(1, ∅, VP , GP , 0) 3: Erstelle gewichteten Produktgraph GP 0 = (VP 0 , EP 0 , αP 0 , βP 0 ) aus G1 , G1 4: k(G1 , G1 ) = SMKernel(1, ∅, VP 0 , GP 0 , 0) 5: Erstelle gewichteten Produktgraph GP 00 = (VP 00 , EP 00 , αP 00 , βP 00 ) aus G2 , G2 6: k(G2 , G2 ) = SMKernel(1, ∅, VP 00 , GP 00 , 0) k(G1 ,G2 ) e 7: return k(G 1 , G2 ) = √ k(G1 ,G1 ) k(G2 ,G2 ) 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: function SMKernel(w, C, P , GP , i) Eingabe: das Gewicht w der Clique C, die Menge möglicher Knoten P für eine Erweiterung der Clique, der Produktgraph GP , Rekursionstiefe i while |P | > 0 und i <= d do v ← ein Element von P C 0 ← C ∪ {v} w0 ← w · αP (v) . Knotengewicht multiplizieren for all u ∈ C do w0 ← w · βP ((u, v)) . Kantengewicht multiplizieren value ← value + w0 · λ(C 0 ) value ← value + SMKernel(w0 , C 0 , P ∩ Succ(GP , v), GP , i + 1) . Clique erweitern P ← P \ {v} return value end function 3.4.1 Komplexität Weil es eine Entsprechung von Cliquen im Produktgraph GP und für die Berechnung des Kernels notwendigen Bijektionen zwischen den Graphen G1 , G2 gibt, können wir eine obere Schranke für den Algorithmus durch Betrachten der Bijektionen erhalten. In einem n Graph mit n Knoten gibt es k mögliche Subgraphen der Größe k. Außerdem gibt es zwischen diesen Subgraphen maximal k! Isomorphismen, sodass wir die Cliquenanzahl abschätzen durch ! ! ! k k X X n1 n2 n1 n2 C(k) = i! ≤ i i i i=0 i=0 Damit besitzt der modifizierte Algorithmus polynomielle Laufzeit:O(nC(k)) = O(knk+1 ), wobei n =n1 n2 . Der ursprüngliche Algorithmus besitzt dagegen exponentielle Laufzeit, P weil ni=0 ni = 2n . 36 Kapitel 4 Implementierung Der folgende Abschnitt beschreibt die Implementierung der im vorherigen Kapitel vorgestellten Algorithmen in Java. In den gezeigten UML-Klassendiagrammen sind wegen der besseren Übersichtlichkeit Getter- und Setter-Methoden nicht explizit aufgelistet. Die Implementierung unterscheidet an einigen Stellen Knoten bzw. Kanten, die mit Strings beschriftet sind, und Knoten bzw. Kanten, die mit Zahlen beschriftet sind. Diese Unterscheidung bezieht sich auf die in Kapitel 5 vorgestellten Anwendungen, in der Knoten und Kanten mit diesen Beschriftungen evaluiert werden. 4.1 Externe Bibliotheken 4.1.1 Die JGraphT-Bibliothek Wir benutzen zur Implementierung der Algorithmen die JGraphT-Bibliothek 1 , die uns Datenstrukturen zur Repräsentation von Graphen zur Verfügung stellt. Wir werden hier die wichtigsten Interfaces kurz vorstellen, einen Überblick bietet Abbildung 4.1. Die Klasse Graph stellt einen Graph dar und kapselt den Zugriff auf Knoten und Kanten. Wie alle Interfaces ist auch dieses ein generischer Typ und besitzt zwei Typparameter: V für die Knotenklasse und E für die Kantenklasse. Die Typparameter sind in Kästen mit gestrichelten Rändern in den UML-Diagrammen verzeichnet. Wir benutzen unsere eigenen beschrifteten Knoten- und Kantenklassen, die insbesondere Methoden anbieten, um die Ähnlichkeit zweier Beschriftungen festzustellen. Während unsere Klassen nach außen das Interface Graph als Eingabe akzeptieren, unterscheiden sie in ihrer internen Funktionsweise zwischen DirectedGraph und UndirectedGraph. 1 http://jgrapht.org - JGraphT offizielle Webseite 37 Nachbarknoten können über das Interface Graph nur durch Abfrage der ein- bzw. ausgehenden Kanten berechnet werden. Um einmal abgefragte Nachbarknoten bei erneuter Abfrage nicht wieder berechnen zu müssen, werden zusätzlich die Klassen NeighborIndex bzw. DirectedNeighborIndex verwendet. Diese speichern Nachbarknoten nach der ersten Abfrage ab und dienen als Cache. Das Interface ListenableGraph bietet die Möglichkeit, dass strukturelle Änderungen am zugrundeliegenden Graph von anderen Klassen bemerkt werden können, sodass zum Beispiel ein NeighborIndex aktualisiert werden kann. Wir repräsentieren die Dekomposition als ListenableGraph, die wir zum Berechnen der fehlerkorrigierenden Subgraphisomorphismen benötigen. Dadurch kann sie einfacher um andere Graphen erweitert werden. org jgrapht V, E V, E «interface» Graph «interface» DirectedGraph V, E «interface» UndirectedGraph V, E «interface» GraphMapping V, E «interface» ListenableGraph org jgrapht alg V, E DirectedNeighborIndex V, E NeighborIndex Abbildung 4.1: Wichtige Interfaces und Klassen in JGraphT 38 4.1.2 Andere Hilfsfunktionen Für einige spezielle Aufgabestellungen greifen wir auf frei verfügbare Bibliotheken zurück. Um die Levenshtein-Distanz zwischen Strings zu berechnen, setzen wir die Funktion getLevenshteinDistance() aus dem Apache Commons Projekt2 ein. Die Permutation der Knoten zur Berechnung der Dictionaries für die diskrete Relaxation wird nicht im Rahmen dieser Arbeit implementiert, sondern hier wird eine Bibliothek aus Google Guava3 eingesetzt. Zuletzt wird für den String Subsequence Kernel, der in Abschnitt 2.4.1 vorgestellt ist, eine Implementierung des Software-Pakets Weka (Waikato Environment for Knowledge Analysis)4 genutzt. 4.2 4.2.1 Allgemeine Klassen Knoten- und Kantenklassen entities L AbstractLabelVertex – label : L + isSimilar (v : AbstractLabelVertex<L>) : boolean + isEqual (v : AbstractLabelVertex<L>) : boolean + relativeDistance (v : AbstractLabelVertex<L>) : double «bind» «bind» L → Double L → String StringLabelVertex NumberLabelVertex – LEVENSHTEIN THRESHOLD : double – THRESHOLD : double Abbildung 4.2: Klassen, die Knoten darstellen 2 http://commons.apache.org/ - Apache Commons https://github.com/google/guava - Google Guava 4 http://www.cs.waikato.ac.nz/~ml/weka/ - Weka 3 39 entities L AbstractLabelEdge – label : L + isSimilar (v : AbstractLabelEdge<L>) : boolean + isEqual (v : AbstractLabelEdge<L>) : boolean + relativeDistance (v : AbstractLabelEdge<L>) : double «bind» «bind» L → Double L → String StringLabelEdge NumberLabelEdge – LEVENSHTEIN THRESHOLD : double – THRESHOLD : double Abbildung 4.3: Klassen, die Kanten darstellen Alle Klassen, die beschriftete Knoten oder Kanten implementieren, erben von den abstrakten Klassen AbstractLabelVertex und AbstractLabelEdge. Diese bieten Methoden an, mit der sich die Ähnlichkeit zweier Knoten feststellen lässt und diese Ähnlichkeit auf ein Distanzmaß d ∈ [0, 1] abbilden lässt. Klassen, die von diesen abstrakten Klassen erben, legen fest, welche Beschriftungen die Knoten bzw. Kanten tragen. Zum Beispiel sind StringLabelVertex und StringLabelEdge mit Strings beschriftet, wohingegen NumberLabelVertex und NumberLabelEdge mit Zahlen beschriftet sind. Die Distanz zwischen zwei mit Strings beschrifteten Knoten bzw. Kanten wird mittels der in Abschnitt 2.2 vorgestellten Levenshtein-Distanz berechnet. Knoten und Kanten, die mit Strings s1 , s2 beschriftet sind, sind ähnlich, wenn für den LevenshteinThreshold t gilt levsim (s1 , s2 ) ≥ t. Ihre relative Distanz beträgt: lev(s1 , s2 ) . max{|s1 | , |s2 | , 1} Knoten und Kanten, die mit Zahlen l1 , l2 beschriftet sind, sind ähnlich, falls für einen Threshold t gilt: |l1 − l2 | ≤ t. Ihre relative Distanz beträgt: |l1 − l2 | min ,1 . t 40 4.2.2 Die GraphMatcher-Klasse Alle Klassen, die die vorgestellten Algorithmen implementieren, erben von der abstrakten Klasse GraphMatcher. Diese bietet mit ihren Methoden eine einheitliche Schnittstelle, um Daten über den Matchingprozess abzufragen. Auf diese Weise kann die programmatische Umsetzung der Evaluation einfacher erfolgen, weil davon ausgegangen wird, dass die entsprechenden Klassen alle diese Methoden besitzen. Wenn also die Klasse SMKernel für die Berechnung des Graph Kernels eine Methode matchGraphs() implementiert, so ist das streng genommen semantisch falsch, weil der Graph Kernel kein Graph Matching ist. entities V, E, VL, EL GraphMatcher + g1 : Graph<V, E> + g2 : Graph<V, E> # runtime : long # timeSteps : Map<String, Long> GraphMatcher(g1 : Graph<V, E>, g2 : Graph<V, E>, edgeClass : Class<E>) + matchGraphs() : void Abbildung 4.4: Die Klasse GraphMatcher 4.3 VF2 Der Algorithmus VF2 wird im Wesentlichen in der Klasse VF2 implementiert. Die Klasse State repräsentiert den Zustand des Matching-Prozesses, die Klasse Pair eine mögliche Zuordnung. Beide Klassen implementieren keine Algorithmen, sondern halten ausschließlich Daten vor. 41 vf2 V, E, VL, EL Pair 1 1 VF2 state 1 1 State Abbildung 4.5: Klassen im Package vf2 Wir halten uns bei der Implementierung an die Vorgaben aus [CFSV04b]. Der gegenwärtige Zustand von zugeordneten Knoten wird durch Arrays core_1 und core_2 dargestellt. Eine Zuordnung (vi , vj ) (der „i-te“ Knoten von G1 wird dem „j-ten“ Knoten von G2 zugeordnet) bedeutet dann, dass core_1[i] = j und umgekehrt core_2[j] = i gilt. Um die Mengen T1in , T2in , T1out , T2out der Knoten zu beschreiben, die durch in das partielle Matching hinein- oder aus ihm herausführende Kanten mit dessen Knoten verbunden sind, verwenden wir die übrigen vier Arrays. Wenn wir in der Rekursionstiefe k feststellen, dass der Knoten i nun zum ersten Mal im Graph G1 aus dem partiellen Matching heraus erreichbar ist, setzen wir out_1[i] = k. Auf diese Weise kann das Wiederherstellen dieser Mengen beim Backtracking von Stufe k auf Stufe k − 1 realisiert werden, indem wir den Array dort zurücksetzen, wo k eingetragen ist. State NULL_NODES : int + G1 : int + G2 : int + core_1 : int[] + core_2 : int[] + in_1 : int[] + in_2 : int[] + out_1 : int[] + out_2 : int[] + depth : int + matchingFound : boolean + N : int + M : int + State (G1 : int, G2 : int) + Pair(n : int, m : int) (a) Die Klasse State (b) Die Klasse Pair Pair In der Klasse VF2 wird die Logik des Matching-Algorithmus implementiert (vgl. Algorithmus 3.1). Die Methode generateCandidates() konstruiert die Menge P neuer möglicher Kombinationen von Knoten (mj , ni ) (Zeile 5). Die dabei eingesetzte Hilfsmethode findPairs() sucht zum kleinsten möglichen Index eines Knotens aus dem Array m alle noch nicht zugeordneten Knoten im Array n und erzeugt daraus eine Liste an 42 möglichen Kombinationen. Diese werden dann auf Gültigkeit im Hinblick auf bestimmte Regeln getestet (Zeile 7). Die nachfolgende Tabelle gibt einen Überblick darüber, in welcher Methode welche Regel evaluiert wird. Regel R1 R2 R3 R4 R5 R6 Methode ruleSemantic() rulePred() ruleSucc() ruleIn() ruleOut() ruleNew() Hilfsmethode – predSuccHelper() predSuccHelper() cardInOutHelper() cardInOutHelper() newHelper() Wenn eine mögliche Kombination alle Regeln erfüllt, so wird der Zustand des Matchings durch updateVector() (Zeile 8) erneuert. Der Algorithmus des Matchings wird durch matchGraphs() koordiniert. 43 V, E, VL, EL VF2 # g1VertexOrder : List<V> # g2VertexOrder : List<V> # directedG1Neighbors : DirectedNeighborIndex<V, E> # directedG2Neighbors : DirectedNeighborIndex<V, E> # undirectedG1Neighbors : NeighborIndex<V, E> # undirectedG2Neighbors : NeighborIndex<V, E> + mapping : GraphMapping<V, E> + VF2(g1 : Graph<V, E>, g2 : Graph<V, E>, edgeClass : Class<E>) + matchGraphs() : GraphMapping<V, E> # match(state : State) : State # updateVector(pairIndex : int, inOut : int[], predSucc : List<V>, orderedVertices : List<V>, recursionDepth : int) : int[] # restoreVector(vector : int[], currentDepth : int) : int[] # generateCandidates (state : State) : List<Pair> # findPairs(n : int[], m : int[], state : State) : List<Pair> # feasible(pair : Pair, state : State) : boolean # ruleSemantic(pair : Pair, state : State) : boolean # rulePred(pair : Pair, state : State) : boolean # ruleSucc(pair : Pair, state : State) : boolean # predSuccHelper(mapping : int[], predSucc1 : List<V>, vertexList1 : List<V>, predSucc2 : List<V>, vertexList2 : List<V>) : boolean # ruleIn(pair : Pair, state : State) : boolean # ruleOut(pair : Pair, state : State) : boolean # cardInOutHelper(state : State, predSucc1 : List<V>, inOut1 : int[], predSucc2 : List<V>, inOut2 : int[]) : boolean # ruleNew(pair : Pair, state : State) : boolean # newHelper(state : State, predSucc1 : List<V>, predSucc2 : List<V>) : boolean – getG1Predecessors(v : V): List<V> – getG2Predecessors(v : V): List<V> – getG1Successors(v : V): List<V> – getG2Successors(v : V): List<V> Abbildung 4.7: Die Klasse VF2 44 4.4 Fehlerkorrigierende Subgraphisomorphismen nsg V, E, VL, EL V, E, VL, EL NSG Decomposition 1 1 1 0...* V, E, VL, EL SubgraphIsomorphism 0...* 1 V, E, VL, EL Combination 1 V, E, VL, EL 0...* E «interface» ICostFunction EdgeMapping «enum» EdgeOperation «enum» VertexOperation EDGESUB EDGEDEL EDGEINS VERTEXSUB VERTEXDEL Abbildung 4.8: Klassen im Package nsg Die Implementierung des Algorithmus, der fehlerkorrigierende Subgraphisomorphismen findet, ist auf acht Klassen verteilt. Zwei Phasen müssen unterschieden werden: In der ersten Phase wird die Dekomposition gebildet, in der zweiten wird ein fehlerkorrigierender Subgraphisomorphismus von mindestens einem in der Dekomposition vorhandenen Graphen zu einem anderen Graph gefunden. Die Klasse NSG dient als Einstiegspunkt in den Algorithmus. Über decompose() wird ein neuer Graph zur Dekomposition hinzugefügt, mittels matchGraphs() der Matchingprozess gestartet. Die ersten fehlerkorrigierenden Subgraphisomorphismen zwischen Graphen aus der Dekomposition, die einen Knoten enthalten, und dem Eingabegraphen werden durch vertexMatching() (siehe unten) ermittelt. Nach dem Matching können die fehlerkorrigierenden Subgraphisomor- 45 phismen mit getAllSubgraphIsomorphisms() erfragt werden. Das Attribut fastEval dient der Vereinfachung der späteren Evaluation: Wenn es wahr ist, wird lediglich ein einziger fehlerkorrigierender Subgraphisomorphismus gefunden. Auf das Finden weiterer fehlerkorrigierender Subgraphisomorphismen mit gleichen Kosten wird verzichtet. V, E, VL, EL NSG # decomposition : Decomposition<V, E, VL, EL> – threshold : int – costFunction : ICostFunction<V, E, VL, EL> – fastEval: boolean + NSG (G1 : Graph<V, E>, G2 : Graph<V, E>, edgeClass : Class<E>) + matchGraphs () : GraphMapping<V, E> + reset (dec : Decomposition<V, E, VL, EL>) : void # matchGraph(matchedGraph : Graph<V, E>, threshold : int, decomposition : Decomposition<V, E, VL, EL>) : void + decompose (decomposedGraph : Graph<V, E>, edgeClass : Class<E>, decomposition : Decomposition<V, E, VL, EL>) : Decomposition<V, E, VL, EL> + getAllSubgraphIsomorphisms() : List<SubgraphIsomorphism<V, E, VL, EL>> # vertexMatching (singleGraphVertex : V, matchedGraph : Graph<V, E>) : List<SubgraphIsomorphism<V, E, VL, EL>> Abbildung 4.9: Die Klasse NSG Die Klasse Decomposition ist die Datenstruktur, in der die Zerlegung der Graphen für das spätere Matching vorgehalten wird. Sie implementiert den rekursiven Zerlegungsalgorithmus in decomposeRec(). Für die in der Dekomposition vorkommenden 4-Tupel (G, S1 , S2 , E) gibt es die Klasse Combination. Die Klasse Decomposition stellt nach außen die Informationen über die Kombinationen zur Verfügung, die im Algorithmus 3.3 von den Listen open(S) und closed(S) benötigt werden. Beispielsweise kann die Kombination, die unter allen Kombinationen der Dekomposition den günstigsten Subgraphisomorphismus enthält, durch getCheapestOpen() erhalten werden. Betrachten wir nun, wie Algorithmus 3.2 zur Berechnung der Dekomposition in decomposeRec() umgesetzt wird. Wir bestimmen den größten Subgraph Smax (Zeile 1), indem wir den Matching-Algorithmus der Klasse NSG so aufrufen, dass dieser nur fehlerkorrigierende Subgraphisomorphismen findet, die keine Kosten besitzen, d.h. „echte“ Subgraphisomorphismen sind. Weil wir die Dekomposition während des rekursiven Aufrufs ändern, wird dieses Matching auf der Kopie der Dekomposition matchingCopy (vgl. D̄(B)) ausgeführt. Algorithmisch eher einfach ist das Erstellen des neuen 4-Tupels (G, Smax , G − Smax , E) (Zeile 12), wohingegen die Umsetzung in der Methode create- 46 NewCombination() mit einigem Aufwand verbunden ist, weil die Objekte der Knoten der Subgraphen Smax bzw. G − Smax nicht zwangsläufig in dem Objekt von G vorkommen. V, E, VL, EL Decomposition – decompositionGraph : ListenableDirectedGraph<Combination<V, E, VL, EL>, DefaultEdge> – decompGraphIndex : DirectedNeighborIndex<Combination<V, E, VL, EL>, DefaultEdge> – singleVertices : List<Combination<V, E, VL, EL>> – matchingCopy : Decomposition<V, E, VL, EL> + + + + + Decomposition() getNoCostAndMaximalFromClose() : Combination<V, E, VL, EL> getCheapestOpen () : Combination<V, E, VL, EL> resetCosts () : void decompose (decomposedGraph : Graph<V, E>, edgeClass : Class<E>, nsg : NSG<V, E, VL, EL>) : void # decomposeRec (decomposedGraph : Graph<V, E>, edgeClass : Class<E>, nsg : NSG<V, E, VL, EL>) : Combination<V, E, VL, EL> # createNewCombination (decomposedGraph : Graph<V, E>, edgeClass : Class<E>, halfVertexSet : Set<V>, otherHalfVertexSet : Set<V>, maxSubgraph : Combination<V, E, VL, EL>, modelMinusMaxSubgraph : Combination<V, E, VL, EL> isoFromMax : Map<V, V>, isoFromSubgraphMinusMax : Map<V, V>) : Combination<V, E, VL, EL> Abbildung 4.10: Die Klasse Decomposition Die Klasse Combination ist die Repräsentation der 4-Tupel aus der Dekomposition D(B). In ihr werden hauptsächlich Listen von fehlerkorrigierenden Subgraphisomorphismen, die in der Klasse SubgraphIsomorphism implementiert sind, verwaltet. Die Klasse stellt nach außen Methoden zur Verfügung, mit den Listen open und close zu interagieren und zum Beispiel die kostengünstigsten fehlerkorrigierenden Subgraphisomorphismen aus der Liste zu erhalten. 47 V, E, VL, EL Combination + G : Graph<V, E> + G1 : Combination<V, E, VL, EL> + G2 : Combination<V, E, VL, EL> + edges : Set<E> – isModelGraph : boolean – open : List<SubgraphIsomorphism<V, E, VL, EL>> – close : List<SubgraphIsomorphism<V, E, VL, EL>> + Combination(g : Graph<V, E>, g1 : Combination<V, E, VL, EL>, g2 : Combination<V, E, VL, EL>, edges : Set<E>, close : SubgraphIsomorphism<V, E, VL, EL>) + openEmpty() : boolean + clearOpen() : void + setOpen(openList : List<SubgraphIsomorphism<V, E, VL, EL>>) : void + getOpenCosts() : double + addToOpen(iso : SubgraphIsomorphism<V, E, VL, EL>) : void + addAllToOpen(isoList : List<SubgraphIsomorphism<V, E, VL, EL>>) : void + closeEmpty() : boolean + clearClose() : void + getCloseCosts() : double + peekFirstFromClose() : SubgraphIsomorphism<V, E, VL, EL> + peekFirstFromOpen() : SubgraphIsomorphism<V, E, VL, EL> + pollFirstFromOpen() : SubgraphIsomorphism<V, E, VL, EL> + combineWithAllFromClose(isomorphism1 : SubgraphIsomorphism<V, E, VL, EL>, successorCombi : Combination<V, E, VL, EL>, mappedGraph : Graph<V, E>) : List<SubgraphIsomorphism<V, E, VL, EL>> Abbildung 4.11: Die Klasse Combination Die Klasse SubgraphIsomorphism repräsentiert einen fehlerkorrigierenden Subgraphisomorphismus, weshalb sie die Kosten costs desselben und die Knoten- bzw. Kantenisomorphismen in vertexMap bzw. edgeMap speichert. Die Menge der Knotenzuordnungen vertexMap wird rekurisv aus den fehlerkorrigierenden Subgraphisomorphismen berechnet, aus denen der betrachtete fehlerkorrigierende Subgraphisomorphismus durch Kombination gebildet wurde. Zu diesem Zweck existieren predecessor1 und predecessor2. Hinzuzufügende Kanten sind in der Menge insertedEdges gespeichert. Kanten, die durch den fehlerkorrigierenden Subgraphisomorphismus gelöscht werden, werden nicht explizit gespeichert. Sie können aber gefunden werden, weil alle Kanten des dazugehörigen Graphen, die nicht durch die edgeMap abgebildet werden, als gelöscht angesehen werden können. Analoges gilt für hinzugefügte Knoten: Wenn ein Knoten hinzugefügt wurde, so wird ein Knoten, der nicht im ursprünglichen Graphen vorhanden ist, durch 48 die vertexMap abgebildet. Der Konstruktor der Klasse SubgraphIsomorphism ist der zentrale Bestandteil der Methode vertexMatching() der Klasse NSG, die Algorithmus 3.4 verwirklicht. Wir bilden die einzelnen Knoten aufeinander ab und erschaffen dafür je ein Objekt der Klasse SubgraphIsomorphism, das beschreibt, mit welcher Grapheditoperation und welchen Kosten diese Abbildung funktioniert. Die Kostenfunktion ist ein Attribut der Klasse Subgraphisomorphism. Sehr wichtig für die Speichereffizienz des Programms ist es, dass wir im Konstruktor auch die Heuristik der Kosten berechnen. Die hier eingesetzte Heuristik, die in 3.2.3 beschrieben ist, kennt nur die Werte Unendlich und Null: Daher werden Subgraphisomorphismen, die mit heuristischen Kosten von Unendlich verbunden sind, nicht in die Dekomposition aufgenommen, um Speicherplatz zu sparen. Außerdem wird Algorithmus 3.5 in der Klasse SubgraphIsomorphism realisiert. Um aus zwei fehlerkorrigierenden Subgraphisomorphismen einen neuen zu bilden, gibt es die Methode combine(). Diese überprüft, ob eine Kombination möglich ist, und wenn dies der Fall ist, mit welchen Kosten ein neuer fehlerkorrigierender Subgraphisomorphismus verbunden ist. Dazu wird intern eine Hilfsklasse EdgeMapping verwendet, die bei der Suche von Mappings zwischen zwei Kanten genutzt wird. 49 V, E, VL, EL SubgraphIsomorphism – – – – – – – – costFunction : ICostFunction<V, E, VL, EL> predecessor1 : SubgraphIsomorphism<V, E, VL, EL> predecessor2 : SubgraphIsomorphism<V, E, VL, EL> costs : double heuristic : double vertexMap : Map<V, V> edgeMap : Map<E, E> insertedEdges : Set<E> + SubgraphIsomorphism(mappingVertex : V, mappedVertex : V costFunction : ICostFunction<V, E, VL, EL>) + getHeuristicAndCosts() : double + combine(input1 : SubgraphIsomorphism<V, E, VL, EL>, input2 : SubgraphIsomorphism<V, E, VL, EL>, combination : Combination<V, E, VL, EL>, mappedGraph : Graph<V, E>) : SubgraphIsomorphism<V, E, VL, EL> # mapEdge(outputEdge : E, inputEdge : E, mutableEdges : Set<E>) : EdgeMapping<E> + compareTo(o : SubgraphIsomorphism<V, E, VL, EL>) : int E EdgeMapping + cost : double + map : Map.Entry<E, E> EdgeMapping(inputEdge : E, outputEdge : E, cost : double) Abbildung 4.12: Die Klasse SubgraphIsomorphism und die Hilfsklasse EdgeMapping Es gibt verschiedene Klassen, die eine Kostenfunktion darstellen. Die Klasse StaticCosts weist allen Grapheditoperationen jeweils ohne Betrachtung der Beschriftung statische Kosten zu. Die Klassen NumberCosts und StringCosts implementieren eine Kostenfunktion, die bei der Berechnung der Kosten für die Ersetzungsoperationen von Knoten bzw. Kanten die Beschriftungen der zu ersetzenden Knoten einbeziehen. Für zwei Knoten v1 , v2 mit relativer Distanz dv (diese ist auf Strings und Zahlen definiert, vgl. 4.2.1) und Threshold tv bzw. für zwei Kanten e1 , e2 mit relativer Distanz de und Threshold te gilt: C(v1 → v2 ) = dv · tv und C(e1 → e2 ) = de · te . Wir nennen diese zweite Kostenfunktion dynamisch. 50 V, E, VL, EL «interface» ICostFunction + getCosts(v1 : AbstractLabelVertex<VL>, v2 : AbstractLabelVertex<VL>) : double + getCosts(e1 : AbstractLabelEdge<EL>, e2 : AbstractLabelEdge<EL>) : double V → StringLabelVertex, E → StringLabelEdge, VL → String, «bind» EL → String V → NumberLabelVertex, E → NumberLabelEdge, VL → Double, «bind» EL → Double NumberCosts StringCosts – edgedel : double – edgeins : double – vertexdel : double – edgedel : double – edgeins : double – vertexdel : double + NumberCosts(edgedel : double, edgeins : double, vertexdel : double) + StringCosts(edgedel : double, edgeins : double, vertexdel : double) V, E, VL, EL StaticCosts – – – – – edgedel : double edgeins : double edgesub : double vertexdel : double vertexsub : double + StaticCosts(edgedel : double, edgeins : double, edgesub : double, vertexdel : double, vertexsub : double) Abbildung 4.13: Klassen der Kostenfunktion Der Algorithmus 3.3 zum Finden der fehlerkorrigierenden Subgraphisomorphismen wird wie bereits angedeutet in der Methode matchGraph() der Klasse NSG umgesetzt. Zu Anfang werden mittels vertexMatching() (Zeile 2) die ersten fehlerkorrigierenden Subgraphisomorphismen konstruiert. Anschließend wird aus der Decomposition jeweils die Combination gesucht, deren Kosten minimal verglichen mit allen anderen und nicht hö- 51 her als der vorgegebene Schwellwert sind (Zeile 3). Der dazugehörige SubgraphIsomorphism wird aus der open-Liste entfernt und in die close-Liste eingefügt. Weil die Dekomposition effektiv als Graph implementiert ist, finden wir die zu diesem fehlerkorrigierenden Subgraphisomorphismus kombinierbaren fehlerkorrigierenden Subgraphisomorphismen ohne Schwierigkeiten (Zeile 7). Zur Bildung der neuen fehlerkorrigierenden Subgraphisomorphismen setzen wir dann wie oben beschrieben die Methode combine() der Klasse SubgraphIsomorphism ein (Zeile 9). 4.5 Diskrete Relaxation Wir implementieren den Algorithmus zur diskreten Relaxation zum Großteil in der Klasse DiscreteRelax. Vor dem Matching müssen wir für jeden Knoten aus dem Graphen G1 ein Dictionary bestimmen. Anstatt die Dictionaries komplett zu bestimmen, wählen wir für jeden Knoten u ∈ G1 und seiner Super-Clique alle Knoten aus dem Graph G2 , deren Super-Cliquen in ihrer Größe um nicht mehr als die maximale Knotendifferenz von der Größe der ersten Super-Clique abweichen. Das Bestimmen der Dictionaries, d.h. also der Permutationen der äußeren Knoten, geschieht erst während der Ausführung der Relaxation. Durch die Methode pad() realisieren wir das Auffüllen der Super-Clique, die kleiner als die andere Super-Clique ist (Zeile 4 in Algorithmus 3.6). Mit permute() werden die äußeren Knoten der größeren Super-Clique permutiert, um alle Vergleiche der abgebildeten Super-Clique mit dem Dictionary-Eintrag zu erhalten (Zeile 5). Der Matchingprozess matchGraphs() setzt Algorithmus 3.7 um. Es wird damit begonnen, dass eine erste Knotenzuordnung für jeden Knoten mit initMatching() gefunden wird. Anschließend beginnt in relax() die eigentliche Relaxation. Die einzelnen Methoden übersetzen sich dann schrittweise in den Algorithmus: findMapping() findet für einen Knoten u ∈ V1 die beste Zuordnung v ∈ V2 und nutzt dabei testMatching(), um für eine konkrete Zuordnung die a-posteriori Wahrscheinlichkeit P (α1 (u), α2 (v) | f ) auszurechnen. Dazu wird ein Wert P (f ) benötigt, für dessen Berechnung wir die apriori Wahrscheinlichkeiten der einzelnen abgebildeten Super-Cliquen P (Γu ) berechnen. Das geschieht in matchedSuperCliqueProb(). Weil dazu über alle Einträge aus dem Dictionary iteriert wird, wird der Exponentialausdruck exp − ke H(Γu , Sv ) + kφ ψ(Γu , Sv ) für jeden Eintrag aus dem Dictionary einzeln in der Methode simplerConditionalProbMapping() berechnet. 52 V, E, VL, EL DiscreteRelax + NULLNODES : int + PE : double + PPHI : double + DELTA : double + KE : double + KPHI : double – directedG1NeighborIndex : DirectedNeighborIndex<V, E> – directedG2NeighborIndex : DirectedNeighborIndex<V, E> – undirectedG1NeighborIndex : NeighborIndex<V, E> – undirectedG2NeighborIndex : NeighborIndex<V, E> – dictionaries : Map<V, Collection<List<V>>> – probabilityFunc : IVertexProbabilities<V, E> + DiscreteRelax (G1 : Graph<V, E>, G2 : Graph<V, E>, edgeClass : Class<E>) + matchGraphs () : GraphMapping<V, E> # initMatching () : Map<V, V> # relax (initMatching : Map<V, V>) : Map<V, V> # findMapping (g1Node : V, matching : Map<V, V>) : V # testMatching (g1CentralNode : V, matching : Map<V, V>) : double # getSuperCliqueMatching (g1Node : V, matching : Map<V, V>) : List<V> # conditionalProb (g1Node : V, g2Node : V) : double # constructDictionary (g1Node : V, nullNodes : int) : Collection<List<V>> # matchedSuperCliqueProb (smaller : Collection<List<V>>, padded : List<V>) : double # simplerConditionalProbMapping (matchedSuperClique : List<V>, dictionaryEntry : List<V>) : double # permute (node : V, nodes : List<V>, nullNodes : int) : Collection<List<V>> # pad (permutation : List<V>, startIndex : int, nullnodes : int) : Collection<List<V>> Abbildung 4.14: Die Klasse DiscreteRelax Weil die bedingte Wahrscheinlichkeit für eine Knotenzuordnung als Parameter in Algorithmus 3.7 eingeht, wird für ihre Berechnung das Interface IVertexProbabilities genutzt, deren Implementierungen in Abbildung 4.15 dargestellt sind. Die Funktion P (u, v) = priorProb() wird sowohl für Strings als auch für Zahlen als gleichverteilt angenommen: 1 P (u, v) = . |V2 | 53 Für Strings als Knotenbeschriftungen wählen wir die Funktion P (u, v|x1u , x2v ) wie folgt: levsim (x1u , x2v ) 1 2 w∈V2 levsim (xu , xw ) P (u, v|x1u , x2v ) = P Die in 2.2 definierte Ähnlichkeit levsim von Knotenbeschriftungen wird durch diese Vorschrift mit allen anderen beobachteten Ähnlichkeiten in Beziehung gesetzt. Für Zahlen als Knotenbeschriftungen wählen wir die Funktion P (u, v|x1u , x2v ) dem Ansatz aus [WH97] folgend: 1 P (u, v|x1u , x2v ) =P 2 2 v) ) exp (−0.5 (xu −x σ2 1 w∈V2 2 2 w) exp (−0.5 (xu −x ) σ2 Dabei ist σ 2 die Varianz der Verteilung der zufällig beschrifteten Knoten. Weil die Knotenbeschriftung in der hier betrachteten Anwendung einer gleichverteilten Verteilung folgt, gilt σ 2 = (max − min)/12. Mit dieser Formel kann in der Klasse NumberProbabilities die Varianz jeder gleichverteilten Knotenbeschriftung durch Angabe von min und max berechnet werden. Die Idee der obigen Formel ist die Differenz der Knotenbeschriftungen als normalverteilt zu interpretieren und die betrachtete Differenz von x1u zu x2v zu allen anderen (a posteriori) beobachteten Differenzen von Knotenbeschriftungen in Beziehung zu setzen. V, E «interface» IVertexProbabilities + priorProb(v1 : V, v2 : V, g1 : Graph<V, E>, g2 : Graph<V, E>) : double + conditionalProb(v1 : V, v2 : V, g1 : Graph<V, E>, g2 : Graph<V, E>) : double V → NumberLabelVertex, E → NumberLabelEdge V → StringLabelVertex, E → StringLabelEdge «bind» «bind» NumberProbabilities StringProbabilities – min : double – max : double Abbildung 4.15: Klassen für die Berechnung der bedingten Wahrscheinlichkeit einer Knotenzuordnung 54 4.6 SMKernel VL, EL «interface» IKernel + vertexKernel(v1 : AbstractLabelVertex<VL>, v2 : AbstractLabelVertex<VL>) : double + edgeKernel(e1 : AbstractLabelEdge<EL>, e2 : AbstractLabelEdge<EL>) : double «bind» VL → Double EL → Double RBFKernelFunc VL, EL DiracKernel – gamma : double – CEdge : double – DEdge : double + RBFKernelFunc(gamma : double) – RBF(x : double, y : double) : double + useCEdges(use : boolean) : void + useDEdges(use : boolean) : void «bind» VL → String EL → String StringKernelFunc – kernel : StringKernel + StringKernelFunc(minSequence : int, maxSequence : int) – init(minSequence : int, maxSequence : int) : void – kernel(label1 : String, label2 : String) : double Abbildung 4.16: Verschiedene Kernel für Knoten und Kanten Wir implementieren verschiedene Kernel für Knoten und Kanten. Der einfachste Kernel ist in der Klasse DiracKernel implementiert (vgl. Abschnitt 2.4.1): Wenn die Beschriftungen der Knoten bzw. Kanten gleich sind (nicht nur ähnlich), so ist der Wert des Kernels 1, sonst 0. Durch die Funktionen useCEdges() bzw. useDEdges() kann die Evaluation von c- und d-Kanten beeinflusst werden. Falls die Graphen G1 , G2 , die den Produktgraph bilden, selbst nur sehr wenige Kanten besitzen, entsteht auch ein Produktgraph mit wenigen Kanten, wenn d-Kanten zu 0 evaluieren. Um eine Intuition dafür zu erhalten, betrachten wir zwei Knoten des Produktgraphen v1P , v2P ∈ GP , die aus den Knoten v11 , v21 ∈ G1 und v12 , v22 ∈ G2 „entstanden“ sind, d.h. αP (v1P ) = KV (v11 , v12 ) und αP (v2P ) = KV (v21 , v22 ). 55 / E2 , so wie / E1 und (v12 , v22 ) ∈ In diesem Fall existieren vielfach keine Kanten (v11 , v21 ) ∈ wir angenommen hatten. Im Produktgraph treten dann aber an dieser Stelle d-Kanten zwischen v1P und v2P auf. Wenn im gewählten Dirac-Kernel d-Kanten zu 0 evaluieren, so gibt es wenige Kanten im Produktgraph, was die Aufzählung der Cliquen beschleunigen kann. Die Klasse StringKernelFunc benutzt das Software-Paket Weka, um für zwei Strings den Subsequence String Kernel zu berechnen. Es fließen nur gemeinsame Substrings der Länge l ∈ [min, max] in die Berechnung dieses Kernels ein. Ein weiterer Kernel für reelle Zahlen ist der sogenannte Radial Basis Function Kernel, der in RBFKernelFunc implementiert ist. Er ist für einen Parameter γ ∈ [0, 1] zwischen zwei Zahlen x, y ∈ R definiert durch kRBF (x, y) = exp{−γ ||x − y||2 }. smkernel V, E, VL, EL V VertexPair 0...∗ 1 SMKernel Abbildung 4.17: Klassen im Package smkernel Die erste Aufgabe in der Implementierung des Algorithmus 3.8 für den Subgraph Matching Kernel ist die Erstellung des gewichteten Produktgraphs von zwei Graphen. Dessen Knoten sind Paare von Knoten, die während der Erstellung durch die Hilfsklasse VertexPair repräsentiert sind. In der Klasse SMKernel wird der Kernel V berechnet. Der Algorithmus untersucht im Wesentlichen rekursiv alle Cliquen des VertexPair Produktgraphs und wird in der Methode smKernel() umgesetzt, die genau der + v1 : V + v2 : V Funktion aus Zeile 8 in Algorithmus 3.8 entspricht. Durch die Attribute depth und + VertexPair(v1 : V, v2 : V) deepest wird der vorzeitige Abbruch der Rekursion umgesetzt, sobald eine maxiAbbildung 4.18: Die Klasse VertexPair male Größe der Clique des Produktgraphs erreicht wird (Zeile 9). Die Kernel k(G1 , G1 ) und k(G2 , G2 ) werden durch die Attribute xxKernel bzw. yyKernel berechnet; mit ihnen wird der Kernel k(G1 , G2 ) normalisiert. 56 V, E, VL, EL SMKernel – productGraph : Graph<NumberLabelVertex, NumberLabelEdge> – neighborIndex : NeighborIndex<NumberLabelVertex, NumberLabelEdge> – directNeighborIndex : DirectedNeighborIndex<NumberLabelVertex, NumberLabelEdge> – weight : double – kernelFunction : IKernel<VL, EL> – depth : int – deepest : int – xxKernel : SMKernel<V, E, VL, EL> – yyKernel : SMKernel<V, E, VL, EL> + SMKernel (G1 : Graph<V, E>, G2 : Graph<V, E>, edgeClass : Class<E>, deepest : int, normalize : boolean) + matchGraphs() : GraphMapping<V, E> # smKernel (localWeight : double, clique : Set<NumberLabelVertex>, candidates : Set<NumberLabelVertex>) : void # createProductGraph (g1 : Graph<V, E>, g2 : Graph<V, E>) : Graph<NumberLabelVertex, NumberLabelEdge> # getProductGraph() : Graph<NumberLabelVertex, NumberLabelEdge> # getWeight() : double – getNeighbors(vertex : NumberLabelVertex, prodGraph : Graph<NumberLabelVertex, NumberLabelEdge>) : Set<NumberLabelVertex> 57 Kapitel 5 Evaluation FIn diesem Abschnitt sollen die in den vorherigen Abschnitten vorgestellten Algorithmen miteinander verglichen werden. Zunächst betrachten wir die Leistungsfähigkeit der Algorithmen, wenn diese auf synthetischen Daten eingesetzt werden. In einem zweiten Schritt sollen die Algorithmen auf realen Daten aus einer Bibliotheksdatenbank eingesetzt werden mit dem Ziel, Duplikate in dieser zu finden. Während der zweite Versuch mit den realen Daten eher dazu dienen soll, die qualitativen Resultate der Algorithmen zu untersuchen, wird mit dem Versuch an den synthetischen Daten eher auf die Laufzeit der Algorithmen hin untersucht. Weil für die synthtetischen Daten allerdings bekannt ist, welche Subgraphisomorphismen gefunden werden können, werden in diesem Abschnitt auch Aussagen über ihre qualitative Leistungsstärke getroffen. Für die Versuche wird ein Rechner des Fachgebiets für Datenbanken und Informationssysteme benutzt. Dieser besitzt einen Intel Xeon E3-1276V3 4-Kern-Prozessor mit 3,6 GHz, 8 virtuellen Kernen und 32 GB Arbeitsspeicher. 5.1 Synthetische Graphen Für einen ersten Vergleich der Laufzeiten benutzen wir die synthetisch erzeugten attributierten Graphen aus [HBA13]. Es handelt sich um 180 Graphpaare (G1 , G2 ), die sich in ihrer Knotenanzahl und der Wahrscheinlichkeit p für eine gerichtete Kante zwischen zwei Knoten unterscheiden. Knoten und Kanten sind gemäß einer gleichförmigen Verteilung mit reellen Ziffern aus dem Bereich [−100, 100] beschriftet. Die Graphpaare werden erzeugt, indem zuerst ein Pattern-Graph G1 entsprechend vorgegebener Knotenzahl und Wahrscheinlichkeit p hergestellt wird und dieser zur Erzeugung vom sogenannten Target-Graph G2 um weitere Knoten und Kanten erweitert wird. Die dabei entstehenden Subgraphen G1 sind im Allgemeinen nicht knoten-induziert durch G2 . Folgende Para- 58 meter sind für die Größe von G1 , die Größe von G2 und die Wahrscheinlichkeit p einer Kante zwischen zwei Knoten möglich: 1. |V1 | ∈ {10, 25, 50} 2. |V2 | ∈ {50, 100, 250, 500} 3. p ∈ {0.01, 0.05, 0.1} Zu jeder Kombination existieren 5 Graphpaare. 5.1.1 VF2 Damit der Algorithmus VF2 auf den gegebenen Graphpaaren funktioniert, werden diese vor der Ausführung so verändert, dass der Graph G1 einen knoten-induzierten Subgraph von Graph G2 darstellt. Für die Ähnlichkeitsfunktion, die zur Auswertung von Regel R1 in Bezug auf Knoten v1 ∈ G1 , v2 ∈ G2 bzw. Kanten e1 ∈ G1 , e2 ∈ G2 genutzt wird, wählen wir den Schwellwert tv = te = 2, d.h. v1 ≈ v2 ⇔ |α1 (v1 ) − α2 (v2 )| ≤ tv = 2 e1 ≈ e2 ⇔ |β1 (e1 ) − β2 (e2 )| ≤ te = 2 Mit diesen Werten benötigt der Algorithmus pro Graph durchschnittlich 7.6 s bei einer Standardabweichung von 95 s. Bei Betrachtung der einzelnen Werte fällt auf, dass der Algorithmus nur bei einigen wenigen Graphen eine sehr viel längere Laufzeit aufweist als bei den anderen. Diese wenigen Graphen zeichnen sich alle durch eine geringe Kantenwahrscheinlichkeit (p = 0.01) aus. Zudem ist die Ordnung der Knoten, die der Algorithmus zur systematischen Untersuchnung benutzt, gerade so, dass die ersten Knoten von Graph G1 im richtigen Matching zu Knoten aus G2 zugeordnet werden müssen, die in der Ordnung sehr weit hinten stehen. Bevor also die ersten richtigen Zuordnungen feststehen, müssen viele weitere Kombinationen probiert werden. Diese Beobachtungen deuten darauf hin, dass sich für den Algorithmus bei Graphen mit wenigen Kanten im Vergleich zur Knotenanzahl ein sehr großer Suchraum ergibt, in dem die Information über die Richtigkeit eines einzelnen Knotenmatchings unter Umständen erst sehr viel später im weiteren Verlauf des Matchingsprozesses erkannt werden kann. Kantenwahrscheinlichkeit 0.01 0.05 0.1 „falsche“ Knotenzuordnungen für tv = 2 502 42 11 „falsche“ Knotenzuordnungen für tv = 1 360 33 8 Tabelle 5.1: „Falsche“ Knotenzuordnungen bei unterschiedlichen Schwellwerten tv und te = 2 59 Eine weitere Beobachtung ist, dass der Algorithmus mit dem obigen Schwellwert 2 viele Matchings ausgibt, in denen nicht der exakte Pattern-Graph wiedergefunden wurde, sondern eine Knotenzuordnung, die nach dem Kriterium der oben definierten Knotenund Kantenähnlichkeit als richtig anzusehen ist. In Tabelle 5.1 sehen wir, dass mit zunehmender Kantenwahrscheinlichkeit p weniger „falsche“ Knotenzuordnungen gefunden werden. Ein Grund dafür könnte sein, dass durch mehr Kanten zwischen den Knoten im Pattern-Graph Knotenzuordnungen ausgeschlossen werden können, weil diese nicht die notwendigen Nachbarknoten besitzen. Wenn wir aber den Schwellwert tv senken, so können wir auf diese Situation einwirken und von vornherein Knoten als verschieden ausschließen. Es sei hier angemerkt, dass der Algorithmus mit einem Schwellwert von tv = te = 0 anstatt 2 ausschließlich „richtige“ Matchings findet. Laufzeit [ms] 3 Pattern-Graph 50 Pattern-Graph 25 Pattern-Graph 10 2 1 0 100 200 300 400 500 Anzahl der Knoten des Target-Graphen Abbildung 5.1: Laufzeiten bei verschiedenen Größen von Pattern- und Target-Graph In Abbildung 5.1 werden nur die Graphen mit Kantenwahrscheinlichkeiten von p ∈ {0.05, 0.1} betrachtet, um die theoretische Laufzeitkomplexität zu untersuchen. Aus diesem Grund sind die Werte auch nicht mehr in der Nähe des oben angegebenen Mittelwertes von 7.6 s. Wir erkennen, dass sowohl mit zunehmender Größe des Target-Graphs als auch mit zunehmender Größe des Pattern-Graphs die Laufzeit ansteigt. Da aus der theoretischen Überlegung folgt, dass die Laufzeit bei zunehmender Graphgröße exponentiell ansteigt, ist eine exponentielle Trendlinie für den Pattern-Graph mit 50 Knoten eingezeichnet. 5.1.2 Fehlerkorrigierende Subgraphisomorphismen Die Menge B an Graphen als Eingabe für den Algorithmus besteht für jeden Durchlauf aus dem Pattern-Graph G2 . Dieser wird in einem ersten Schritt in die Dekomposition überführt, was in der Zeitmessung berücksichtigt ist. Zudem wird als Eingabe in den 60 Algorithmus eine Kostenfunktion der Grapheditoperationen und eine Heuristik, die Kosten schätzt, übergeben. In Versuchen stellt sich heraus, dass der Algorithmus wegen der Optimalität der gelieferten Ergebnisse sehr viel Hauptspeicher benötigt, um alle möglichen Grapheditieroperationen miteinander kombinieren zu können. Bei der Definition der Heuristik halten wir uns an die Beschreibungen aus 3.2.3 und definieren diese für einen fehlerkorrigierenden Subgraphisomorphismus S = (∆, S∆ ): h(S) = 0 falls für alle (v1 → v2 ) ∈ ∆ gilt: |α1 (v1 ) − α2 (v2 )| ≤ 1 ∞ sonst Das heißt, dass nur solche Knotenersetzungen überhaupt betrachtet werden sollen, bei denen die Beschriftungen nicht mehr als um Wert 1 verschieden sind. Weitere Versuche haben den Einfluss der Kostenfunktion gezeigt: Wenn wir alle Grapheditieroperationen mit dem gleichen Gewicht bewerten, können wir bei einzelnen Graphen nicht den kostengünstigsten fehlerkorrigierenden Subgraphisomorphismus finden, weil die Suche danach mehr als 28 GB Hauptspeicher benötigt. Aus diesem Grund wird hier eine statische Kostenfunktion gewählt, bei der wir die Operationen auf Kanten mit dem Wert 1 gewichten und Operationen auf Knoten (d.h. Umbenennung und Löschung) mit dem Wert 60 gewichten. Damit drücken wir also vereinfacht gesagt aus, dass in dieser Anwendung jede Grapheditierung der Kanten ausprobiert werden soll, während eine Grapheditierung der Knoten eher unwahrscheinlich ist. In die Kostenfunktion fließt damit das Wissen ein, das wir über die Anwendung haben: dass wir nämlich die Beschriftungen des Pattern-Graphs genau so im Target-Graphen wiederfinden. Laufzeit [ms] 104 Pattern-Graph 50 Pattern-Graph 25 Pattern-Graph 10 103 102 101 100 100 200 300 400 500 Anzahl der Knoten des Target-Graphen Abbildung 5.2: Laufzeiten bei verschiedenen Größen von Pattern- und Target-Graph Auch in Abbildung 5.2 erkennen wir den Unterschied zwischen verschiedenen Graphgrößen sowohl von Pattern- als auch von Target-Graph. Die exponentielle Trendlinie deutet die zunehmende Laufzeit an, die theoretisch zu erwarten ist. 61 Grapheditkosten als Ähnlichkeitsmaß Es stellt sich die Frage, ob es möglich ist, aus einer Menge von Graphen den Graph zu finden, der am ähnlichsten zu einem vorgegebenen ist. Als Ähnlichkeitsmaß bieten sich in diesem Zusammenhang die Grapheditkosten an. Wir geben also einen Pattern-Graph an und führen für jeden Target-Graph die Suche nach einem fehlerkorrigierenden Subgraphisomorphismus durch. Anschließend berechnen wir die relativen Grapheditkosten, d.h. wir dividieren die jeweils errechneten Kosten, die sich zwischen dem Target- und dem Pattern-Graph ergeben, durch die Kosten, die sich zwischen dem Pattern-Graph und dem „richtigen“ Target-Graph ergeben. Die relativen Grapheditkosten sind also größer als 100%, weil die Grapheditkosten zwischen Pattern-Graph und dem „richtigen“ Target-Graph minimal sind. Relative Grapheditkosten In Abbildung 5.3 ist das Ergebnis zu sehen, wenn wir die Durchschnittswerte der relativen Grapheditkosten für fünf Pattern-Graphen mit |V1 | = 10, |V2 | = 50, p = 0.1 berechnen. Es werden dynamische Kosten für Knoten und Kanten eingesetzt, die Heuristik ist die gleiche, die oben bereits beschrieben wurde. Mit zunehmender Größe der TargetGraphen werden die Kosten geringer. Dies ist damit zu begründen, dass mehr Knoten in den Target-Graphen vorkommen und damit die Wahrscheinlichkeit erhöht wird, dass deren Beschriftung betragsmäßig nahe an den durch den Pattern-Graph vorgegebenen Knoten liegt. Bei den Kantenwahrscheinlichkeiten verhält es sich andersherum: Durch eine hohe Kantenwahrscheinlichkeit exisitieren eher viele Kanten, die entfernt werden müssen, was die Kosten erhöht. p = 0.1 p = 0.05 p = 0.01 2.8 2.6 2.4 2.2 2 1.8 100 200 300 400 500 Anzahl der Knoten des Target-Graphen Abbildung 5.3: Relative Grapheditkosten bei verschiedenen Größen von Pattern- und Target-Graph Insgesamt können wir festhalten, dass sich die Grapheditkosten als ein Ähnlichkeitsmaß eignen, weil die durchschnittlichen Grapheditkosten 280% bis 180% über den günstigsten 62 Grapheditkosten für den (fast) exakten Subgraphisomorphismus liegen. Die Grapheditkosten werden im Mittel für ein Paar aus Pattern- und Target-Graph in rund 1, 5s berechnet. 5.1.3 Diskrete Relaxation Ein wesentlicher Teil der Berechnung des Graph Matchings bei der diskreten Relaxation ist die Berechnung der sogenannten Dictionaries Θu , d.h. der Permutationen von SuperCliquen Sv um Knoten v ∈ G2 , auf die eine Super-Clique Cu um den Knoten u ∈ G1 abgebildet werden kann. Wir nehmen in das Dictionary nur Super-Cliquen auf, die nicht mehr als maximal 5 Knoten verschieden groß sind: |Sv | − |Cu | ≤5 (Rel 1) Die maximale Schrittzahl I ist als Parameter des Algorithmus auf 14 gesetzt. Wie bereits in Abschnitt 3.3 erwähnt, wird der Parameter Pφ , der die Wahrscheinlichkeit für strukturelle Unterschiede beschreibt, vor allem aus formalen Gründen eingeführt. In dem hier vorgestellten Experiment zeigt sich, dass bei den von uns betrachteten Daten mit einem Wert von Pφ = 0.5 die meisten richtigen Knotenzuordnungen erfolgen. Im Folgenden sollen nur die Graphen mit Kantenwahrscheinlichkeit p = 0.01 betrachtet werden, weil die Bildung der Permutationen der in den Dictionaries enthaltenen SuperCliquen sehr zeitaufwendig ist. Für Knotenwahrscheinlichkeiten p ∈ {0.05, 0.1} müssen zum Beispiel Permutationen von 11 Knoten errechnet werden, was 11! ≈ 40 Millionen Mögichkeiten entspricht. Laufzeit [ms] 105 104 103 Pattern-Graph 50 Pattern-Graph 25 Pattern-Graph 10 2 10 100 200 300 400 500 Anzahl der Knoten des Target-Graphen Abbildung 5.4: Laufzeiten bei verschiedenen Größen von Pattern- und Target-Graph In Abbildung 5.4 erkennen wir das bereits bei den vorherigen Algorithmen beobachtete 63 Muster, dass die Laufzeit bei ansteigender Graphgröße wächst. Es ist eine exponentielle Trendlinie für die Pattern-Graphen der Größe 50 eingezeichnet. Wir können außerdem beobachten, dass der Algorithmus in den 25 Fällen nach zwei Schritten, in den verbleibenden 35 Fällen nach drei Schritten konvergiert. Richtige Knotenzuordnungen (korrigiert) Es stellt sich die Frage, ob durch die diskrete Relaxation die richtigen Subgraphisomorphismen gefunden werden. Wir stellen zunächst fest, dass die Einschränkung der Dictionaries in (Rel 1) dazu führt, dass gerade bei den Target-Graphen der Größe 50 die Super-Cliquen Sv um den Knoten v ∈ G2 , auf den Knoten u ∈ G1 richtigerweise abgebildet werden müssen, nicht in das Dictionary Θu aufgenommen werden. Wenn aber ein größerer Wert als 5 für die Differenz der Knotenanzahlen gewählt wird, wird die Berechnung der Permutationen zeitlich wieder „zu aufwendig“. 0.6 Pattern-Graph 50 Pattern-Graph 25 Pattern-Graph 10 0.5 0.4 0.3 0.2 0.1 100 200 300 400 500 Anzahl der Knoten des Target-Graphen Abbildung 5.5: Richtige Knotenzuordnungen bei verschiedenen Größen von Pattern- und Target-Graph In Abbildung 5.5 ist die Gesamtheit der Knotenzuordnung insofern korrigiert, als nur die Knotenzuordnungen berücksichtigt sind, die durch die Vorgabe aus (Rel 1) durch den Algorithmus überhaupt erkannt werden können. Mit zunehmender Größe des TargetGraphen werden immer weniger richtige Zuordnungen gefunden. Werden alle falschen Knotenzuordnungen betrachtet, die eigentlich auffindbar wären, so weicht die Zahl, mit der der gefundene Knoten v ∈ G2 bechriftet ist, von der Zahl, mit der der vorgegebene Knoten u ∈ G1 beschriftet ist, d.h. also |α1 (u) − α2 (v)|, im arithmetischen Mittel um 2.7 von dieser ab. Der Algorithmus findet also grundsätzlich nicht völlig abwegige Knotenzuordnungen, wenn nur die Bechriftung der Knoten betrachtet wird. Bei 50% der findbaren, aber falsch zugeordneten Knoten ist die Super-Clique des Knotens u ∈ G1 im Pattern-Graph von der Größe 0 und der zuzuordnende Knoten v ∈ G2 im Target-Graph besitzt eine Clique mit einer größeren Super-Clique. Dagegen besitzt 64 der zugeordnete Knoten v 0 ∈ G2 aus dem Target-Graph ebenfalls eine Super-Clique der Größe 0. Wir erkennen in diesen Fällen, dass der Algorithmus hier die relationale Information, dass die Super-Cliquen um u bzw. v für eine Zuordnung zu verschieden sind, nutzt und eine Zuordnung ausschließt. Stattdessen werden diese Knoten einem anderen Knoten v 0 zugeordnet. Höhere Kantenwahrscheinlichkeiten Weil bisher nur die Graphen mit Kantenwahrscheinlichkeit p = 0.01 betrachtet wurden, sollen die Experimente nun auf einige ausgewählte Kombinationen von Graphen mit höheren Kantenwahrscheinlichkeiten ausgeweitet, um das eben erläuterte Ergebnis näher zu untersuchen. Insbesondere soll überprüft werden, ob der Algorithmus bei mehr verfügbarer relationaler Information durch die höhere Kantenwahrscheinlichkeit mehr richtige Knotenzuordnungen findet. Dabei betrachten wir nur Kombinationen, in denen keine Super-Cliquen mit mehr als 10 Knoten berechnet werden müssen. Weil nun zu betrachtende Super-Cliquen größer sind, erhöht sich auch die Laufzeit des Algorithmus: Zum Beispiel dauert es bei p = 0.1 für Pattern-Graphen der Größe 10 und Target-Graphen der Größe 50 im Durchschnitt 941ms, wohingegen es bei p = 0.01 nur 30ms dauert. p = 0.1 Pattern-Graph 25 Pattern-Graph 10 Richtige Knotenzuordnungen (korrigiert) 0.6 0.5 p = 0.05 Pattern-Graph 25 Pattern-Graph 10 0.4 p = 0.01 Pattern-Graph 25 Pattern-Graph 10 0.3 0.2 40 50 60 70 80 90 100 110 Anzahl der Knoten des Target-Graphen Abbildung 5.6: Richtige Knotenzuordnungen bei verschiedenen Größen von Pattern- und Target-Graph für höhere Kantenwahrscheinlichkeiten In Abbildung 5.6 ist die relative Anzahl richtig zugeordneter Knoten für verschiedene Kantenwahrscheinlichkeiten zu sehen. Für den Target-Graphen der Größe 100 liegt das Ergebnis, das sich für die Graphen mit p = 0.01 und p = 0.05 bildet, in einem Bereich 65 von 30−40%. Bei dem Target-Graphen der Größe 50 ist eine höhere Streuung der relativ richtigen Knotenzuordnungen auszumachen. Auffällig ist zudem, dass für p = 0.05 bei einem Pattern-Graph der Größe 10 die Anzahl richtiger Knotenzuordnungen zunimmt. Wir können zusammenfassend aber festhalten, dass höhere Kantenwahrscheinlichkeiten eher keine Verbesserung der erzielten Ergebnisse ermöglichen. Auch bei den Graphen höherer Kantenwahrscheinlichkeit ist bei den auffindbaren Knotenzuordnungen auffällig, dass 50% der Knoten, die falsch zugeordnet werden, SuperCliquen der Größe 0 besitzen. Von den richtig zugeordneten Knotenzuordnungen besitzen nur 25% der Knoten Super-Cliquen der Größe 0. Eine Super-Clique der Größe 0 beeinträchtigt also den Algorithmus, weil keine relationale Information außer der, dass der Knoten keine Super-Clique besitzt, benutzt werden kann. 5.1.4 SMKernel Die Berechnung des Graph Kernels hängt wesentlich von den Kerneln für Knoten und Kanten ab. Insbesondere entstehen mit Kerneln auf Kanten, die hauptsächlich Werte > 0 liefern, Produktgraphen, die sehr dicht sind und folglich viele Cliquen enthalten. Deren Aufzählung dauert sehr lange, sodass wir uns hier auf den Dirac-Kernel für Kanten beschränken und diesen so modifizieren, dass d-Kanten zu 0 evaluieren. Auch für die Knoten benutzen wir einen Dirac-Kernel: 1 falls α1 (v1 ) = α2 (v2 ) KV = 0 sonst KE = 1 0 falls e1 ∈ E1 ∧ e2 ∈ E2 ∧ β1 (e1 ) = β2 (e2 ) sonst Wir berechnen den normalisierten Graph-Kernel ohne Einschränkung der maximalen Cliquen-Größe, d.h. k(G1 , G2 ) e k(G 1 , G2 ) = q k(G1 , G1 ) k(G2 , G2 ) 66 250 Laufzeit [ms] 200 Pattern-Graph 50 Pattern-Graph 25 Pattern-Graph 10 150 100 50 0 100 200 300 400 500 Anzahl der Knoten des Target-Graphen Abbildung 5.7: Laufzeiten bei verschiedenen Größen von Pattern- und Target-Graph In Abbildung 5.7 sehen wir, dass die Laufzeit für die Berechnung des Kernels mit zunehmender Größe des Target-Graphen steigt. Die Größe des Pattern-Graphen hingegen spielt für die Laufzeit eine untergeordnete Rolle. Die Beobachtung, dass die Laufzeit im Wesentlichen von der Größe des Target-Graphen abhängt, kann durch eine weitere Zahl bekräftigt werden: Insgesamt 99% der durchschnittlichen Laufzeit wird für die Berechnung des Kernels k(G2 , G2 ) verwendet. Die Berechnung dieses Kernels ist für große Graphen der entscheidende Faktor in der Zusammensetzung der Laufzeitkomponenten. Es stellt sich die Frage, wie aussagekräftig der berechnete Kernelwert ist. Dazu tragen wir für die verschiedenen Größen der Target-Graphen die Kernelwerte bei verschiedenen Größen der Pattern-Graphen in ein Diagramm ein. Wenn wir zunächst die Grapheditkosten in Abbildung 5.8 vernachlässigen, erkennen wir, dass mit zunehmender Größe der Target-Graphen bei allen Pattern-Graphen der Kernelwert abnimmt. Diese Daten deuten darauf hin, dass der Kernelwert kein guter Indikator für die Güte des gefundenen Subgraphisomorphismus ist. 67 120 0.8 e Kernel k(G 1 , G2 ) 100 80 0.6 60 0.4 40 20 0.2 0 0 durchschnittliche Graphedit-Kosten Grapheditkosten Pattern-Graph 50 Pattern-Graph 25 Pattern-Graph 10 Kernel Pattern-Graph 50 Pattern-Graph 25 Pattern-Graph 10 100 200 300 400 500 Anzahl der Knoten des Target-Graphen e Abbildung 5.8: Vergleich von Kernelwerten k(G 1 , G2 ) und Grapheditkosten Um die Hypothese zu überprüfen, dass aus den Kernelwerten keine Rückschlüsse gewonnen werden können, wie „gut“ ein Subgraphisomorphismus zwischen den Graphen ist, tragen wir außerdem die in Abschnitt 5.1.2 berechneten Grapheditkosten in das Diagramm ein. Während diese unabhängig von der Größe des Target-Graphen für je eine Größe des Pattern-Graphen annähernd konstant bleiben, nimmt der Kernel für zunehmende Größen ab. Der Kernelwert lässt sich nicht (zumindest nicht so intuitiv) wie die Grapheditkosten als Distanzmaß zwischen dem perfekten und dem gefundenen fehlerkorrigierten Subgraphisomorphismus interpretieren. Graph Kernel als Ähnlichkeitsmaß Wie schon bei den fehlerkorrigierenden Subgraphisomorphismen untersuchen wir nun, ob es möglich ist, aus einer Menge von Graphen den Graph zu finden, der am ähnlichsten zu einem vorgegebenen ist. Der Graph Kernel soll als Ähnlichkeitsmaß verwendet werden. Der normalisierte Graph Kernel wird für den ersten Pattern-Graph mit |V1 | = 10 und p = 0.1 als G1 und alle Target-Graphen jeweils als G2 berechnet. Als Kernel auf den Knoten benutzen wir einen RBF-Kernel mit γ = 0.5 (siehe dazu 4.6), auf den Kanten benutzen wir einen Dirac-Kernel. Wir beschränken die Cliquen-Größe nicht. Nach der 68 Berechnung dividieren wir den normalisierten Graph Kernel durch den normalisierten Graph Kernel, der sich zwischen Pattern- und „richtigem“ Target-Graphen ergibt. Wir sprechen vom relativen Graph Kernel, weil die sich daraus ergebenden Werte also kleiner als 100% sind, da der Graph Kernel mit diesem „richtigen“ Target-Graphen maximal ist. Relativer Graph Kernel 0.6 p = 0.1 p = 0.05 p = 0.01 0.55 0.5 0.45 0.4 100 200 300 400 500 Anzahl der Knoten des Target-Graphen Abbildung 5.9: Relative Graph Kernel bei verschiedenen Größen von Pattern- und Target-Graph In Abbildung 5.9 erkennen wir, dass sich die Werte des relativen Graph Kernels zwischen 40% und 60% bewegen. Das heißt, dass selbst sehr große Werte für den Graph Kernel nur etwa 60% der Ähnlichkeit anzeigen, die zwischen Pattern- und „richtigem“ TargetGraph erreicht wird. Ein systematicher Einfluss der Kantenwahrscheinlichkeit ist nicht zu erkennen. Allerdings kann davon gesprochen werden, dass größere Target-Graphen eine höhere Ähnlichkeit zu dem Pattern-Graphen besitzen als kleinere Target-Graphen. Von diesen Ergebnissen ausgehend könnte man den Graph Kernel für geeignet halten, den ähnlichsten Graph aus einer Menge zu bestimmen. Allerdings können die Ergebnisse im Gegensatz zu den fehlerkorrigierenden Subgraphisomorphismen nur unter einem sehr viel höheren Zeitaufwand bestimmt werden: Durchschnittlich dauert es 31 Minuten, den normalisierten Graph Kernel auszurechnen. Dabei wird 95% der Zeit dafür aufgewendet, den Produktgraphen zu erstellen. Das bedeutet, dass selbst ein schneller zu berechnender Graph Kernel keinen Vorteil bringt, wenn dieser immer noch den Produktgraph benötigt. 69 5.2 Reale Daten einer Bibliotheksdatenbank Für einen qualitativen Vergleich der Algorithmen verwenden wir die Daten der Bibliotheksdatenbank des Fachgebiets für Datenbanken und Informationssysteme. Diese beinhaltet die Daten zu mehr als 20.000 Büchern, Zeitschriftenartikeln und anderen wissenschaftlichen Arbeiten. Ein Auschnitt ihres Schemas ist in Listing 5.1 gezeigt. SCHRIFT (Schrift, Autor, Titel, Schrifttyp → SCHRIFTTYP) FLIT (Schrift, ISBN, Verlag, Auflage, Instorg, Ort, Erscheinungsjahr, Zeitschrreihe) SCHRIFTTYP (Schrifttyp, Typ) SCHLAGWORT (Schrift → SCHRIFT, Schlagwort) Listing 5.1: Ausschnitt aus dem Schema der Bibliotheksdatenbank Weil die Datenbank Duplikate enthält, d.h. Bücher, die mit verschiedenen Informationen eingetragen sind, aber das gleiche Buch in dem Bestand der Bibliothek bezeichnen, wollen wir mittels Graph Matching versuchen, diese zu identifizieren. Zu diesem Zweck werden die Daten der Datenbank als Graph modelliert und die Matching-Algorithmen mit dem Ziel angewendet, einen Subgraph, der ein Buch beschreibt, an anderer Stelle in dem Datenbankgraph wiederzufinden und damit mögliche Duplikate zu erkennen. Der Datenbankgraph wird erstellt, indem aus den einzelnen Schriften Graphen wie in Abbildung 5.10 gezeigt gebildet werden: Um den Titel werden die einzelnen Attribute als Knoten angelegt. In dem Beispiel sind manche Beschriftungen zur übersichtlicheren Darstellung mit drei Punkten abgekürzt. Diese Graphen nennen wir im Folgenden Büchergraphen. temporal DBS Barbara Studienarbeit Implementierung einer . . . Kuhn Barbara Implementierung einer . . . Institut für . . . Studienarbeit SQL+T Hannover Kuhn (a) Buch mit Inventarnummer 95 (b) Buch mit Inventarnummer 5934 Abbildung 5.10: Büchergraphen für Duplikate aus der Bibliotheksdatenbank Schließlich werden die einzelnen Graphen in einen gemeinsamen Graphen überführt, den wir im Folgenden als Datenbankgraph G2 bezeichnen. Wenn die einzelnen Graphen gemeinsame Knoten (d.h. Knoten, die das gleiche beschreiben, z.B. einen Verlag oder ein Erscheinungsjahr, und genau die gleiche Beschriftung tragen) besitzen, wird von diesen Attributknoten im Graph G2 jeweils nur ein einziger erstellt. Ausgenommen von dieser 70 Regel sind die Attributknoten, die den Titel der Schrift bezeichnen: Für jede Schrift wird ein Titelknoten erstellt. Im Beispiel besitzen die beiden Bücher 95 und 5934 den Namen der Autorin und den Typ „Studienarbeit“. Die Knoten der Titel sind daher im Datenbankgraph jeweils über eigene Kanten mit diesen Attributknoten verbunden. Ein Beispiel dazu ist in Abbildung 5.11 dargestellt. Implementierung einer . . . Barbara SQL+T Kuhn Studienarbeit Implementierung einer . . . Hannover temporal DBS Institut für . . . Abbildung 5.11: Der Datenbankgraph für die Bücher 95 und 5934 Für das Graph Matching wird jeweils ein Buchgraph G1 aus dem Datenbankgraph G2 entfernt. Attributknoten, die der Graph G1 mit anderen Graphen gemeinsam hat, werden nicht entfernt. Für den konkreten Versuch werden einige zufällig ausgewählte Einträge aus der Datenbank und die oben gezeigten Bücher mit den Primärschlüsseln 95 und 5934 zu einem Datenbankgraph zusammengestellt. 5.2.1 VF2 Der Algorithmus VF2 wird mit dem Schwellert 0.8 für die Levenshtein-Distanz für Knoten- und Kantenbeschriftungen eingesetzt. Anders als für die anderen Algorithmen können wir in den hier beschriebenen Versuchen nach einem Buchgraph G1 in dem gesamten aus 20.000 Büchern bestehenden Datenbankgraph G2 suchen. Die Suche nach jedem Buch mittels des oben beschrieben Vorgehens erfolgt in insgesamt 100 Minuten. Bei der Suche werden 1583 Buchgraphen gefunden, die sich auf andere Subgraphen des Datenbankgraphs abbilden lassen. Wir schildern im Folgenden einige Beobachtungen zu den gefundenen Subgraphisomorphismen, die anhand von Stichproben gemacht werden. Zunächst gibt es einige Abschluss- und Studienarbeiten, die am Fachgebiet für Datenbanken und Informationssysteme verfasst wurden, die als Duplikate in der Datenbank vorhanden sind (siehe dazu das Beispiel in 5.10). Die Arbeiten sind zweifach eingetragen: Eine Eintragung ist nur in der Relation SCHRIFT vorgenommen, die andere Eintragung ist zusätzlich noch in der Relation FLIT verzeichnet. Insofern bildet ein Buchgraph einen echten Teilgraph des anderen Buchgraphs und dieser ist auch als Subgraph im Datenbankgraph auffindbar. Allerdings gelingt es nicht, einen Subgraphisomorphismus von dem zweiten umfangreicheren Buchgraph zum Datenbankgraph zu identifizieren. 71 Die zweite und wohl auch umfangreichste Gruppe von gefundenen Subgraphisomorphismen besteht aus Einträgen in die Datenbank, bei denen beide Bücher exakt gleiche Attribute (abgesehen vom Primärschlüssel Schrift) besitzen. Es könnte sich um mehrfach angeschaffte Bücher handeln, weshalb in diesen Fällen weitere Attribute manuell verglichen werden wie etwa die DokNr oder das Erfdatum (Erfassungsdatum), die in dieser Modellierung vernachlässigt werden. Bei den Büchern handelt es sich laut Dokumentennummer um unterschiedliche Bücher. Bei einigen wenigen eventuellen Duplikaten ist aber auffällig, dass die Erfassungsdaten auseinanderfallen, sodass es sich zum Beispiel um Nachbestellungen oder um doppelte Erfassungen handeln könnte. An dieser Stelle ist also Expertenwissen über die Bibliotheksdatenbank notwendig, um beurteilen zu können, ob die Bücher Mehrfachanschaffungen oder tatsächlich Duplikate sind. Die dritte Gruppe, die wir identifizieren, sind Bücher, die Attribute besitzen, deren Namen nicht exakt übereinstimmen. Hierunter fallen vor allem Bücher, die aus mehreren Teilen bestehen, wie etwa „The Art of Computer Programming“ von Donald Knuth. Der Algorithmus findet hier wegen der geringen Levenshtein-Distanz zwischen „Vol. 1“ und „Vol. 2“ fälschlicherweise einen Subgraphisomorphismus. Richtiger Attributname Das ist Informatik Concepts in Programming Languages A Dynamic Framework for Object Projection Views OOPSLA + ECOOP ’90 . . . Universität Antwerpen Falscher Attributname Was ist Informatik? Concepts of Programming Languages A dynamic f ramework for object projection views OOPSLA ’90/ECOOP ’90 . . . Universiteit Antwerpen Tabelle 5.2: Ähnliche Knotenbeschriftungen in erkannten Subgraphisomorphismen In Tabelle 5.2 sind gefundene Knotenbeschriftungen aufgelistet, die aus durch den Algorithmus erkannten Subgraphisomorphismen stammen. Alle diese Bücher könnten Mehrfachanschaffungen sein, die ein Mal falsch erfasst wurden. Es könnten aber auch Duplikate sein, wenn sie zum Beispiel zuerst falsch erfasst wurden, später festgestellt wurde, dass sie noch nicht in der Datenbank enthalten sind, weil die Suche nach dem Titel ohne Resultat blieb, und sie schließlich erneut erfasst wurden. Die weitere Analyse der Ergebnisse des Algorithmus VF2 muss hier durch einen Experten erfolgen. 5.2.2 Fehlerkorrigierende Subgraphisomorphismen Wie bereits gesehen ist für die Konstruktion der fehlerkorrigierenden Subgraphisomorphismen von besonders großer Bedeutung, dass mit einer „guten“ Heuristik der Suchraum stark eingeschränkt wird, sodass dessen Durchsuchen nicht zu viel Speicherplatz benötigt. Aus diesem Grund modifizieren wir die Heuristik für einen fehlerkorrigierenden 72 Subgraphisomorphismus S = (∆, S∆ ): 0 levsim (α1 (v1 ), α2 (v2 )) ≤ 0.8, falls |α (v )| ≥ 4 und |α (v 1 1 2 falls für alle (v1 → v2 ) ∈ ∆ gilt: h(S) = α (v ) = α2 (v2 ), 1 1 sonst ∞ sonst 2 )| ≥4 Mit dieser Modifikation stellen wir sicher, dass kurze Strings (kürzer als 5 Zeichen) als Knotenbeschriftungen nicht nach dem Kriterium der Levenshtein-Distanz bzw. Ähnlichkeit beurteilt werden. In den gefundenen fehlerkorrigierenden Subgraphisomorphismen können sie nur auf identische Strings abgebildet werden. Insbesondere verhindern wir so, dass sehr viele Ersetzungen von Knotenbeschriftungen für Jahreszahlen oder Initialen von Autoren vorgenommen werden. Durch die im weiteren Verlauf entstehenden Kombinationsmöglichkeiten würde der Algorithmus zu viele fehlerkorrigierende Subgraphisomorphismen produzieren. Weil wir in der Modellierung Initialen von verschiedenen Autoren unterscheiden, auch wenn diese gleich lauten, ergibt sich ein weiteres Problem: Der Vornamenbuchstabe beispielsweise „M.“ kann in dem von uns konstruierten Datenbankgraph aus 1000 Buchgraphen 40 verschiedenen Autoren zugeordnet werden. Wenn alle möglichen Ersetzungen ausprobiert würden, so würde ein zu großer Suchraum entstehen. Dieses Problem wird gelöst, indem die ansonsten verwendete Modellierung geändert wird und die Attributknoten für Vor- und Nachname der Autoren zu einem Attributknoten zusammengefasst werden. Kuhn, Barbara 95 Kuhn, Barbara Studienarbeit temporal DBS Studienarbeit 5934 SQL+T Implementierung einer . . . temporal DBS (a) Buch mit Inventarnummer 5934 SQL+T Hannover Institut für Informationssysteme . . . Hannover Institut für Informatik . . . Implementierung einer . . . (b) Fehlerkorrigierender Subgraphisomorphismus auf Buch mit Inventarnummer 95 Abbildung 5.12: Ein fehlerkorrigierender Subgraphisomorphismus Der Versuch wird auf einem Datenbankgraph von 1000 Buchgraphen durchgeführt. In Abbildung 5.12 ist der fehlerkorrigierende Subgraphisomorphismus von Buch 5934 auf den Datenbankgraph dargestellt. Es ist zu erkennen, dass der Knoten „SQL+T“ gelöscht 73 wird und der Knoten „Institut für Informatik“ ersetzt wird. Weil der Graph um das Buch 95 nur Kanten zu zwei Knoten enthält, werden einige Kanten eingefügt: Diese sind gestrichelt dargestellt. Wir können ersehen, dass der Algorithmus plausible Ergebnisse liefert. Im Gegensatz zum Algorithmus VF2 ist es hier aber nicht so einfach, Duplikate auszuschließen: Der Algorithmus findet immer einen fehlerkorrigierenden Subgraphisomorphismus. Die Kosten sind zwar intuitiv zu interpretieren, aber ohne Normierung kann keine direkte Erkenntnis gewonnen werden, ob ein Duplikat vorliegt. 5.2.3 Diskrete Relaxation Wie bereits in 5.1.3 ist auch in dieser Anwendung die Bildung der Permutationen der Super-Cliquen problematisch, weil sie sehr zeitaufwendig ist. Zunächst werden aus den 20000 Buchgraphen all diejenigen herausgesucht, die aus höchstens 7 Knoten bestehen, um Permutationen mit höchstens 6 Knoten berechnen zu müssen. Es verbleiben 4620 Graphen, die in einen Datenbankgraph überführt werden. Die Zuordnung der Attributknoten, die um den mittleren Knoten gelegen sind, wird im Wesentlichen über die Wahrscheinlichkeit P (u, v|x1u , x2v ) bestimmt, weil sie keine Super-Clique mit anderen Knoten besitzen. Das heißt, sie werden dem Knoten mit der geringsten Levenshtein-Distanz zugeordnet. Diese Überlegung bestätigt sich im praktischen Versuch. Für den mittleren Knoten findet eine Relaxation statt. Auch wenn auf das Auffüllen mit „dummy“ Knoten verzichtet wird, dauert die Relaxation für einen Buchgraphen im Durchschnitt etwa 5 Minuten. Wir können beobachten, dass auch diesem Knoten ein Knoten mit besonders geringer Levenshtein-Distanz zugeordnet wird. Insgesamt können wir festhalten, dass die hier vorgenommene Modellierung für die Evaluation der diskreten Relaxation ungeeignet ist. Wir können in diesem Versuch nicht den Einfluss der relationalen Information für die Relaxation beobachten. 5.2.4 SMKernel Die Normalisierung des Kernels k(G1 , G2 ) stellt ein großes Problem bei der Berechnung dar. Selbst bei der Wahl sehr restriktiver Kernelfunktionen für Knoten und Kanten auf einem Datenbankgraph G2 für 52 Buchgraphen kann der Kernel k(G2 , G2 ), der für die Normalisierung benötigt wird, nicht ohne erheblichen Zeitaufwand berechnet werden. Wir wählen für den Kernel auf den Knoten einen Substring Sequence Kernel mit der minimalen Substringlänge 5 und der maximalen Substringlänge 9, um zu verhindern, dass kurze gemeinsame Substrings wie zum Beispiel gemeinsame Jahreszahlen im Produktgraph einen Knoten bilden. Weiter wählen wir für den Kernel auf den Kanten einen Dirac-Kernel, bei dem die d-Kanten nicht ausgewertet werden (zur Begründung siehe 4.6). Wir setzen die maximale Cliquengröße auf 5 fest. 74 Für die Versuche bilden wir einen Datenbankgraph aus 50 zufällig ausgewählten Buchgraphen und den beiden Büchern 95 und 5934. Der entstehende Produktgraph besitzt ungefähr 35.000 Knoten und 18.000 Kanten. Die Berechnung des für die Normalisierung benötigten Wertes k(G2 , G2 ) dauert im Durchschnitt ungefähr 5 Minuten. normalisierter Kernel 0.3 Buchgraphen Buch 5934 Buch 95 0.25 0.2 0.15 0 10 20 30 40 50 Nach Kernelwert sortierte Buchgraphen Abbildung 5.13: Die normalisierten Kernelwerte für verschiedene Buchgraphen In Abbildung 5.13 erkennen wir, dass fast alle Kernelwerte nahe beieinander in einem Berich von 15% − 30% liegen. Es zeigt sich, dass das Buch 95, für das der Algorithmus VF2 einen exakten Subgraphisomorphismus findet, hier nicht an erster Stelle liegt. Stattdessen ist für das Buch 5934 der Kernelwert der größte. Dies kann daran liegen, dass von einer Teilmenge der Knoten von Buch 5934 zu allen Knoten des Buchs 95 ein Isomorphismus existiert. Darüber hinaus besitzt das Buch 5934 viele Attribute, die zumindest als Substring als irgendein Attributwert auch in anderen Buchgraphen auftreten. Für das Buch 95 hingegen besteht ein Subgraphisomorphismus zu dem Buch 5934, zu anderen Buchgraphen werden allerdings keine Isomorphismen aus mehreren Knoten mit hohen Kernelwerten gefunden. Aus diesem Grund ist der Kernelwert insgesamt „im Mittelfeld“. Der zweitgrößte Wert gehört zu einem technischen Report mit dem Titel „Specification and Prototyping of a Compiler for a small Applicative Language“. Der Buchgraph ist kein Subgraph zu einem anderen Buchgraphen. Wir können nur vermuten, warum er einen vergleichsweise hohen Kernelwert erhalten hat. Zum einen besitzt er einen sehr langen Titel mit einigen Schlüsselwörtern, die auch zumindest als Teilstring in anderen Titeln zu finden sind. Dies führt dazu, dass solche anderen Titel mit diesem einen gemeinsamen Knoten im Produktgraph bilden. Zudem besitzt er Attribute, die Substrings wie „Informatik“ oder „Universität“ besitzen, die sich auch in vielen anderen Buchgraphen wiederfinden. Dies führt dazu, dass ein relativ zu den anderen Buchgraphen hoher Wert k(G1 , G2 ) erzeugt wird. 75 Der Buchgraph mit dem geringsten Kernelwert besitzt das Titelattribut „Proc. of the 1994 CAS Conference, Toronto, Canada“. Die Beschriftungen der Attributknoten im Datenbankgraph bieten kaum Übereinstimmungen zu diesem. Außerdem besitzt der Buchgraph lediglich zwei weitere Attribute, sodass auch in der Summation kein größerer Kernelwert entsteht. 76 Kapitel 6 Fazit & Ausblick In dieser Arbeit wurden vier Algorithmen zum Graph Matching auf attributierten Graphen vorgestellt, implementiert und evaluiert. Die vier Algorithmen decken ein breites Spektrum der in diesem Bereich entwickelten Algorithmen ab. Die Ergebnisse sollen nun zusammengefasst werden und ein Vergleich der Algorithmen trotz ihrer Unterschiedlichkeit formuliert werden. Der Algorithmus VF2 ist als Vertreter des exakten Graph Matchings sehr schnell und auch die Anforderungen an die Speicherkapazität sind sehr gering: Einige Arrays genügen, um die erforderlichen Informationen zu repräsentieren. Falls wenig strukturelle Information vorhanden ist und die Schwellwerte für die Vergleiche von Knoten- und Kantenbeschriftungen nicht „eng genug“ gewählt sind, kann es zu falschen Knotenzuordnungen kommen. Außerdem sind bei Graphen mit wenigen Kanten die vorausschauenden Regeln wenig effektiv und der Suchraum kann nicht wesentlich eingeschränkt werden. Der Algorithmus VF2 war der einzige, der aus einem Datenbankgraph, der alle Bücher der Bibliotheksdatenbank umfasst, Subgraphisomorphismen finden konnte. Sein Einsatz scheint insbesondere dann geeignet, wenn von den zu matchenden Buchgraphen bekannt ist, dass sie die gleiche Struktur besitzen, die Beschriftungen der Knoten und Kanten aber voneinander abweichen können. Beim Algorithmus der fehlerkorrigierenden Subgraphisomorphismen zeigte sich, dass die Optimalität, mit der der Algorithmus einen fehlerkorrigierenden Subgraphisomorphismus findet, zu langen Laufzeiten bzw. einem hohen Speicherbedarf führt. Dieser Laufzeit muss in einer Anwendung durch den Einsatz einer Heuristik begegnet werden, die den Suchraum geeignet einschränkt. Die Kosten, die sich ergeben, sind direkt als Distanzmaß zwischen zwei Graphen zu interpretieren. Wenn der ähnlichste Graph aus einer Menge von Graphen zu einem vorgegebenen Graphen gefunden werden soll, sind die geringsten Kosten ein eindeutiges Anzeichen für eine große Ähnlichkeit der Graphen. Um den Algorithmus zu verbessern, könnte bei der Bildung der Dekomposition angesetzt werden: Wenn stark zusammenhängende Komponenten bei der Zerlegung eines hinzu- 77 zufügenden Knotens möglichst spät getrennt werden, ergibt sich für das Matching der umgekehrte Effekt, dass die dazu gebildeten fehlerkorrigierenden Subgraphisomorphismen sehr früh im Matching-Prozess miteinander kombiniert werden. Dadurch wird die Kombination von vermeintlich „günstigen“ Fehlerkorrekturen vermieden, deren Kosten sonst erst in späteren Stadien erkennbar wären. Die diskrete Relaxation hängt sehr stark von der Größe der Super-Cliquen ab: Das Bilden der Permutation dauert bei vielen Vorgängerknoten sehr lange und ist für Anwendungen, in denen schnelle Antworten benötigt werden, also nicht verwendbar. Die Struktur der Modellierung der Bibliotheksdatenbank ist nicht geeignet, um sinnvolle Aussagen über die Leistungsfähigkeit des Algorithmus zu treffen. Bei den synthetischen Algorithmen zeigt sich, dass die Größenunterschiede der abgebildeten Super-Clique zu den Einträgen des Dictionaries dafür sorgen, dass falsche Knotenzuordnung gewählt werden. Wir vermuten, dass sich die diskrete Relaxation eher dazu eignet, sehr schnell zwischen zwei ähnlich großen Graphen, die richtige Knotenzuordnung zu finden, denn gerade bei den kleineren synthetischen Target-Graphen wurden viele richtige Knotenzuordnungen identifiziert. Es könnte also weiterführend überprüft werden, ob sich für strukturell verschiedene, aber ähnlich große Graphen ein Geschwindigkeitsvorteil der diskreten Relaxation gegenüber der Suche nach fehlerkorrigierenden Subgraphisomorphismen ergibt. Für die Berechnung des Graph Kernels zeigt sich, dass diese in ihrer Laufzeit sehr stark von der Größe des größeren der beiden Graphen abhängt. Anhand der synthetischen Graphen wird deutlich, dass die Werte des normalisierten Kernels abnehmen, je mehr sich die beiden Graphen in ihrer Größe unterscheiden, obwohl ein (quasi) exakter Subgraphisomorphismus zwischen den Graphen existiert. In der Anwendung, aus einer Menge von Graphen denjenigen aus der Menge mit der größten Ähnlichkeit zu einem anderen Graph zu finden, sind die Werte des Kernels zwar prinzipiell geeignet. Wegen der langwierigen Erstellung des Produktgraphs dauert die Ausführung aber insgesamt sehr viel länger als die Berechnung des fehlerkorrigierenden Subgraphisomorphismus. Insbesondere anhand der Bibliotheksdatenbank können wir erkennen, dass nicht ein Subgraphisomorphismus zu hohen Kernelwerten führt, sondern viele kleine Gemeinsamkeiten der verglichenen Graphen. Die Ausdrucksstärke des Graph Kernels können wir also insofern bestätigen, als er sehr viele übereinstimmende Details zwischen den beiden Graphen anzeigt. Zusammenfassend scheint der Algorithmus VF2 das Mittel der Wahl für strukturell übereinstimmende Graphen zu sein. Der Algorithmus der fehlerkorrigierenden Subgraphisomorphismen muss für jede Anwendung in seinen Parametern sehr genau angepasst werden, ist dann aber ein geeigneter Kandidat, auch strukturelle Unterschiede zu überbrücken. Die diskrete Relaxation scheint eher geeignet, in Szenarien eingesetzt zu werden, wenn zwei Graphen ähnlich groß sind. In seiner hier vorgestellten Form ist die Berechnung des Graph Kernels nicht geeignet, bei der Suche nach Ähnlichkeit zwischen Graphen eingesetzt zu werden. 78 Abbildungsverzeichnis 3.1 Die Regeln (R2) und (R3) . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.2 Die Regeln (R4) und (R5) . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.3 Die Regel (R6) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.4 Dekomposition von zwei Graphen . . . . . . . . . . . . . . . . . . . . . . 23 3.5 Beispiele von Super-Cliquen und Dictionaries . . . . . . . . . . . . . . . . 31 3.6 Zwei Graphen 3.6a, 3.6b und ihr Produktgraph 3.6c . . . . . . . . . . . . 35 4.1 Wichtige Interfaces und Klassen in JGraphT . . . . . . . . . . . . . . . . 38 4.2 Klassen, die Knoten darstellen . . . . . . . . . . . . . . . . . . . . . . . . 39 4.3 Klassen, die Kanten darstellen . . . . . . . . . . . . . . . . . . . . . . . . 40 4.4 Die Klasse GraphMatcher . . . . . . . . . . . . . . . . . . . . . . . . . . 41 4.5 Klassen im Package vf2 . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 4.7 Die Klasse VF2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 4.8 Klassen im Package nsg . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 4.9 Die Klasse NSG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 4.10 Die Klasse Decomposition . . . . . . . . . . . . . . . . . . . . . . . . . . 47 4.11 Die Klasse Combination . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 4.12 Die Klasse SubgraphIsomorphism und die Hilfsklasse EdgeMapping . . . 50 4.13 Klassen der Kostenfunktion . . . . . . . . . . . . . . . . . . . . . . . . . 51 4.14 Die Klasse DiscreteRelax . . . . . . . . . . . . . . . . . . . . . . . . . . 53 4.15 Klassen für die Berechnung der bedingten Wahrscheinlichkeit einer Knotenzuordnung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 4.16 Verschiedene Kernel für Knoten und Kanten . . . . . . . . . . . . . . . . 55 4.17 Klassen im Package smkernel . . . . . . . . . . . . . . . . . . . . . . . . 56 4.18 Die Klasse VertexPair . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 79 5.1 Laufzeiten bei verschiedenen Größen von Pattern- und Target-Graph . . 60 5.2 Laufzeiten bei verschiedenen Größen von Pattern- und Target-Graph . . 61 5.3 Relative Grapheditkosten bei verschiedenen Größen von Pattern- und Target-Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 5.4 Laufzeiten bei verschiedenen Größen von Pattern- und Target-Graph . . 63 5.5 Richtige Knotenzuordnungen bei verschiedenen Größen von Pattern- und Target-Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Richtige Knotenzuordnungen bei verschiedenen Größen von Pattern- und Target-Graph für höhere Kantenwahrscheinlichkeiten . . . . . . . . . . . 65 5.7 Laufzeiten bei verschiedenen Größen von Pattern- und Target-Graph . . 67 5.8 e Vergleich von Kernelwerten k(G 1 , G2 ) und Grapheditkosten . . . . . . . . 68 5.9 Relative Graph Kernel bei verschiedenen Größen von Pattern- und TargetGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 5.10 Büchergraphen für Duplikate aus der Bibliotheksdatenbank . . . . . . . . 70 5.11 Der Datenbankgraph für die Bücher 95 und 5934 . . . . . . . . . . . . . 71 5.12 Ein fehlerkorrigierender Subgraphisomorphismus . . . . . . . . . . . . . . 73 5.13 Die normalisierten Kernelwerte für verschiedene Buchgraphen . . . . . . 75 5.6 80 Tabellenverzeichnis 5.1 5.2 „Falsche“ Knotenzuordnungen bei unterschiedlichen Schwellwerten tv und te = 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 Ähnliche Knotenbeschriftungen in erkannten Subgraphisomorphismen . . 72 81 Literaturverzeichnis [Bor07] K. M. Borgwardt. Graph Kernels. In D. Wagner, Hg., Ausgezeichnete Informatikdissertationen 2007, Bd. D-8 von LNI. GI, 2007. ISBN 978-3-88579412-7, 41–50. [BOS+ 05] K. M. Borgwardt, C. S. Ong, S. Schönauer, S. V. N. Vishwanathan, A. J. Smola, H. Kriegel. Protein function prediction via graph kernels. In Proceedings Thirteenth International Conference on Intelligent Systems for Molecular Biology 2005, Detroit, MI, USA, 25-29 June 2005. 2005, 47–56. URL http://dx.doi.org/10.1093/bioinformatics/bti1007. [CFSV04a] D. Conte, P. Foggia, C. Sansone, M. Vento. Thirty Years Of Graph Matching In Pattern Recognition. IJPRAI, 18(3), 2004, 265–298. URL http://dx. doi.org/10.1142/S0218001404003228. [CFSV04b] L. P. Cordella, P. Foggia, C. Sansone, M. Vento. A (Sub)Graph Isomorphism Algorithm for Matching Large Graphs. IEEE Trans. Pattern Anal. Mach. Intell., 26(10), 2004, 1367–1372. URL http://doi.ieeecomputersociety. org/10.1109/TPAMI.2004.75. [CHM04] C. Cortes, P. Haffner, M. Mohri. Rational Kernels: Theory and Algorithms. Journal of Machine Learning Research, 5, 2004, 1035– 1062. URL http://www.ai.mit.edu/projects/jmlr/papers/volume5/ cortes04a/cortes04a.pdf. [FPV14] P. Foggia, G. Percannella, M. Vento. Graph Matching and Learning in Pattern Recognition in the Last 10 Years. IJPRAI, 28(1), 2014. URL http://dx.doi.org/10.1142/S0218001414500013. [GJ79] M. R. Garey, D. S. Johnson. Computers and Intractability: A Guide to the Theory of NP-Completeness. W. H. Freeman, 1979. ISBN 0-7167-1044-7. [HBA13] P. Héroux, P. L. Bodic, S. Adam. Datasets for the Evaluation of Substitution-Tolerant Subgraph Isomorphism. In B. Lamiroy, J. Ogier, Hg., Graphics Recognition. Current Trends and Challenges - 10th International Workshop, GREC 2013, Bethlehem, PA, USA, August 20-21, 2013, Revised 82 Selected Papers, Bd. 8746 von Lecture Notes in Computer Science. Springer, 2013. ISBN 978-3-662-44853-3, 240–251. URL http://dx.doi.org/ 10.1007/978-3-662-44854-0_19. [KM12] N. Kriege, P. Mutzel. Subgraph Matching Kernels for Attributed Graphs. In Proceedings of the 29th International Conference on Machine Learning, ICML 2012, Edinburgh, Scotland, UK, June 26 - July 1, 2012. icml.cc / Omnipress, 2012. URL http://icml.cc/discuss/2012/542.html. [KS99] E. W. Kamen, J. K. Su. Introduction to Optimal Estimation. Springer London, 1999. ISBN 978-1-85233-133-7, 978-1-4471-0417-9. [Lev73] G. Levi. A note on the derivation of maximal common subgraphs of two directed or undirected graphs. CALCOLO, 9(4), 1973, 341–352. ISSN 00080624. URL http://dx.doi.org/10.1007/BF02575586. [LSS+ 02] H. Lodhi, C. Saunders, J. Shawe-Taylor, N. Cristianini, C. J. C. H. Watkins. Text Classification using String Kernels. Journal of Machine Learning Research, 2, 2002, 419–444. URL http://www.jmlr.org/papers/v2/lodhi02a. html. [MB98] B. T. Messmer, H. Bunke. A New Algorithm for Error-Tolerant Subgraph Isomorphism Detection. IEEE Trans. Pattern Anal. Mach. Intell., 20(5), 1998, 493–504. URL http://doi.ieeecomputersociety.org/10.1109/ 34.682179. [Mes95] B. T. Messmer. Efficient Graph Matching Algorithms for Preprocessed Model Graphs. 1995. Aufgerufen: 04.07.2015, URL http://citeseerx.ist.psu. edu/viewdoc/summary?doi=10.1.1.33.4206. [MRS08] C. D. Manning, P. Raghavan, H. Schütze. Introduction to Information Retrieval. Cambridge University Press, New York, NY, USA, 2008. ISBN 0521865719, 9780521865715. [Mur12] K. P. Murphy. Machine Learning: A Probabilistic Perspective. The MIT Press, 2012. ISBN 0262018020, 9780262018029. [SS01] B. Schölkopf, A. J. Smola. Learning with Kernels: Support Vector Machines, Regularization, Optimization, and Beyond. MIT Press, Cambridge, MA, USA, 2001. ISBN 0262194759. [WH96] R. C. Wilson, E. R. Hancock. A Bayesian compatibility model for graph matching. Pattern Recognition Letters, 17(3), 1996, 263–276. URL http: //dx.doi.org/10.1016/0167-8655(95)00115-8. [WH97] R. C. Wilson, E. R. Hancock. Structural Matching by Discrete Relaxation. IEEE Trans. Pattern Anal. Mach. Intell., 19(6), 1997, 634–648. URL http: //doi.ieeecomputersociety.org/10.1109/34.601251. 83 [Wil96] R. Wilson. Inexact Graph Matching Using Symbolic Constraints. Dissertation, University of York, 1996. URL http://www.bmva.org/ thesis-archive/1996/1996-wilson.pdf. 84 Erklärung Hiermit versichere ich, dass ich die vorliegende Arbeit und die zugehörige Implementierung selbstständig verfasst und dabei nur die angegebeben Quellen und Hilfsmittel verwendet habe. Hannover, 09.07.2015 Simon Wingert 85