C && C++/C++ X 머신러닝

다항회귀 예제(C++ with eigen library)

힘법사 2020. 10. 17. 21:08
728x90

 이번 포스팅에서는 C++로 다항회귀를 하는 것을 보여드리겠습니다. 이 예제를 차근차근 따라하시면 C++로 다항회귀를 해보실 수 있습니다.

0) 데이터 세트 만들기

 C++에서 회귀에 사용할 노이즈를 포함한 데이터 세트를 python코드를 통해서 만들었습니다. 아래에 코드를 첨부하겠습니다.

import numpy as np
import matplotlib.pyplot as plt

x = 5 * np.random.rand(1,150)
y = -2 * pow(x,3) + 9 *pow(x,2) + -3 * x + 7 + 4 * np.random.rand(1,150) 

그림. 타겟함수

위의 타겟함수에 대해서 데이터가 생성되었고, 이를 시각화 하니아래의 그림과 같은 도표를 얻었습니다.

그림. 노이즈가 포함된 데이터 세트

 이제 이를 C++에서 사용한 txt 파일로 만드는 작업을 해주면 C++에서 다항 회귀를할 준비가 끝나게 됩니다. 참고로, Eigen library가 사용되니 Eigen library 설치법을 모르시는 분은 아래의 포스팅을 보고 오시면 도움이 되실 겁니다.

xx = open("data_150.txt","w")
xx.write("150")
for i in range(150):
    xx.write("\n")
    xx.write(str(round(x[0][i],5)))
    xx.write("\t")
    xx.write(str(round(y[0][i],5)))
xx.close()

himbopsa.tistory.com/8

 

Eigen library 설치하기(C++)

1. Eigen library 다운하기 i) 다음 사이트에 접속합니다. eigen.tuxfamily.org/index.php?title=Main_Page Eigen Eigen is a C++ template library for linear algebra: matrices, vectors, numerical solvers, a..

himbopsa.tistory.com

 자 이제 txt file까지 획득 하시고 Eigen library도 준비가 되셨을 겁니다.  txt file을 C++ project folder에 옮겨 주십시오. 지금부터 C++로 다항회귀하는법을 설명드리겠습니다.

 

1) 아이디어

그림. 이번 포스팅의 원리를 설명하는 그림

이번 포스팅은 위의 식과 같이 선형대수학적인 해결법으로 Theta 값들 A, B, C, D를 얻게 됩니다. 이를 코드로 구현 하기만 한다면 A, B, C, D가 얻어질 것입니다.

 

2)C++ 코드

#include <iostream>
#include <fstream>
#include <Eigen/Dense>
#include <Eigen/LU>

using namespace std;
using namespace Eigen;

int main() {
	float N; // the number of dataes
	ifstream file("data_150.txt");
	file >> N;
	float *data_x = new float[N];
	float* data_y = new float[N];
	for (int i = 0; i < N; i++) {
		file >> data_x[i] >> data_y[i];
		cout << data_x[i];
		cout << " " << data_y[i] << endl;
	}
	float Nx = 0, Nx2 = 0, Nx3 = 0, Nx4 = 0, Nx5 = 0, Nx6 = 0, y = 0, yx = 0, yx2 = 0, yx3 = 0;
	Matrix4d X;
	Matrix4d XI;
	Vector4d Y;
	Vector4d O;
	for (int i = 0; i < N; i++) {
		Nx += data_x[i];
		Nx2 += data_x[i] * data_x[i];
		Nx3 += data_x[i] * data_x[i] * data_x[i];
		Nx4 += data_x[i] * data_x[i] * data_x[i] * data_x[i];
		Nx5 += data_x[i] * data_x[i] * data_x[i] * data_x[i] * data_x[i];
		Nx6 += data_x[i] * data_x[i] * data_x[i] * data_x[i] * data_x[i] * data_x[i];
		y += data_y[i];
		yx +=  data_y[i] * data_x[i];
		yx2 += data_y[i] * data_x[i] * data_x[i];
		yx3 += data_y[i] * data_x[i] * data_x[i] * data_x[i];
	}
	X(0, 0) = Nx3; X(0, 1) = Nx2; X(0, 2) = Nx; X(0, 3) = N;
	X(1, 0) = Nx4; X(1, 1) = Nx3; X(1, 2) = Nx2; X(1, 3) = Nx;
	X(2, 0) = Nx5; X(2, 1) = Nx4; X(2, 2) = Nx3; X(2, 3) = Nx2;
	X(3, 0) = Nx6; X(3, 1) = Nx5; X(3, 2) = Nx4; X(3, 3) = Nx3;

	XI = X.inverse();

	Y[0] = y; Y[1] = yx; Y[2] = yx2; Y[3] = yx3;
	cout << X << endl;
	cout << Y << endl;
	O = XI * Y;

	cout << O[0] << "x3" << " + "   << O[1] << "x2"<< " + " << O[2] << "x1"<< " + " << O[3] << endl;
	file.close();
	ofstream result("3-dgree-regression_results.txt");
	for (int i = 0; i < N; i++) {
		result << round(0.033 * i * 1000) / 1000 << "\t" << O[0] * round(0.033 * i * 1000) / 1000 * round(0.033 * i * 1000) / 1000* round(0.033 * i * 1000) / 1000 +
			O[1] * round(0.033 * i * 1000) / 1000 * round(0.033 * i * 1000) / 1000 + O[2] * round(0.033 * i * 1000) / 1000+ O[3];
		result << endl;
	}
	result.close();
	return 0;
}

1. 먼저 txt file에서 데이터의 크기 150을 받아오고 data_x data_y array에 메모리 할당을 해줍니다.

2. for문을 이용해 앞선 할당한 array에 데이터를 대입하여 줍니다.

3. 계산에 필요한 여러 변수들을 선언해주고 이때, Matrix와 Vector 또한 선언해 줍니다.

4. for문을 이용해 아이디에서 적었던 X행렬과 Y행렬을 구현합니다.

5. XI에 X의 역행렬을 저장하고 XI * Y를 Matrix O에 저장합니다.

6. A, B, C, D값을 대입해 함수를 프롬포트에 출력해주고 이를 이용해 다시 txtfile을 제작합니다.

 

3) 결과

 우리가 원하는 계수는 -2, 9, -3, 7 이었습니다.

그림. C++을 통해 얻은 계수

 이것이 C++로 다항회귀를 하여 복구한 함수입니다. 저희가 원했던 것과 매우 비슷하게 함수를 복구해냈습니다. x의 차수가 낮을 수록 오차가 늘었지만, 큰 차수가 함수에 영향을 더 많이 주기 때문에 크게 신경쓰이지 않는군요. 이를 다시 python으로 돌아가 그림을 그려 얼마나 회귀가 잘되었나 확인해보겠습니다.

import pandas as pd

xxx = open("3-dgree-regression_results.txt", "r")
data = xxx.read()
data = data.split("\n")
n_data = []
for i in data:
    n_data.append(i.split("\t"))
data = n_data[:-1]
X = []
Y = []

for i in data:
    X.append(float(i[0]))
    Y.append(float(i[1]))
plt.title("data for regression")
plt.plot(X,Y,"r-",label="recovered")
plt.plot(x,y, "b.")
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
plt.show()

  다음과 같이 코드를 써주시면 깔끔하게 도표를 얻으실 수있습니다.

그림. 기존 데이터와 이를 통해 회귀를 한 곡선의 모습

 도표를 보니 회귀가 깔끔하게 잘 이루어졌다는 것을 확인했습니다. 이런 방식으로 C++로도 다항 회귀 작업이 가능합니다. 이 예제에서는 3차 다항회귀를 다뤘지만 고차 다항회귀도 별 차이가 없기 때문에 코드를 조금 수정하는 것으로 빠르게 구현할 수 있습니다. 다음시간에는 또 다른 주제로 돌아오겠습니다. 감사합니다.

(python으로 회귀를 하는 법을 보고싶으신 분은 아래 링크로 가주시면 감사하겠습니다.)

himbopsa.tistory.com/10

 

다항 회귀(python with sklearn)

이 포스팅에서는 핸즈온 머신러닝 교재에 나오는 다항회귀에 대해서 논하겠습니다. C++로 회귀하는 작업을 보고싶은신 분은 아래 게시물을 참고해 주세요.( C++에서 회귀 코드를 보시면 정확하게

himbopsa.tistory.com

 

728x90