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