Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

(시리즈 글이 7개 있습니다.)
Math: 15. 그래프 그리기로 알아보는 뉴턴-랩슨(Newton-Raphson's method)법과 제곱근 구하기 - C#
; https://www.sysnet.pe.kr/2/0/10911

Math: 53. C# - 행렬식을 이용한 최소 자승법(LSM: Least Square Method)
; https://www.sysnet.pe.kr/2/0/11918

Math: 54. C# - 최소 자승법의 1차 함수에 대한 매개변수를 단순 for 문으로 구하는 방법
; https://www.sysnet.pe.kr/2/0/11919

Math: 55. C# - 다항식을 위한 최소 자승법(Least Squares Method)
; https://www.sysnet.pe.kr/2/0/11921

Math: 56. C# - 그래프 그리기로 알아보는 경사 하강법의 최소/최댓값 구하기
; https://www.sysnet.pe.kr/2/0/11923

Math: 57. C# - 해석학적 방법을 이용한 최소 자승법
; https://www.sysnet.pe.kr/2/0/11924

Math: 58. C# - 최소 자승법의 1차, 2차 수렴 그래프 변화 확인
; https://www.sysnet.pe.kr/2/0/11936




C# - 최소 자승법의 1차 함수에 대한 매개변수를 단순 for 문으로 구하는 방법

일단 행렬식을 이용하면,

C# - 행렬식을 이용한 최소 자승법(LSM: Least Square Method)
; https://www.sysnet.pe.kr/2/0/11918

범용적으로 다항식에 대한 근사를 최소 자승법(최소 제곱법)으로 구할 수 있습니다. 하지만, 만약 대상을 "1차 함수"로 직선에 대한 근사만을 구한다면 복잡한 행렬 연산 없이 for 문만으로 매개 변수를 구하는 것이 가능합니다.

가령, 지난번 예제의 행렬식을 보겠습니다.

θ0 + θ1x1 = y1
θ0 + θ1x2 = y2
...
θ0 + θ1xn = yn



AX=B
A-1AX=A-1B
X=A-1B (A-1 == 의사역행렬)

결국 중요한 것은, 위의 식에서 A-1B 연산 결과를 구하는 것인데요, 헷갈리니까 일단 의사역행렬을 A+라고 정의하고, 이것을 행렬 라이브러리를 이용하면 단순히 Matrix 타입의 PseudoInverse를 호출하는 것으로 쉽게 해결했지만 만약 직접 구하고 싶다면 다음과 같은 과정을 거쳐야 합니다.

A+ = (ATA)-1AT

따라서, 매개변수를 나타내는 행렬 X는 B 행렬까지 곱해주면서 다음과 같이 계산할 수 있습니다.

X = A+ * B
  = (ATA)-1AT * B

이제 남은 작업은 위의 식을 간략하게 바꿔주면 됩니다. ^^




이 상태에서 A 행렬을 보면 "n x 2" 행렬이고 이것의 전치 행렬(AT)은 "2 x n" 행렬이 됩니다. 또한 B 행렬도 "n x 1" 행렬임을 감안하면 연산 결과가 다음과 같이 정리될 수 있습니다.

X = (ATA)-1AT * B
  = ((2 x n) * (n x 2))-1 * (2 x n) * (n x 1)
  = (2 x 2)-1 * (2 x 1)
  = (2 x 2) * (2 x 1)
  = (2 x 1)

즉, X 행렬은 (당연히 1차 함수의 매개변수 2개를 구하는 것이므로) 언제나 "2 x 1" 행렬이 나오므로 X 행렬의 인덱스에 해당하는 값을 정리해 볼 수도 있습니다. 이 과정을 단계별로 천천히 ^^ 접근해 볼까요?





2 x 2 행렬의 역행렬은 다음과 같이 간략화할 수 있으므로,



ATA 결과의 역행렬을 구할 수 있습니다.



위의 결과를 2 x n 행렬의 AT와 연산을 하면 과정이 좀 복잡하니 어차피 행렬곱은 결합법칙이 성립하므로 뒤의 AT B 연산을 먼저 다음과 같이 정리할 수 있습니다.



마지막으로 (ATA)-1(2 x 2 행렬)에 AT * B(2 x 1 행렬)을 곱하는 것이므로 다음과 같이 최종 정리가 됩니다.



따라서 1차 방정식의 매개변수는 이렇게 단일 식으로 각각 구할 수 있습니다.






구하는 과정에 정리할 식이 좀 끼어들어서 그렇지, 사실 C# 코드로 위의 계산을 나타내면 별거 아닙니다. ^^

// 단순 for 루프를 이용한 계산
private static (double theta1, double theta0) GetEquation2(double[] xData, double[] yData)
{
    double sumAnBn = 0.0;
    double sumAn = 0.0;
    double sumBn = 0.0;
    double sumAnAn = 0.0;

    for (int i = 0; i < xData.Length; i ++)
    {
        sumAnBn += xData[i] * yData[i];
        sumAn += xData[i];
        sumBn += yData[i];
        sumAnAn += xData[i] * xData[i];
    }

    int n = xData.Length;
    double Q = sumAnAn * n - sumAn * sumAn;

    double theta1 = (n * sumAnBn - sumAn * sumBn) / Q;
    double theta0 = (-sumAn * sumAnBn + sumAnAn * sumBn) / Q;

    return (theta1, theta0);
}

// 행렬을 이용한 계산
private static (double theta1, double theta0) GetEquation(double[] xData, double[] yData)
{
    Matrix matA = CreateMatrix.DenseOfColumnMajor(xData.Count(), 1, xData);
    Vector add1 = Vector.Build.DenseOfArray(Enumerable.Repeat(1.0, xData.Count()).ToArray());
    Matrix matAwith1 = matA.InsertColumn(1, add1);

    Console.WriteLine(matAwith1);
    Matrix matB = CreateMatrix.DenseOfColumnMajor(yData.Count(), 1, yData);

    Matrix pinvMatA = matAwith1.PseudoInverse();
    Console.WriteLine(pinvMatA);

    Matrix matX = pinvMatA * matB;

    return (matX[0, 0], matX[1, 0]);
}

당연하겠지만 행렬식을 이용했던 GetEquation 메서드와 비교해 보면,

{
    // y = theta0 + (theta1 * x)
    (double theta1, double theta0) = GetEquation(xData, yData);
    Console.WriteLine($"[method1] y = {theta0} + {theta1} * x");
    /* 출력 결과
    [method1] y = 231.545758451005 + 1.39551018043075 * x
    */
}

{
    (double theta1, double theta0) = GetEquation2(xData, yData);
    Console.WriteLine($"[method2] y = {theta0} + {theta1} * x");
    /* 출력 결과
    [method2] y = 231.545758451006 + 1.39551018043075 * x
    */
}

부동소수점 계산임을 감안해 값이 거의 동일하다는 것을 알 수 있습니다.

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 5/28/2019]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2019-05-28 10시35분
선형 최소 제곱법(Linear Least Squares Method) 사용하기
; https://icodebroker.tistory.com/5579
정성태

... 16  17  18  [19]  20  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13156정성태11/7/20226806.NET Framework: 2064. C# - Mutex와 Semaphore/SemaphoreSlim 차이점파일 다운로드1
13155정성태11/4/20226306디버깅 기술: 183. TCP 동시 접속 (연결이 아닌) 시도를 1개로 제한한 서버
13154정성태11/3/20225780.NET Framework: 2063. .NET 5+부터 지원되는 GC.GetGCMemoryInfo파일 다운로드1
13153정성태11/2/20227072.NET Framework: 2062. C# - 코드로 재현하는 소켓 상태(SYN_SENT, SYN_RECV)
13152정성태11/1/20225692.NET Framework: 2061. ASP.NET Core - DI로 추가한 클래스의 초기화 방법 [1]
13151정성태10/31/20225792C/C++: 161. Windows 11 환경에서 raw socket 테스트하는 방법파일 다운로드1
13150정성태10/30/20225856C/C++: 160. Visual Studio 2022로 빌드한 C++ 프로그램을 위한 다른 PC에서 실행하는 방법
13149정성태10/27/20225775오류 유형: 825. C# - CLR ETW 이벤트 수신이 GCHeapStats_V1/V2에 대해 안 되는 문제파일 다운로드1
13148정성태10/26/20225783오류 유형: 824. msbuild 에러 - error NETSDK1005: Assets file '...\project.assets.json' doesn't have a target for 'net5.0'. Ensure that restore has run and that you have included 'net5.0' in the TargetFramew
13147정성태10/25/20224873오류 유형: 823. Visual Studio 2022 - Unable to attach to CoreCLR. The debugger's protocol is incompatible with the debuggee.
13146정성태10/24/20225710.NET Framework: 2060. C# - Java의 Xmx와 유사한 힙 메모리 최댓값 제어 옵션 HeapHardLimit
13145정성태10/21/20225997오류 유형: 822. db2 - Password validation for user db2inst1 failed with rc = -2146500508
13144정성태10/20/20225812.NET Framework: 2059. ClrMD를 이용해 윈도우 환경의 메모리 덤프로부터 닷넷 모듈을 추출하는 방법파일 다운로드1
13143정성태10/19/20226355오류 유형: 821. windbg/sos - Error code - 0x000021BE
13142정성태10/18/20225170도서: 시작하세요! C# 12 프로그래밍
13141정성태10/17/20226837.NET Framework: 2058. [in,out] 배열을 C#에서 C/C++로 넘기는 방법 - 세 번째 이야기파일 다운로드1
13140정성태10/11/20226215C/C++: 159. C/C++ - 리눅스 환경에서 u16string 문자열을 출력하는 방법 [2]
13139정성태10/9/20226023.NET Framework: 2057. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프로부터 모든 닷넷 모듈을 추출하는 방법파일 다운로드1
13138정성태10/8/20227339.NET Framework: 2056. C# - await 비동기 호출을 기대한 메서드가 동기로 호출되었을 때의 부작용 [1]
13137정성태10/8/20225685.NET Framework: 2055. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프로부터 닷넷 모듈을 추출하는 방법
13136정성태10/7/20226262.NET Framework: 2054. .NET Core/5+ SDK 설치 없이 dotnet-dump 사용하는 방법
13135정성태10/5/20226502.NET Framework: 2053. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프를 분석하는 방법 - 두 번째 이야기
13134정성태10/4/20225235오류 유형: 820. There is a problem with AMD Radeon RX 5600 XT device. For more information, search for 'graphics device driver error code 31'
13133정성태10/4/20225560Windows: 211. Windows - (commit이 아닌) reserved 메모리 사용량 확인 방법 [1]
13132정성태10/3/20225454스크립트: 42. 파이썬 - latexify-py 패키지 소개 - 함수를 mathjax 식으로 표현
13131정성태10/3/20228173.NET Framework: 2052. C# - Windows Forms의 데이터 바인딩 지원(DataBinding, DataSource) [2]파일 다운로드1
... 16  17  18  [19]  20  21  22  23  24  25  26  27  28  29  30  ...