תְנַאִי מוּקדָם: K הקרוב ביותר שכנים
מָבוֹא
נניח שאנו מקבלים מערך נתונים של פריטים שלכל אחד מהם מאפיינים בעלי ערך מספרי (כמו גיל גובה משקל וכו'). אם ספירת התכונות היא נ אנחנו יכולים לייצג את הפריטים כנקודות ב-an נ -רשת ממדית. בהינתן פריט חדש נוכל לחשב את המרחק מהפריט לכל פריט אחר בסט. אנחנו בוחרים את ק השכנים הקרובים ביותר ואנחנו רואים איפה רוב השכנים האלה מסווגים. אנחנו מסווגים את הפריט החדש שם.
אז הבעיה הופכת כיצד נוכל לחשב את המרחקים בין פריטים. הפתרון לכך תלוי במערך הנתונים. אם הערכים אמיתיים אנו משתמשים בדרך כלל במרחק האוקלידי. אם הערכים הם קטגוריים או בינאריים אנו משתמשים בדרך כלל במרחק האמינג.
אַלגוֹרִיתְם:
Given a new item: 1. Find distances between new item and all other items 2. Pick k shorter distances 3. Pick the most common class in these k distances 4. That class is where we will classify the new item
קריאת נתונים
תן לקובץ הקלט שלנו להיות בפורמט הבא:
Height Weight Age Class 1.70 65 20 Programmer 1.90 85 33 Builder 1.78 76 31 Builder 1.73 74 24 Programmer 1.81 75 35 Builder 1.73 70 75 Scientist 1.80 71 63 Scientist 1.75 69 25 Programmer
כל פריט הוא שורה ותחת 'Class' אנו רואים היכן הפריט מסווג. הערכים מתחת לשמות התכונה ('Height' וכו') הם הערך שיש לפריט עבור אותה תכונה. כל הערכים והתכונות מופרדים בפסיקים.
מקם את קבצי הנתונים האלה בספריית העבודה נתונים2 ו נְתוּנִים . בחר אחד והדבק את התוכן כפי שהוא בקובץ טקסט בשם נְתוּנִים .
נקרא מהקובץ (ששמו 'data.txt') ונפצל את הקלט לפי שורות:
f = open('data.txt' 'r'); lines = f.read().splitlines(); f.close();
השורה הראשונה של הקובץ מכילה את שמות התכונות עם מילת המפתח 'כיתה' בסוף. אנו רוצים לאחסן את שמות התכונות ברשימה:
# Split the first line by commas # remove the first element and # save the rest into a list. The # list now holds the feature # names of the data set. features = lines[0].split(' ')[:-1];
לאחר מכן נעבור למערך הנתונים עצמו. נשמור את הפריטים ברשימה בשם פריטים שהמרכיבים שלהם הם מילונים (אחד לכל פריט). המפתחות למילון הפריטים הללו הם שמות התכונות בתוספת 'Class' כדי להחזיק את מחלקת הפריטים. בסופו של דבר אנחנו רוצים לערבב את הפריטים ברשימה (זהו אמצעי בטיחות למקרה שהפריטים נמצאים בסדר מוזר).
items = []; for i in range(1 len(lines)): line = lines[i].split(' '); itemFeatures = {'Class' : line[-1]}; # Iterate through the features for j in range(len(features)): # Get the feature at index j f = features[j]; # The first item in the line # is the class skip it v = float(line[j]); # Add feature to dict itemFeatures[f] = v; # Append temp dict to items items.append(itemFeatures); shuffle(items);
סיווג הנתונים
עם הנתונים המאוחסנים ב פריטים עכשיו אנחנו מתחילים לבנות את המסווג שלנו. עבור המסווג ניצור פונקציה חדשה לסווג . זה ייקח כקלט את הפריט שאנו רוצים לסווג את רשימת הפריטים ו ק מספר השכנים הקרובים ביותר.
אִם ק גדול מאורך מערך הנתונים, איננו ממשיכים בסיווג מכיוון שלא יכולים להיות לנו יותר שכנים קרובים יותר מכמות הפריטים הכוללת במערך הנתונים. (לחלופין נוכל להגדיר k בתור פריטים אורך במקום להחזיר הודעת שגיאה)
רשימה מקושרת java
if(k > len(Items)): # k is larger than list # length abort return 'k larger than list length';
אנחנו רוצים לחשב את המרחק בין הפריט שיש לסווג לבין כל הפריטים בסט ההדרכה בסופו של דבר תוך שמירה על ק המרחקים הקצרים ביותר. כדי לשמור על השכנים הקרובים ביותר הנוכחיים אנו משתמשים ברשימה בשם שכנים . כל רכיב לפחות מכיל שני ערכים, אחד עבור המרחק מהפריט שיש לסווג ואחר עבור המחלקה שבה נמצא השכן. נחשב מרחק באמצעות הנוסחה האוקלידית המוכללת (עבור נ מידות). לאחר מכן נבחר את הכיתה שמופיעה רוב הזמן בה שכנים וזו תהיה הבחירה שלנו. בקוד:
def Classify(nItem k Items): if(k > len(Items)): # k is larger than list # length abort return 'k larger than list length'; # Hold nearest neighbors. # First item is distance # second class neighbors = []; for item in Items: # Find Euclidean Distance distance = EuclideanDistance(nItem item); # Update neighbors either adding # the current item in neighbors # or not. neighbors = UpdateNeighbors(neighbors item distance k); # Count the number of each # class in neighbors count = CalculateNeighborsClass(neighbors k); # Find the max in count aka the # class with the most appearances. return FindMax(count);
הפונקציות החיצוניות שאנו צריכים ליישם הן מרחק אוקלידי עדכון שכנים חשב שכנים בכיתה ו FindMax .
מציאת מרחק אוקלידי
הנוסחה האוקלידית המוכללת עבור שני וקטורים x ו-y היא זו:
distance = sqrt{(x_{1}-y_{1})^2 + (x_{2}-y_{2})^2 + ... + (x_{n}-y_{n})^2}
בקוד:
מילון מיון פיתוןPython3
def EuclideanDistance(x y): # The sum of the squared # differences of the elements S = 0; for key in x.keys(): S += math.pow(x[key]-y[key] 2); # The square root of the sum return math.sqrt(S);
מעדכן את השכנים
יש לנו את רשימת השכנים שלנו (שצריכה להיות לכל היותר באורך של ק ) ואנו רוצים להוסיף פריט לרשימה עם מרחק נתון. ראשית נבדוק אם שכנים יש אורך של ק . אם יש לו פחות אנו מוסיפים לו את הפריט ללא קשר למרחק (כפי שאנו צריכים למלא את הרשימה עד ק לפני שנתחיל לדחות פריטים). אם לא נבדוק אם לפריט יש מרחק קצר יותר מהפריט עם המרחק המקסימלי ברשימה. אם זה נכון, נחליף את הפריט עם מרחק מקסימלי בפריט החדש.
כדי למצוא את פריט המרחק המקסימלי מהר יותר, נשמור את הרשימה ממוינת בסדר עולה. אז לפריט האחרון ברשימה יהיה המרחק המקסימלי. נחליף אותו בפריט חדש ונמיין אותו שוב.
כדי להאיץ את התהליך הזה אנחנו יכולים ליישם Insertion Sort שבו נכניס פריטים חדשים לרשימה מבלי שנצטרך למיין את הרשימה כולה. הקוד עבור זה עם זאת הוא די ארוך ולמרות שהוא פשוט יבלבל את ההדרכה.
def UpdateNeighbors(neighbors item distance k): if(len(neighbors) > distance): # If yes replace the last # element with new item neighbors[-1] = [distance item['Class']]; neighbors = sorted(neighbors); return neighbors;
חשב שכנים בכיתה
כאן נחשב את המחלקה המופיעה בתדירות הגבוהה ביותר שכנים . לשם כך נשתמש במילון אחר בשם לִסְפּוֹר כאשר המפתחות הם שמות המחלקות המופיעות ב שכנים . אם מפתח לא קיים נוסיף אותו אחרת נעלה את הערך שלו.
def CalculateNeighborsClass(neighbors k): count = {}; for i in range(k): if(neighbors[i][1] not in count): # The class at the ith index # is not in the count dict. # Initialize it to 1. count[neighbors[i][1]] = 1; else: # Found another item of class # c[i]. Increment its counter. count[neighbors[i][1]] += 1; return count;
FindMax
נזין לפונקציה זו את המילון לִסְפּוֹר אנחנו בונים חשב שכנים בכיתה ונחזיר את המקסימום שלו.
def FindMax(countList): # Hold the max maximum = -1; # Hold the classification classification = ''; for key in countList.keys(): if(countList[key] > maximum): maximum = countList[key]; classification = key; return classification maximum;
מַסְקָנָה
בכך הסתיים הדרכת kNN הזו.
כעת תוכל לסווג הגדרות פריטים חדשים ק כפי שאתה רואה לנכון. בדרך כלל עבור k משתמשים במספר אי זוגי אבל זה לא הכרחי. כדי לסווג פריט חדש עליך ליצור מילון עם מפתחות שמות התכונות והערכים המאפיינים את הפריט. דוגמה לסיווג:
newItem = {'Height' : 1.74 'Weight' : 67 'Age' : 22}; print Classify(newItem 3 items);
הקוד המלא של הגישה לעיל ניתן להלן: -
# Python Program to illustrate # KNN algorithm # For pow and sqrt import math from random import shuffle ###_Reading_### def ReadData(fileName): # Read the file splitting by lines f = open(fileName 'r') lines = f.read().splitlines() f.close() # Split the first line by commas # remove the first element and save # the rest into a list. The list # holds the feature names of the # data set. features = lines[0].split(' ')[:-1] items = [] for i in range(1 len(lines)): line = lines[i].split(' ') itemFeatures = {'Class': line[-1]} for j in range(len(features)): # Get the feature at index j f = features[j] # Convert feature value to float v = float(line[j]) # Add feature value to dict itemFeatures[f] = v items.append(itemFeatures) shuffle(items) return items ###_Auxiliary Function_### def EuclideanDistance(x y): # The sum of the squared differences # of the elements S = 0 for key in x.keys(): S += math.pow(x[key] - y[key] 2) # The square root of the sum return math.sqrt(S) def CalculateNeighborsClass(neighbors k): count = {} for i in range(k): if neighbors[i][1] not in count: # The class at the ith index is # not in the count dict. # Initialize it to 1. count[neighbors[i][1]] = 1 else: # Found another item of class # c[i]. Increment its counter. count[neighbors[i][1]] += 1 return count def FindMax(Dict): # Find max in dictionary return # max value and max index maximum = -1 classification = '' for key in Dict.keys(): if Dict[key] > maximum: maximum = Dict[key] classification = key return (classification maximum) ###_Core Functions_### def Classify(nItem k Items): # Hold nearest neighbours. First item # is distance second class neighbors = [] for item in Items: # Find Euclidean Distance distance = EuclideanDistance(nItem item) # Update neighbors either adding the # current item in neighbors or not. neighbors = UpdateNeighbors(neighbors item distance k) # Count the number of each class # in neighbors count = CalculateNeighborsClass(neighbors k) # Find the max in count aka the # class with the most appearances return FindMax(count) def UpdateNeighbors(neighbors item distance k ): if len(neighbors) < k: # List is not full add # new item and sort neighbors.append([distance item['Class']]) neighbors = sorted(neighbors) else: # List is full Check if new # item should be entered if neighbors[-1][0] > distance: # If yes replace the # last element with new item neighbors[-1] = [distance item['Class']] neighbors = sorted(neighbors) return neighbors ###_Evaluation Functions_### def K_FoldValidation(K k Items): if K > len(Items): return -1 # The number of correct classifications correct = 0 # The total number of classifications total = len(Items) * (K - 1) # The length of a fold l = int(len(Items) / K) for i in range(K): # Split data into training set # and test set trainingSet = Items[i * l:(i + 1) * l] testSet = Items[:i * l] + Items[(i + 1) * l:] for item in testSet: itemClass = item['Class'] itemFeatures = {} # Get feature values for key in item: if key != 'Class': # If key isn't 'Class' add # it to itemFeatures itemFeatures[key] = item[key] # Categorize item based on # its feature values guess = Classify(itemFeatures k trainingSet)[0] if guess == itemClass: # Guessed correctly correct += 1 accuracy = correct / float(total) return accuracy def Evaluate(K k items iterations): # Run algorithm the number of # iterations pick average accuracy = 0 for i in range(iterations): shuffle(items) accuracy += K_FoldValidation(K k items) print accuracy / float(iterations) ###_Main_### def main(): items = ReadData('data.txt') Evaluate(5 5 items 100) if __name__ == '__main__': main()
תְפוּקָה:
0.9375
התפוקה יכולה להשתנות ממכונה למכונה. הקוד כולל פונקציית Fold Validation אבל זה לא קשור לאלגוריתם הוא שם לחישוב הדיוק של האלגוריתם.
צור חידון