מדריך סופי ל-K-Means Clustering עם Scikit-Learn PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

מדריך סופי ל-K-Means Clustering עם Scikit-Learn

מבוא

אשכול K-Means הוא אחד האלגוריתמים הנפוצים ביותר של למידת מכונה ללא פיקוח היוצרים אשכולות של נתונים המבוססים על הדמיון בין מופעי נתונים.

במדריך זה, נסקור תחילה דוגמה פשוטה כדי להבין כיצד פועל אלגוריתם K-Means לפני הטמעתו באמצעות Scikit-Learn. לאחר מכן, נדון כיצד לקבוע את מספר האשכולות (Ks) ב-K-Means, וכן לכסות מדדי מרחק, שונות ו-K-Means יתרונות וחסרונות.

מוטיבציה

דמיינו את המצב הבא. יום אחד, כשהסתובבת בשכונה, שמת לב שיש 10 חנויות נוחות והתחלת לתהות אילו חנויות דומות - יותר קרובות אחת לשנייה. תוך כדי חיפוש אחר דרכים לענות על השאלה הזו, נתקלת בגישה מעניינת שמחלקת את החנויות לקבוצות על סמך הקואורדינטות שלהן על מפה.

לדוגמה, אם חנות אחת הייתה ממוקמת 5 ק"מ מערבה ו-3 ק"מ צפון - היית מקצה (5, 3) קואורדינטות אליו, ומייצגים אותו בגרף. בואו נשרטט את הנקודה הראשונה הזו כדי לדמיין מה קורה:

import matplotlib.pyplot as plt

plt.title("Store With Coordinates (5, 3)")
plt.scatter(x=5, y=3)

זו רק הנקודה הראשונה, כדי שנוכל לקבל מושג כיצד נוכל לייצג חנות. נניח שיש לנו כבר 10 קואורדינטות ל-10 החנויות שנאספו. לאחר שארגנו אותם בא numpy מערך, נוכל גם לשרטט את המיקומים שלהם:

import numpy as np

points = np.array([[5, 3], [10, 15], [15, 12], [24, 10], [30, 45], [85, 70], [71, 80], [60, 78], [55, 52],[80, 91]])

xs = points[:,0] 
ys = points[:,1]  

plt.title("10 Stores Coordinates")
plt.scatter(x=xs, y=ys)

מדריך סופי ל-K-Means Clustering עם Scikit-Learn PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

כיצד ליישם באופן ידני את אלגוריתם K-Means

כעת אנו יכולים להסתכל על 10 החנויות על גבי גרף, והבעיה העיקרית היא למצוא האם ניתן לחלק אותן לקבוצות שונות על סמך קרבה? רק על ידי מבט מהיר על הגרף, סביר להניח שנבחין שתי קבוצות של חנויות - האחת היא הנקודות התחתונות משמאל למטה, והשנייה היא הנקודות הימנית העליונה. אולי, אנחנו אפילו יכולים להבדיל בין שתי הנקודות האלה באמצע כקבוצה נפרדת - ולכן יוצרים שלוש קבוצות שונות.

בחלק זה, נעבור על התהליך של קיבוץ נקודות ידני - נחלק אותם למספר הקבוצות הנתון. בדרך זו, בעצם נעבור בזהירות על כל השלבים של ה K-Means אלגוריתם אשכולות. בסוף סעיף זה, תקבל הבנה אינטואיטיבית ומעשית כאחד של כל השלבים שבוצעו במהלך אשכול K-Means. לאחר מכן, נעביר אותו ל-Skikit-Learn.

מה תהיה הדרך הטובה ביותר לקבוע אם יש שתיים או שלוש קבוצות של נקודות? דרך פשוטה אחת תהיה פשוט לבחור מספר אחד של קבוצות - למשל שתיים - ואז לנסות לקבץ נקודות על סמך הבחירה הזו.

מדריך סופי ל-K-Means Clustering עם Scikit-Learn PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

נניח שהחלטנו שיש שתי קבוצות של החנויות שלנו (נקודות). כעת, עלינו למצוא דרך להבין אילו נקודות שייכות לאיזו קבוצה. ניתן לעשות זאת על ידי בחירת נקודה אחת לייצג קבוצה 1 ואחד לייצג קבוצה 2. נקודות אלו ישמשו כאסמכתא בעת מדידת המרחק מכל שאר הנקודות לכל קבוצה.

בצורה כזו, תגיד נקודה (5, 3) בסופו של דבר שייך לקבוצה 1, ונקודה (79, 60) לקבוצה 2. כאשר מנסים להקצות נקודה חדשה (6, 3) לקבוצות, עלינו למדוד את המרחק שלו לשתי הנקודות הללו. במקרה של הנקודה (6, 3) is קרוב יותר אל ה (5, 3), לכן הוא שייך לקבוצה המיוצגת על ידי אותה נקודה - קבוצה 1. כך נוכל לקבץ בקלות את כל הנקודות לקבוצות מתאימות.

בדוגמה זו, מלבד קביעת מספר הקבוצות (אשכולות) – אנחנו גם בוחרים כמה נקודות להיות א הפניה של מרחק עבור נקודות חדשות של כל קבוצה.

זה הרעיון הכללי להבין את קווי הדמיון בין החנויות שלנו. בואו ליישם את זה בפועל - אנחנו יכולים לבחור תחילה את שתי נקודות ההתייחסות ב אקראי. נקודת ההתייחסות של קבוצה 1 יהיה (5, 3) ונקודת ההתייחסות של קבוצה 2 יהיה (10, 15). אנחנו יכולים לבחור את שתי הנקודות שלנו numpy מערך לפי [0] ו [1] אינדקסים ולאחסן אותם g1 (קבוצה 1) ו g2 משתנים (קבוצה 2):

g1 = points[0]
g2 = points[1]

לאחר שנעשה זאת, עלינו לחשב את המרחק מכל שאר הנקודות לנקודות הייחוס הללו. זה מעלה שאלה חשובה - איך למדוד את המרחק הזה. למעשה, אנו יכולים להשתמש בכל מדידת מרחק, אך לצורך המדריך הזה, הבה נשתמש ב-Euclidian Distance_.

זה יכול להיות שימושי לדעת שמידת המרחק האוקלידית מבוססת על משפט פיתגורס:

$$
c^2 = a^2 + b^2
$$

כאשר מותאם לנקודות במטוס - (a1, b1) ו (a2, b2), הנוסחה הקודמת הופכת:

$$
c^2 = (a2-a1)^2 + (b2-b1)^2
$$

המרחק יהיה השורש הריבועי של c, כך שנוכל גם לכתוב את הנוסחה כ:

$$
euclidean_{dist} = sqrt[2][(a2 – a1)^2 + (b2 – b1) ^2)]
$$

הערה: ניתן גם להכליל את נוסחת המרחק האוקלידי עבור נקודות רב-ממדיות. לדוגמה, במרחב תלת מימדי, לנקודות יש שלוש קואורדינטות - הנוסחה שלנו משקפת זאת בצורה הבאה:
$$
euclidean_{dist} = sqrt[2][(a2 – a1)^2 + (b2 – b1) ^2 + (c2 – c1) ^2)]
$$
אותו עיקרון פועל ללא קשר למספר הממדים של החלל בו אנו פועלים.

עד כה, בחרנו את הנקודות לייצוג קבוצות, ואנחנו יודעים לחשב מרחקים. כעת, בואו נחבר את המרחקים והקבוצות על ידי הקצאת כל אחת מנקודות החנות שנאספו לקבוצה.

כדי לדמיין זאת טוב יותר, נכריז על שלוש רשימות. הראשון לאחסן נקודות של הקבוצה הראשונה - points_in_g1. השני לאחסן נקודות מקבוצה 2 - points_in_g2, והאחרון - group, כדי תווית הנקודות כאחת 1 (שייך לקבוצה 1) או 2 (שייך לקבוצה 2):

points_in_g1 = []
points_in_g2 = []
group = []

כעת אנו יכולים לחזור על הנקודות שלנו ולחשב את המרחק האוקלידי בינן לבין כל אחת מההתייחסויות הקבוצתיות שלנו. כל נקודה תהיה קרוב יותר לאחת משתי קבוצות - על סמך איזו קבוצה היא הקרובה ביותר, נקצה כל נקודה לרשימה המתאימה, תוך הוספת 1 or 2 אל ה group רשימה:

for p in points:
    x1, y1 = p[0], p[1]
    euclidean_distance_g1 = np.sqrt((g1[0] - x1)**2 + (g1[1] - y1)**2)
    euclidean_distance_g2 = np.sqrt((g2[0] - x1)**2 + (g2[1] - y1)**2)
    if euclidean_distance_g1 < euclidean_distance_g2:
        points_in_g1.append(p)
        group.append('1')
    else:
        points_in_g2.append(p)
        group.append('2')

בואו נסתכל על התוצאות של איטרציה זו כדי לראות מה קרה:

print(f'points_in_g1:{points_in_g1}n 
npoints_in_g2:{points_in_g2}n 
ngroup:{group}')

מה שמביא ל:

points_in_g1:[array([5, 3])]
 
points_in_g2:[array([10, 15]), array([15, 12]), 
              array([24, 10]), array([30, 45]), 
              array([85, 70]), array([71, 80]),
              array([60, 78]), array([55, 52]), 
              array([80, 91])]
 
group:[1, 2, 2, 2, 2, 2, 2, 2, 2, 2] 

אנחנו יכולים גם לשרטט את תוצאת האשכולות, עם צבעים שונים המבוססים על הקבוצות שהוקצו, באמצעות Seaborn's scatterplot() עם group בתור hue טענה:

import seaborn as sns

sns.scatterplot(x=points[:, 0], y=points[:, 1], hue=group)

מדריך סופי ל-K-Means Clustering עם Scikit-Learn PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

ניכר בבירור שרק הנקודה הראשונה שלנו מוקצית לקבוצה 1, וכל שאר הנקודות הוקצו לקבוצה 2. התוצאה הזו שונה ממה שדמיינו בהתחלה. בהתחשב בהבדל בין התוצאות שלנו לציפיות הראשוניות שלנו - האם יש דרך לשנות זאת? נראה שיש!

גישה אחת היא לחזור על התהליך ולבחור נקודות שונות שיהוו התייחסות לקבוצות. זה ישנה את התוצאות שלנו, בתקווה, יותר בקנה אחד עם מה שחזינו בהתחלה. בפעם השנייה, יכולנו לבחור אותם לא באקראי כפי שעשינו בעבר, אלא על ידי קבלת א אומר מכל הנקודות שכבר מקובצות שלנו. כך, ניתן יהיה למקם את הנקודות החדשות הללו באמצע הקבוצות המקבילות.

למשל, אם לקבוצה השנייה היו רק נקודות (10, 15), (30, 45). החדש מֶרכָּזִי הנקודה תהיה (10 + 30)/2 ו (15+45)/2 – ששווה ל (20, 30).

מכיוון ששמנו את התוצאות שלנו ברשימות, נוכל להמיר אותן תחילה ל numpy מערכים, בחר את ה-xs, ys שלהם ולאחר מכן השג את אומר:

g1_center = [np.array(points_in_g1)[:, 0].mean(), np.array(points_in_g1)[:, 1].mean()]
g2_center = [np.array(points_in_g2)[:, 0].mean(), np.array(points_in_g2)[:, 1].mean()]
g1_center, g2_center

עֵצָה: נסה להשתמש numpy ומערכי NumPy ככל האפשר. הם מותאמים לביצועים טובים יותר ומפשטים פעולות אלגברה ליניאריות רבות. בכל פעם שאתה מנסה לפתור בעיית אלגברה לינארית כלשהי, אתה בהחלט צריך להסתכל על numpy תיעוד כדי לבדוק אם יש numpy שיטה שנועדה לפתור את הבעיה שלך. הסיכוי הוא שיש!

כדי לעזור לחזור על התהליך עם נקודות המרכז החדשות שלנו, הבה נהפוך את הקוד הקודם שלנו לפונקציה, נבצע אותה ונראה אם ​​היו שינויים כלשהם באופן שבו הנקודות מקובצות:

def assigns_points_to_two_groups(g1_center, g2_center):
    points_in_g1 = []
    points_in_g2 = []
    group = []

    for p in points:
        x1, y1 = p[0], p[1]
        euclidean_distance_g1 = np.sqrt((g1_center[0] - x1)**2 + (g1_center[1] - y1)**2)
        euclidean_distance_g2 = np.sqrt((g2_center[0] - x1)**2 + (g2_center[1] - y1)**2)
        if euclidean_distance_g1 < euclidean_distance_g2:
            points_in_g1.append(p)
            group.append(1)
        else:
            points_in_g2.append(p)
            group.append(2)
    return points_in_g1, points_in_g2, group

הערה: אם אתה מבחין שאתה ממשיך לחזור על אותו קוד שוב ושוב, עליך לעטוף את הקוד הזה לפונקציה נפרדת. זה נחשב לשיטה מומלצת לארגן קוד בפונקציות, במיוחד משום שהן מקלות על הבדיקה. קל יותר לבדוק פיסת קוד ומבודדת מאשר קוד מלא ללא כל פונקציה.

בואו נקרא לפונקציה ונשמור את התוצאות שלה points_in_g1, points_in_g2, ו group משתנים:

points_in_g1, points_in_g2, group = assigns_points_to_two_groups(g1_center, g2_center)
points_in_g1, points_in_g2, group

כמו כן, מתווים את תרשים הפיזור עם הנקודות הצבעוניות כדי לדמיין את חלוקת הקבוצות:

sns.scatterplot(x=points[:, 0], y=points[:, 1], hue=group)

מדריך סופי ל-K-Means Clustering עם Scikit-Learn PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

נראה שצבירת הנקודות שלנו היא משתפר. אבל עדיין, ישנן שתי נקודות באמצע הגרף שניתן להקצות לכל אחת מהקבוצות כאשר בוחנים את הקרבה שלהן לשתי הקבוצות. האלגוריתם שפיתחנו עד כה מקצה את שתי הנקודות הללו לקבוצה השנייה.

זה אומר שכנראה נוכל לחזור על התהליך פעם נוספת על ידי נטילת האמצעים של Xs ו-Ys, יצירת שתי נקודות מרכזיות חדשות (צנטרואידים) לקבוצות שלנו והקצאתן מחדש על סמך מרחק.

בואו גם ניצור פונקציה לעדכון ה-centroids. ניתן לצמצם את כל התהליך כעת למספר קריאות של פונקציה זו:

def updates_centroids(points_in_g1, points_in_g2):
    g1_center = np.array(points_in_g1)[:, 0].mean(), np.array(points_in_g1)[:, 1].mean()
    g2_center = np.array(points_in_g2)[:, 0].mean(), np.array(points_in_g2)[:, 1].mean()
    return g1_center, g2_center

g1_center, g2_center = updates_centroids(points_in_g1, points_in_g2)
points_in_g1, points_in_g2, group = assigns_points_to_two_groups(g1_center, g2_center)
sns.scatterplot(x=points[:, 0], y=points[:, 1], hue=group)

מדריך סופי ל-K-Means Clustering עם Scikit-Learn PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

שימו לב שאחרי איטרציה שלישית זו, כל אחת מהנקודות שייכת כעת לאשכולות שונים. נראה שהתוצאות משתפרות - בואו נעשה זאת שוב. עכשיו הולך ל איטרציה רביעית של השיטה שלנו:

g1_center, g2_center = updates_centroids(points_in_g1, points_in_g2)
points_in_g1, points_in_g2, group = assigns_points_to_two_groups(g1_center, g2_center)
sns.scatterplot(x=points[:, 0], y=points[:, 1], hue=group)

מדריך סופי ל-K-Means Clustering עם Scikit-Learn PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

הפעם הרביעית קיבלנו אותה תוצאה כמו הקודם. אז נראה שהנקודות שלנו לא ישנו קבוצות יותר, התוצאה שלנו הגיעה לאיזושהי יציבות - היא הגיעה למצב בלתי ניתן לשינוי, או התכנסו. חוץ מזה, יש לנו בדיוק את אותה תוצאה כפי שדמיינו עבור 2 הקבוצות. אנחנו יכולים גם לראות אם החלוקה שהגיעה זו הגיונית.

בואו נסכם במהירות את מה שעשינו עד כה. חילקנו את 10 החנויות שלנו מבחינה גיאוגרפית לשני חלקים - אחד באזורים הדרום-מערביים הנמוכים ואחרים בצפון מזרח. זה יכול להיות מעניין לאסוף נתונים נוספים מלבד מה שכבר יש לנו - הכנסות, מספר הלקוחות היומי ועוד רבים. כך נוכל לבצע ניתוח עשיר יותר ואולי לייצר תוצאות מעניינות יותר.

מחקרים מקבצים כמו זה יכולים להתבצע כאשר מותג מבוסס כבר רוצה לבחור אזור לפתיחת חנות חדשה. במקרה זה, ישנם משתנים רבים נוספים שנלקחים בחשבון מלבד המיקום.

מה כל זה קשור לאלגוריתם K-Means?

בזמן ביצוע השלבים האלה אולי תהיתם מה הם קשורים לאלגוריתם K-Means. התהליך שביצענו עד כה הוא אלגוריתם K-Means. בקצרה, קבענו את מספר הקבוצות/אשכולות, בחרנו באקראי נקודות ראשוניות ועדכנו מוקדים בכל איטרציה עד שהאשכולות התכנסו. בעצם ביצענו את כל האלגוריתם ביד - בצענו בקפידה כל שלב.

השמיים K ב-K-Means מגיע מה מספר אשכולות שיש להגדיר לפני תחילת תהליך האיטרציה. במקרה שלנו K = 2. מאפיין זה נתפס לפעמים כ שלילי בהתחשב שישנן שיטות אשכולות אחרות, כמו אשכול היררכי, שאינן צריכות להיות עם מספר קבוע של אשכולות מראש.

בשל השימוש באמצעים, גם K-means הופך רגיש לחריגות וערכים קיצוניים - הם משפרים את השונות ומקשים על הסנטרואידים שלנו למלא את תפקידם. לכן, היו מודעים לצורך לבצע ערכים קיצוניים וניתוח חריגים לפני ביצוע אשכול באמצעות אלגוריתם K-Means.

כמו כן, שימו לב שהנקודות שלנו היו מפולחות בחלקים ישרים, אין עקומות בעת יצירת האשכולות. זה יכול להיות גם חסרון של אלגוריתם K-Means.

הערה: כאשר אתה צריך שזה יהיה גמיש יותר וניתן להתאמה לאליפסות וצורות אחרות, נסה להשתמש ב-a מודל תערובת גאוסית כללית של K-פירושו. מודל זה יכול להסתגל לאשכולות פילוח אליפטיים.

גם ל-K-Means יש הרבה יתרונות! זה מתפקד טוב על מערכי נתונים גדולים דבר שעלול להיות קשה לטיפול אם אתה משתמש בכמה סוגים של אלגוריתמי אשכול היררכי. זה גם מבטיחה התכנסות, ויכול בקלות לְהַכלִיל ו להסתגל. חוץ מזה, זה כנראה אלגוריתם האשכולות הנפוץ ביותר.

כעת, לאחר שעברנו על כל השלבים שבוצעו באלגוריתם K-Means, והבנו את כל היתרונות והחסרונות שלו, נוכל סוף סוף ליישם את K-Means באמצעות ספריית Scikit-Learn.

כיצד ליישם אלגוריתם K-Means באמצעות סקיקיט-למד

כדי לבדוק שוב את התוצאה שלנו, בוא נעשה את התהליך הזה שוב, אבל עכשיו נשתמש ב-3 שורות קוד עם sklearn:

from sklearn.cluster import KMeans


kmeans = KMeans(n_clusters=2, random_state=42) 
kmeans.fit(points)
kmeans.labels_

כאן, התוויות זהות לקבוצות הקודמות שלנו. בוא נתווה במהירות את התוצאה:

sns.scatterplot(x = points[:,0], y = points[:,1], hue=kmeans.labels_)

מדריך סופי ל-K-Means Clustering עם Scikit-Learn PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

העלילה שהתקבלה זהה לזו מהסעיף הקודם.

עיין במדריך המעשי והמעשי שלנו ללימוד Git, עם שיטות עבודה מומלצות, סטנדרטים מקובלים בתעשייה ודף רמאות כלול. תפסיק לגוגל פקודות Git ולמעשה ללמוד זה!

הערה: רק להסתכל על האופן שבו ביצענו את אלגוריתם K-Means באמצעות Scikit-Learn עשוי לתת לך את הרושם שזה לא מובן מאליו ושאינך צריך לדאוג יותר מדי בקשר לזה. רק 3 שורות קוד מבצעות את כל השלבים שדיברנו עליהם בסעיף הקודם כאשר עברנו על אלגוריתם K-Means שלב אחר שלב. אבל, השטן נמצא בפרטים במקרה הזה! אם לא תבינו את כל השלבים והמגבלות של האלגוריתם, סביר להניח שתתמודדו עם המצב שבו אלגוריתם K-Means נותן לכם תוצאות שלא ציפיתם.

עם Scikit-Learn, אתה יכול גם לאתחל את K-Means להתכנסות מהירה יותר על ידי הגדרת ה- init='k-means++' טַעֲנָה. במונחים רחבים יותר, K-Means++ עדיין בוחר את k מרכזי אשכול ראשוני באקראי בעקבות התפלגות אחידה. לאחר מכן, כל מרכז אשכול עוקב נבחר מתוך נקודות הנתונים הנותרות לא על ידי חישוב רק מדד מרחק - אלא על ידי שימוש בהסתברות. השימוש בהסתברות מאיץ את האלגוריתם וזה מועיל כאשר עוסקים במערכי נתונים גדולים מאוד.

שיטת המרפק - בחירת מספר הקבוצות הטוב ביותר

בינתיים הכל טוב! ריכזנו 10 חנויות על סמך המרחק האוקלידי בין נקודות ומרכזיות. אבל מה לגבי שתי הנקודות האלה באמצע הגרף שקצת יותר קשה לרכז אותן? הם לא יכלו להקים גם קבוצה נפרדת? האם באמת עשינו טעות בבחירה K = 2 קבוצות? אולי באמת היה לנו K = 3 קבוצות? יכולנו אפילו לקיים יותר משלוש קבוצות ולא להיות מודעים לכך.

השאלה הנשאלת כאן היא כיצד לקבוע את מספר הקבוצות (K) ב-K-Means. כדי לענות על השאלה הזו, עלינו להבין אם יהיה אשכול "טוב יותר" עבור ערך שונה של K.

הדרך הנאיבית לגלות זאת היא על ידי קיבוץ נקודות עם ערכים שונים של K, אז , עבור K=2, K=3, K=4 וכן הלאה:

for number_of_clusters in range(1, 11): 
    kmeans = KMeans(n_clusters = number_of_clusters, random_state = 42)
    kmeans.fit(points) 

אבל, מקבץ נקודות עבור שונים Ks לבד לא יספיק כדי להבין אם בחרנו את הערך האידיאלי עבורו K. אנחנו צריכים דרך להעריך את איכות האשכולות עבור כל אחד מהם K אנחנו בחרנו.

חישוב ידני של בתוך אשכול סכום של ריבועים (WCSS)

הנה המקום האידיאלי להציג מדד עד כמה הנקודות המקובצות שלנו קרובות זו לזו. זה בעצם מתאר כמה שונות יש לנו בתוך אשכול יחיד. מדד זה נקרא בתוך אשכול סכום של ריבועים, או WCSS בקיצור. ככל שה-WCSS קטן יותר, כך הנקודות שלנו קרובות יותר, ולכן יש לנו אשכול מעוצב יותר. ניתן להשתמש בנוסחת WCSS עבור כל מספר של אשכולות:

$$
WCSS = sum(Pi_1 – Centroid_1)^2 + cdots + sum(Pi_n – Centroid_n)^2
$$

הערה: במדריך זה, אנו משתמשים ב- מרחק אוקלידי כדי להשיג את הסנטרואידים, אך ניתן להשתמש גם באמצעי מרחק אחרים, כגון מנהטן.

כעת אנו יכולים להניח שבחרנו להחזיק בשני אשכולות ולנסות ליישם את ה-WCSS כדי להבין טוב יותר מהו ה-WCSS וכיצד להשתמש בו. כפי שהנוסחה קובעת, עלינו לסכם את ההבדלים בריבוע בין כל נקודות האשכול והצנטרואידים. אז, אם הנקודה הראשונה שלנו מהקבוצה הראשונה היא (5, 3) והמרכז האחרון שלנו (לאחר התכנסות) של הקבוצה הראשונה הוא (16.8, 17.0), ה-WCSS יהיה:

$$
WCSS = sum((5,3) – (16.8, 17.0))^2
$$

$$
WCSS = sum((5-16.8) + (3-17.0))^2
$$

$$
WCSS = sum((-11.8) + (-14.0))^2
$$

$$
WCSS = sum((-25.8))^2
$$

$$
WCSS = 335.24
$$

דוגמה זו ממחישה כיצד אנו מחשבים את ה-WCSS עבור הנקודה האחת מהאשכול. אבל האשכול מכיל בדרך כלל יותר מנקודה אחת, ועלינו לקחת את כולן בחשבון בעת ​​חישוב ה-WCSS. נעשה זאת על ידי הגדרת פונקציה שמקבלת צביר של נקודות ומרכזיות, ומחזירה את סכום הריבועים:

def sum_of_squares(cluster, centroid):
    squares = []
    for p in cluster:
        squares.append((p - centroid)**2)
        ss = np.array(squares).sum()
    return ss

כעת נוכל לקבל את סכום הריבועים עבור כל אשכול:

g1 = sum_of_squares(points_in_g1, g1_center)
g2 = sum_of_squares(points_in_g2, g2_center)

וסכם את התוצאות כדי לקבל את הסכום הכולל WCSS:

g1 + g2

זו התוצאה:

2964.3999999999996

אז, במקרה שלנו, מתי K שווה ל-2, סך ה-WCSS הוא 2964.39. כעת, אנו יכולים להחליף Ks ולחשב את WCSS עבור כולם. כך נוכל לקבל תובנה לגבי מה K עלינו לבחור לגרום לאשכולות שלנו לבצע את הביצועים הטובים ביותר.

חישוב WCSS שימוש סקיקיט-למד

למרבה המזל, אנחנו לא צריכים לחשב ידנית את ה-WCSS עבור כל אחד מהם K. לאחר ביצוע אשכול K-Means עבור מספר האשכולות הנתון, נוכל להשיג את ה-WCSS שלו באמצעות inertia_ תְכוּנָה. עכשיו, אנחנו יכולים לחזור ל-K-Means שלנו for לולאה, השתמש בה כדי להתאים את מספר האשכולות ולפרט ערכי WCSS תואמים:

wcss = [] 
for number_of_clusters in range(1, 11): 
    kmeans = KMeans(n_clusters = number_of_clusters, random_state = 42)
    kmeans.fit(points) 
    wcss.append(kmeans.inertia_)
wcss

שימו לב שהערך השני ברשימה, הוא בדיוק אותו ערך שחישבנו בעבר עבורו K = 2:

[18272.9, # For k=1 
 2964.3999999999996, # For k=2
 1198.75, # For k=3
 861.75,
 570.5,
 337.5,
 175.83333333333334,
 79.5,
 17.0,
 0.0]

כדי לדמיין את התוצאות הללו, בואו נתווה את שלנו Ks יחד עם ערכי WCSS:

ks = [1, 2, 3, 4, 5 , 6 , 7 , 8, 9, 10]
plt.plot(ks, wcss)

מדריך סופי ל-K-Means Clustering עם Scikit-Learn PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

יש הפרעה בעלילה כאשר x = 2, נקודה נמוכה בקו, ועוד נמוכה יותר כאשר x = 3. שימו לב שזה מזכיר לנו את ה צורה של מרפק. על ידי שרטוט ה-Ks יחד עם ה-WCSS, אנו משתמשים ב- שיטת המרפק כדי לבחור את מספר Ks. וה שנבחרה K היא בדיוק נקודת המרפק הנמוכה ביותר, אז זה יהיה 3 במקום 2, במקרה שלנו:

ks = [1, 2, 3, 4, 5 , 6 , 7 , 8, 9, 10]
plt.plot(ks, wcss);
plt.axvline(3, linestyle='--', color='r')

מדריך סופי ל-K-Means Clustering עם Scikit-Learn PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

נוכל להפעיל את אלגוריתם האשכולות K-Means שוב, כדי לראות איך הנתונים שלנו ייראו עם שלושה אשכולות:

kmeans = KMeans(n_clusters=3, random_state=42)
kmeans.fit(points)
sns.scatterplot(x = points[:,0], y = points[:,1], hue=kmeans.labels_)

מדריך סופי ל-K-Means Clustering עם Scikit-Learn PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

כבר היינו מרוצים משני אשכולות, אבל לפי שיטת המרפק, שלושה אשכולות יתאימו יותר לנתונים שלנו. במקרה זה, יהיו לנו שלושה סוגים של חנויות במקום שתיים. לפני השימוש בשיטת המרפק חשבנו על מקבצי חנויות דרום מערב וצפון מזרח, עכשיו יש לנו גם חנויות במרכז. אולי זה יכול להיות מיקום טוב לפתוח חנות נוספת מכיוון שתהיה לה פחות תחרות בקרבת מקום.

מדדי איכות חלופיים של אשכול

ישנם גם מדדים אחרים שניתן להשתמש בהם בעת הערכת איכות האשכול:

  • ציון צללית - מנתח לא רק את המרחק בין נקודות בתוך אשכול אלא גם בין אשכולות עצמם
  • בין אשכולות סכום ריבועים (BCSS) - מדד משלים ל-WCSS
  • שגיאת סכום הריבועים (SSE)
  • רדיוס מקסימלי - מודד את המרחק הגדול ביותר מנקודה למרכז שלה
  • רדיוס ממוצע – סכום המרחק הגדול ביותר מנקודה למרכזה חלקי מספר האשכולות.

מומלץ להתנסות ולהכיר כל אחד מהם שכן בהתאם לבעיה, חלק מהחלופות יכולות להיות ישימות יותר מהמדדים הנפוצים ביותר. (WCSS וציון צללית).

בסופו של דבר, כמו באלגוריתמים רבים של מדעי הנתונים, אנו רוצים לצמצם את השונות בתוך כל אשכול ולמקסם את השונות בין אשכולות שונים. אז יש לנו אשכולות מוגדרים יותר וניתנים להפרדה.

החלת K-Means על מערך נתונים אחר

בואו נשתמש במה שלמדנו על מערך נתונים אחר. הפעם, ננסה למצוא קבוצות של יינות דומים.

הערה: אתה יכול להוריד את מערך הנתונים כאן.

אנחנו מתחילים בייבוא pandas לקרוא את wine-clustering CSV (ערכים מופרדים בפסיק) התיק לתוך Dataframe מבנה you

import pandas as pd

df = pd.read_csv('wine-clustering.csv')

לאחר טעינתו, הבה נציץ בחמשת רשומות הנתונים הראשונות עם ה- head() שיטה:

df.head()

זו התוצאה:

	Alcohol 	Malic_Acid 	Ash 	Ash_Alcanity 	Magnesium 	Total_Phenols 	Flavanoids 	Nonflavanoid_Phenols 	Proanthocyanins 	Color_Intensity 	Hue 	OD280 	Proline
0 	14.23 		1.71 		2.43 	15.6 			127 		2.80 			3.06 		0.28 					2.29 				5.64 				1.04 	3.92 	1065
1 	13.20 		1.78 		2.14 	11.2 			100 		2.65 			2.76 		0.26 					1.28 				4.38 				1.05 	3.40 	1050
2 	13.16 		2.36 		2.67 	18.6 			101 		2.80 			3.24 		0.30 					2.81 				5.68 				1.03 	3.17 	1185
3 	14.37 		1.95 		2.50 	16.8 			113 		3.85 			3.49 		0.24 					2.18 				7.80 				0.86 	3.45 	1480
4 	13.24 		2.59 		2.87 	21.0 			118 		2.80 			2.69 		0.39 					1.82 				4.32 				1.04 	2.93 	735

יש לנו מדידות רבות של חומרים הנמצאים ביינות. כאן, גם לא נצטרך לשנות עמודות קטגוריות כי כולן מספריות. כעת, בואו נסתכל על הסטטיסטיקה התיאורית עם ה describe() שיטה:

df.describe().T 

טבלת התיאור:

 						count 	mean 		std 		min 	25% 	50% 	75% 		max
Alcohol 				178.0 	13.000618 	0.811827 	11.03 	12.3625 13.050 	13.6775 	14.83
Malic_Acid 				178.0 	2.336348 	1.117146 	0.74 	1.6025 	1.865 	3.0825 		5.80
Ash 					178.0 	2.366517 	0.274344 	1.36 	2.2100 	2.360 	2.5575 		3.23
Ash_Alcanity 			178.0 	19.494944 	3.339564 	10.60 	17.2000 19.500 	21.5000 	30.00
Magnesium 				178.0 	99.741573 	14.282484 	70.00 	88.0000 98.000 	107.0000 	162.00
Total_Phenols 			178.0 	2.295112 	0.625851 	0.98 	1.7425 	2.355 	2.8000 		3.88
Flavanoids 				178.0 	2.029270 	0.998859 	0.34 	1.2050 	2.135 	2.8750 		5.08
Nonflavanoid_Phenols 	178.0 	0.361854 	0.124453 	0.13 	0.2700 	0.340 	0.4375 		0.66
Proanthocyanins 		178.0 	1.590899 	0.572359 	0.41 	1.2500 	1.555 	1.9500 		3.58
Color_Intensity 		178.0 	5.058090 	2.318286 	1.28 	3.2200 	4.690 	6.2000 		13.00
Hue 					178.0 	0.957449 	0.228572 	0.48 	0.7825 	0.965 	1.1200 		1.71
OD280 					178.0 	2.611685 	0.709990 	1.27 	1.9375 	2.780 	3.1700 		4.00
Proline 				178.0 	746.893258 	314.907474 	278.00 	500.500 673.500 985.0000 	1680.00

בהסתכלות בטבלה ברור שיש כמה שונות בנתונים – עבור כמה עמודות כגון Alchool יש עוד, ועבור אחרים, כגון Malic_Acid, פחות. עכשיו אנחנו יכולים לבדוק אם יש כאלה null, או NaN ערכים במערך הנתונים שלנו:

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 178 entries, 0 to 177
Data columns (total 13 columns):
 #   Column                Non-Null Count  Dtype  
---  ------                --------------  -----  
 0   Alcohol               178 non-null    float64
 1   Malic_Acid            178 non-null    float64
 2   Ash                   178 non-null    float64
 3   Ash_Alcanity          178 non-null    float64
 4   Magnesium             178 non-null    int64  
 5   Total_Phenols         178 non-null    float64
 6   Flavanoids            178 non-null    float64
 7   Nonflavanoid_Phenols  178 non-null    float64
 8   Proanthocyanins       178 non-null    float64
 9   Color_Intensity       178 non-null    float64
 10  Hue                   178 non-null    float64
 11  OD280                 178 non-null    float64
 12  Proline               178 non-null    int64  
dtypes: float64(11), int64(2)
memory usage: 18.2 KB

אין צורך לשחרר או להזין נתונים, בהתחשב בעובדה שאין ערכים ריקים במערך הנתונים. אנחנו יכולים להשתמש ב-Seaborn pairplot() כדי לראות את התפלגות הנתונים ולבדוק אם מערך הנתונים יוצר זוגות של עמודות שיכולות להיות מעניינות לאשכולות:

sns.pairplot(df)

מדריך סופי ל-K-Means Clustering עם Scikit-Learn PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

בהתבוננות בתרשים הזוגיות, שתי עמודות נראות מבטיחות למטרות מקבץ - Alcohol ו OD280 (שהיא שיטה לקביעת ריכוז החלבון ביינות). נראה שיש 3 אשכולות נפרדים על חלקות המשלבות שניים מהם.

ישנן עמודות אחרות שנראות גם בקורלציה. בעיקר Alcohol ו Total_Phenols, ו Alcohol ו Flavanoids. יש להם קשרים ליניאריים נהדרים שניתן לראות בתרשים הזוגי.

מכיוון שההתמקדות שלנו היא התקבצות עם K-Means, בואו נבחר זוג עמודות אחד, נניח Alcohol ו OD280, ובדוק את שיטת המרפק עבור מערך נתונים זה.

הערה: בעת שימוש בעמודות נוספות של מערך הנתונים, יהיה צורך בשרטוט ב-3 ממדים או בהקטנת הנתונים ל- רכיבים עיקריים (שימוש ב-PCA). זוהי גישה תקפה, ונפוצה יותר, רק הקפידו לבחור את הרכיבים העיקריים על סמך מידת ההסבר שלהם וקחו בחשבון שכאשר מצמצמים את ממדי הנתונים, יש אובדן מידע מסוים - כך שהעלילה היא אוּמדָן של הנתונים האמיתיים, לא איך הם באמת.

הבה נשרטט את תמונת הפיזור עם שתי העמודות הללו כציר שלה כדי להסתכל מקרוב על הנקודות שאנו רוצים לחלק לקבוצות:

sns.scatterplot(data=df, x='OD280', y='Alcohol')

מדריך סופי ל-K-Means Clustering עם Scikit-Learn PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

כעת נוכל להגדיר את העמודות שלנו ולהשתמש בשיטת המרפק כדי לקבוע את מספר האשכולות. אנו גם ניזום את האלגוריתם עם kmeans++ רק כדי לוודא שהוא מתכנס מהר יותר:

values = df[['OD280', 'Alcohol']]

wcss_wine = [] 
for i in range(1, 11): 
    kmeans = KMeans(n_clusters = i, init = 'k-means++', random_state = 42)
    kmeans.fit(values) 
    wcss_wine.append(kmeans.inertia_)

חישבנו את ה-WCSS, כדי שנוכל לשרטט את התוצאות:

clusters_wine = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
plt.plot(clusters_wine, wcss_wine)
plt.axvline(3, linestyle='--', color='r')

מדריך סופי ל-K-Means Clustering עם Scikit-Learn PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

לפי שיטת המרפק אמורים להיות לנו כאן 3 אשכולות. לשלב האחרון, בואו נקבץ את הנקודות שלנו ל-3 אשכולות ונתווה את האשכולות המזוהים לפי צבעים:

kmeans_wine = KMeans(n_clusters=3, random_state=42)
kmeans_wine.fit(values)
sns.scatterplot(x = values['OD280'], y = values['Alcohol'], hue=kmeans_wine.labels_)

מדריך סופי ל-K-Means Clustering עם Scikit-Learn PlatoBlockchain Data Intelligence. חיפוש אנכי. איי.

אנחנו יכולים לראות אשכולות 0, 1, ו 2 בגרף. בהתבסס על הניתוח שלנו, קבוצה 0 יש יינות עם תכולת חלבון גבוהה יותר ואלכוהול נמוך יותר, קבוצה 1 יש יינות עם אחוז אלכוהול גבוה יותר וחלבון נמוך, ו קבוצה 2 יש גם חלבון גבוה וגם אלכוהול גבוה ביינות.

זהו מערך נתונים מעניין מאוד ואני ממליץ לך ללכת רחוק יותר לתוך הניתוח על ידי קיבוץ הנתונים לאחר נורמליזציה ו-PCA - גם על ידי פירוש התוצאות ומציאת קשרים חדשים.

סיכום

K- אמצעים clustering הוא אלגוריתם למידת מכונה פשוט אך יעיל מאוד ללא פיקוח לאשכול נתונים. הוא מקבץ נתונים המבוססים על המרחק האוקלידי בין נקודות נתונים. לאלגוריתם K-Means אשכולות יש שימושים רבים לקיבוץ מסמכי טקסט, תמונות, סרטונים ועוד הרבה יותר.

בול זמן:

עוד מ Stackabuse