برازش منحنی مبتنی بر داده در متلب

یکی از سئوالاتی که غالبا در میان ایمیلهای ارسالی می بینم ، و در کلاس های آموزشی نیز به کرات مطرح می شوند، چگونگی برازش منحنی در متلب بر اساس داده است. فرض کنید:

  • شما یک دیتا ست دارید که متشکل از دو متغیر است، که آن ها را در این پست با نام X و Y نشان خواهیم داد؛
  • شما نیازمند مدلی (تابعی) هستید که ارتباط میان این دو متغیر را به صورت ریاضی توصیف نماید؛
  • و هیچ دانش اولیه ای در مورد چگونگی معادلات ریاضی توصیف کننده این ارتباط ندارید و فقط می بایست با استفاده از داده ای که در دست دارید، این مسأله را حل نمایید.

سئوال اصلی: چگونه می توان بهترین منحنی توصیف کننده ارتباط X و Y را به دست آورد؟

برای حل این مسأله روش های مختلفی عددی ارائه شده اند که می توان از روش های کلاسیک مبتنی بر چند جمله ای (مثلا روش لاگرانژ)، سیستم های فازی و شبکه های عصبی مصنوعی نام برد. در این پست، که از سلسله ترفندهای متلب در ایران متلب است، قصد داریم برخی از امکانات جعبه ابزار (تولباکس) برازش منحنی یا Curve Fitting Toolbox را برای حل مسأله برازش منحنی مبتنی بر داده به کار بگیریم. با ما در ادامه مطلب همراه باشید.

یکی از سئوالاتی که غالبا در میان ایمیلهای ارسالی می بینم، و در کلاس های آموزشی نیز به کرات مطرح می شوند، چگونگی برازش منحنی در متلب بر اساس داده است. فرض کنید:

  • شما یک دیتا ست دارید که متشکل از دو متغیر است، که آن ها را در این پست با نام X و Y نشان خواهیم داد؛
  • شما نیازمند مدلی (تابعی) هستید که ارتباط میان این دو متغیر را به صورت ریاضی توصیف نماید؛
  • و هیچ دانش اولیه ای در مورد چگونگی معادلات ریاضی توصیف کننده این ارتباط ندارید و فقط می بایست با استفاده از داده ای که در دست دارید، این مسأله را حل نمایید.

سئوال اصلی: چگونه می توان بهترین منحنی توصیف کننده ارتباط X و Y را به دست آورد؟

برای حل این مسأله روش های مختلفی عددی ارائه شده اند که می توان از روش های کلاسیک مبتنی بر چند جمله ای (مثلا روش لاگرانژ)، سیستم های فازی و شبکه های عصبی مصنوعی نام برد. در این پست، که از سلسله ترفندهای متلب در ایران متلب است، قصد داریم برخی از امکانات جعبه ابزار (تولباکس) برازش منحنی یا Curve Fitting Toolbox را برای حل مسأله برازش منحنی مبتنی بر داده به کار بگیریم.

تولید داده برای انجام برازش منحنی

هر چند کسی که قصد حل مسأله برازش منحنی را دارد، اصولا بایستی داده های مربوط به مسأله اش را داشته باشد، اما به دلیل این که این پست به منظور استفاده آموزشی نوشته می شود، ترجیح دادم که داده های مورد استفاده را به صورت مصنوعی و توسط خود متلب ایجاد نمایم. قطعا برای کسانی که قصد استفاده از مطالب مندرج در این پست را دارند، انجام مرحله تولید داده الزامی نیست. داده های مورد استفاده در این پست را با استفاده از قطعه کد زیر تولید می کنیم:

s = RandStream(‘mt19937ar’,’seed’,1971);
RandStream.setDefaultStream(s);

X = linspace(1,10,100);
X = X’;

% Specify the parameters for a second order Fourier series

w = .6067;
a0 = 1.6345;
a1 = -.6235;
b1 = -1.3501;
a2 = -1.1622;
b2 = -.9443;

% Fourier2 is the true (unknown) relationship between X and Y
Y = a0 + a1*cos(X*w) + b1*sin(X*w) + a2*cos(2*X*w) + b2*sin(2*X*w);

% Add in a noise vector

K = max(Y) – min(Y);
noisy = Y + .2*K*randn(100,1);

% Generate a scatterplot
scatter(X,noisy,’k’);
L2 = legend(‘Noisy Data Sample’, 2);
snapnow

این داده ها در واقع از یک سری فوریه دو جمله ای که با نویز جمع شده است، ایجاد شده اند. نمودار داده های تولید شده به صورتی است که در ادامه مشاهده می کنید:

رگرسیون غیر خطی

اگر شما (بر فرض محال) بدانید که داده های فوق از یک قاعده سری فوریه دو جمله ای تبعیت می کنند، با استفاده از رگرسیون غیر خطی می توانید تابعی مانند $$Y=f(X)$$ را بیابید. این کار با استفاده از کد زیر قابل انجام است:

foo = fit(X, noisy, ‘fourier2′)

% Plot the results

hold on
plot(foo)
L3 = legend(‘Noisy Data Sample’,’Nonlinear Regression’, 2);
hold off
snapnow

نتیجه اجرای این کد، که به صورت متغیر foo است، در ادامه نشان داده شده است. همچنین نموداری که پس از اجرای این قطعه کد نمایش داده می شود، در ادامه آمده است.

foo =
General model Fourier2:
foo(x) = a0 + a1*cos(x*w) + b1*sin(x*w) +
a2*cos(2*x*w) + b2*sin(2*x*w)
Coefficients (with 95% confidence bounds):
a0 = 1.734 (1.446, 2.021)
a1 = -0.1998 (-1.065, 0.6655)
b1 = -1.413 (-1.68, -1.146)
a2 = -0.7688 (-1.752, 0.2142)
b2 = -1.317 (-1.867, -0.7668)
w = 0.6334 (0.5802, 0.6866)

برازش منحنی بدون پارامتر

ظاهرا همه چیز مرتب به نظر می آید. اما در واقع ما تقلب کرده ایم. زیرا در ابتدای همین پست، فرض را بر این گذاشته بودیم که هیچ دانش اولیه ای در خصوص نوع رابطه موجود میان متغیرها نداریم. اما در کدی که در بند پیش نوشته شد، فرض کردیم که رابطه ریاضی موجود میان متغیرها، به صورت یک سری فوریه قابل توصیف است. در نتیجه، اعدادی هم که برای ضرایب به دست آمدند، نزدیک به اعدادی هستند که ما در مرحله تولید داده ها استفاده کرده بودیم.

در ادامه کدی را ارائه خواهیم کرد که بدون استفاده از هیچ دانش اضافی، و فقط صرفا با تکیه بر داده های اصلی (و البته با کمی فشار آوردن بر CPU کامپیوتر) مسأله برازش منحنی مبتنی بر داده را حل می کند. برای انجام این کار از الگوریتمی به نام LOWESS استفاده خواهیم کرد. دقت این الگوریتم وابسته به پارامتری است که در این الگوریتم به کار برده شده است و به نام پارامتر هموارسازی یا Smoothing شناخته می شود. ما در این بخش ۹۹ مدل LOWESS ایجاد خواهیم کرد که پارامتر هموارسازی آنها از صفر تا یک تغییر می کنند. از میان ۹۹ مدل ایجاد شده، بهترین مدل را انتخاب خواهیم کرد.

این روش منجر به ایجاد مشکل Over-fitting خواهد شد. زیرا الگوریتم LOWESS بخش های زاید داده ها (نویزها) را با همان دقتی تخمین خواهد زد که بخش اصلی داده ها را تخمین زده است. برای پیشگیری از پدیده Over-fitting از تکنیکی به نام اعتبار سنجی متقابل یا Cross Validation استفاده خواهیم کرد. در این روش داده های تولید شده به دو گروه تقسیم می شوند: (الف) داده های آموزش یا Train Data و (ب) داده های آزمایش یا Test Data.

الگوریتم LOWESS از داده های آموزش برای ایجاد مدل استفاده خواهد کرد، اما دقت مدل ارائه شده، با توجه به داده های آزمایش (تست) تعیین خواهند شد. معیار مورد استفاده برای ارزیابی مدل نیز، مجموع مربعات خطا در نظر گرفته شده است.

کدی که این عملیات را انجام می دهد در ادامه آمده است:

num = 99;
spans = linspace(.01,.99,num);
sse = zeros(size(spans));
cp = cvpartition(100,’k’,10);

for j=1:length(spans),
f = @(train,test) norm(test(:,2) – mylowess(train,test(:,1),spans(j)))^2;
sse(j) = sum(crossval(f,[X,noisy],’partition’,cp));
end

[minsse,minj] = min(sse);
span = spans(minj);

در کد فوق از تابعی به نام mylowess استفاده شده است که می بایست در فایلی به نام mylowess.m ذخیره شود. محتویات این فایل در ادامه آمده است:

function ys=mylowess(xy,xs,span)
%MYLOWESS Lowess smoothing, preserving x values
% YS=MYLOWESS(XY,XS) returns the smoothed version of the x/y data in the
% two-column matrix XY, but evaluates the smooth at XS and returns the
% smoothed values in YS. Any values outside the range of XY are taken to
% be equal to the closest values.

if nargin<3 || isempty(span)
span = .3;
end

% Sort and get smoothed version of xy data
xy = sortrows(xy);
x1 = xy(:,1);
y1 = xy(:,2);
ys1 = smooth(x1,y1,span,’loess’);

% Remove repeats so we can interpolate
t = diff(x1)==0;
x1(t)=[]; ys1(t) = [];

% Interpolate to evaluate this at the xs values
ys = interp1(x1,ys1,xs,’linear’,NaN);

% Some of the original points may have x values outside the range of the
% resampled data. Those are now NaN because we could not interpolate them.
% Replace NaN by the closest smoothed value. This amounts to extending the
% smooth curve using a horizontal line.
if any(isnan(ys))
ys(xsx1(end)) = ys1(end);
end

مقایسه نتایج به دست آمده

در ادامه نمودار مربوط به

  • داده های اصلی (که از معادله سری فئریه دو جمله ای تبعیت می کنند)،
  • نمونه های نویزی،
  • مدل رگرسیون غیر خطی (به دست آمده از تابع fit)،
  • و مدل به دست آمده از طریق الگوریتم LOWESS

را در کنار یکدیگیر و به منظور مقایسه ترسیم می کنیم. کدی که این کار را برای ما انجام می دهد، در ادامه آمده است.

plot(X,Y, ‘k’);
hold on
scatter(X,noisy, ‘k’);
plot(foo, ‘r’)

x = linspace(min(X),max(X));
line(x,mylowess([X,noisy],x,span),’color’,’b’,’linestyle’,’-’, ‘linewidth’,2)
legend(‘Clean Data’, ‘Noisy Sample’, ‘Nonlinear Regression’, ‘LOWESS’, 2)
hold off
snapnow

نمودار به دست آمده پس از اجرای این کد در ادامه نمایش داده شده است:

مشاهده می شود که نمودار مربوط به الگوریتم LOWESS بسیار نزدیک به نمودار اصلی است و توانسته است که به خوبی رفتار داده ها را مدل سازی نماید.

ترسیم بازه های اطمینان

با استفاده از قطعه کد زیر می توان بازه اطمینان مربوط به مدل LOWESS به دست آمده را نمایش داد:

 

scatter(X, noisy)

f = @(xy) mylowess(xy,X,span);
yboot2 = bootstrp(1000,f,[X,noisy])’;
meanloess = mean(yboot2,2);
h1 = line(X, meanloess,’color’,’k’,’linestyle’,’-’,’linewidth’,2);

stdloess = std(yboot2,0,2);
h2 = line(X, meanloess+2*stdloess,’color’,’r’,’linestyle’,’–’,’linewidth’,2);
h3 = line(X, meanloess-2*stdloess,’color’,’r’,’linestyle’,’–’,’linewidth’,2);

L5 = legend(‘Localized Regression’,’Confidence Intervals’,2);
snapnow

نموداری که پس از اجرای این کد به دست می آید در ادامه نمایش داده شده است:

 

امیدوارم که این مطلب بتواند پاسخگوی نیاز شما و سئوالاتی که در زمینه برازش منحنی داشته اید باشد. لطفا نظرات، سئوالات و پیشنهادهای خود را در بخش نظرات مربوط به همین پست وارد نمایید. منتظر پست های بعدی در زمینه ترفندهای متلب باشید.

 

 

4 دیدگاه دربارهٔ «برازش منحنی مبتنی بر داده در متلب»

  1. blank
    محمد مهدي كريمي قهي

    سلام وقت به خیر
    کاش علاوه بر متن آموزشی، فیلم آموزش این مورد رو هم بارگذاری کنید تا بتونه عمق یادگیری رو بیشتر کنه
    ممنون

دیدگاه‌ خود را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *