یادگیری ماشین برای توسعه دهندگان جاوا، قسمت 1
برآورد تابع هدف
به یاد بیاوریم که تابع هدف
hθ
، که به عنوان تابع پیش بینی نیز شناخته می شود، نتیجه فرآیند آماده سازی یا آموزش است. از نظر ریاضی، چالش پیدا کردن تابعی است که یک متغیر را به عنوان ورودی دریافت کرده
х
و مقدار پیشبینی شده را برمیگرداند
у
.
در یادگیری ماشینی، یک تابع هزینه
(J(θ))
برای محاسبه مقدار خطا یا "هزینه" یک تابع هدف معین استفاده می شود.
تابع هزینه نشان می دهد که مدل چقدر با داده های آموزشی مطابقت دارد. برای تعیین هزینه تابع هدف نشان داده شده در بالا، لازم است مجذور خطای هر خانه نمونه محاسبه شود
(i)
. خطا فاصله بین مقدار محاسبه شده
у
و ارزش واقعی
y
خانه از مثال است
i
.
به عنوان مثال، قیمت واقعی یک خانه با متراژ
1330 = 6،500،000 € . و تفاوت بین قیمت مسکن پیش بینی شده توسط تابع هدف آموزش دیده
7,032,478 یورو است : تفاوت (یا خطا)
532,478 یورو است . همچنین می توانید این تفاوت را در نمودار بالا مشاهده کنید. تفاوت (یا خطا) به صورت خطوط قرمز چین عمودی برای هر جفت آموزشی منطقه قیمت نشان داده می شود. پس از محاسبه هزینه تابع هدف آموزش داده شده، باید مجذور خطای هر خانه را در مثال جمع کنید و مقدار اصلی را محاسبه کنید. هرچه مقدار قیمت کوچکتر باشد
(J(θ))
، پیش بینی های تابع هدف ما دقیق تر خواهد بود. لیست
3 یک پیاده سازی ساده جاوا از یک تابع هزینه را نشان می دهد که یک تابع هدف، لیستی از داده های آموزشی و برچسب های مرتبط با آنها را به عنوان ورودی می گیرد. مقادیر پیش بینی در یک حلقه محاسبه می شود و خطا با کم کردن ارزش واقعی قیمت (برگرفته از برچسب) محاسبه می شود. بعداً مجذور خطاها جمع می شود و مقدار خطا محاسبه می شود. هزینه به عنوان مقدار از نوع برگردانده می شود
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);
double predicted = targetFunction.apply(featureVector);
double label = labels.get(i);
double gap = predicted - label;
sumSquaredErrors += Math.pow(gap, 2);
}
return (1.0 / (2 * m)) * sumSquaredErrors;
}
یادگیری تابع هدف
اگرچه تابع هزینه به ارزیابی کیفیت تابع هدف و پارامترهای تتا کمک می کند، اما همچنان باید مناسب ترین پارامترهای تتا را پیدا کنید. برای این کار می توانید از الگوریتم نزول گرادیان استفاده کنید.
گرادیان نزول
نزول گرادیان تابع هزینه را به حداقل می رساند. این بدان معنی است که برای یافتن پارامترهای تتا که حداقل هزینه را
(J(θ))
بر اساس داده های آموزشی دارند، استفاده می شود. در اینجا یک الگوریتم ساده شده برای محاسبه مقادیر تتا جدید و مناسب تر آمده است:
بنابراین، پارامترهای بردار تتا با هر تکرار الگوریتم بهبود می یابد. ضریب یادگیری α تعداد محاسبات را در هر تکرار مشخص می کند. این محاسبات را می توان تا زمانی که مقادیر تتا "خوب" پیدا کرد انجام داد. به عنوان مثال، تابع رگرسیون خطی زیر دارای سه پارامتر تتا است:
در هر تکرار، یک مقدار جدید برای هر یک از پارامترهای تتا محاسبه می شود: , و . پس از هر تکرار، می توان یک پیاده سازی جدید و مناسب تر با استفاده از بردار تتا جدید
{θ 0 , θ 1 , θ 2 } ایجاد کرد . لیست
-4 کد جاوا را برای الگوریتم فروپاشی گرادیان نشان می دهد. تتا برای تابع رگرسیون با استفاده از داده های آموزشی، داده های نشانگر، نرخ یادگیری آموزش داده می شود . نتیجه یک تابع هدف بهبود یافته با استفاده از پارامترهای تتا خواهد بود. این روش بارها و بارها فراخوانی می شود و تابع هدف جدید و پارامترهای تتا جدید از محاسبات قبلی را ارسال می کند. و این فراخوانی ها تا زمانی که تابع هدف پیکربندی شده به حداقل پلاتو برسد تکرار خواهند شد:
θ0
θ1
θ2
LinearRegressionFunction
(α)
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];
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 تکرار، پارامترهای تتا دیگر تغییر قابل توجهی نمی کنند و قیمت به یک فلات ثابت می رسد. پس از این، دقت تابع هدف را نمی توان به این روش بهبود بخشید.
در این حالت، حتی اگر هزینه بعد از 500-600 تکرار دیگر به طور قابل توجهی کاهش نمی یابد، تابع هدف هنوز بهینه نیست. این نشان دهنده
یک اختلاف است . در یادگیری ماشینی، اصطلاح "ناسازگاری" برای نشان دادن اینکه الگوریتم یادگیری روندهای اساسی در داده ها را پیدا نمی کند استفاده می شود. بر اساس تجربه واقعی، احتمالاً انتظار کاهش قیمت هر متر مربع برای املاک بزرگتر وجود دارد. از این می توان نتیجه گرفت که مدل مورد استفاده برای فرآیند یادگیری تابع هدف به اندازه کافی با داده ها مطابقت ندارد. این اختلاف اغلب به دلیل ساده سازی بیش از حد مدل است. این در مورد ما اتفاق افتاد ، تابع هدف بسیار ساده است و برای تجزیه و تحلیل از یک پارامتر واحد - مساحت خانه استفاده می کند. اما این اطلاعات برای پیش بینی دقیق قیمت یک خانه کافی نیست.
افزودن ویژگی ها و مقیاس بندی آنها
اگر متوجه شدید که تابع هدف شما با مشکلی که میخواهید حل کنید مطابقت ندارد، باید آن را تنظیم کنید. یک راه متداول برای تصحیح ناسازگاری، افزودن ویژگی های اضافی به بردار ویژگی است. در مثال قیمت خانه می توانید مشخصاتی مانند تعداد اتاق یا قدمت خانه را اضافه کنید. یعنی به جای استفاده از یک بردار با یک مقدار ویژگی
{size}
برای توصیف یک خانه، می توانید از یک بردار با چندین مقدار استفاده کنید، به عنوان مثال،
{size, number-of-rooms, age}.
در برخی موارد، تعداد ویژگی ها در داده های آموزشی موجود کافی نیست. سپس ارزش تلاش برای استفاده از ویژگی های چند جمله ای را دارد که با استفاده از ویژگی های موجود محاسبه می شوند. به عنوان مثال، شما این فرصت را دارید که تابع هدف را برای تعیین قیمت یک خانه گسترش دهید تا شامل یک ویژگی محاسبه شده متر مربع (x2) باشد:
استفاده از ویژگیهای چندگانه به
مقیاسبندی ویژگی نیاز دارد که برای استاندارد کردن محدوده بین ویژگیهای مختلف استفاده میشود. بنابراین، محدوده مقادیر برای ویژگی
اندازه 2 به طور قابل توجهی بزرگتر از محدوده مقادیر برای ویژگی اندازه است. بدون مقیاس بندی ویژگی،
اندازه 2 به طور غیرمنطقی بر تابع هزینه تأثیر می گذارد. خطای معرفی شده توسط ویژگی
اندازه 2 به طور قابل توجهی بیشتر از خطای معرفی شده توسط ویژگی اندازه خواهد بود. یک الگوریتم مقیاس بندی ویژگی ساده در زیر آورده شده است:
FeaturesScaling
این الگوریتم در کد مثال زیر در کلاس پیاده سازی شده است . کلاس
FeaturesScaling
یک روش تجاری برای ایجاد یک تابع مقیاس بندی ارائه می دهد که برای داده های آموزشی تنظیم شده است. در داخل، نمونه های داده های آموزشی برای محاسبه مقادیر میانگین، حداقل و حداکثر استفاده می شود. تابع به دست آمده بردار ویژگی را می گیرد و یک بردار جدید با ویژگی های مقیاس شده تولید می کند. مقیاس بندی ویژگی هم برای فرآیند یادگیری و هم برای فرآیند پیش بینی ضروری است، همانطور که در زیر نشان داده شده است:
List<ltDouble[]> dataset = new ArrayList<>();
dataset.add(new Double[] { 1.0, 90.0, 8100.0 });
dataset.add(new Double[] { 1.0, 101.0, 10201.0 });
dataset.add(new Double[] { 1.0, 103.0, 10609.0 });
List<ltDouble> labels = new ArrayList<>();
labels.add(249.0);
labels.add(338.0);
labels.add(304.0);
Function<ltDouble[], Double[]> scalingFunc = FeaturesScaling.createFunction(dataset);
List<ltDouble[]> scaledDataset = dataset.stream().map(scalingFunc).collect(Collectors.toList());
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);
}
Double[] scaledFeatureVector = scalingFunc.apply(new Double[] { 1.0, 600.0, 360000.0 });
double predictedPrice = targetFunction.apply(scaledFeatureVector);
با افزودن ویژگی های بیشتر و بیشتر، تناسب با تابع هدف افزایش می یابد، اما مراقب باشید. اگر بیش از حد پیش بروید و ویژگی های زیادی اضافه کنید، ممکن است در نهایت یک تابع هدف را یاد بگیرید که بیش از حد مناسب است.
تطبیق بیش از حد و اعتبار متقابل
تطبیق بیش از حد زمانی اتفاق میافتد که تابع یا مدل هدف به خوبی با دادههای آموزشی مطابقت داشته باشد، به طوری که نویز یا تغییرات تصادفی در دادههای آموزشی را ضبط کند. نمونه ای از نصب بیش از حد در سمت راست نمودار زیر نشان داده شده است:
با این حال، یک مدل بیش از حد برازش در داده های آموزشی بسیار خوب عمل می کند، اما در داده های ناشناخته واقعی ضعیف عمل می کند. راه های مختلفی برای جلوگیری از برازش بیش از حد وجود دارد.
- از مجموعه داده های بزرگتری برای آموزش استفاده کنید.
- همانطور که در نمودارهای بالا نشان داده شده است از ویژگی های کمتری استفاده کنید.
- از یک الگوریتم یادگیری ماشین بهبودیافته استفاده کنید که منظمسازی را در نظر میگیرد.
اگر یک الگوریتم پیشبینی بیش از حد با دادههای آموزشی تناسب داشته باشد، لازم است ویژگیهایی که به دقت آن فایده ندارند حذف شوند. مشکل در یافتن ویژگی هایی است که تأثیر قابل توجهی بر دقت پیش بینی نسبت به سایرین دارند. همانطور که در نمودارها نشان داده شده است، اضافه فیت را می توان به صورت بصری با استفاده از نمودارها تعیین کرد. این برای نمودارهایی با 2 یا 3 مختصات خوب عمل می کند، اگر از بیش از 2 ویژگی استفاده کنید ترسیم و ارزیابی نمودار دشوار می شود. در اعتبارسنجی متقاطع، پس از تکمیل فرآیند آموزش، مدلها را پس از آموزش با استفاده از دادههای ناشناخته برای الگوریتم، دوباره آزمایش میکنید. داده های برچسب دار موجود باید به 3 مجموعه تقسیم شوند:
- داده های آموزشی؛
- داده های تأیید؛
- داده های تست
در این مورد، 60 درصد از رکوردهای برچسب زده شده مشخص کننده خانه ها باید در فرآیند آموزش انواع الگوریتم هدف استفاده شود. پس از فرآیند آموزش، نیمی از دادههای باقیمانده (که قبلاً استفاده نشدهاند) باید برای تأیید اینکه الگوریتم هدف آموزشدیده به خوبی روی دادههای ناشناخته عمل میکند، استفاده شود. به طور معمول، الگوریتمی که عملکرد بهتری نسبت به سایرین دارد برای استفاده انتخاب می شود. داده های باقی مانده برای محاسبه مقدار خطا برای مدل نهایی انتخاب شده استفاده می شود. تکنیکهای اعتبارسنجی متقابل دیگری مانند
k-fold وجود دارد . با این حال، من آنها را در این مقاله شرح نمی دهم.
ابزارهای یادگیری ماشین و چارچوب Weka
اکثر چارچوب ها و کتابخانه ها مجموعه گسترده ای از الگوریتم های یادگیری ماشین را ارائه می دهند. علاوه بر این، آنها یک رابط کاربری سطح بالا برای آموزش، آزمایش و پردازش مدلهای داده ارائه میکنند. Weka یکی از محبوب ترین فریم ورک ها برای JVM است. Weka یک کتابخانه کاربردی جاوا است که شامل تست های گرافیکی برای اعتبارسنجی مدل ها می باشد. مثال زیر از کتابخانه 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);
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 (Attribute Relation File Format) استفاده می کند که توسط معیارهای گرافیکی 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:
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.
GO TO FULL VERSION