التعلم الآلي لمطوري جافا، الجزء الأول
التعلم الآلي لمطوري جافا، الجزء 2 - 1

تقدير الوظيفة الموضوعية

دعونا نتذكر أن وظيفة الهدف ، والمعروفة أيضًا بوظيفة التنبؤ، هي نتيجة عملية الإعداد أو التدريب. رياضيًا، يتمثل التحدي في العثور على دالة تأخذ متغيرًا كمدخل хوتعيد القيمة المتوقعة у.
التعلم الآلي لمطوري جافا، الجزء 2 - 2
في التعلم الآلي، يتم استخدام دالة التكلفة (J(θ))لحساب قيمة الخطأ أو "تكلفة" دالة موضوعية معينة.
التعلم الآلي لمطوري جافا، الجزء 2 - 3
توضح دالة التكلفة مدى ملاءمة النموذج لبيانات التدريب. لتحديد تكلفة دالة الهدف الموضحة أعلاه، من الضروري حساب مربع الخطأ لكل منزل مثال (i). الخطأ هو المسافة بين القيمة المحسوبة уوالقيمة الحقيقية yللمنزل من المثال i.
التعلم الآلي لمطوري جافا، الجزء 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;
}
هل أنت مهتم بالقراءة عن جافا؟ انضم إلى مجموعة مطوري جافا !

تعلم وظيفة الهدف

على الرغم من أن دالة التكلفة تساعد في تقييم جودة الدالة الموضوعية ومعلمات ثيتا، إلا أنك لا تزال بحاجة إلى العثور على معلمات ثيتا الأكثر ملاءمة. يمكنك استخدام خوارزمية النسب المتدرجة لهذا الغرض.

نزول متدرج

يقلل النزول المتدرج من وظيفة التكلفة. وهذا يعني أنه يتم استخدامه للعثور على معلمات ثيتا التي لها الحد الأدنى من التكلفة (J(θ))بناءً على بيانات التدريب. فيما يلي خوارزمية مبسطة لحساب قيم ثيتا الجديدة والأكثر ملاءمة:
التعلم الآلي لمطوري جافا، الجزء 2 - 5
لذا، فإن معلمات ناقل ثيتا سوف تتحسن مع كل تكرار للخوارزمية. يحدد معامل التعلم α عدد العمليات الحسابية في كل تكرار. يمكن إجراء هذه الحسابات حتى يتم العثور على قيم ثيتا "الجيدة". على سبيل المثال، تحتوي دالة الانحدار الخطي أدناه على ثلاث معلمات ثيتا:
التعلم الآلي لمطوري جافا، الجزء 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 تكرار، لم تعد معلمات ثيتا تتغير بشكل ملحوظ، ويصل السعر إلى هضبة مستقرة. وبعد ذلك، لا يمكن تحسين دقة الوظيفة المستهدفة بهذه الطريقة.
التعلم الآلي لمطوري جافا، الجزء 2 - 7
في هذه الحالة، على الرغم من أن التكلفة لم تعد تنخفض بشكل ملحوظ بعد 500-600 تكرار، فإن الوظيفة الهدف لا تزال غير مثالية. وهذا يدل على وجود تناقض . في التعلم الآلي، يُستخدم مصطلح "عدم الاتساق" للإشارة إلى أن خوارزمية التعلم لا تجد الاتجاهات الأساسية في البيانات. واستنادا إلى تجربة الحياة الواقعية، فمن المرجح أن نتوقع انخفاضا في سعر المتر المربع للعقارات الأكبر حجما. من هذا يمكننا أن نستنتج أن النموذج المستخدم لعملية تعلم الوظيفة المستهدفة لا يتناسب مع البيانات بشكل كافٍ. غالبًا ما يكون التناقض بسبب التبسيط المفرط للنموذج. لقد حدث هذا في حالتنا، فالدالة الموضوعية بسيطة للغاية، وللتحليل تستخدم معلمة واحدة - مساحة المنزل. لكن هذه المعلومات ليست كافية للتنبؤ بدقة بسعر المنزل.

إضافة الميزات وتوسيع نطاقها

إذا وجدت أن وظيفتك الهدفية لا تتوافق مع المشكلة التي تحاول حلها، فيجب تعديلها. إحدى الطرق الشائعة لتصحيح عدم الاتساق هي إضافة ميزات إضافية إلى ناقل الميزات. في مثال سعر المنزل، يمكنك إضافة خصائص مثل عدد الغرف أو عمر المنزل. أي أنه بدلاً من استخدام متجه بقيمة ميزة واحدة {size}لوصف منزل، يمكنك استخدام متجه بعدة قيم، على سبيل المثال، {size, number-of-rooms, age}. في بعض الحالات، لا يكون عدد الميزات في بيانات التدريب المتوفرة كافيًا. ثم يجدر محاولة استخدام ميزات متعددة الحدود التي يتم حسابها باستخدام الميزات الموجودة. على سبيل المثال، لديك الفرصة لتوسيع الدالة الموضوعية لتحديد سعر المنزل بحيث تتضمن الخاصية المحسوبة للمتر المربع (x2):
التعلم الآلي لمطوري جافا، الجزء 2 - 8
يتطلب استخدام ميزات متعددة تحجيم الميزات ، والذي يُستخدم لتوحيد النطاق عبر ميزات مختلفة. وبالتالي، فإن نطاق قيم سمة الحجم 2 أكبر بكثير من نطاق قيم سمة الحجم. وبدون تحجيم الميزات، سيؤثر الحجم 2 بشكل غير ملائم على دالة التكلفة. سيكون الخطأ الذي تقدمه سمة الحجم 2 أكبر بكثير من الخطأ الذي تقدمه سمة الحجم. فيما يلي خوارزمية بسيطة لقياس الميزات:
التعلم الآلي لمطوري جافا، الجزء 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);
مع إضافة المزيد والمزيد من الميزات، يزداد الملاءمة للوظيفة الموضوعية، ولكن كن حذرًا. إذا ذهبت بعيدًا وأضفت الكثير من الميزات، فقد ينتهي بك الأمر إلى تعلم وظيفة موضوعية زائدة عن الحاجة.

الإفراط في المطابقة والتحقق المتبادل

يحدث التجاوز عندما تتناسب الوظيفة أو النموذج الهدف مع بيانات التدريب بشكل جيد للغاية، لدرجة أنها تلتقط الضوضاء أو الاختلافات العشوائية في بيانات التدريب. يظهر مثال على التجهيز الزائد في الرسم البياني الموجود في أقصى اليمين أدناه:
التعلم الآلي لمطوري جافا، الجزء 2 - 10
ومع ذلك، فإن نموذج التجهيز الزائد يؤدي أداءً جيدًا للغاية في بيانات التدريب، ولكنه سيكون أداؤه سيئًا في البيانات الحقيقية غير المعروفة. هناك عدة طرق لتجنب التجهيز الزائد.
  • استخدم مجموعة بيانات أكبر للتدريب.
  • استخدم ميزات أقل كما هو موضح في الرسوم البيانية أعلاه.
  • استخدم خوارزمية التعلم الآلي المحسنة التي تأخذ التنظيم في الاعتبار.
إذا تجاوزت خوارزمية التنبؤ بيانات التدريب، فمن الضروري إزالة الميزات التي لا تفيد دقتها. تكمن الصعوبة في العثور على الميزات التي لها تأثير أكبر على دقة التنبؤ من غيرها. كما هو موضح في الرسوم البيانية، يمكن تحديد التناسب الزائد بصريًا باستخدام الرسوم البيانية. يعمل هذا بشكل جيد مع الرسوم البيانية ذات الإحداثيات 2 أو 3، ويصبح من الصعب رسم الرسم البياني وتقييمه إذا كنت تستخدم أكثر من ميزتين. في التحقق المتبادل، يمكنك إعادة اختبار النماذج بعد التدريب باستخدام بيانات غير معروفة للخوارزمية بعد اكتمال عملية التدريب. ينبغي تقسيم البيانات المصنفة المتوفرة إلى 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.