Linear Regression - 4

지금까지 해왔던 것과 달리, 이번 글에서는 행렬을 이용해 Linear Regression을 해보도록 하겠습니다.

데이터와 가중치는 아래 그림과 같이 행렬로 구성되어 있으며, 행렬 곱을 이용해 계산합니다.

위 사진의 식은 아래와 같이 계산됩니다.

사실 바로 전 글인 변수와 가중치가 각각 n개 있는 데이터를 학습하는 것과 같지만, 데이터가 행렬 형태로 들어온다는 점이 다릅니다.

데이터는 아래와 같이 저장합니다.

1
2
3
4
5
6
7
8
9
typedef vector<float> vf;
typedef vector<vf> vff;
vff data = {
		{1.0, 1.0, 3.0, 1.0, 5.0},
		{1.0, 2.0, 1.0, 4.0, 1.0},
		{1.0, 1.0, 1.0, 1.0, 1.0}
	};
vf y = {2.0, 4.0, 6.0, 8.0, 10.0};
vf w(3, 20);

여기서 쓰일 가설 함수는 이전에 쓰던 것과 매개변수의 종류가 약간 다릅니다.

1
2
3
4
5
6
7
float f(vf &w, vff &x, int idx){ //idx번째 데이터에 대한 가설 함수
	float sum = 0.0f;
	for(int i=0; i<w.size(); i++){
		sum += w[i] * x[i][idx];
	}
	return sum;
}

idx라는 매개 변수는 행렬에 있는 전체 데이터 중 몇 번째 데이터를 사용할 지를 결정하는 매개변수이며, 뒤에 나올 코드에도 계속 활용이 됩니다.
비용 함수는 다음과 같습니다.

1
2
3
4
float cost(vf &w, vff &x, vf &y, int idx){ //idx번째 데이터에 대한 비용 함수
	float tmp = f(w, x, idx) - y[idx];
	return tmp * tmp;
}

미분 함수는 idx번째 데이터를 활용하되, wi에 대해 편미분한 값을 반환하는 함수입니다.

1
2
3
4
5
6
7
8
9
10
float descent(int i, vf &w, vff &x, vf &y, int idx){ //idx번째 데이터에서 wi에 대해 편미분
	float d = w[i];
	w[i] = d - alpha;
	float c1 = cost(w, x, y, idx);
	w[i] = d + alpha;
	float c2 = cost(w, x, y, idx);
	w[i] = d;
	d = (c2-c1) / (alpha*2);
	return d;
}

여기까지 잘 구현했다면 학습 함수는 이전 글에서 썼던 코드를 약간 수정하는 정도밖에 되지 않습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void linear_matrix_learning(vf &w, vff &x, vf &y, int cnt){
	float c;
	vf d(w.size());
	for(int step=1; step<=cnt; step++){
		c = 0.0f; for(int i=0; i<d.size(); i++) d[i] = 0.0f;

		for(int i=0; i<x.size(); i++){ //각 데이터를 순회
			c += cost(w, x, y, i);
			for(int j=0; j<d.size(); j++){
				d[j] += descent(j, w, x, y, i);
			}
		}

		c /= d.size();
		for(int i=0; i<w.size(); i++) w[i] -= alpha * d[i] / d.size();
		if(step%100 == 0){
			printf("step : %d\tcost : %f\n", step, c);
			for(int i=0; i<w.size(); i++) printf("%f ", w[i]);
			printf("\n");
		}

	}
}

전체 코드는 아래에 있습니다. 이전 글과 마찬가지로 inline함수와 register변수를 함께 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <stdio.h>
#include <vector>
#include <utility>
using namespace std;

const float alpha = 0.01;
const float target = 0.0001;
typedef vector<float> vf;
typedef vector<vf> vff;

inline float f(vf &w, vff &x, int idx){ //idx번째 데이터에 대한 가설 함수
	register float sum = 0.0f;
	for(int i=0; i<w.size(); i++){
		sum += w[i] * x[i][idx];
	}
	return sum;
}

inline float cost(vf &w, vff &x, vf &y, int idx){ //idx번째 데이터에 대한 비용 함수
	float tmp = f(w, x, idx) - y[idx];
	return tmp * tmp;
}

inline float descent(int i, vf &w, vff &x, vf &y, int idx){ //idx번째 데이터에서 wi에 대해 편미분
	float d = w[i];
	w[i] = d - alpha;
	float c1 = cost(w, x, y, idx);
	w[i] = d + alpha;
	float c2 = cost(w, x, y, idx);
	w[i] = d;
	d = (c2-c1) / (alpha*2);
	return d;
}

void linear_matrix_learning(vf &w, vff &x, vf &y, int cnt){
	register float c;
	vf d(w.size());
	for(int step=1; step<=cnt; step++){
		c = 0.0f; for(int i=0; i<d.size(); i++) d[i] = 0.0f;

		for(int i=0; i<x.size(); i++){ //각 데이터를 순회
			c += cost(w, x, y, i);
			for(int j=0; j<d.size(); j++){
				d[j] += descent(j, w, x, y, i);
			}
		}

		c /= d.size();
		for(int i=0; i<w.size(); i++) w[i] -= alpha * d[i] / d.size();
		if(step%100 == 0){
			printf("step : %d\tcost : %f\n", step, c);
			for(int i=0; i<w.size(); i++) printf("%f ", w[i]);
			printf("\n");
		}

	}
}

int main(){
	vff data = {
		{1.0, 1.0, 3.0, 1.0, 5.0},
		{1.0, 2.0, 1.0, 4.0, 1.0},
		{1.0, 1.0, 1.0, 1.0, 1.0}
	};
	vf y = {2.0, 4.0, 6.0, 8.0, 10.0};
	vf w(3, 20);

	linear_matrix_learning(w, data, y, 15000);
	for(int i=0; i<w.size(); i++) printf("%f ", w[i]);
}

이 글을 마지막으로 Linear Regression의 설명이 끝났습니다. 다음 글부터는 Classification에 대해 쓸 예정입니다.