wintertreey 님의 블로그
MLP 구현 1: 데이터 전처리 본문
데이터전처리
저장해둔 데이터 경로를 찾아 읽어오고, readFile
데이터 정규화작업을 해주고, normalization
데이터를 분리해주어야한다. separate
내가 가진 데이터파일의 형태는 다음과 같다.
A2 A3 A4 A5 .... A54 Y
1.95173 115.151 27.2738 72.2082 ...
1.97996 115.486 27.0532 72.2082 ...
2.02079 115.316 27.0514 72.2082 ...
2.0012 115.338 27.3179 72.2082 ...
...
55*54의 형태.
readFile
읽어오는 작업에 대한 설명은 패스.
Normalization
데이터의 값이 제각각. 각 값들의 영향력을 제어하기 위해 0~1사이로 맞춰주는 작업.
vector<vector<double>> normalization(vector<vector<double>>& rowData, double& ymax, double& ymin)
{
int numrows = rowData.size();
int numcols = rowData[0].size();
double min = 0;
double max = 0;
for (int i = 0; i < numcols; i++) {
max = rowData[0][i];
min = rowData[0][i];
for (int j = 0; j < rowData.size(); j++) {
if (rowData[j][i] > max) {
max = rowData[j][i];
}
if (rowData[j][i] < min) {
min = rowData[j][i];
}
}
if (i == numcols - 1) {
ymax = max;
ymin = min;
}
for (int j = 0; j < numrows; j++) {
rowData[j][i] = (rowData[j][i] - min) / (max - min);
}
}
return rowData;
}
컬럼별로 최대값, 최소값을 초기화해줘야하는이유
각 컬럼은 독립적인 데이터이고, 각 컬럼에 대해 최댓값과 최솟값을 구하는것이기 때문.
예시로 이해해보자.
예시데이터
col1 col2 col3
2 4 7
3 5 8
1 6 9
첫 번째 컬럼 (col1)의 경우
max = 3, min = 1
정규화 후:
(2-1)/(3-1) = 0.5
(3-1)/(3-1) = 1.0
(1-1)/(3-1) = 0.0
두 번째 컬럼 (col2)의 경우
max = 6, min = 4
정규화 후:
(4-4)/(6-4) = 0.0
(5-4)/(6-4) = 0.5
(6-4)/(6-4) = 1.0
세 번째 컬럼 (col3)의 경우
max = 9, min = 7
정규화 후:
(7-7)/(9-7) = 0.0
(8-7)/(9-7) = 0.5
(9-7)/(9-7) = 1.0
마지막 열(numcols - 1)의 경우, Y값이기에 따로 처리해주는것 잊지말자.
Separation
void seperateData(vector<vector<double>> data)
{
int rowSize = data.size();
int colSize = data[0].size();
x.resize(rowSize, vector<double>(colSize - 1, 1));
y.resize(rowSize, vector<double>(1, 0));
for (int i = 0; i < rowSize; i++) {
for (int j = 0; j < colSize - 1; j++) {
x[i][j] = data[i][j];
}
y[i][0] = data[i][colSize - 1];
}
}
colSize가 필요한 이유
예제 데이터 (5개의 샘플, 4개의 입력 특징 + 1개의 정답값)
vector<vector<double>> data = {
{1.2, 2.3, 3.1, 4.5, 0}, // 입력값: [1.2, 2.3, 3.1, 4.5], 정답: 0
{2.1, 3.4, 1.8, 2.7, 1}, // 입력값: [2.1, 3.4, 1.8, 2.7], 정답: 1
{0.5, 1.7, 2.9, 3.6, 0}, // 입력값: [0.5, 1.7, 2.9, 3.6], 정답: 0
{3.3, 4.2, 2.1, 1.5, 1}, // 입력값: [3.3, 4.2, 2.1, 1.5], 정답: 1
{1.1, 2.5, 3.7, 4.8, 0} // 입력값: [1.1, 2.5, 3.7, 4.8], 정답: 0
};
이 데이터를 separateData를 통해 분리하면:
rowSize = 5; // 데이터 샘플 개수
colSize = 5; // 특징 4개 + 정답 1개 (총 열 개수)
x = [
[1.2, 2.3, 3.1, 4.5],
[2.1, 3.4, 1.8, 2.7],
[0.5, 1.7, 2.9, 3.6],
[3.3, 4.2, 2.1, 1.5],
[1.1, 2.5, 3.7, 4.8]
];
y = [
[0],
[1],
[0],
[1],
[0]
];
이제 x에는 입력 데이터가, y에는 정답이 정확하게 들어가게 됨!
행: 샘플 데이터 수
열: 특징 수 + 1개 Y
바이어스 포함 여부
바이어스를 데이터전처리과정에 포함할지 말지 고민했다.
1. 바이어스를 포함하는경우
입력 행렬 x 자체에 바이어스(첫 번째 열 1)을 포함.
feedforward 계산 시 코드가 단순해짐.
하지만 데이터 원본에는 없는 값을 추가하는 과정이므로, 전처리 과정에 데이터 변형이 포함됨.
2. 바이어스를 제외한 경우
데이터 전처리과정에서는 바이어스를 추가하지않고, feedforward()과정에서 따로 계산하는 방식
원본 데이터를 유지할 수 있다.
다만 연산시 코드가 좀 더 복잡해짐.
나의 경우 데이터 변형을 최소화하고, 연산이 복잡해지더라도 y = w*x + b의 형태를 명확히하고 싶어서
데이터 전처리 과정에 바이어스를 제외하였다.
메인함수를 빌드시켜서 확인해보자.
#define _CRT_SECURE_NO_WARNINGS
#include "mlp.h"
int main() {
string dataPath = "pathofdatafile";
const char* NameofData = dataPath.c_str();
vector<vector<double>> rawData;
rawData = readFile(NameofData);
vector<vector<double>> data;
double ymin = 0;
double ymax = 0;
data = normalization(rawData, ymin, ymax);
seperateData(data);
std::cout << "x.size(): " << x.size() << ", x[0].size(): " << x[0].size() << std::endl;
std::cout << "y.size(): " << y.size() << ", y[0].size(): " << y[0].size() << std::endl;
std::cout << "First x row: ";
for (double val : x[0]) cout << val << " ";
std::cout << std::endl;
std::cout << "First y value: " << y[0][0] << std::endl;
return 0;
}
![](https://blog.kakaocdn.net/dn/3kCwm/btsL3AuFXMz/l5vEkO7ZDhYf3OkVe1KgCK/img.png)
'머신러닝과 딥러닝' 카테고리의 다른 글
MLP 구현 3: 학습단계 (0) | 2025.02.02 |
---|---|
MLP 구현 2: 신경망 초기화 (0) | 2025.02.02 |
MLP 개념 (0) | 2025.02.01 |