JavaRush /בלוג Java /Random-HE /למידת מכונה למפתחי Java, חלק 2

למידת מכונה למפתחי Java, חלק 2

פורסם בקבוצה
למידת מכונה למפתחי Java, חלק 1
למידת מכונה למפתחי Java, חלק 2 - 1

הערכת פונקציה אובייקטיבית

הבה נזכיר שפונקציית המטרה , הידועה גם כפונקציית החיזוי, היא תוצאה של תהליך ההכנה או האימון. מבחינה מתמטית, האתגר הוא למצוא פונקציה שלוקחת משתנה כקלט хומחזירה את הערך החזוי у.
למידת מכונה למפתחי Java, חלק 2 - 2
בלמידת מכונה, פונקציית עלות (J(θ))משמשת לחישוב ערך השגיאה או ה"עלות" של פונקציית מטרה נתונה.
למידת מכונה למפתחי Java, חלק 2 - 3
פונקציית העלות מראה עד כמה המודל מתאים לנתוני האימון. כדי לקבוע את העלות של פונקציית המטרה המוצגת לעיל, יש צורך לחשב את השגיאה בריבוע של כל בית לדוגמה (i). שגיאה היא המרחק בין הערך המחושב уלערך האמיתי yשל הבית מהדוגמה i.
למידת מכונה למפתחי Java, חלק 2 - 4
לדוגמה, המחיר האמיתי של בית עם שטח של 1330 = 6,500,000 € . וההבדל בין מחיר הבית החזוי על ידי פונקציית המטרה המאומנת הוא €7,032,478 : ההפרש (או השגיאה) הוא €532,478 . אתה יכול גם לראות את ההבדל הזה בגרף למעלה. ההבדל (או השגיאה) מוצג כקווים אדומים מקווקו אנכיים עבור כל זוג אימונים של מחיר-אזור. לאחר חישוב העלות של פונקציית המטרה המאומנת, עליך לסכם את השגיאה בריבוע עבור כל בית בדוגמה ולחשב את הערך העיקרי. ככל שערך המחיר קטן יותר (J(θ)), כך תחזיות הפונקציה האובייקטיבית שלנו יהיו מדויקות יותר. רשימה 3 מציגה יישום פשוט של Java של פונקציית עלות שמקבלת כקלט פונקציה אובייקטיבית, רשימה של נתוני אימון ותוויות הקשורות אליהם. ערכי החיזוי יחושבו בלולאה והשגיאה תחושב על ידי הפחתת ערך המחיר האמיתי (נלקח מהתווית). בהמשך, ריבוע השגיאות יסוכם וערך השגיאה יחושב. העלות תוחזר כערך מסוג double:

רישום-3

public static double cost(Function<ltDouble[], Double> targetFunction,
 List<ltDouble[]> dataset,
 List<ltDouble> labels) {
 int m = dataset.size();
 double sumSquaredErrors = 0;

 // рассчет квадрата ошибки («разницы») для каждого тренировочного примера и //добавление его к сумме
 for (int i = 0; i < m; i++) {
 // получаем вектор признаков из текущего примера
 Double[] featureVector = dataset.get(i);
 // предсказываем meaning и вычисляем ошибку базируясь на реальном
 //значении (метка)
 double predicted = targetFunction.apply(featureVector);
 double label = labels.get(i);
 double gap = predicted - label;
 sumSquaredErrors += Math.pow(gap, 2);
 }

 // Вычисляем и возращаем meaning ошибки (чем меньше тем лучше)
 return (1.0 / (2 * m)) * sumSquaredErrors;
}
מעוניין לקרוא על Java? הצטרף לקבוצת מפתחי Java !

לימוד פונקציית המטרה

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

ירידה בשיפוע

ירידה בשיפוע ממזערת את פונקציית העלות. המשמעות היא שהוא משמש כדי למצוא את פרמטרי התטא בעלי העלות המינימלית (J(θ))בהתבסס על נתוני ההדרכה. להלן אלגוריתם פשוט לחישוב ערכי תטא חדשים ומתאימים יותר:
למידת מכונה למפתחי Java, חלק 2 - 5
לכן, הפרמטרים של וקטור התטא ישתפרו עם כל איטרציה של האלגוריתם. מקדם הלמידה α מציין את מספר החישובים בכל איטרציה. ניתן לבצע חישובים אלה עד למציאת ערכי תטא "טובים". לדוגמה, לפונקציית הרגרסיה הלינארית למטה יש שלושה פרמטרים של תטא:
למידת מכונה למפתחי Java, חלק 2 - 6
בכל איטרציה, יחושב ערך חדש עבור כל אחד מפרמטרי תטא: , , ו . לאחר כל איטרציה, ניתן ליצור מימוש חדש ומתאים יותר באמצעות וקטור התטא החדש 0 , θ 1 , θ 2 } . רישום -4 מציג את קוד ה-Java עבור אלגוריתם הדעיכה של גרדיאנט. תטא עבור פונקציית הרגרסיה יאומן באמצעות נתוני אימון, נתוני סמן, קצב למידה . התוצאה תהיה פונקציה אובייקטיבית משופרת באמצעות פרמטרי תטא. השיטה תיקרא שוב ושוב, ותעביר את פונקציית המטרה החדשה ואת פרמטרי התטא החדשים מחישובים קודמים. והשיחות הללו יחזרו על עצמן עד שפונקציית המטרה המוגדרת תגיע לרמה מינימלית: θ0θ1θ2LinearRegressionFunction(α)train()

רישום-4

public static LinearRegressionFunction train(LinearRegressionFunction targetFunction,
 List<ltDouble[]> dataset,
 List<ltDouble> labels,
 double alpha) {
 int m = dataset.size();
 double[] thetaVector = targetFunction.getThetas();
 double[] newThetaVector = new double[thetaVector.length];

 // вычисление нового значения тета для каждого element тета массива
 for (int j = 0; j < thetaVector.length; j++) {
 // сумируем разницу ошибки * признак
 double sumErrors = 0;
 for (int i = 0; i < m; i++) {
 Double[] featureVector = dataset.get(i);
 double error = targetFunction.apply(featureVector) - labels.get(i);
 sumErrors += error * featureVector[j];
 }

 //вычисляем новые значения тета
 double gradient = (1.0 / m) * sumErrors;
 newThetaVector[j] = thetaVector[j] - alpha * gradient;
 }

 return new LinearRegressionFunction(newThetaVector);
}
כדי להבטיח שהעלות יורדת ללא הרף, תוכל להפעיל את פונקציית העלות J(θ)לאחר כל שלב אימון. לאחר כל איטרציה, העלות אמורה לרדת. אם זה לא קורה, זה אומר שהערך של מקדם הלמידה גדול מדי והאלגוריתם פשוט החמיץ את הערך המינימלי. במקרה כזה, אלגוריתם דעיכת השיפוע נכשל. התרשים שלהלן מציגים את פונקציית המטרה תוך שימוש בפרמטרים החדשים והמחושבים של תטא, החל בוקטור התטא ההתחלתי {1.0, 1.0}. העמודה השמאלית מציגה את העלילה של פונקציית החיזוי לאחר 50 איטרציות; עמודה אמצעית לאחר 200 חזרות; והעמודה הימנית לאחר 1000 חזרות. מתוך אלו אנו יכולים לראות שהמחיר יורד לאחר כל איטרציה, ופונקציית האובייקטיב החדשה מתאימה יותר ויותר. לאחר 500-600 חזרות, פרמטרי התטא כבר לא משתנים משמעותית, והמחיר מגיע לרמה יציבה. לאחר מכן, לא ניתן לשפר את הדיוק של פונקציית המטרה בדרך זו.
למידת מכונה למפתחי Java, חלק 2 - 7
במקרה זה, למרות שהעלות כבר לא יורדת משמעותית לאחר 500-600 איטרציות, הפונקציה האובייקטיבית עדיין לא אופטימלית. זה מעיד על אי התאמה . בלמידת מכונה, המונח "חוסר עקביות" משמש כדי לציין שאלגוריתם הלמידה אינו מוצא מגמות בסיסיות בנתונים. בהתבסס על ניסיון בחיים האמיתיים, סביר להניח שצפוי להוזלה במחיר למ"ר עבור נכסים גדולים יותר. מכאן נוכל להסיק שהמודל המשמש לתהליך למידת פונקציית המטרה אינו מתאים מספיק לנתונים. הפער נובע לרוב מפישוט יתר של המודל. זה קרה במקרה שלנו, הפונקציה האובייקטיבית פשוטה מדי, ולניתוח היא משתמשת בפרמטר בודד - שטח הבית. אבל המידע הזה לא מספיק כדי לחזות במדויק את מחיר הבית.

הוספת תכונות והגדלתן

אם אתה מגלה שפונקציית המטרה שלך אינה מתאימה לבעיה שאתה מנסה לפתור, יש להתאים אותה. דרך נפוצה לתקן חוסר עקביות היא הוספת תכונות נוספות לווקטור התכונה. בדוגמה של מחיר בית ניתן להוסיף מאפיינים כמו מספר חדרים או גיל הבית. כלומר, במקום להשתמש בוקטור בעל ערך תכונה אחד {size}לתיאור בית, ניתן להשתמש בווקטור עם מספר ערכים, לדוגמה, {size, number-of-rooms, age}. במקרים מסוימים, מספר התכונות בנתוני האימון הזמינים אינו מספיק. אז כדאי לנסות להשתמש בתכונות פולינומיות שמחושבות באמצעות הקיימות. לדוגמה, יש לך הזדמנות להרחיב את הפונקציה האובייקטיבית לקביעת מחיר הבית כך שתכלול תכונה מחושבת של מטרים רבועים (x2):
למידת מכונה למפתחי Java, חלק 2 - 8
שימוש במספר תכונות דורש קנה מידה של תכונות , המשמש לסטנדרטיזציה של הטווח על פני תכונות שונות. לפיכך, טווח הערכים של תכונת הגודל 2 גדול משמעותית מטווח הערכים של תכונת הגודל. ללא קנה מידה של תכונה, גודל 2 ישפיע בצורה בלתי הולמת על פונקציית העלות. השגיאה שמוצגת על ידי תכונת size 2 תהיה גדולה משמעותית מהשגיאה שמוצגת על ידי תכונת size. אלגוריתם קנה מידה פשוט של תכונה ניתן להלן:
למידת מכונה למפתחי Java, חלק 2 - 9
אלגוריתם זה מיושם במחלקה FeaturesScalingבקוד לדוגמה למטה. הכיתה FeaturesScalingמציגה שיטה מסחרית ליצירת פונקציית קנה מידה המכווננת לנתוני אימון. באופן פנימי, מופעי נתוני האימון משמשים לחישוב הערכים הממוצעים, המינימום והמקסימום. הפונקציה המתקבלת לוקחת את וקטור התכונה ומייצרת אחד חדש עם התכונות המותאמות. קנה המידה של תכונות הכרחי הן לתהליך הלמידה והן לתהליך החיזוי, כפי שמוצג להלן:
// создание массива данных
List<ltDouble[]> dataset = new ArrayList<>();
dataset.add(new Double[] { 1.0, 90.0, 8100.0 }); // feature vector of house#1
dataset.add(new Double[] { 1.0, 101.0, 10201.0 }); // feature vector of house#2
dataset.add(new Double[] { 1.0, 103.0, 10609.0 }); // ...
//...

// создание меток
List<ltDouble> labels = new ArrayList<>();
labels.add(249.0); // price label of house#1
labels.add(338.0); // price label of house#2
labels.add(304.0); // ...
//...

// создание расширенного списка признаков
Function<ltDouble[], Double[]> scalingFunc = FeaturesScaling.createFunction(dataset);
List<ltDouble[]> scaledDataset = dataset.stream().map(scalingFunc).collect(Collectors.toList());

// создаем функцию которая инициализирует теты и осуществляет обучение //используя коэффициент обучения 0.1

LinearRegressionFunction targetFunction = new LinearRegressionFunction(new double[] { 1.0, 1.0, 1.0 });
for (int i = 0; i < 10000; i++) {
 targetFunction = Learner.train(targetFunction, scaledDataset, labels, 0.1);
}

// делаем предсказание стоимости дома с площадью 600 m2
Double[] scaledFeatureVector = scalingFunc.apply(new Double[] { 1.0, 600.0, 360000.0 });
double predictedPrice = targetFunction.apply(scaledFeatureVector);
ככל שמתווספים יותר ויותר תכונות, ההתאמה לפונקציה האובייקטיבית גדלה, אך היזהר. אם תלך רחוק מדי ותוסיף יותר מדי תכונות, אתה עלול בסופו של דבר ללמוד פונקציה אובייקטיבית שהיא כושר יתר.

התאמה יתר ואימות צולב

התאמת יתר מתרחשת כאשר הפונקציה או המודל האובייקטיבי מתאימים יותר מדי לנתוני האימון, עד כדי כך שהם קולטים רעש או שינויים אקראיים בנתוני האימון. דוגמה להתאמת יתר מוצגת בגרף הימני ביותר למטה:
למידת מכונה למפתחי Java, חלק 2 - 10
עם זאת, מודל התאמה יתר מתפקד טוב מאוד בנתוני אימון, אך יבצע ביצועים גרועים בנתונים לא ידועים אמיתיים. ישנן מספר דרכים להימנע מהתאמה יתר.
  • השתמש במערך נתונים גדול יותר לאימון.
  • השתמש בפחות תכונות כפי שמוצג בתרשימים למעלה.
  • השתמש באלגוריתם למידת מכונה משופר שלוקח בחשבון רגולציה.
אם אלגוריתם חיזוי מתאים יותר מדי לנתוני האימון, יש צורך לבטל תכונות שאינן מועילות לדיוק שלה. הקושי הוא למצוא תכונות שיש להן השפעה משמעותית יותר על דיוק החיזוי מאחרות. כפי שמוצג בגרפים, ניתן לקבוע התאמה חזותית באמצעות גרפים. זה עובד היטב עבור גרפים עם 2 או 3 קואורדינטות, זה הופך להיות קשה לשרטט ולהעריך את הגרף אם אתה משתמש ביותר מ-2 תכונות. באימות צולב, אתה בודק מחדש מודלים לאחר אימון תוך שימוש בנתונים שאינם ידועים לאלגוריתם לאחר השלמת תהליך האימון. יש לחלק את הנתונים המסומנים הזמינים ל-3 קבוצות:
  • נתוני אימון;
  • נתוני אימות;
  • נתוני בדיקה.
במקרה זה, יש להשתמש ב-60 אחוז מהרשומות המסומנות המאפיינות את הבתים בתהליך של אימון גרסאות של אלגוריתם היעד. לאחר תהליך האימון, יש להשתמש במחצית מהנתונים הנותרים (שלא נעשה בהם שימוש קודם) כדי לוודא שאלגוריתם היעד המאומן מתפקד היטב על הנתונים הלא ידועים. בדרך כלל, האלגוריתם שפועל טוב יותר מאחרים נבחר לשימוש. שאר הנתונים משמשים לחישוב ערך השגיאה עבור המודל הסופי שנבחר. ישנן טכניקות אימות צולבות אחרות, כגון k-fold . עם זאת, לא אתאר אותם במאמר זה.

כלי למידת מכונה ומסגרת Weka

רוב המסגרות והספריות מספקות אוסף נרחב של אלגוריתמים של למידת מכונה. בנוסף, הם מספקים ממשק נוח ברמה גבוהה להדרכה, בדיקה ועיבוד מודלים של נתונים. Weka היא אחת המסגרות הפופולריות ביותר עבור ה-JVM. Weka היא ספריית Java מעשית המכילה מבחנים גרפיים לאימות מודלים. הדוגמה שלהלן משתמשת בספריית Weka כדי ליצור מערך הדרכה המכיל תכונות ותוויות. שיטה setClassIndex()- לסימון. ב-Weka, תווית מוגדרת כמחלקה:
// определяем атрибуты для признаков и меток
ArrayList<ltAttribute> attributes = new ArrayList<>();
Attribute sizeAttribute = new Attribute("sizeFeature");
attributes.add(sizeAttribute);
Attribute squaredSizeAttribute = new Attribute("squaredSizeFeature");
attributes.add(squaredSizeAttribute);
Attribute priceAttribute = new Attribute("priceLabel");
attributes.add(priceAttribute);


// создаем и заполняем список признаков 5000 примеров
Instances trainingDataset = new Instances("trainData", attributes, 5000);
trainingDataset.setClassIndex(trainingSet.numAttributes() - 1);
Instance instance = new DenseInstance(3);

instance.setValue(sizeAttribute, 90.0);
instance.setValue(squaredSizeAttribute, Math.pow(90.0, 2));
instance.setValue(priceAttribute, 249.0);
trainingDataset.add(instance);
Instance instance = new DenseInstance(3);
instance.setValue(sizeAttribute, 101.0);
...
ניתן לשמור ולטעון את ערכת הנתונים ואת אובייקט הדוגמה מקובץ. Weka משתמשת ב-ARFF (פורמט קובץ יחסי תכונה) הנתמך על ידי המדדים הגרפיים של Weka. מערך נתונים זה משמש לאימון פונקציה אובייקטיבית המכונה מסווג ב-Weka. קודם כל, עליך להגדיר את פונקציית המטרה. הקוד שלהלן LinearRegressionיצור מופע של המסווג. מסווג זה יקבל הכשרה באמצעות buildClassifier(). השיטה buildClassifier()בוחרת פרמטרי תטא על סמך נתוני אימון בחיפוש אחר מודל היעד הטוב ביותר. עם Weka, אתה לא צריך לדאוג לגבי הגדרת קצב הלמידה או מספר האיטרציות. Weka גם מבצעת שינוי קנה מידה עצמאי.
Classifier targetFunction = new LinearRegression();
targetFunction.buildClassifier(trainingDataset);
לאחר ביצוע הגדרות אלה, ניתן להשתמש בפונקציה האובייקטיבית כדי לחזות את מחיר הבית, כפי שמוצג להלן:
Instances unlabeledInstances = new Instances("predictionset", attributes, 1);
unlabeledInstances.setClassIndex(trainingSet.numAttributes() - 1);
Instance unlabeled = new DenseInstance(3);
unlabeled.setValue(sizeAttribute, 1330.0);
unlabeled.setValue(squaredSizeAttribute, Math.pow(1330.0, 2));
unlabeledInstances.add(unlabeled);

double prediction = targetFunction.classifyInstance(unlabeledInstances.get(0));
Weka מספקת שיעור Evaluationלבדיקת מסווג או דגם מיומן. בקוד שלהלן, נעשה שימוש במערך נבחר של נתוני אימות כדי למנוע תוצאות שגויות. תוצאות המדידה (עלות השגיאה) יוצגו בקונסולה. בדרך כלל, תוצאות הערכה משמשות להשוואת מודלים שהוכשרו באמצעות אלגוריתמים שונים של למידת מכונה, או וריאציות של אלה:
Evaluation evaluation = new Evaluation(trainingDataset);
evaluation.evaluateModel(targetFunction, validationDataset);
System.out.println(evaluation.toSummaryString("Results", false));
הדוגמה שלמעלה משתמשת ברגרסיה ליניארית, אשר מנבאת ערכים מספריים, כגון מחיר בית, על סמך ערכי קלט. רגרסיה לינארית תומכת בחיזוי של ערכים מספריים מתמשכים. כדי לחזות ערכים בינאריים ("כן" ו"לא"), עליך להשתמש באלגוריתמים אחרים של למידת מכונה. למשל, עץ החלטות, רשתות עצביות או רגרסיה לוגיסטית.
// использование логистической регрессии
Classifier targetFunction = new Logistic();
targetFunction.buildClassifier(trainingSet);
אתה יכול להשתמש באחד מאלגוריתמים אלה, למשל, כדי לחזות אם הודעת דואר אלקטרוני היא ספאם, או לחזות את מזג האוויר, או לחזות אם בית יימכר היטב. אם אתה רוצה ללמד את האלגוריתם שלך לחזות את מזג האוויר או כמה מהר בית יימכר, אתה צריך מערך נתונים אחר, למשל.topseller:
// использование атрибута маркера topseller instead of атрибута маркера цена
ArrayList<string> classVal = new ArrayList<>();
classVal.add("true");
classVal.add("false");

Attribute topsellerAttribute = new Attribute("topsellerLabel", classVal);
attributes.add(topsellerAttribute);
מערך נתונים זה ישמש לאימון מסווג חדש topseller. לאחר הכשרה, קריאת החיזוי אמורה להחזיר אינדקס מחלקה אסימון שניתן להשתמש בו כדי לקבל את הערך החזוי.
int idx = (int) targetFunction.classifyInstance(unlabeledInstances.get(0));
String prediction = classVal.get(idx);

סיכום

למרות שלמידת מכונה קשורה קשר הדוק לסטטיסטיקה ומשתמשת במושגים מתמטיים רבים, ערכת הכלים למידת מכונה מאפשרת לך להתחיל לשלב למידת מכונה בתוכניות שלך ללא ידע מעמיק במתמטיקה. עם זאת, ככל שתבינו טוב יותר את האלגוריתמים הבסיסיים של למידת מכונה, כמו אלגוריתם הרגרסיה הליניארית שבדקנו במאמר זה, כך תוכלו לבחור את האלגוריתם הנכון ולכוון אותו לביצועים מיטביים. תרגום מאנגלית. מחבר: גרגור רוט, אדריכל תוכנה, JavaWorld.
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION