[MATLAB] 비선형 회귀, nlinfit함수와 nlpredci함수

코딩/matlab|2019. 11. 6. 10:06

선형 회귀가 어떤 데이터 분포를 가장 잘 설명해내는 직선을 찾아내는 것이라면, 비선형 회귀는 곡선을 찾아내는 것이다. 오늘은 비선형 회귀를 MATLAB으로 어떻게 구현하는지 알아보자. 

 

어떤 한 동네의 원룸 월세가 아래와 같다고 가정해보자. 

 

 

데이터를 보기 쉽게 그래프로 나타내면 다음과 같다. 

 

 

우리가 하고 싶은 것은 data fitting을 해서 이 데이터를 가장 잘 설명할 수 있는 곡선, 즉 함수를 찾는 것이다. 이 데이터를 잘 설명할 수 있을 것 같은 함수의 형태는 아래와 같다. 

 

 

눈치 빠른 분들은 이미 알아차리셨겠지만 일종의 시그모이드(sigmoid) 함수다. nonlinear fitting을 위해서는 이러한 함수를 잘 찾아주는 것이 중요하다. 데이터의 분포를 이차 함수로 잘 fitting 해낼 수 있을 것 같으면 이차 함수로, 3차 함수가 적당할 것 같으면 3차 함수로, 로그 함수가 적당할 것 같으면 로그 함수로 해주는 것이다. 

 

이제 관건은 최적의 파라미터들 b1, b2, b3를 찾는 것이다. 그 작업을 nlinfit 함수가 해준다. nlinfit은 nonlinear fitting의 약자다. 먼저 b1, b2, b3에 대한 초기 값을 설정해준다. 제대로 된 b1, b2, b3를 찾기 위해서는 가장 실제와 비슷할 만한 값으로 설정해주는 것이 좋다. 그래서 나는 b1 = (월세의 최댓값) - (월세의 최소값), b2 = (평수의 평균값), b3 = (월세의 최솟값)으로 초기설정해줬다. 그 다음에 nlinfit 함수를 실행하면 이 데이터 분포에 가장 적합하다고 생각하는 b1, b2, b3를 알려준다. 이제 b1은 초기값 25에서 25.1082로, b2는 초기값 7.8792에서 8.2202로, b3는 초기값 19에서 19.2907로 조정되었다. 이 조정된 파라미터들을 대입해서 함수의 그래프를 그려보면 아래와 같이 굵은 선으로 표현된다. 데이터를 적절히 fitting 했음을 확인할 수 있다. 

 

 

이제 nlpredci 함수(nonlinear regression prediction confidence intervals)를 이용하면 임의의 평수에 대한 월세를 예측할 수 있다. 평수가 3.5, 7.8, 13.2일 때의 월세를 예측해보자. 사실상 파라미터가 조정된 함수에 x값들을 대입하는 과정이라고 보면 된다. 

 

 

빨간 십자가로 표시된 부분이 평수 3.5, 7.8, 13.2에 대한 월세들을 알려주는 지점들이다. 3.5평의 집은 19.5125만원, 7.8평은 29.2454만원, 13.2평은 44.2275만원의 월세가 예상된다.

 

이 전체 과정에 대한 MATLAB 코드는 다음과 같다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
clc, clear, close all
 
= [3; 3.6; 4; 4.4; 5; 5.3; 5.5; 6; 6.5; 7; 7.5; 8; 8.3; 8.5; 9; 9.2; 9.7; 10; 10.5; 11; 11.2; 11.5; 12; 12.4]; % 집의 평수
= [19; 19; 20; 20; 20; 21; 21; 22; 23; 25; 28; 30; 32; 34; 37; 36; 40; 42; 42; 43; 43; 43; 44; 44]; % 월세
 
plot(x, y, 'b*');
xlabel('집의 평수 (m^2)');
ylabel('월세 (만원)');
grid on
 
modelfun = @(b, x)(b(1)./(1+exp(-x+b(2)))+b(3));
= [max(y)-min(y); mean(x); min(y)];
 
[beta, R, J] = nlinfit(x, y, modelfun, b); % beta에 b의 업데이트된 값이 저장된다.
 
= 0:0.01:15;
[ypred1, delta1] = nlpredci(modelfun, t, beta, R, 'Jacobian', J);
hold on;
plot(t, ypred1, 'Color', 'black', 'LineWidth', 2);
 
test_x = [3.5; 7.8; 13.2]; % 월세를 예측하고 싶은 집의 평수들
[test_ypred, delta] = nlpredci(modelfun, test_x, beta, R, 'Jacobian', J); % test_ypred에 예측된 월세가 저장된다.
plot(test_x, test_ypred, 'r+', 'MarkerSize', 10, 'LineWidth', 5);
cs

 

코드를 한 줄씩 분석해보면 그다지 어렵지 않을 것입니다. 혹시 궁금점 또는 잘못된 점이 있다면 댓글로 남겨주세요.^^ 

 

  1. AnYongHyun 2020.06.29 05:22 댓글주소  수정/삭제  댓글쓰기

    안녕하세요 좋은 글 감사합니다. 큰 도움이 되었습니다. 그런데 제가 이글을 따라서 아래 데이터를 회귀하려고해보니.. 자꾸 초기값으로 회귀가 되네요.. 초기 값을 나름 미리 적절히 비슷하게 맞춰준건데도.. 그리고 초기 값을 엉터리로 넣어줘도 회귀가 그냥 초기 값으로 되네요 무엇이 문제일까요 ㅠㅠ

    function p = Poly()

    x=[0 ; 0.05; 0.1; 0.2 ; 0.3; 0.4; 0.5; 0.7; 0.8; 0.9; 1; ];
    y=[0.95 ;0.9; 0.87 ;0.8 ;0.6; 0.6; 0.4; 0.3; 0.3; 0.3; 0.3;];

    modelfun = @(b,x)(b(1) ./ (1+exp(b(2)*(x+b(3))))+b(4));
    b = [1; 8; -0.65; 1;] % 초기 값
    [beta, R, J] = nlinfit(x, y, modelfun,b); % beta에 새로운 b 값 저장


    % p = polyfit(x,y,16);
    test_x = linspace(0.01,1);
    [gamma delta] = nlpredci(modelfun, test_x, beta, R, 'Jacobian', J);
    plot(test_x, gamma)
    % y= polyval(p,x);
    plot(x,y)

    • BlogIcon 비스카이비전 2020.06.29 13:55 신고 댓글주소  수정/삭제

      저는 남겨주신 코드 그대로 실행해보니 초기값으로 수렴하지 않고, 0.7313, 7.1183, -0.3061, 0.2813의 beta값을 얻었습니다. 제 생각엔 제대로 실행되는 것 같은데요??^^