ניתוח אשכולות ספקטרלי
ניתוח אשכולות ספקטרלי הוא שיטה לפתרון בעיית ניתוח אשכולות באמצעות שימוש בערכים העצמיים (ספקטרום) של מטריצת הלפלסיאן המתארת את המידע. במקרה ובו הקלט ניתן כגרף, מטריצת הלפלסיאן נחשבת כקלט של האלגוריתם (מאחר שקיימת התאמה אחת ויחידה בין גרף בעל צמתים לבין מטריצת לפלסיאן מסדר ), אמנם במקרים רבים הקלט אינו מתקבל כך, (אלא למשל אוסף נקודות במימד ) ונדרשת בניית גרף כשלב מקדים לשימוש באלגוריתם. קודקודי הגרף מייצגים את המידע אותו רוצים לנתח, וקשתות הגרף מייצגות את הקשר בין הפרטים בגרף.
הגדרת הבעיה
[עריכת קוד מקור | עריכה]תיאור הבעיה
[עריכת קוד מקור | עריכה]ניתוח המידע לאשכולות שקול לחלוקת הגרף לתת קבוצות זרות זו לזו, נרצה למצוא חלוקה שאכן מחלקת את המידע בצורה "טבעית" כלשהי, כלומר אינטואיטיבית קל להבין כי נרצה למצוא חלוקה כזו שבה הקשרים בין חברי קבוצות שונות רופפים עד לא קיימים, ובנוסף הקשרים שבין חברי כל קבוצה זה לזה חזקים, דרך נוספת שעל פיה ניתן לחלק את המידע היא אוסף קבוצות אשר הקשרים ביניהן רופפים, וכן גודלן דומה זה לזה.
יש לשים לב כי בעוד התנאי הראשון הוא בעצם בעיית החתך המינימלי (אנ'), אשר ידועה כבעיה הניתנת לפתרון בזמן ריצה פולינומי, קיומו אינו מספיק לפתרון אופטימלי לבעיית הניתוח מאחר שפעמים רבות תוצאתו היא פיצול קודקוד אחד (או קבוצה קטנה של קודקודים) משאר הגרף, ובהוספת התנאי השני מתקבלת בעיה NP-קשה[1]. קירוב לפתרון בעיה זו ניתן להיעשות על ידי שימוש במטריצת הלפלסיאן של הגרף.
הגדרה פורמלית
[עריכת קוד מקור | עריכה]בצורה פורמלית ניתן לתאר את הבעיה באחת מהדרכים הבאות - בהינתן גרף ממושקל ולא מכוון , ומספר נרצה למצוא חלוקה המביאה למינימום את אחת משתי פונקציות המטרה הבאות:
כאשר מגדירים:
- היא תת-קבוצה של צמתים בגרף, מתקיים ,
- הוא מספר הצמתים ברכיב .
- הוא סכום ערכי הקשתות המחברות את צמתי זה לזה.
- הוא סכום ערכי הקשתות המחברות בין הקבוצה לבין שאר הגרף.
- הוא מספר האשכולות, כלומר מספר תת-הקבוצות שאליהן נחלק את הגרף.
ההבדל בין שתי הפונקציות הוא שבעוד "מענישה" רכיבי קשירות בעלי מספר צמתים נמוך, ובשימוש בה נצפה לתוצאה של אשכולות בעלי מספר צמתים דומה, "מענישה" רכיבי קשירות בעלי סכום קשתות נמוך, ולכן נצפה לקשרים חזקים בתוך כל אשכול.
בדוגמה שבתמונה ניתן להראות על ידי חישוב כי שתי פונקציות המטרה יביאו לחלוקה שונה, שימוש בפונקציה יוביל ל"שבירת" הקשת שערכה 14, לצורך חלוקת הגרף לשתי הקבוצות (השוות בגודלן) . לעומת זאת, השימוש בפונקציה יוביל לחלוקה לקבוצות , מאחר שגודל הקבוצות לא רלוונטי אלא סכום הקשתות, ותחת חישוב זה שבירת הקשת בעלת משקל 13 עדיפה על הקשת הכבדה יותר.
ניתן להניח כי ההבדלים בין הגישות נובעים מסיבות היסטוריות, פורסמה בהקשר של חלוקה של רכיבי מעגל חשמלי לתת קבוצות[2] אשר נוח "לטפל" בכל אחת בפני עצמה, בהקשר זה חשוב לדאוג שהקבוצות תהיינה בעלות גודל דומה וכן שלא יהיו קשרים חזקים בין קבוצה לקבוצה (לדוגמה, כדי שיהיה אפשר למקם כל תת-קבוצה במקום בלתי תלוי ככל האפשר בשאר המעגל החשמלי). לעומתה, פורסמה בהקשר של סגמנטציה[3], ובהקשר זה חשוב לשים לב לכלול באותו אשכול אך ורק פרטים הקשורים זה לזה.
הגדרת מטריצת הלפלסיאן
[עריכת קוד מקור | עריכה]מטריצת הלפלסיאן מוגדרת בצורה הבאה:
כאשר היא מטריצה השכנות של הגרף (במקרה של גרף לא ממושקל) או מטריצת הדמיון(אנ') של הגרף (במקרה של גרף ממושקל), וכן היא מטריצה אלכסונית שבה האיבר הוא הדרגה של האיבר ה, כלומר מתקיים
למטריצת הלפלסיאן קיימות שתי גרסאות מנורמלות:
מטריצת לפלסיאן סימטרית מנורמלת (symmetric normalized Laplacian matrix) מוגדרת כך:
מטריצת לפלסיאן מנורמלת של מהלך אקראי (random-walk normalized Laplacian matrix) מוגדרת כך:תוצאת הנרמול הי מטריצה שבה ערכי האלכסון הם 1, והשימוש במטריצה מנורמלת ייעשה כדי להביא למינימום את פונקציית המטרה , יש לציין כי בגרף רגולרי בדרך כלל לא נדרש נרמול, שכן תוצאת השימוש מטריצה הלא מנורמלת יהיה דומה מאוד למנורמלת.
שימוש במטריצת הלפלסיאן
[עריכת קוד מקור | עריכה]מתכונות מטריצת הלפלסיאן הלא מנורמלת, ניתן להוכיח[4] כי בהינתן תת-הגרף וכן וקטור אינדיקטור(אנ') מנורמל של תת-גרף זה:
מתקיים השוויון הבא:
ובאופן כללי, עבור מטריצה מגודל אשר עמודותיה הן וקטורים אינדיקטורים נקבל את השוויון הבא:כלומר, מציאת מינימום לפונקציה שקולה למציאת מינימום לביטוי תחת ההנחה שעמודות הן וקטורים אינדיקטורים וכן שהן אורתונורמליות זו לזו.
בעיה זו אמנם עדיין NP-קשה, אך ניתן למצוא פתרון מקורב לבעיה זו אם מסירים את הדרישה שהעמודות הן אינדיקטורים, ומניחים רק שהן אורתונורמליות, וזוהי כבר בעיה ידועה אשר פתרונה נתון על ידי הווקטורים העצמיים המתאימים ל- הערכים העצמיים הקטנים ביותר של המטריצה , וסיבוכיות החישוב שלה פולינמיאלית. באופן דומה ניתן למצוא את הווקטורים העצמיים המתאימים ל- הערכים העצמיים הקטנים ביותר של מטריצת הלפלסיאן המנורמלת כדי לקרב את פונקציית המטרה [5].
ניתן להבין קירוב זה בצורה אינטואיטיבית - קל להראות כי עבור גרף בעל רכיבי קשירות, הפתרון האופטימלי עבור אשכולות יהיה חלוקת הגרף לאותם רכיבי קשירות, והווקטורים העצמיים המתאימים לערך העצמי 0 הם אינדיקטורים של רכיבי הקשירות[6], כמו כן אלו הם הערכים העצמיים הקטנים ביותר, מאחר שידוע כי הערכים העצמיים של מטריצת הלפלסיאן אינם שליליים. כעת עבור גרף דומה המכיל בנוסף "רעש קטן", כלומר קשתות מעטות בין רכיב קשירות אחד למשנהו, נוכל לצפות שהערכים העצמיים ישתנו מעט, והמרחב הווקטורי הנפרש על ידי הווקטורים העצמיים המתאימים להם ישתנה מעט אף הוא, כלומר ניתן להבין כי בהינתן רעש קטן, קירוב הפתרון יהיה בסביבת הפתרון המקורי[7].
חשוב לציין כי אין ערובה לכך שקירוב זה אכן יניב את הפתרון לבעיה המקורית (המניחה שהווקטורים הם אינדיקטורים), דוגמאות קלאסיות לכך נסקרו בספרות במהלך השנים[8]. ובנוסף, בעוד פתרון הבעיה המקורית הוא אוסף אינדיקטורים המחלק את המידע ל קבוצות בצורה מפורשת, קירוב הפתרון כבר מכריח אותנו לנתח את הווקטורים העצמיים בעזרת אלגוריתם נוסף (למשל בעזרת אלגוריתם k-מרכזים).
דוגמאות
[עריכת קוד מקור | עריכה]להלן דוגמה לגרף בעלי 2 רכיבי קשירות, וכן גרף זהה עם תוספת של רעש ההופך אותו לבעל רכיב קשירות אחד,
גרף | מטריצת לפלסיאן | 2 וקטורים עצמיים ראשונים |
---|---|---|
גרף | מטריצת לפלסיאן | 2 וקטורים עצמיים ראשונים |
---|---|---|
ניתן לראות בגרף השני את הקשת המחברת את הצומת הראשון והשלישי כרעש קטן, ואכן הווקטורים העצמיים פורשים מרחב קרוב למרחב הנפרש על ידי האינדיקטורים של "רכיבי הקשירות" שכן מתקיים:
הווקטור העצמי הראשון
[עריכת קוד מקור | עריכה]חשוב לציין כי במרבית המקרים הקלט המתקבל הוא גרף קשיר, ומכאן 0 הוא ערך עצמי מריבוי 1, והווקטור העצמי המתאים לו יהיה וקטור אינדיקטור של כל צמתי הגרף, כלומר - .
שימוש בוקטור זה לא מוסיף מידע על הקשר בין צמתי הגרף, ולכן ניתן לא להשתמש בו כאשר מחלקים את הגרף לאשכולות. לדוגמה, עבור חלוקה לשני אשכולות, ניתן להסתכל ישירות על הווקטור העצמי השני הקטן ביותר (הנקרא Fiedler Vector), ולחלק את הגרף על פיו, אשכול אחד יהיה קבוצת הצמתים בעלי ערך חיובי בוקטור, והאשכול השני יהיו קבוצת הצמתים בעלי ערך שלילי.
אלגוריתם בסיסי
[עריכת קוד מקור | עריכה]כפי שהוזכר, בגרף בעל רכיבי קשירות, הפתרון המתקבל עבור הווקטורים העצמיים הראשונים פורש את אותו המרחב הנפרש גם כן על ידי האינדיקטורים של רכיבי הקשירות, ועל כן מציאת האשכולות תתקבל על ידי יצירת מטריצה בגודל מהווקטורים העצמיים, והבאתה לצורתה הקנונית.
אמנם בדרך כלל הגרף יכיל רעש, או שמראש מספר האשכולות לא יהיה ידוע, ועל כן מספר רכיבי הקשירות יהיה שונה ממספר האשכולות ויידרש עיבוד נוסף של המידע, דרך נפוצה אחת היא שימוש באלגוריתם k-מרכזים. בנוסף, במקרים רבים קלט המערכת אינו מטריצת לפלסיאן אלא, למשל, אוסף נקודות במימד גבוה כלשהו, במקרה כזה נדרש להגדיר פונקציית סמיכות המציינת את עוצמת הקשר בין כל שני איברים כדי ליצור את מטריצת הלפלסיאן. האלגוריתם השלם יכלול את השלבים הבאים:
- יצירת גרף ומטריצת לפלסיאן מהמידע
- חישוב הווקטורים העצמיים הראשונים של המטריצה
- הטלת מטריצת הווקטורים הללו למרחב , השורה ה של מטריצה זו היא הטלת הצומת בגרף אל
- הפעלת אלגוריתם לניתוח אשכולות במרחב זה (לדוגמה אלגוריתם k-מרכזים).
להלן קטע קוד המממש את האלגוריתם המתואר[9]
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_circles, make_blobs
from sklearn.cluster import KMeans
from sklearn.neighbors import NearestNeighbors
from scipy.linalg import eigh
# Step 0: Generate a dataset (Circles + Blobs)
np.random.seed(42)
X_circles, y_circles = make_circles(n_samples=400, factor=0.5, noise=0.05)
X_blobs, y_blobs = make_blobs(n_samples=100, centers=[(-1.5, -1.5)], cluster_std=0.1)
X = np.vstack((X_circles, X_blobs))
y_true = np.hstack((y_circles, y_blobs + 2)) # Adjust labels for clarity
# Step 1: Create the graph Laplacian matrix using epsilon-neighborhood
epsilon = 0.2
nbrs = NearestNeighbors(radius=epsilon).fit(X)
adj_matrix = nbrs.radius_neighbors_graph(X, mode='connectivity').toarray()
D = np.diag(np.sum(adj_matrix, axis=1))
L = D - adj_matrix
# Step 2: Compute the eigenvectors of the Laplacian matrix
eigenvalues, eigenvectors = eigh(L)
eigenvectors = eigenvectors[:, 0:3]
# Step 3+4: Analyze the data with k-means
kmeans = KMeans(n_clusters=3, random_state=42)
labels = kmeans.fit_predict(eigenvectors)
# Draw the original labels and clustering labels
plt.figure(figsize=(14, 6))
plt.subplot(1, 2, 1)
plt.scatter(X[:, 0], X[:, 1], c=y_true)
plt.title('Original Data (True Labels)')
plt.subplot(1, 2, 2)
plt.scatter(X[:, 0], X[:, 1], c=labels)
plt.title('Spectral Clustering Result')
plt.tight_layout()
plt.show()
מעבר מדאטה במימד גבוה למטריצת שכנות
[עריכת קוד מקור | עריכה]קיימות מספר שיטות ליצור גרף בהינתן אוסף נקודות, הבולטות שבהן הן:
- גרסה של אלגוריתם שכן קרוב - נגדיר קשת אם הוא אחד מ השכנים הקרובים של , חשוב לציין כי:
- הערך נתון לבחירה, ככל שיהיה גדול יותר כך הגרף יכיל יותר קשתות.
- הגרף המתקבל בדרך זו הוא מכוון, וזו תופעה שאיננה רצויה, לכן ניתן לבחור האם להגדיר קשת רק במקרים שבהם שתי הקשתות , קיימות (אלגוריתם שכן קרוב הדדי), או גם במקרים שבהם לפחות אחת משתי הקשתות קיימות.
- הגרף המתקבל איננו ממושקל
- סביבת - נגדיר קשת אם המרחק בין שני הצמתים קטן או שווה ל-, נשים לב כי:
- הערך נתון לבחירה, ככל שיהיה גדול יותר כך הגרף יכיל יותר קשתות.
- הגרף המתקבל בדרך זו איננו מכוון ואיננו ממושקל.
- שימוש במטריקת מרחק המודדת קרבה בין שתי נקודות, פונקציה שכיחה בהקשר זה היא - , בשימוש במטריקה זו:
מאפיינים מרכזיים בהשוואה לשיטות שונות
[עריכת קוד מקור | עריכה]סוג הקלט
[עריכת קוד מקור | עריכה]חשוב לשים לב כי האלגוריתם מתבסס על דמיון וקירבה בין איברים, ולכן ניתן להשתמש בקלט בעל אופי מגוון. בניגוד לאלגוריתם k-מרכזים או אלגוריתם DBSCAN(אנ') לדוגמה, אשר מתאימים יותר לקלט ממימד , כאן ניתן להשתמש גם בערכים בוליאנים (כאשר ניתן להגדיר מטריקת מרחק המסתמכת על מרחק ג'אקרד לדוגמה).
אופן החישוב
[עריכת קוד מקור | עריכה]השלבים ה"כבדים" הם יצירת מטריצת הלפלסיאן, וחישוב הווקטורים העצמיים. עבור נקודות קלט, בניית מטריצת לפלסיאן מלאה פרופורציונלית למספר הקשתות - כלומר סיבוכיות של , ומספר פעולות החישוב עבור כל הווקטורים העצמיים במדויק פרופורציונלי ל.
חשוב לציין כי לשימוש מעשי נדרש רק קירוב של הווקטורים העצמיים, ובנוסף ביישומים רבים, תחת הנחות ריאליות של ידע מקדים על הקלט ניתן להגביל את מספר הקשתות של כל צומת, ובכך עבור וקטורים עצמיים ניתן להוריד את הסיבוכיות עד לסדר גודל של בעזרת שימוש באלגוריתם לנצוש[10][11]. לכן זמן הריצה תלוי מאוד באופי הקלט ואופן מימוש האלגוריתם.
עוד יש לציין כי מספר הממדים של הקלט איננו משפיע על זמני הריצה של שלבים אלה בצורה ישירה, מה שמבדיל אותו מאלגוריתמים רבים אשר יעילותם פוחתת וזמני הריצה שלהם עולים משמעותית תחת קלט של מידע ממימד גבוה.
התפלגות הקלט
[עריכת קוד מקור | עריכה]אחד היתרונות המרכזיים של האלגוריתם הוא העובדה שאין הנחות יסוד על האשכולות. בניגוד ל--מרכזים המניח שכל אשכול הוא בעל צורה כללית של ספירה, או DBSCAN המניח צפיפות אחידה באשכול, כאן פונקציית המטרה לא מתייחסת בצורה ישירה לתכונות גלובליות אלה של האשכול.
ראו גם
[עריכת קוד מקור | עריכה]קישורים חיצוניים
[עריכת קוד מקור | עריכה]- מאמר מקיף מאת פרופסור ulrike von luxburg.
- סדרת סרטונים בנושא מאוניברסיטת סטנפורד
- תרגול קוד פייתון בסיסי עבור סגמנטציה מאת scikit-learn
- תיעוד ספריית sklearn.cluster.SpectralClustering
הערות שוליים
[עריכת קוד מקור | עריכה]- ^ Wagner, D. and Wagner, F. (1993). Between min cut and graph bisection
- ^ Hagen and Kahng, 1992
- ^ Shi and Malik, 2000
- ^ להוכחה המלאה ראו A Tutorial on Spectral Clustering by Ulrike von Luxburg בפרק 5.2
- ^ להוכחה המלאה ראו A Tutorial on Spectral Clustering by Ulrike von Luxburg בפרק 5.3
- ^ ליתר דיוק יש לומר כי המרחב הנפרש על ידי הווקטורים העצמיים זהה למרחב הנפרש על ידי אינדיקטורים, מאחר שלא ניתן לשייך וקטור עצמי אחד מסוים אל ערך עצמי בעל ריבוי גאומטרי גדול מאחד אלא יש לדבר על המרחב הווקטורי המתאים לערך העצמי
- ^ davis and kahan, 1970
- ^ ראו לדוגמה Guattery and Miller, 1978
- ^ קוד זה עוקב אחר השלבים המרכזיים של האלגוריתם התאורטי, ברמה המעשית ניתן להשתמש בספריות ייעודיות כגון sklearn.cluster.SpectralClustering
- ^ למעשה השילוב של הסתפקות בקירוב הווקטורים העצמיים עם הגבלת מספר הקשתות המחוברות לכל צומת הוא שהופך את השימוש באלגוריתם לנצוש לאטרקטיבי מאחר שזמן החישוב של אלגוריתם זה יורד משמעותית עבור מטריצות דלילות, בהיעדר הנחה זו ניתן להגיע לסיבוכיות של עבור הווקטורים העצמיים הראשונים
- ^ ראו בהרחבה בshi and malik, 2000, פרק 3.1