diff --git "a/server/Data\353\252\205\354\204\270\354\204\234.md" "b/server/Data\353\252\205\354\204\270\354\204\234.md" index 2760bf8fd3a6c0a5572c386e5d23d1ff852c6c81..f723ce9aecebb648c33ea50413d2d401bf401e30 100644 --- "a/server/Data\353\252\205\354\204\270\354\204\234.md" +++ "b/server/Data\353\252\205\354\204\270\354\204\234.md" @@ -1,5 +1,7 @@ ### _데이터 명세서_ + +
# 1. Data Directory Structure @@ -19,7 +21,8 @@ ∟ YYYYMM (연/월) ∟ YYYYMMDD (연/월/일) ∟ weather.csv - ∟ weights.csv + ∟ analysis_parameters.csv + ∟ predict_parameters.csv 데이터가 저장되는 경로의 구조입니다. @@ -33,7 +36,7 @@ 데이터들은 CSV(Comma Separated Values) 형식의 파일로 저장됩니다.

-## Ouside Data +## Ouside Data - weather.csv 외부 데이터는 다음과 같은 형식으로 저장됩니다. @@ -43,7 +46,7 @@

-## User Side Data +## User Side Data - weather.csv 사용자가 설정한 장소의 데이터는 다음과 같은 형식으로 저장됩니다. @@ -53,9 +56,27 @@

+## User Side Data - parameters.csv + +Linear Regression을 진행하며 조정된 가중치와 편향 값입니다. 마지막 한개의 값이 편향이며, 이외의 값은 가중치 입니다. + +| Weights | … | Bias | +| :-----: | :-: | :--: | +| 가중치 | … | 편향 | + +

+ # 3. Data Processing EUE가 제일 중요하게 수행해야할 부분입니다. 데이터에 대해 선형회귀 분석을 진행합니다. 이 결과를 바탕으로 단위 시간 후의 온도를 예측해봅니다. +각 데이터들 마다 그 크기가 다양해, 크기가 클 수록 결과에 많은 영향을 미칩니다. 이에 따라 **_Z 점수 정규화_**를 진행하겠습니다. + +- [정규화]() +- [z 점수 (표준 점수)](https://ko.wikipedia.org/wiki/%ED%91%9C%EC%A4%80_%EC%A0%90%EC%88%98) + +예측 결과는 표준 점수로 나올 것이며, 해당 점수에 실내 온도의 표준편차를 곱하고, 평균을 더해 줌으로써 예측한 온도 값을 구할 수 있습니다. + +$$z ={x - m} \over { \theta }$$ -> $$x = z * \theta + m$$ ## Input Data @@ -90,3 +111,39 @@ EUE가 제일 중요하게 수행해야할 부분입니다. 데이터에 대해 [Linear Regression](https://ko.wikipedia.org/wiki/선형_회귀)를 통해서 데이터들의 선형 관계를 파악 후 다음의 온도를 예측해보려 합니다. 매일 자정(Day K) 데이터 처리 과정이 진행 됩니다. 따라서 (Day K - 1)의 데이터들과 (Day K - 1)까지 사용된 가중치 데이터들을 이용해 Linear Regression을 진행합니다. 데이터 처리 과정이 진행된 후의 가중치들은 (Day K)의 가중치 파일로 생성되어 저장됩니다. + +## Data Processing Files + +데이터 처리에 관한 부분은 파이썬 코드로 진행 됩니다. + + server + ∟ src + ∟ ... + ∟ data_processing + ∟ linear_regression.py + ∟ main.py + ∟ preprocessing.py + +1. 매일 자정이 되면 서버는 child process를 생성해 **/src/data_processing/main.py** 를 호출합니다.

+2. main.py는 DB에 관한 정보를 넘겨받아, data들이 존재하는 링크를 획득합니다.

+3. 링크들을 **/src/data_processing/preprocessing.py**의 preprocessing 메소드로 넘겨줍니다.

+4. preprocessing 메소드는 수집된 데이터들과 이전까지 진행한 변수들의 정보를 연산 가능한 상태로 가공하여 반환합니다.

+ +- 가공 후의 데이터 타입 및 Shape + | 변수 명 | 의미 | Shape | + | :---:|:---:|:---:| + |train_x|정규화된 수집 데이터 | (n , 1)| + |train_t| 정규화된 온도 값|(n, 1)| + |weights| 가중치|(10, 1) or None| + |bias |편향|float or None| + |mean |각 데이터 범주의 평균| (10, 1)| + |std_d |각 데이터 범주의 표준 편차| (10, 1)| + +

+ + Preprocessing 과정에서 구해진 평균과 표준 오차는 사용자의 링크 하위에 **prediction_parameters.csv**로 저장합니다. + +

+ +5. 가공된 데이터들을 바탕으로 학습률이 0.05이며, 비용 함수는 평균제곱 오차(MSE)인 선형회귀 분석을 진행합니다.

+6. 선형 회귀 분석이 후 생긴 가중치와 편향을 **analysis_parameters.csv**로 저장합니다.

diff --git a/server/src/data_processing/linear_regression.py b/server/src/data_processing/linear_regression.py new file mode 100644 index 0000000000000000000000000000000000000000..91c597ecff6bc7909bfbc1a88ad7145196fc4e8c --- /dev/null +++ b/server/src/data_processing/linear_regression.py @@ -0,0 +1,140 @@ +''' + # 선형 회귀 분석 + + - EUE에서 사용하는 데이터에 맞추어진 Linear Regression Class 입니다. + - 기존에 진행한 가중치 값이 있는지 확인합니다. + - 존재하지 않는 다면, 가중치를 초기화 합니다. + - 초기화 된 가중치와 함께 linear regression을 진행합니다. +''' + +import numpy as np + + +class LinearRegression: + def __init__(self, train_x, train_t, weights, bias, delta=1e-4, learning_rate=0.05, err_rate=0.01): + self.train_x = train_x.T # (n, 10) -> (10, n) + self.train_t = train_t.T # (n, 01) -> (01, n) + + if weights == None or bias == None: + # Initialize Parameters - w : (1, 10), b : float + self.weights, self.bias = self.initialize(self.train_x) + else: + self.weights = weights.T # (10, 1) -> (1, 10) + self.bias = bias + + self.delta = delta + self.learning_rate = learning_rate + self.err_rate = err_rate + + def initialize(self, x): + ''' + ### 가중치 및 편향 초기화 함수 + - Weight : (1, m) 꼴의 행렬 + - Bias : 실수 + ''' + weights = np.random.random((1, x.shape[0])) + bias = np.random.random() + return weights, bias + + def predict(self, x, w, b): + ''' + ### 예측값 계산 함수 + - y_hat = x * w + b + - (1, n) = (1, 10) * (10, n) + (1, n) + ''' + y_predict = np.dot(w, x) + b + return y_predict + + def cost_MAE(self, x, y, w, b): + ''' + ### 비용 함수 + - MAE (Mean Square Error) : 1/n * sigma|y_i - y_hat_i| + ''' + y_predict = self.predict(x, w, b) + n = y_predict.shape[1] + mae = np.sum(np.abs(y - y_predict)) / n + return mae + + def cost_MSE(self, x, y, w, b): + ''' + ### 비용 함수 _ MAE + - MSE (Mean Square Error) : 1/n * sigma(y_i - y_hat_i)^2 + ''' + y_predict = self.predict(x, w, b) + n = y_predict.shape[1] + mse = np.sum((y - y_predict)**2) / n + return mse + + def gradient(self, x, y, delta, w, b): + ''' + ### 미분 함수 + - 가중치와 편향에 대한 편미분 값 반환 + ''' + loss_w_delta_plus = self.cost_MSE(x, y, w + delta, b) + loss_w_delta_minus = self.cost_MSE(x, y, w - delta, b) + w_grad = (loss_w_delta_plus - loss_w_delta_minus) / (2*delta) + + loss_b_delta_plus = self.cost_MSE(x, y, w, b+delta) + loss_b_delta_minus = self.cost_MSE(x, y, w, b-delta) + b_grad = np.sum(loss_b_delta_plus - loss_b_delta_minus) / (2*delta) + + return w_grad, b_grad + + def gradientDescent(self): + + iteration = 0 + + w = self.weights + b = self.bias + + while iteration <= 3000: + grad_w, grad_b = self.gradient( + self.train_x, self.train_t, self.delta, w, b) + + loss = self.cost_MSE(self.train_x, self.train_t, w, b) + + if iteration % 100 == 0: + print(iteration, " iters - cost :", loss) + print("Gradients - W :", grad_w, ", b : ", grad_b) + print("W : ", w) + print("b : ", b) + print("\n") + + if loss <= self.err_rate: + print("At iter NO.{0} stop the process.\nCost : {1}".format( + iteration, loss)) + break + + w = w - (self.learning_rate * grad_w) + b = b - (self.learning_rate * grad_b) + + iteration += 1 + + self.weights = w + self.bias = b + + +if __name__ == "__main__": + print("This is test set.") + + x = np.array([[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], + [5, 6, 7], [6, 7, 8]], dtype=float) + y = np.array([[2], [3], [4], [5], [6], [7]], dtype=float) + test_model = LinearRegression( + x, y, None, None, learning_rate=0.0005) + + print("X : ", test_model.train_x) + print("T : ", test_model.train_t, " Shape : ", test_model.train_t.shape) + print("W : ", test_model.weights) + print("b : ", test_model.bias) + print("delta : ", test_model.delta) + print("learning rate : ", test_model.learning_rate) + print("Error Rate : ", test_model.err_rate) + + test_model.gradientDescent() + + print("After L.R.____") + print("* Costs *\n", test_model.cost_MSE(test_model.train_x, + test_model.train_t, test_model.weights, test_model.bias)) + print("* Weights *\n", test_model.weights) + print("* Bias *\n", test_model.bias) diff --git a/server/src/data_processing/main.py b/server/src/data_processing/main.py index ea7fb3724965cac604beee0576b026643b1aeb2b..25ee5e44308b35bed8bc13569a791c05c889ff54 100644 --- a/server/src/data_processing/main.py +++ b/server/src/data_processing/main.py @@ -5,30 +5,48 @@ - 진행된 후의 Weights를 파일로 저장합니다. """ +import datetime +from os import getcwd import sys import pymysql +import numpy as np from preprocessing import preprocessingData +from linear_regression import LinearRegression -def getUsersDataLinks(dbconfig): - eue_db = pymysql.connect(user=dbconfig["user"], password=dbconfig["password"], - host=dbconfig["host"], db=dbconfig["database"], charset='utf8') - cursor = eue_db.cursor(pymysql.cursors.DictCursor) +def storeParameters(link, filename, data): + today = datetime.datetime.today() + year = str(today.year) + month = str(today.month) if today.month >= 10 else '0'+str(today.month) + day = str(today.day) if today.day >= 10 else '0'+str(today.day) - query = "SELECT ID,DATALINK FROM USER;" - cursor.execute(query) - result = cursor.fetchall() + time_dir = '/' + year + '/' + year+month + '/' + year + month + day - return result + file_dir = getcwd() + '/server' + link + time_dir + filename + + file = open(file_dir, "w") + + file.write(data) + + file.close() dbconfig = {"host": sys.argv[1], "user": sys.argv[2], "password": sys.argv[3], "database": sys.argv[4]} +eue_db = pymysql.connect(user=dbconfig["user"], password=dbconfig["password"], + host=dbconfig["host"], db=dbconfig["database"], charset='utf8') +cursor = eue_db.cursor(pymysql.cursors.DictCursor) + +query = "SELECT ID,DATALINK FROM USER;" +cursor.execute(query) +result = cursor.fetchall() + +for userdata in result: -users = getUsersDataLinks(dbconfig) + print("User ID : ", userdata["ID"]) + print("Data Processing Start...") -for userdata in users: # Get Data links # ./data/DO/SGG/EMD/Users/ID user_datalink = userdata["DATALINK"] @@ -37,7 +55,65 @@ for userdata in users: outside_datalink = ("/").join(dir_ls[:-2]) + "/Outside" # data load - train_x, train_t = preprocessingData(user_datalink, outside_datalink) + train_x, train_t, weights, bias, mean, std_d = preprocessingData( + user_datalink, outside_datalink) # linear regression - pass + model = LinearRegression(train_x, train_t, weights, + bias, learning_rate=0.05) + model.gradientDescent() + + +''' + # Test Codes Start. +''' + +print("After Linear Regression -\n") +test_data = np.array([[5], [20], [0], [16.87], [40], [ + 1011], [0.72], [26.70], [47.00], [64]]) +print(test_data.shape, mean.shape, std_d.shape) +test_data = (test_data - mean) / std_d +y_hat = model.predict(test_data, model.weights, model.bias) +print(y_hat.shape) + +print("Test Data.\n", test_data, "\n") +print("Predict - standard deviation : ", y_hat) +print("Predict - temperature : ", y_hat*std_d[7][0] + mean[7][0], "\n") +print("Cost.") +print(model.cost_MSE(model.train_x, model.train_t, + model.weights, model.bias), "\n") +print("Weights.") +print(model.weights, "\n") +print("Bias.") +print(model.bias) + + +''' + # Test Codes End. +''' + +# Save the Parameters. + +# - analysis_parameters +analysis_data = "" + +for i in range(len(model.weights[0])): + analysis_data += str(model.weights[0][i]) + ',' +analysis_data += str(model.bias) + +storeParameters(user_datalink, "/analysis_parameters.csv", analysis_data) + +# - prediction_parameters +prediction_data = "" + +for i in range(len(mean)): + prediction_data += str(mean[i][0]) + ',' +prediction_data = prediction_data[:-1] +prediction_data += '\n' + +for i in range(len(std_d)): + prediction_data += str(std_d[i][0]) + ',' +prediction_data = prediction_data[:-1] + +storeParameters( + user_datalink, "/prediction_parameters.csv", prediction_data) diff --git a/server/src/data_processing/preprocessing.py b/server/src/data_processing/preprocessing.py index 7b1c7bed17d729305f258513e2970db2f000a2eb..4f5afd550abed4c77ce852ebc19cc0c161e41ae4 100644 --- a/server/src/data_processing/preprocessing.py +++ b/server/src/data_processing/preprocessing.py @@ -10,10 +10,9 @@ import csv import numpy as np -def loadRawData(link): +def loadRawData(link, file_name): ''' - # CSV 파일의 내용을 반환하는 함수 - - 어제 하루 기록된 파일들에 대해 진행하기 위해 날짜 정보를 생성합니다. + ### CSV 파일의 내용을 반환하는 함수 - 제공 받은 링크를 통해 파일을 읽고 반환합니다. ''' raw_data = [] @@ -28,9 +27,14 @@ def loadRawData(link): str(yesterday.year) + str(yMonth) + "/" + \ str(yesterday.year) + str(yMonth) + str(yDay) - weather_dir = os.getcwd() + "/server" + link + time_dir + "/weather.csv" + file_dir = os.getcwd() + "/server" + link + time_dir + file_name - data_file = open(weather_dir, 'r', newline='') + if not os.path.isfile(file_dir): + print("File doesn't exist on {0}".format(file_dir)) + return None + + data_file = open(file_dir, 'r', newline='') + print("Data File before CSV reader.\n", data_file) csv_data = csv.reader(data_file) for line in csv_data: @@ -109,6 +113,22 @@ def handleOutRawData(out_data): return out_dict +def handleParameters(raw_w): + + if raw_w == None: + return None, None + + weights = [] + + for line in raw_w: + for fig in line: + weights.append([float(fig)]) + + bias = weights.pop()[0] + + return weights, bias + + def combineXdata(user_x, out_dict): ''' # 분리된 입력 데이터를 합치는 함수 @@ -118,30 +138,104 @@ def combineXdata(user_x, out_dict): for line in user_x: hour, temp, humi, lights = line - x = out_dict[hour] + [temp, humi, lights] + + # 데이터 수집이 균일하게 이루어지지 않은 경우 처리 + if hour in out_dict: + key_hour = hour + else: + minimum = 4 + key_hour = None + for h in range(hour-3, hour + 3): + if h in out_dict and abs(h - hour) < minimum: + minimum = abs(h-hour) + key_hour = h + + x = out_dict[key_hour] + [temp, humi, lights] train_x.append(x) return train_x +def Xnormalize(data): + ''' + ### 정규화 함수 + - 입력 층의 데이터를 정규화 시킵니다. + ''' + + normalized_data = data.T # (n,10) -> (10,n) + + mean = np.mean(normalized_data, axis=1) # 평균 (10, 1) + std_d = np.std(normalized_data, axis=1) # 표준편차 + + # 월, 일의 평균과 표준편차 지정 + new_mean = [] + for i, fig in enumerate(list(mean)): + if i == 0: + new_mean.append(6.5) + elif i == 1: + new_mean.append(16.0) + else: + new_mean.append(fig) + new_mean = np.array(new_mean).reshape((-1, 1)) + + new_std_d = [] + for i, fig in enumerate(list(std_d)): + if i == 0: + new_std_d.append(3.45205253) + elif i == 1: + new_std_d.append(8.94427191) + else: + new_std_d.append(fig) + new_std_d = np.array(new_std_d).reshape((-1, 1)) + + normalized_data = (normalized_data - new_mean) / new_std_d + + normalized_data = normalized_data.T + + return normalized_data, new_mean, new_std_d + + +def normalize(data): + n_data = data.T # (n,10) -> (10,n) + + mean = np.mean(n_data, axis=1) # 평균 + std_d = np.std(n_data, axis=1) # 표준편차 + + n_data = (n_data - mean) / std_d + + n_data = n_data.T + + return n_data + + def preprocessingData(user_link, out_link): ''' # 데이터 분석 전 데이터 전처리 함수입니다. 1. 데이터 로드 2. 데이터 1차 가공 (handle~RawData) 3. 데이터 2차 가공 (combineXdata) + 4. 데이터 3차 가공 (nomalize~) + 4. 데이터 넘파이 형식 배열로 변환 5. 반환 ''' - raw_user_data = loadRawData(user_link) - raw_out_data = loadRawData(out_link) + raw_user_data = loadRawData(user_link, "/weather.csv") + raw_out_data = loadRawData(out_link, "/weather.csv") + raw_parameters = loadRawData(user_link, "/parameters.csv") user_x, train_t = handleUserRawData(raw_user_data) out_dict = handleOutRawData(raw_out_data) + weights, bias = handleParameters(raw_parameters) train_x = combineXdata(user_x, out_dict) - train_x = np.array(train_x) - train_t = np.array(train_t) + train_x = np.array(train_x) # (n ,10) + train_x, mean, std_d = Xnormalize(train_x) + + train_t = np.array(train_t) # (10,1) + train_t = normalize(train_t) + + weights = np.array(weights) if weights != None else None + bias = float(bias) if bias != None else None - return train_x, train_t + return train_x, train_t, weights, bias, mean, std_d