Myeongchan-Kim / Mobile_Doctor

For Mobile Doctor's summer project
1 stars 3 forks source link

체온 감소량 예측 프로그램 #14

Open GY-jung opened 7 years ago

GY-jung commented 7 years ago

아이의 신상정보 (체중, 성별, 나이) 해열제 섭취정보(섭취한 해열제 종류, 섭취 용량, 해열제 먹은 후 경과 시간, 해열제 섭취 당시 체온) 을 predict.py의 example_info에 입력하면 에상 체온 감소량을 출력하는 프로그램을 만들었습니다. hidden layer가 2개인 neural net을 사용했으며, 모델이 예상한 체온 감소량과 실제 체온 감소량의 차의 제곱을 minimize하도록 network를 train했습니다. 컴퓨터 계산 성능의 문제로 데이터 120만개 중 5만개만 사용해서 모델을 만들었습니다.

dependencies : python 3.6 , python library( tensorflow, numpy ) 디렉토리를 통째로 업로드하겠습니다.

GY-jung commented 7 years ago

1-12 directory에 프로그램이 돌아가는데 필요한 데이터와 파이썬 파일을 업로드했습니다. preprocessing.py 는 돌아가는데 필요한 데이터가 너무 커서 업로드하지 못했고, regression.py와 predict.py는 데이터가 잘 있어서 제대로 작동합니다.

Myeongchan-Kim commented 7 years ago

Goood!

나중에 이건 서버에 이식해서 서비스에 활용해도 좋을 것 같습니다.

실제 서비스에 이식할때 library의존성 문제가 항상 있는데요, 이기회에 Docker 공부해보는것도 좋을 것 같네요. Docker 는 OS를 포함해서 하나의 배포용 패키지를 만들어주는 툴입니다. 실제 서비스 제작에 관심있다면 반드시 알아야함!

Docker이용해 배포 및 실서버 이식은 다른 인턴분들하고 같이 해보면 좋을것 같네요. 어차피 실제 개발하게 되면 배워야 하는거라서...

root169 commented 7 years ago

utils.py

import csv 
import numpy as np

def csvreader(filename, delimiter = ' '):
    fever = []
    with open(filename, 'r') as csvfile:
        feverreader = csv.reader(csvfile, delimiter = delimiter)
        for row in feverreader:
            fever.append(row)
    return fever

def is_number(s):

    try:
        float(s)
        return True
    except ValueError:
        return False

def tonumpy(fever) : 
    fever = np.asarray(fever)
    return fever

def normalize(row):
    avg = np.mean(row)
    row -= avg
    var = np.var(row) + 1e-8
    row /= np.sqrt(var)
    return row, avg, var

def printmax(data):
    print ("max")
    print (np.amax(data, axis = 0))

def printmin(data):
    print ("min")
    print (np.amin(data, axis = 0))
root169 commented 7 years ago

preprocessing.py

#coding=utf-8
# 데이터셋을 합치고 가공하는 과정. 비정상적인 값들을 제거하고, 필요한 feature들을 더 넣어줌.
# 디렉토리에 있는 "total.csv" 파일을 받아 "final%d.csv (% datanum)", "avg.csv", "var.csv" 파일을 저장함. 

import csv
import numpy as np
import time
from utils import *

scalable = [0, 1, 16, 18] # 해열제 섭취량, 초기 온도, 체중, 나이의 경우 평균 0, variance 1로 scale 해주는 것이 도움될 것으로 추정됨. scale 가능한 변수들의 index임.  
datanum = 10  # 사용할 data 개수. 전체 데이터는 백 이십만개이지만 컴퓨터 계산 사양의 한계로 오만 개 정도가 최대로 사용할 수 있는 data 개수인 것 같음.

def regvalue(data):  # dataset에서 regression에 쓸 value만 남김

  total__ = data[:,[3,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,21,22,23]]
  return total__

def nanfilter(data,p) : # dataset에서 missing value 지움
  nan= data[:, p] 
  nan_mask = np.zeros_like(nan, dtype = "bool")
  for i in np.arange(len(nan)):
    if ( is_number(nan[i]) ) :
      nan_mask[i] = True
  data = (data[nan_mask])
  return data

def elimoutlier(data): # 체중, 해열제 섭취량이 정상 범위에서 벗어난 것 지움.
  weight = data[:,16]
  weight_mask = np.zeros_like(weight, dtype = "bool")
  for i in np.arange(len(weight)):
    if is_number(weight[i]) and ( float(weight[i]) >=2 ) and (float(weight[i])<=40):
      weight_mask[i] = True
  data = data[weight_mask]
  volume = data[:,1]
  volume_mask = np.zeros_like(volume, dtype = "bool")
  for i in np.arange(len(volume)):
    if is_number(volume[i]) and ( float(volume[i]) >=0.1 ) and (float(volume[i])<=30):
      volume_mask[i] = True
  data = (data[volume_mask])
  return data

def tempdownfilter(data): # 온도가 떨어지는 것을 예측하는 것이므로 초기 온도가 너무 낮은 것(35도 미만) 지움
  temp = data[:, 0] 
  temp_mask = np.zeros_like(temp, dtype = "bool")
  for i in np.arange(len(temp)):
    if ( float(temp[i]) >= 35.0 ) :
      temp_mask[i] = True
  data = (data[temp_mask])
  return data

def timezerofilter(data): # 해열제를 먹은 직후 체온 데이터는 쓰지 않을 것이므로 지움 
  tpass = data[:,2]
  tpass_mask = np.zeros_like(tpass, dtype = "bool")
  for i in np.arange(len(tpass)):
    if ( float(tpass[i]) > 600 ) :
      tpass_mask[i] = True
  data = (data[tpass_mask])
  return data

def filterwrapper(data): # 위의 5개 filter wrap함
  data1 = data[:datanum]

  data2 = regvalue(data1)
  for cnt in np.arange(data2.shape[1]):
    data3 = nanfilter(data2, cnt)

  data4 = np.array(data3, dtype = "float") 
  data5  = elimoutlier(data4)  
  data6 = tempdownfilter(data5)
  data7 = timezerofilter(data6)
  return data7

def dummyhour(fever): #해열제 섭취 후 다시 체온을 젤 때까지 걸리는 시간 (정수 단위) 을 dummy 변수화하여 regression에 사용하면 좋을 것이라고 생각함. 해열제 섭취 후 3시간 - 4시간 구간에 잰 체온인 경우 hour3 변수가 1이고, hour1 hour2 hour4 hour5 hour6은 모두 0. 
  dummyarray = np.zeros((len(fever[:,2]),6))

  for i in range(5):
    j = i+1  
    for t in range(len(fever[:,2])):
      if (fever[t,2] == j):
        dummyarray[t,j] = 1  
      if (fever[t,2] >= 6):
        dummyarray[t,5] = 1
  fever = np.concatenate((fever, dummyarray), axis = 1)
  return fever

def addinv(data, scalable): # scale 가능한 변수에 한해 역수 취한 값을 feature로 추가함.
  inv = np.zeros((len(data), len(scalable)))
  for cnt in np.arange(len(scalable)):
    inv[:, cnt] = 1 / (data[:, scalable[cnt]] + 1e-2) 
  datainv = np.concatenate((data, inv), axis = 1)
  return datainv

def normalizescalable(data, scalable) : # scale 가능한 변수와 그것의 역수들을 normalize함. 나중에 평균과 분산을 predict.py에서 사용할 것이기 때문에 csv로 저장.
  avg = np.zeros(2 * len(scalable))
  var = np.zeros(2 * len(scalable))  
  for cnt in np.arange(len(scalable)):
    data[:, scalable[cnt]], avg[cnt], var[cnt] = normalize(data[:, scalable[cnt]])
    data[:, data.shape[1] - len(scalable) + cnt], avg[cnt+len(scalable)], var[cnt+len(scalable)] = normalize(data[:, data.shape[1] - len(scalable) + cnt])
  return data, avg, var

def timescale(data)  : # 해열제 섭취 후 체온 잰 시간을 3600으로 나눠 시간 단위로 변환함. 초 단위로 하면 해열제 섭취 후 시간만 scale이 너무 커 training에 문제 있음.
  data[:, 2] = data[:, 2] / 3600
  return data

def featureaddwrapper(data) : # 위의 4개 feature 변환 및 추가하는 함수들 wrap함
  data1 = dummyhour(data)
  data2 = timescale(data1)
  data3 = addinv(data2,scalable)
  data4, avg, var = normalizescalable(data3, scalable)

  with open("avg.csv", 'wb') as writer :
    np.savetxt("avg.csv", avg, fmt = '%s', delimiter = ' ')

  with open("var.csv", 'wb') as writer : 
    np.savetxt("var.csv", var, fmt = '%s', delimiter = ' ' )
  return data4

def generate_data(datanum, now = False): # 실제로 사용할 data 만들어서 csv로 저장
  if now : 
    data = tonumpy(csvreader("total.csv"))
    data1 = data[:datanum]
    data2 = filterwrapper(data1)
    data3 = featureaddwrapper(data2)
    with open("final%d .csv" %datanum, 'wb') as writer:
      np.savetxt("final%d.csv" %datanum, data3, fmt = '%s', delimiter = ' ')

generate_data(datanum, now = True)
root169 commented 7 years ago

regression.py

# coding=utf-8 
# 아이의 정보 (체중, 성별, 나이) 와 섭취한 해열제 종류 및 양, 해열제 먹인 순간의 측정 초기 온도, 해열제 먹인 후 경과 시간을 바탕으로, 초기에 비해 온도 감소량이 얼마나 될지 예측할 수 있는 모델을 설계함.
# 모델은 hidden layer가 2개인 neural net이고, 모델에 의해 예측된 체온감소와 실제 체온감소의 차의 제곱을 최소화하도록 설계함
# 대략 100번정도의 iteration이 진행되면 validation set에서 모델에 의해 예측된 체온감소와 실제 체온감소의 차의 제곱의 평균이 0.65 ~ 0.7 정도 나옴. 즉 체온 감소량을 평균적으로 0.8도 범위 정도로 예측함.

# 디렉토리에 "final%d.csv" %datanum 을 필요로 함 

import numpy as np
import csv
import time
import tensorflow as tf
from preprocessing import *
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
# 모델 hyperparameter
eps = 1e-1
lr = 0.001
decay = 0.9
scalable = [0,1,16,18]
hidden1 = 100
hidden2 = 50
datanum = 10

""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
#데이터 로딩

data = tonumpy(csvreader("final%d.csv" %datanum))
data = np.array(data, dtype = "float")

np.random.shuffle(data)
printmax(data)
printmin(data)
data = np.transpose(data)

""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
#dataset 분할

train_set = data[:, 0:int(data.shape[1] * 0.6)]
dev_set = data[:, int(data.shape[1]* 0.6) : int(data.shape[1]* 0.8)]
test_set = data[:, int(data.shape[1]* 0.8):]             # 6 : 2 : 2 로 training / validation / test 을 분할함

""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
# 체온 감소량을 예측할 것이므로 체온 감소량을 제외한 값들을 training에 사용
temp_mask = np.ones(data.shape[0], dtype = "bool")
temp_mask[3] = False                                    

X_train = np.array(train_set[temp_mask], dtype = "float")
temp_train = np.array(train_set[3], dtype = "float")

X_dev = np.array(dev_set[temp_mask], dtype = "float32")
temp_dev = np.array(dev_set[3], dtype = "float32")

X_test = np.array(test_set[temp_mask], dtype = "float32")
temp_test = np.array(test_set[3], dtype = "float32")

X_train = np.transpose(X_train)

X_dev = np.transpose(X_dev)
X_test = np.transpose(X_test)

""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
#some tensorflow...

#placeholder
inputs_placeholder = tf.placeholder(tf.float32, (None, X_train.shape[1]), name = "inputs")
temps_placeholder = tf.placeholder(tf.float32, (None,), name = "temps")

#affine layers
W1 = tf.Variable(tf.random_uniform((X_train.shape[1],hidden1), minval = -np.sqrt(6.0/(X_train.shape[1] + hidden1) ), maxval = np.sqrt(6.0/(X_train.shape[1] + hidden1))), dtype = tf.float32)
b1 = tf.Variable(tf.zeros((1,hidden1)) , dtype = tf.float32)
W2 = tf.Variable(tf.random_uniform((hidden1,hidden2), minval = -np.sqrt(6.0/(hidden1 + hidden2)), maxval = np.sqrt(6.0/(hidden1 + hidden2))), dtype = tf.float32)
b2 = tf.Variable(tf.zeros((1,hidden2)), dtype = tf.float32)
W3 = tf.Variable(tf.random_uniform((hidden2,1), minval = -np.sqrt(6.0/hidden2), maxval = np.sqrt(6.0/hidden2)), dtype = tf.float32)
b3 = tf.Variable(tf.zeros((1,1)), dtype = tf.float32)

#activation : tanh 
z1 = tf.matmul(inputs_placeholder, W1)  + b1
h1 = tf.nn.tanh(z1)
z2 = tf.matmul(h1, W2) + b2
h2 = tf.nn.tanh(z2)
z3 = tf.add(tf.matmul(h2, W3) , b3, name = "op_to_restore")  # 예상 체온

y1 = tf.matmul(X_dev, W1) + b1
g1 = tf.nn.tanh(y1)
y2 = tf.matmul(g1, W2) + b2
g2 = tf.tanh(y2)
y3 = tf.matmul(g2,W3) + b3

#L2 loss
loss = tf.reduce_mean((z3 - temps_placeholder)**2)
dev_loss = tf.reduce_mean((y3 - temp_dev)**2)

#model 재사용을 위한 saver
saver = tf.train.Saver()

#Adamoptimizer
optimizer = tf.train.AdamOptimizer(lr)
train_op = optimizer.minimize(loss)

init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)

#Training. 5번의 step마다 loss 확인. devset에서 loss가 늘어난 경우에 learning rate를 0.9배로 decay

best_dev = 10
for step in range(101):
    sess.run(train_op, feed_dict = {inputs_placeholder : X_train, temps_placeholder : temp_train})

    if step%5 == 0:   # 매 스텝마다 예상 감소 체온과 실제 감소 체온 3개, train_loss, dev_loss 출력
        print(step, sess.run(loss,  feed_dict = {inputs_placeholder : X_train, temps_placeholder : temp_train}), sess.run(loss, feed_dict = {inputs_placeholder : X_dev, temps_placeholder : temp_dev}))
        print(sess.run((z3[0],z3[1], z3[2]), feed_dict = {inputs_placeholder : X_train, temps_placeholder : temp_train}))
        print (temp_train[0], temp_train[1], temp_train[2])
        if best_dev < sess.run(loss, feed_dict = {inputs_placeholder : X_dev, temps_placeholder : temp_dev}) : 
            lr *= decay
            print ("decay")
            print (lr)
        else :
            best_dev = sess.run(dev_loss, feed_dict = {inputs_placeholder : X_train, temps_placeholder : temp_train})

print ("test set loss")
print (sess.run(loss, feed_dict = {inputs_placeholder : X_test, temps_placeholder : temp_test}))

#save model. predict에서 사용함
save_path = saver.save(sess, 'C:\\Users\\MobileDoctor\\1-12\\model')
root169 commented 7 years ago

predict.py

# coding=utf-8 
# regression.py 에서 만든 모델을 바탕으로 예상 감소 체온을 예측해주는 프로그램. example_info에 차례로 체온, 해열제 양, 해열제 섭취 후 경과 시간, 해열제 종류, 체중, 성별, 나이( 단위 : day) 를 입력하면, 예상 감소 체온을 알려주는 프로그램.
import tensorflow as tf
from preprocessing import *
from utils import *
from merge12 import *
scalable = [0,1,16,18]
example_info = (38.0, 5.0, 3600, 1, 15.0, 0, 1140)  # 예시 데이터. 

def predict_temp(info): 
    temp, volume, time_pass, kind, weight, gender, born_date = info[:]
    final_data = np.zeros((19,1))
    final_data[0] = temp
    final_data[1] = volume
    final_data[2] = time_pass
    final_data[3] = 0.0      # dummy
    final_data[3+kind] = 1.0
    final_data[16] = weight
    final_data[17] = gender
    final_data[18] = born_date
    final_data = np.transpose(final_data)
    final_data = dummyhour(final_data)
    processed_data = addinv(final_data, scalable)
    processed_data = timescale(processed_data)
    processed_data = np.transpose(processed_data)
    return processed_data

def pseudonormalize(data): 
    avg = tonumpy(csvreader("avg.csv", delimiter = ' '))
    var = tonumpy(csvreader("var.csv", delimiter = ' '))

    for cnt in np.arange(len(scalable)):
        data[scalable[cnt]] -= float(np.asscalar(avg[cnt]))
        data[scalable[cnt]] /= np.sqrt(float(np.asscalar(var[cnt])))
    for cnt in np.arange(len(scalable)):
        data[len(data) - len(scalable) + cnt] -= float(np.asscalar(avg[cnt + len(scalable)]))
        data[len(data) - len(scalable) + cnt] /= np.sqrt(float(np.asscalar(var[cnt + len(scalable)])))
    return data

""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
# example_info를 모델에 들어갈 수 있게 변환
test = pseudonormalize(predict_temp(example_info))
time_mask = np.ones(len(test), dtype = "bool")
time_mask[3] = False

""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

#model loading

sess = tf.Session()
saver = tf.train.import_meta_graph("C:\\Users\\MobileDoctor\\1-12\\model.meta")
saver.restore(sess, "C:\\Users\\MobileDoctor\\1-12\\model")

""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
#모델에 예측 체온 넣어 계산
X_test = np.array(test[time_mask], dtype = "float32")
temp_test = np.array(test[3], dtype = "float32")

X_test = np.transpose(X_test)
temp_test = np.transpose(temp_test)

graph = tf.get_default_graph()
temps_placeholder = graph.get_tensor_by_name("temps:0")
inputs_placeholder = graph.get_tensor_by_name("inputs:0")
feed_dict = { inputs_placeholder : X_test, temps_placeholder : temp_test }
op_to_restore = graph.get_tensor_by_name("op_to_restore:0")

""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
#체온 출력

print ("estimated temp")
print (sess.run(op_to_restore, feed_dict = feed_dict))
Myeongchan-Kim commented 7 years ago

실제 train에 쓰지 않은 데이터에 test 해보고 결과좀 알려주세요~ 그냥 서비스를 만드는 것보다 이게 맞는지 아닌지 검증 할 수 있는 방법을 아는게 더 중요합니다.