├── 4day_prediction.png
├── 4day_result_plot.png
├── LICENSE
├── README.md
├── _config.yml
├── code
├── model.py
├── process.py
├── readme.md
├── train.py
└── utils.py
├── data
└── readme.md
├── deepRnn.png
├── demo.png
├── plotResult.png
└── table1.png
/4day_prediction.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psu1/DeepRNN/65ac0c84f5ca85d498e3028902265e48845024e0/4day_prediction.png
--------------------------------------------------------------------------------
/4day_result_plot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psu1/DeepRNN/65ac0c84f5ca85d498e3028902265e48845024e0/4day_result_plot.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Peng Su
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [arXiv](https://arxiv.org/abs/1705.04524)
2 | [Paper](https://ieeexplore.ieee.org/abstract/document/8333434/)
3 | [Poster](http://www.ee.cuhk.edu.hk/~psu/img/BHI2018.pdf)
4 | [Project](https://psu1.github.io/DeepRNN/)
5 |
6 |
7 | ## Demo (real-time BP prediction)
8 | In nutshell, we build a novel Recurrent Neural Networks to predict arterial blood pressure (BP) from ECG and PPG signals which can be easily collected from wearable devices. The top two channels in the demo below are input ECG and PPG signals, the bottom channel plots the real-time BP prediction and BP ground truth.
9 | [](https://www.youtube.com/watch?v=4fLw79l3Dh4&feature=youtu.be)
10 |
11 | ## Project at a glance
12 |
13 | Existing methods for arterial blood pressure (BP) estimation directly map the input physiological signals to output BP values without explicitly modeling the underlying temporal dependencies in BP dynamics. As a result, these models suffer from accuracy decay over a long time and thus require frequent calibration. In this work, we address this issue by formulating BP estimation as a sequence prediction problem in which both the input and target are temporal sequences. We propose a novel deep recurrent neural network (RNN) consisting of multilayered Long Short-Term Memory (LSTM) networks, which are incorporated with (1) a bidirectional structure to access larger-scale context information of input sequence, and (2) residual connections to allow gradients in deep RNN to propagate more effectively.The proposed deep RNN model was tested on a static BP dataset, and it achieved root mean square error (RMSE) of 3.90 and 2.66 mmHg for systolic BP (SBP) and diastolic BP (DBP) prediction respectively, surpassing the accuracy of traditional BP prediction models. On a multi-day BP dataset, the deep RNN achieved RMSE of 3.84, 5.25, 5.80 and 5.81 mmHg for the 1st day, 2nd day, 4th day and 6th month after the 1st day SBP prediction, and 1.80, 4.78, 5.0, 5.21 mmHg for corresponding DBP prediction, respectively, which outperforms all previous models with notable improvement. The experimental results suggest that modeling the temporal dependencies in BP dynamics significantly improves the long-term BP prediction accuracy.
14 |
15 | ## Network Architecture
16 |
17 | Simple but proven to work very well. Detailed mathematical explanation behind such design is provided in paper Section 3.
18 |
19 | 
20 | DeepRNN architecture. Each rectangular box is an LSTM cell. The green dashed box at bottom is a bidirectional
21 | LSTM layer consisting of forward (orange) and backward (green) LSTM. The orange dashed box depicts the LSTM
22 | layer with residual connections.
23 |
24 |
25 | ## Results
26 |
27 | ### 1.Generalization capability across different subjects.
28 | Detailed analysis of our Deep RNN models with comparison with different reference models. DeepRNN-xL
29 | represents a x layer RNN model. All the models are validated on the static continuous BP dataset. (unit: mmHg)
30 |
31 |
32 |
33 |
34 |
35 | Bland-Altman plots of the overall SBP and DBP predictions by a DeepRNN-4L model on the static continuous BP dataset.
36 | 
37 |
38 |
39 | ### 2.Generalization capability over a long period of time.
40 |
41 | Overall RMSE comparison of different models on the multi-day continuous BP dataset.
42 | 
43 |
44 | Qualitative example to show the
45 | capability of DeepRNN to track long-term BP variation
46 | Figure (a), (b), (c) and (d) represent the results of 1st day, 2nd day, 4th day and 6th month after the 1st day, respectively.
47 | 
48 |
49 |
50 |
51 |
52 | ## Citation
53 | @inproceedings{su2018long,
54 | title={Long-term Blood Pressure Prediction with Deep Recurrent Neural Networks},
55 | author={Su, Peng and Ding, Xiao-Rong and Zhang, Yuan-Ting and Liu, Jing and Miao, Fen and Zhao, Ni},
56 | booktitle={Biomedical \& Health Informatics (BHI), 2018 IEEE EMBS International Conference on},
57 | pages={323--328},
58 | year={2018},
59 | organization={IEEE}
60 | }
61 |
62 | @article{su2017predicting,
63 | title={Predicting Blood Pressure with Deep Bidirectional LSTM Network},
64 | author={Su, Peng and Ding, Xiaorong and Zhang, Yuanting and Li, Ye and Zhao, Ni},
65 | journal={arXiv preprint arXiv:1705.04524},
66 | year={2017}
67 | }
68 |
69 | ## Acknowledgment
70 | The Chinese University of Hong Kong
71 | Intelligent Sensing Limited
72 | Key Laboratory for Health Informatics of the Chinese Academy of Sciences
73 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
2 | title: Long-term Blood Pressure Prediction with Deep Recurrent Neural Networks
3 | description: Peng Su, Xiao-Rong Ding, Yuan-Ting Zhang, Jing Liu, Fen Miao, and Ni Zhao
4 |
--------------------------------------------------------------------------------
/code/model.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 |
3 | import collections
4 |
5 | import tensorflow as tf
6 | import numpy as np
7 |
8 | import process
9 | import utils
10 |
11 | TrainForwardOutputTuple = collections.namedtuple("TrainForwardOutputTuplee",
12 | ("train_summary", "train_loss", # need to change to train_forward_loss
13 | "predict_beat_count",
14 | "global_step", "batch_size", "grad_norm",
15 | "learning_rate", "train_src_ref", "train_tgt_ref",
16 | "train_src2tgt_output", "train_tgt2src_output",
17 | "src_seq_len", "tgt_seq_len"))
18 |
19 | TrainBackwardOutputTuple = collections.namedtuple("TrainBackwardOutputTuplee",
20 | ("train_summary", "train_backward_loss",
21 | "global_step", "batch_size", "grad_norm",
22 | "learning_rate", "train_src_ref", "train_tgt_ref",
23 | "train_src2tgt2src_output", "train_tgt2src2tgt_output"))
24 |
25 | EvalOutputTuple = collections.namedtuple("EvalOutputTuple", ("eval_loss", "predict_beat_count", "batch_size"))
26 |
27 | InferOutputTuple = collections.namedtuple("InferOutputTuple",
28 | ("infer_tgt_output", "infer_tgt_ref", "infer_loss", "predict_beat_count",
29 | "batch_size"))
30 |
31 | class Model(object):
32 | """
33 | This class implements a multi-layer recurrent neural network as "lstm"
34 | """
35 |
36 | def __init__(self, hparams, iterator, mode):
37 | # Set params
38 | self.set_params(hparams, iterator, mode)
39 |
40 | # build graph
41 | self.build_graph(hparams)
42 |
43 | def set_params(self, hparams, iterator, mode):
44 | """Set various params for self and initialize."""
45 | self.architecture = hparams.architecture
46 | assert hparams.src_len == hparams.tgt_len
47 | self.length = hparams.src_len
48 | self.iterator = iterator
49 | self.mode = mode
50 | self.dtype = tf.float64
51 | self.unit_type = hparams.unit_type
52 | self.num_units = hparams.num_units ## this is the size in each cell
53 | self.forget_bias = hparams.forget_bias
54 | self.dropout = hparams.dropout
55 | self.src_feature_size = hparams.src_feature_size
56 | self.tgt_feature_size = hparams.tgt_feature_size
57 | if self.architecture == "deepRNN":
58 | self.num_bi_layers = hparams.num_bi_layers
59 | self.num_uni_layers = hparams.num_uni_layers
60 |
61 | # Set num layers
62 | self.num_layers=hparams.num_layers
63 | # Set num residual layers
64 | self.num_residual_layers = hparams.num_residual_layers
65 |
66 | # Batch size
67 | self.batch_size = hparams.batch_size
68 | self.batch_size_tensor = tf.constant(self.batch_size)
69 | # train directions
70 | self.forward_directions = hparams.forward_directions
71 | self.backward_directions = hparams.backward_directions
72 |
73 | # Global step
74 | self.global_step = tf.Variable(0, trainable=False)
75 | self.epoch_num = tf.Variable(0, trainable=False)
76 |
77 | """--------------------------"""
78 |
79 | def build_graph(self, hparams):
80 | utils.print_out("# Creating %s graph ..." % self.mode)
81 |
82 | with tf.variable_scope("network", dtype=self.dtype, reuse=tf.AUTO_REUSE):
83 | self.top_scope = tf.get_variable_scope()
84 |
85 | # Initializer
86 | initializer = tf.random_uniform_initializer(-hparams.init_weight, hparams.init_weight,
87 | seed=hparams.random_seed)
88 | self.top_scope.set_initializer(initializer)
89 | ## so the initializer will be the default initializer for the following variable in this variable scope
90 | "---------"
91 | # the variable scope specification is left for _encode function
92 | # common components in three mode
93 | if self.architecture == "deepRNN":
94 | # lstm (bi_lstm, then stacked with uni_lstm)
95 | self.src_bi_lstm, self.src_bi_lstm_condition = self._build_bi_lstm(hparams)
96 | self.src_uni_lstm, self.src_uni_lstm_condition = self._build_uni_lstm(hparams)
97 | self.tgt_bi_lstm, self.tgt_bi_lstm_condition = self._build_bi_lstm(hparams)
98 | self.tgt_uni_lstm, self.tgt_uni_lstm_condition = self._build_uni_lstm(hparams)
99 |
100 | # Projector
101 | self.src_projector = self._build_projector(hparams, field='src')
102 | self.tgt_projector = self._build_projector(hparams, field='tgt')
103 | else:
104 | raise ValueError("Unknown architecture_type %s" % hparams.lstm_type)
105 |
106 | "------------"
107 |
108 | # set mode-specific component
109 | self.set_mode_phase(hparams)
110 |
111 | # Saver
112 | self.saver = tf.train.Saver(tf.global_variables(), max_to_keep=5)
113 |
114 | def _single_cell(self, unit_type, num_units, forget_bias, dropout, mode, residual_connection=False):
115 | """Create an instance of a single RNN cell."""
116 | # dropout (= 1 - keep_prob) is set to 0 during eval and infer
117 | dropout = dropout if mode == tf.contrib.learn.ModeKeys.TRAIN else 0.0
118 | # Cell Type
119 | if unit_type == "lstm":
120 | utils.print_out(" LSTM, forget_bias=%g" % forget_bias, new_line=False)
121 | single_cell = tf.nn.rnn_cell.LSTMCell(
122 | num_units,
123 | forget_bias=forget_bias)
124 | else:
125 | raise ValueError("Unknown unit type %s!" % unit_type)
126 | # Dropout (= 1 - keep_prob)
127 | if dropout > 0.0:
128 | single_cell = tf.nn.rnn_cell.DropoutWrapper(
129 | cell=single_cell, input_keep_prob=(1.0 - dropout))
130 | utils.print_out(" %s, dropout=%g " % (type(single_cell).__name__, dropout), new_line=False)
131 | # Residual
132 | if residual_connection:
133 | single_cell = tf.nn.rnn_cell.ResidualWrapper(single_cell)
134 | utils.print_out(" %s" % type(single_cell).__name__, new_line=False)
135 |
136 | return single_cell
137 |
138 | def _build_cell(self, num_layers, num_residual_layers):
139 | cell_list = []
140 | for i in range(num_layers):
141 | utils.print_out(" cell %d " % i, new_line=False)
142 | single_cell = self._single_cell(
143 | unit_type=self.unit_type,
144 | num_units=self.num_units,
145 | forget_bias=self.forget_bias,
146 | dropout=self.dropout,
147 | mode=self.mode,
148 | residual_connection=(i >= (num_layers - num_residual_layers)))
149 | utils.print_out("", new_line=True)
150 | cell_list.append(single_cell)
151 |
152 | if len(cell_list) == 1: # Single layer.
153 | return cell_list[0]
154 | else: # Multi layers
155 | return tf.nn.rnn_cell.MultiRNNCell(cell_list)
156 |
157 | def _build_bi_lstm(self, hparams):
158 | utils.print_out("# Build bidirectional lstm")
159 | num_bi_layers = self.num_bi_layers
160 | num_bi_residual_layers = 0
161 | utils.print_out(" num_bi_layers = %d, num_bi_residual_layers=%d" % (num_bi_layers, num_bi_residual_layers))
162 | # Construct forward and backward cells
163 | fw_cell = self._build_cell(num_bi_layers, num_bi_residual_layers)
164 | bw_cell = self._build_cell(num_bi_layers, num_bi_residual_layers)
165 | bi_lstm = (fw_cell, bw_cell)
166 | bi_lstm_condition = ("bi", num_bi_layers)
167 | return bi_lstm, bi_lstm_condition
168 |
169 | def _build_uni_lstm(self, hparams):
170 | utils.print_out("# Build unidirectional lstm")
171 | num_uni_layers = self.num_uni_layers
172 | num_uni_residual_layers = self.num_uni_layers - 1
173 | utils.print_out(" num_layers = %d, num_residual_layers=%d" % (num_uni_layers, num_uni_residual_layers))
174 | cell = self._build_cell(num_uni_layers, num_uni_residual_layers)
175 | uni_lstm = cell
176 | uni_lstm_condition = ("uni", None)
177 | return uni_lstm, uni_lstm_condition
178 |
179 | def _build_projector(self, hparams, field=None):
180 | assert field
181 | if field == "src":
182 | projector = tf.layers.Dense(self.src_feature_size, use_bias=True)
183 | elif field == "tgt":
184 | projector = tf.layers.Dense(self.tgt_feature_size, use_bias=True)
185 | else:
186 | raise ValueError("Unknown field type %s" % field)
187 | return projector
188 |
189 | """----------------------"""
190 |
191 | def _set_input_ref(self, direction, lstm_input_given, ref_given, seq_len_given):
192 | if direction == "src2tgt":
193 | lstm_input = self.iterator.source_ref
194 | ref = self.iterator.target_ref
195 | seq_len = self.iterator.target_sequence_length
196 | return lstm_input, ref, seq_len
197 |
198 | def _set_lstm_projector(self, direction):
199 | # lstm type is related to the input, while the projector used is related to output!!!
200 | if direction in ["src2tgt"]:
201 | bi_lstm, bi_lstm_condition = self.src_bi_lstm, self.src_bi_lstm_condition
202 | uni_lstm, uni_lstm_condition = self.src_uni_lstm, self.src_uni_lstm_condition
203 | lstm_scope = "src_lstm"
204 |
205 | lstm_list = [bi_lstm, uni_lstm] # bi lstm then stacked with uni lstm
206 | lstm_condition_list = [bi_lstm_condition, uni_lstm_condition]
207 |
208 | if direction in ["src2tgt"]:
209 | projector = self.tgt_projector
210 | projector_scope = "tgt_projector"
211 | return lstm_list, lstm_condition_list, lstm_scope, projector, projector_scope
212 |
213 | def _encode(self, lstm_scope, input, seq_len, lstm_list, lstm_condition_list):
214 | # here input is rank-3, [batch size, seq_len, src_feature_size]
215 | assert len(lstm_list) == len(lstm_condition_list)
216 | lstm_outputs = None
217 | with tf.variable_scope(lstm_scope):
218 | for i in range(len(lstm_list)):
219 | if i == 0:
220 | input = input
221 | else:
222 | input = lstm_outputs
223 | lstm = lstm_list[i]
224 | lstm_condition = lstm_condition_list[i]
225 | lstm_type = lstm_condition[0]
226 | if lstm_type == "uni":
227 | lstm_outputs, lstm_state = tf.nn.dynamic_rnn(
228 | cell=lstm, inputs=input, dtype=self.dtype, sequence_length=seq_len, swap_memory=True, scope="")
229 | elif lstm_type == "bi":
230 | fw_cell, bw_cell = lstm
231 | bi_outputs, bi_lstm_state = tf.nn.bidirectional_dynamic_rnn(fw_cell, bw_cell, input,
232 | dtype=self.dtype,
233 | sequence_length=seq_len,
234 | swap_memory=True, scope="")
235 | lstm_outputs = tf.concat(bi_outputs, -1)
236 | num_bi_layers = lstm_condition[1]
237 | if num_bi_layers == 1:
238 | lstm_state = bi_lstm_state
239 | else:
240 | # alternatively concat forward and backward states
241 | lstm_state = []
242 | for layer_id in range(num_bi_layers):
243 | lstm_state.append(bi_lstm_state[0][layer_id]) # forward
244 | lstm_state.append(bi_lstm_state[1][layer_id]) # backward
245 | lstm_state = tuple(lstm_state)
246 | else:
247 | raise ValueError("Unknown lstm or lstm_condition")
248 |
249 | return lstm_outputs, lstm_state
250 |
251 | def compute_loss(self, hparams, direction, lstm_input_given, ref_given, seq_len_given, feature_size):
252 |
253 | lstm_input, ref, seq_len = self._set_input_ref(direction, lstm_input_given, ref_given, seq_len_given)
254 | lstm_list, lstm_condition_list, lstm_scope, projector, projector_scope = self._set_lstm_projector(direction)
255 | lstm_output, lstm_state = self._encode(lstm_scope, lstm_input, seq_len, lstm_list, lstm_condition_list)
256 | output = projector(lstm_output)
257 | loss = tf.sqrt(
258 | tf.reduce_sum(tf.square(output - ref)) / (tf.to_double(self.batch_size * hparams.src_len * feature_size)))
259 | return loss, output
260 |
261 | def forward(self, hparams, directions=None):
262 | src2tgt_loss = 0
263 | src2tgt_output = None
264 | ## forward directions just include ["src2tgt", "tgt2src", "src2src", "tgt2tgt"] at most
265 |
266 | if "src2tgt" in directions:
267 | src2tgt_loss, src2tgt_output = self.compute_loss(hparams, direction="src2tgt", lstm_input_given=None,
268 | ref_given=None, seq_len_given=None,
269 | forward_loss = src2tgt_loss
270 | return forward_loss, src2tgt_output
271 |
272 | def set_mode_phase(self, hparams):
273 | self.predict_beat_count = tf.math.reduce_sum(self.iterator.target_sequence_length) # need modification
274 | if self.mode == tf.contrib.learn.ModeKeys.TRAIN:
275 |
276 | # set optimizer
277 | self.learning_rate = tf.constant(hparams.learning_rate)
278 | if hparams.optimizer == "adam":
279 | self.optimizer = tf.train.AdamOptimizer(self.learning_rate)
280 | else:
281 | raise ValueError("Unknown optimizer type %s" % hparams.optimizer)
282 | "-------------"
283 | # forward phase
284 | # self.train_forward_loss, self.src2tgt_output, self.tgt2src_output = self.forward(
285 | # hparams, directions=["src2tgt", "tgt2src", "src2src", "tgt2tgt"])
286 | self.train_forward_loss, self.src2tgt_output, self.tgt2src_output = self.forward(
287 | hparams, directions=self.forward_directions) # this is for test of basic unidirection seq2seq model
288 |
289 | # test variable
290 | utils.test_trainable_variables()
291 |
292 | # Gradients
293 | params = tf.trainable_variables()
294 | forward_gradients = tf.gradients(self.train_forward_loss, params)
295 | forward_clipped_grads, self.forward_grad_norm = tf.clip_by_global_norm(forward_gradients, 5.0)
296 |
297 | # key point: to update the parameter
298 | self.forward_update = self.optimizer.apply_gradients(zip(forward_clipped_grads, params),
299 | global_step=self.global_step)
300 | ## in this step, global step will increase by 1!!
301 |
302 | # Summary
303 | self.forward_train_summary = tf.summary.merge(
304 | [tf.summary.scalar("lr", self.learning_rate),
305 | tf.summary.scalar("forward_train_loss", self.train_forward_loss),
306 | tf.summary.scalar("forward_grad_norm", self.forward_grad_norm),
307 | tf.summary.scalar("forward_clipped_gradient", tf.global_norm(forward_clipped_grads))])
308 | "---------------"
309 |
310 | # backward phase
311 | self.src_ref_given = tf.placeholder(self.dtype, shape=(self.batch_size, self.length, self.src_feature_size))
312 | self.tgt_ref_given = tf.placeholder(self.dtype, shape=(self.batch_size, self.length, self.tgt_feature_size))
313 | self.src2tgt_output_given = tf.placeholder(self.dtype,
314 | shape=(self.batch_size, self.length, self.tgt_feature_size))
315 | self.tgt2src_output_given = tf.placeholder(self.dtype,
316 | shape=(self.batch_size, self.length, self.src_feature_size))
317 | """
318 | self.src_sos_given = tf.placeholder(self.dtype, shape=(1, self.src_feature_size))
319 | self.tgt_sos_given = tf.placeholder(self.dtype, shape=(1, self.tgt_feature_size))
320 | """
321 | self.src_seq_len_given = tf.placeholder(tf.int32, shape=(self.batch_size))
322 | self.tgt_seq_len_given = tf.placeholder(tf.int32, shape=(self.batch_size))
323 |
324 | self.train_backward_loss, self.src2tgt2src_output, self.tgt2src2tgt_output = self.backward(
325 | hparams, directions=self.backward_directions,
326 | src2tgt_output=self.src2tgt_output_given, tgt2src_output=self.tgt2src_output_given,
327 | src_ref=self.src_ref_given, tgt_ref=self.tgt_ref_given,
328 | src_seq_len=self.src_seq_len_given, tgt_seq_len=self.tgt_seq_len_given)
329 |
330 | # test variable
331 | utils.test_trainable_variables()
332 |
333 | # Gradients
334 | params = tf.trainable_variables()
335 | backward_gradients = tf.gradients(self.train_backward_loss, params)
336 | backward_clipped_grads, self.backward_grad_norm = tf.clip_by_global_norm(backward_gradients, 5.0)
337 |
338 | # key point: to update the parameter
339 | self.backward_update = self.optimizer.apply_gradients(zip(backward_clipped_grads, params))
340 | # here we doesn't list the global step to avoid increment by 1
341 |
342 | # Summary
343 | self.backward_train_summary = tf.summary.merge(
344 | [tf.summary.scalar("lr", self.learning_rate),
345 | tf.summary.scalar("backward_train_loss", self.train_backward_loss),
346 | tf.summary.scalar("backward_grad_norm", self.backward_grad_norm),
347 | tf.summary.scalar("backward_clipped_gradient", tf.global_norm(backward_clipped_grads))])
348 |
349 | elif self.mode == tf.contrib.learn.ModeKeys.EVAL:
350 | self.eval_loss, _, _ = self.forward(hparams, directions=["src2tgt"])
351 |
352 | elif self.mode == tf.contrib.learn.ModeKeys.INFER:
353 | self.infer_loss, self.infer_target_output, _ = self.forward(hparams, directions=["src2tgt"])
354 |
355 | """"------------------------"""
356 |
357 | def increase_epoch_num(self, sess):
358 | assert self.mode == tf.contrib.learn.ModeKeys.TRAIN
359 | return sess.run(tf.assign_add(self.epoch_num, 1))
360 |
361 | def train_forward(self, sess):
362 | assert self.mode == tf.contrib.learn.ModeKeys.TRAIN
363 | output_tuple = TrainForwardOutputTuple(train_summary=self.forward_train_summary,
364 | train_loss=self.train_forward_loss,
365 | predict_beat_count=self.predict_beat_count,
366 | global_step=self.global_step,
367 | batch_size=self.batch_size_tensor,
368 | grad_norm=self.forward_grad_norm,
369 | learning_rate=self.learning_rate,
370 | train_src_ref=self.iterator.source_ref,
371 | train_tgt_ref=self.iterator.target_ref,
372 | train_src2tgt_output=self.src2tgt_output,
373 | src_seq_len=self.iterator.source_sequence_length,
374 | tgt_seq_len=self.iterator.target_sequence_length)
375 | return sess.run([self.forward_update, output_tuple])
376 |
377 | def train_backward(self, sess, input):
378 | assert self.mode == tf.contrib.learn.ModeKeys.TRAIN
379 | src_ref, tgt_ref, src2tgt_output, tgt2src_output, src_seq_len, tgt_seq_len = input
380 | feed_dict = {self.src_ref_given: src_ref, self.tgt_ref_given: tgt_ref,
381 | self.src2tgt_output_given: src2tgt_output, self.tgt2src_output_given: tgt2src_output,
382 | self.src_seq_len_given: src_seq_len, self.tgt_seq_len_given: tgt_seq_len}
383 | output_tuple = TrainBackwardOutputTuple(train_summary=self.backward_train_summary,
384 | train_backward_loss=self.train_backward_loss,
385 | global_step=self.global_step,
386 | batch_size=self.batch_size_tensor,
387 | grad_norm=self.backward_grad_norm,
388 | learning_rate=self.learning_rate,
389 | train_src_ref=self.src_ref_given,
390 | train_tgt_ref=self.tgt_ref_given)
391 |
392 | return sess.run([self.backward_update, output_tuple], feed_dict=feed_dict)
393 |
394 | def eval(self, sess):
395 | """Execute eval graph."""
396 | assert self.mode == tf.contrib.learn.ModeKeys.EVAL
397 | output_tuple = EvalOutputTuple(eval_loss=self.eval_loss,
398 | predict_beat_count=self.predict_beat_count,
399 | batch_size=self.batch_size_tensor)
400 | return sess.run(output_tuple)
401 |
402 | def infer(self, sess):
403 | assert self.mode == tf.contrib.learn.ModeKeys.INFER
404 | output_tuple = InferOutputTuple(infer_tgt_output=self.infer_target_output,
405 | infer_tgt_ref=self.iterator.target_ref,
406 | infer_loss=self.infer_loss,
407 | predict_beat_count=self.predict_beat_count,
408 | batch_size=self.batch_size_tensor)
409 | return sess.run(output_tuple)
410 |
--------------------------------------------------------------------------------
/code/process.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 |
3 | import collections
4 | import time
5 | import os
6 |
7 | import numpy as np
8 | import tensorflow as tf
9 | import scipy.io as scio
10 |
11 | import model
12 | import utils
13 |
14 | BatchedInput = collections.namedtuple("BatchedInput",
15 | ("initializer", "source_ref", "target_ref",
16 | "source_sequence_length", "target_sequence_length"))
17 |
18 |
19 | def get_iterator(src_dataset, tgt_dataset, batch_size, random_seed, is_train):
20 | output_buffer_size = batch_size * 1000
21 | src_tgt_dataset = tf.data.Dataset.zip((src_dataset, tgt_dataset))
22 | # just try not to use random seed
23 | if is_train:
24 | src_tgt_dataset = src_tgt_dataset.shuffle(output_buffer_size,# random_seed,
25 | reshuffle_each_iteration=True)
26 | # Filter zero length input sequences.
27 | src_tgt_dataset = src_tgt_dataset.filter(
28 | lambda src_ref, tgt_ref: tf.logical_and(tf.size(src_ref) > 0, tf.size(tgt_ref) > 0))
29 |
30 | # Add in sequence lengths.
31 | src_tgt_dataset = src_tgt_dataset.map(
32 | lambda src_ref, tgt_ref: (src_ref, tgt_ref, tf.shape(src_ref)[0], tf.shape(tgt_ref)[0]))
33 |
34 | batched_dataset = src_tgt_dataset.batch(batch_size)
35 |
36 | batched_iter = batched_dataset.make_initializable_iterator()
37 | (src_ref, tgt_ref, src_seq_len, tgt_seq_len) = (batched_iter.get_next())
38 | return BatchedInput(
39 | initializer=batched_iter.initializer,
40 | source_ref=src_ref, ## this should be rank-3
41 | target_ref=tgt_ref, ## this should be rank-3
42 | source_sequence_length=src_seq_len, ## this should be rank-1
43 | target_sequence_length=tgt_seq_len) ## this should be rank-1
44 |
45 |
46 | def get_model_creator(hparams):
47 | """Get the right model class depending on configuration."""
48 | if hparams.architecture == 'peng':
49 | model_creator = model.Model
50 | """vanilla lstm, seq2seq"""
51 | return model_creator
52 |
53 |
54 | def load_data_mean_std(hparams, data_dir):
55 | if hparams.normalize_principle == "all":
56 | data_mean = scio.loadmat(os.path.join(data_dir, 'all_data_mean'))['data']
57 | data_std = scio.loadmat(os.path.join(data_dir, 'all_data_std'))['data']
58 | elif hparams.normalize_principle == "train":
59 | data_mean = scio.loadmat(os.path.join(data_dir, 'train_data_mean'))['data']
60 | data_std = scio.loadmat(os.path.join(data_dir, 'train_data_std'))['data']
61 | return data_mean, data_std
62 |
63 |
64 | TrainModel = collections.namedtuple("TrainModel", ("graph", "model", "iterator"))
65 |
66 |
67 | def create_train_model(model_creator, hparams, data_dir):
68 | """Create train graph, model, and iterator."""
69 | train_data_path = []
70 | for root, _, name in os.walk(os.path.join(data_dir, 'train_data')):
71 | for x in name:
72 | if x.split('.')[-1] == 'mat':
73 | train_data_path.append(os.path.join(root, x))
74 | assert len(train_data_path) == 1
75 | train_data = scio.loadmat(*train_data_path)['data']
76 | assert hparams.src_len == hparams.tgt_len == train_data.shape[1]
77 | graph = tf.Graph()
78 |
79 | with graph.as_default(), tf.container("train"):
80 | # channels: [features, SBP, DBP, MBP]
81 | train_src_data = train_data[:, :, 0:hparams.src_feature_size]
82 | train_tgt_data = train_data[:, :, hparams.src_feature_size:hparams.src_feature_size + hparams.tgt_feature_size]
83 | src_dataset = tf.data.Dataset.from_tensor_slices(train_src_data)
84 | tgt_dataset = tf.data.Dataset.from_tensor_slices(train_tgt_data)
85 | iterator = get_iterator(src_dataset, tgt_dataset, batch_size=hparams.batch_size,
86 | random_seed=hparams.random_seed, is_train=True)
87 | model = model_creator(hparams, iterator=iterator, mode=tf.contrib.learn.ModeKeys.TRAIN)
88 | return TrainModel(graph=graph, model=model, iterator=iterator)
89 |
90 |
91 | EvalModel = collections.namedtuple("EvalModel", ("graph", "model", "iterator", "data_mean", "data_std"))
92 |
93 |
94 | def create_eval_model(model_creator, hparams, data_dir):
95 | """Create eval graph, model and iterator."""
96 | eval_data_path = []
97 | for root, _, name in os.walk(os.path.join(data_dir, 'eval_data')):
98 | for x in name:
99 | if x.split('.')[-1] == 'mat':
100 | eval_data_path.append(os.path.join(root, x))
101 | assert len(eval_data_path) == 1
102 | eval_data = scio.loadmat(*eval_data_path)['data']
103 | data_mean, data_std = load_data_mean_std(hparams, data_dir)
104 | batch_size = eval_data.shape[0]
105 | graph = tf.Graph()
106 |
107 | with graph.as_default(), tf.container("eval"):
108 | eval_src_data = eval_data[:, :, 0:hparams.src_feature_size]
109 | # channels: [features, SBP, DBP, MBP]
110 | eval_tgt_data = eval_data[:, :, hparams.src_feature_size:hparams.src_feature_size + hparams.tgt_feature_size]
111 | src_dataset = tf.data.Dataset.from_tensor_slices(eval_src_data)
112 | tgt_dataset = tf.data.Dataset.from_tensor_slices(eval_tgt_data)
113 | iterator = get_iterator(src_dataset, tgt_dataset, batch_size=batch_size,
114 | random_seed=hparams.random_seed, is_train=False)
115 | model = model_creator(hparams, iterator=iterator, mode=tf.contrib.learn.ModeKeys.EVAL)
116 | return EvalModel(graph=graph, model=model, iterator=iterator, data_mean=data_mean, data_std=data_std)
117 |
118 |
119 | InferModel = collections.namedtuple("InferModel", ("graph", "model", "iterator"))
120 |
121 |
122 | def create_infer_model(model_creator, hparams, infer_data, batch_size):
123 | """Create inference model."""
124 | graph = tf.Graph()
125 |
126 | with graph.as_default(), tf.container("infer"):
127 | infer_src_data = infer_data[:, :, 0:hparams.src_feature_size]
128 | # channels:[features, SBP, SBP, MBP]
129 | infer_tgt_data = infer_data[:, :, hparams.src_feature_size:hparams.src_feature_size + hparams.tgt_feature_size]
130 | src_dataset = tf.data.Dataset.from_tensor_slices(infer_src_data)
131 | tgt_dataset = tf.data.Dataset.from_tensor_slices(infer_tgt_data)
132 | iterator = get_iterator(src_dataset, tgt_dataset, batch_size=batch_size,
133 | random_seed=hparams.random_seed, is_train=False)
134 | model = model_creator(hparams, iterator=iterator, mode=tf.contrib.learn.ModeKeys.INFER)
135 | return InferModel(graph=graph, model=model, iterator=iterator)
136 |
137 |
138 | def load_model(model, ckpt_path, session, name):
139 | """Load model from a checkpoint."""
140 | try:
141 | model.saver.restore(session, ckpt_path)
142 | except tf.errors.NotFoundError as e:
143 | utils.print_out("Can't load checkpoint")
144 | utils.print_out("%s" % str(e))
145 | # session.run(tf.tables_initializer()) ## why table still need to be initialized even model loaded??
146 | utils.print_out(" loaded %s model parameters from %s" % (name, ckpt_path))
147 | return model
148 |
149 |
150 | def create_or_load_model(model, model_dir, session, name):
151 | """Create translation model and initialize or load parameters in session."""
152 | latest_ckpt = tf.train.latest_checkpoint(model_dir)
153 | if latest_ckpt:
154 | model._replace(model=load_model(model.model, latest_ckpt, session, name))
155 | utils.print_out("checkpoint found, load checkpoint\n %s" % latest_ckpt)
156 | else:
157 | utils.print_out(" checkpoint not found in %s" % (model_dir))
158 | utils.print_out(" created %s model with fresh parameters" % (name))
159 | session.run(tf.global_variables_initializer())
160 | # session.run(tf.tables_initializer())
161 | global_step = model.model.global_step.eval(session=session)
162 | epoch_num = model.model.epoch_num.eval(session=session)
163 | return model, global_step, epoch_num
164 |
165 |
166 | def train_eval(hparams, data_dir, model_dir, log_dir):
167 | # Log and output files
168 | log_file = os.path.join(log_dir, "log_%d" % time.time())
169 | log_f = tf.gfile.GFile(log_file, mode="a")
170 | utils.print_out("# log_file=%s" % log_file, log_f)
171 |
172 | # create model and session
173 | model_creator = get_model_creator(hparams) # model_creator is just a factory function
174 | train_model = create_train_model(model_creator, hparams, data_dir)
175 | eval_model = create_eval_model(model_creator, hparams, data_dir)
176 | config_proto = tf.ConfigProto(log_device_placement=False, allow_soft_placement=True)
177 | config_proto.gpu_options.allow_growth = True
178 | train_sess = tf.Session(config=config_proto, graph=train_model.graph)
179 | eval_sess = tf.Session(config=config_proto, graph=eval_model.graph)
180 |
181 | # Summary writer
182 | summary_writer = tf.summary.FileWriter(os.path.join(log_dir, "train_log"), train_model.graph)
183 |
184 | with train_model.graph.as_default():
185 | train_model, global_step, epoch_num = create_or_load_model(train_model, model_dir, train_sess, "train")
186 |
187 | # first time train
188 | # start_train_time = time.time()
189 | utils.print_out("# Start step %d, %s" % (global_step, time.ctime()), log_f)
190 | epoch_step = 0 # just to count the current step in this epoch
191 | stats = {"step_time": 0.0, "train_loss": 0.0,
192 | "predict_beat_count": 0.0, # number of beat count
193 | "sequence_count": 0.0, # number of training examples processed
194 | }
195 | info = {}
196 | # Initialize iterators
197 | train_sess.run(train_model.iterator.initializer)
198 | # This is the training loop.
199 | while global_step < hparams.num_train_steps:
200 | ### Run a step ###
201 | start_time = time.time()
202 | try:
203 | # forward train phase
204 | forward_result = train_model.model.train_forward(train_sess)[1]
205 | step_result = forward_result
206 | src_ref, tgt_ref, src2tgt_output, tgt2src_output, src_seq_len, tgt_seq_len = (
207 | forward_result.train_src_ref, forward_result.train_tgt_ref,
208 | forward_result.train_src2tgt_output, forward_result.train_tgt2src_output,
209 | forward_result.src_seq_len, forward_result.tgt_seq_len)
210 | # backward train phase
211 | if hparams.backward_enable:
212 | backward_input = [src_ref, tgt_ref, src2tgt_output, tgt2src_output, src_seq_len, tgt_seq_len]
213 | backward_result = train_model.model.train_backward(train_sess, backward_input)[1]
214 | epoch_step += 1
215 | except tf.errors.OutOfRangeError:
216 | # Finished going through the training dataset. Go to next epoch.
217 | epoch_step = 0
218 | epoch_num = train_model.model.increase_epoch_num(train_sess)
219 | utils.print_out("# Finished an epoch, global step %d. Perform evaluation" % global_step)
220 | # save model for evaluation
221 | train_model.model.saver.save(train_sess, os.path.join(model_dir, "model.ckpt"), global_step=global_step)
222 | eval(eval_model, eval_sess, model_dir, hparams, summary_writer, log_f)
223 | train_sess.run(train_model.iterator.initializer)
224 | continue
225 |
226 | # Process step_result, accumulate stats, and write summary
227 | global_step = step_result.global_step
228 | summary_writer.add_summary(step_result.train_summary, global_step)
229 | # Update statistics
230 | stats["step_time"] += time.time() - start_time
231 | stats["train_loss"] += step_result.train_loss * step_result.predict_beat_count
232 | stats["predict_beat_count"] += step_result.predict_beat_count
233 | stats["sequence_count"] += step_result.batch_size
234 | if global_step % hparams.steps_per_stats == 0:
235 | info["avg_step_time"] = stats["step_time"] / hparams.steps_per_stats
236 | # the time to take for process one batch
237 | info["sample_per_second"] = stats["sequence_count"] / (stats["step_time"])
238 | info["avg_beat_loss"] = (stats["train_loss"] / stats["predict_beat_count"])
239 | info["epoch_num"] = epoch_num
240 | utils.print_out(
241 | "global step: %d, epoch_num: %d, avg step time: %.2fs, sample per second: %.2f, avg beat loss: %.2f, time;%s" %
242 | (
243 | global_step, info['epoch_num'], info["avg_step_time"], info["sample_per_second"],
244 | info["avg_beat_loss"],
245 | time.ctime()), log_f)
246 | for key in info:
247 | summary_writer.add_summary(tf.Summary(value=[tf.Summary.Value(tag=key, simple_value=info[key])]),
248 | global_step)
249 |
250 | stats = {"step_time": 0.0, "train_loss": 0.0,
251 | "predict_beat_count": 0.0, # word count on the target side
252 | "sequence_count": 0.0, # number of training examples processed
253 | }
254 |
255 | # Done training in one epoch
256 | train_model.model.saver.save(
257 | train_sess,
258 | os.path.join(model_dir, "model.ckpt"),
259 | global_step=global_step)
260 |
261 | # ...result summary
262 |
263 | summary_writer.close()
264 |
265 |
266 | def eval(eval_model, eval_sess, model_dir, hparams, summary_writer, log_f):
267 | with eval_model.graph.as_default():
268 | eval_model, global_step, epoch_num = create_or_load_model(eval_model, model_dir, eval_sess, "eval")
269 | eval_sess.run(eval_model.model.iterator.initializer)
270 | eval_info = {}
271 | total_loss = 0
272 | total_predict_beat_count = 0
273 | total_sequence_count = 0
274 | while True:
275 | try:
276 | step_result = eval_model.model.eval(eval_sess)
277 | total_loss += step_result.eval_loss * step_result.predict_beat_count
278 | total_predict_beat_count += step_result.predict_beat_count
279 | total_sequence_count += step_result.batch_size
280 | except tf.errors.OutOfRangeError:
281 | eval_info['epoch_num'] = epoch_num
282 | eval_info['eval_avg_beat_loss'] = total_loss / total_predict_beat_count
283 | eval_info['eval_predict_beat_count'] = total_predict_beat_count
284 | eval_info['eval_sample_num'] = total_sequence_count
285 | utils.print_out(
286 | "\neval: global step: %d, epoch_num: %d, eval_avg beat loss: %.2f, eval_predict_beat_count: %d, eval_sample_num: %d, time;%s\n" %
287 | (global_step, eval_info['epoch_num'], eval_info["eval_avg_beat_loss"],
288 | eval_info['eval_predict_beat_count'], eval_info['eval_sample_num'], time.ctime()), log_f)
289 | for key in eval_info:
290 | summary_writer.add_summary(
291 | tf.Summary(value=[tf.Summary.Value(tag=key, simple_value=eval_info[key])]),
292 | global_step)
293 | break
294 |
295 |
296 | def infer(hparams, data_dir, model_dir, result_dir):
297 | # infer mode is different from train and eval mode that we prepare data in advanced
298 | # becasue we may have various test dataset, but only one dataset in the case of train and eval
299 | # prepare data
300 | infer_data_path = []
301 | for root, _, name in os.walk(os.path.join(data_dir, 'test_data')):
302 | for x in name:
303 | if x.split('.')[-1] == 'mat':
304 | infer_data_path.append(os.path.join(root, x))
305 |
306 | for j in range(len(infer_data_path)):
307 | data_name = os.path.basename(infer_data_path[j]).split('.')[0]
308 | infer_data = scio.loadmat(infer_data_path[j])['data']
309 | data_mean, data_std = load_data_mean_std(hparams, data_dir)
310 | batch_size = infer_data.shape[0]
311 |
312 | # Create model
313 | model_creator = get_model_creator(hparams)
314 | infer_model = create_infer_model(model_creator, hparams, infer_data, batch_size)
315 | # TensorFlow model
316 | config_proto = tf.ConfigProto(log_device_placement=False, allow_soft_placement=True)
317 | config_proto.gpu_options.allow_growth = True
318 | infer_sess = tf.Session(config=config_proto, graph=infer_model.graph)
319 | with infer_model.graph.as_default():
320 | infer_model, global_step, epoch_num = create_or_load_model(
321 | infer_model, model_dir, infer_sess, "infer")
322 | infer_sess.run(infer_model.model.iterator.initializer)
323 | # because we have already set batch size to be the size of all sample, so we dont use loop but just calculate the metrics
324 | # otherwise, we can implement similarly as eval mode
325 |
326 | step_result = infer_model.model.infer(infer_sess)
327 | infer_avg_beat_loss = step_result.infer_loss
328 | total_predict_beat_count = step_result.predict_beat_count
329 | total_sequence_count = step_result.batch_size
330 | infer_tgt_output = step_result.infer_tgt_output
331 | # scale back to the original size
332 | # (both the infered one and the input one needed to be scaled back, because they are both normalized)
333 | infer_real_tgt_feature = np.zeros(infer_tgt_output.shape)
334 | for i in range(hparams.tgt_feature_size):
335 | infer_real_tgt_feature[:, :, i] = (
336 | infer_tgt_output[:, :, i] * data_std[:, hparams.src_feature_size + i]
337 | + data_mean[:, hparams.src_feature_size + i])
338 |
339 | origin_tgt_data = infer_data[:, :,
340 | hparams.src_feature_size: hparams.src_feature_size + hparams.tgt_feature_size]
341 | origin_real_tgt_data = np.zeros(origin_tgt_data.shape)
342 | for i in range(hparams.tgt_feature_size):
343 | origin_real_tgt_data[:, :, i] = (
344 | origin_tgt_data[:, :, i] * data_std[:, hparams.src_feature_size + i]
345 | + data_mean[:, hparams.src_feature_size + i])
346 |
347 | # infer_info['epoch_num'] = epoch_num
348 | infer_info = {}
349 | infer_info['infer_avg_beat_loss'] = infer_avg_beat_loss
350 | infer_info['infer_predict_beat_count'] = total_predict_beat_count
351 | infer_info['infer_sample_num'] = total_sequence_count
352 | utils.print_out(
353 | "\ninfer: global step: %d, infer_avg beat loss: %.2f, infer_predict_beat_count: %d, infer_sample_num: %d, time;%s\n" %
354 | (global_step, infer_info["infer_avg_beat_loss"], infer_info['infer_predict_beat_count'],
355 | infer_info['infer_sample_num'], time.ctime()))
356 | """
357 | for key in eval_info:
358 | summary_writer.add_summary(
359 | tf.Summary(value=[tf.Summary.Value(tag=key, simple_value=eval_info[key])]),
360 | global_step)
361 | """
362 |
363 | # save_result()
364 | step_result_dir = os.path.join(result_dir, 'step-' + str(global_step))
365 | if not tf.gfile.Exists(step_result_dir):
366 | tf.gfile.MakeDirs(step_result_dir)
367 | scio.savemat(os.path.join(step_result_dir, data_name + '_real_origin.mat'), {'data': origin_real_tgt_data})
368 | utils.print_out("save origin data to %s" % os.path.join(step_result_dir, data_name + '_real_origin.mat'))
369 | scio.savemat(os.path.join(step_result_dir, data_name + '_real_prediction.mat'),
370 | {'data': infer_real_tgt_feature})
371 | utils.print_out(
372 | "save prediction data to %s" % os.path.join(step_result_dir, data_name + '_real_prediction.mat'))
373 |
374 | # performance metrics calculation
375 | # RMSE: root mean square error
376 | real_test_loss_SBP_RMSE = np.sqrt(
377 | np.mean(np.square(origin_real_tgt_data[:, :, 0] - infer_real_tgt_feature[:, :, 0])))
378 | real_test_loss_DBP_RMSE = np.sqrt(
379 | np.mean(np.square(origin_real_tgt_data[:, :, 1] - infer_real_tgt_feature[:, :, 1])))
380 | real_test_loss_MBP_RMSE = np.sqrt(
381 | np.mean(np.square(origin_real_tgt_data[:, :, 2] - infer_real_tgt_feature[:, :, 2])))
382 | # MAD: mean absolute difference
383 | real_test_loss_SBP_MAD = np.mean(
384 | np.absolute(origin_real_tgt_data[:, :, 0] - infer_real_tgt_feature[:, :, 0]))
385 | real_test_loss_DBP_MAD = np.mean(
386 | np.absolute(origin_real_tgt_data[:, :, 1] - infer_real_tgt_feature[:, :, 1]))
387 | real_test_loss_MBP_MAD = np.mean(
388 | np.absolute(origin_real_tgt_data[:, :, 2] - infer_real_tgt_feature[:, :, 2]))
389 | # MD: mean difference (absolute is not used!)
390 | real_test_loss_SBP_MD = np.mean(origin_real_tgt_data[:, :, 0] - infer_real_tgt_feature[:, :, 0])
391 | real_test_loss_DBP_MD = np.mean(origin_real_tgt_data[:, :, 1] - infer_real_tgt_feature[:, :, 1])
392 | real_test_loss_MBP_MD = np.mean(origin_real_tgt_data[:, :, 2] - infer_real_tgt_feature[:, :, 2])
393 | # SD1: the standard deviation of delta
394 | real_test_loss_SBP_SD1 = np.sqrt(
395 | np.mean(np.square(
396 | (origin_real_tgt_data[:, :, 0] - infer_real_tgt_feature[:, :, 0]) - real_test_loss_SBP_MD)))
397 | real_test_loss_DBP_SD1 = np.sqrt(
398 | np.mean(np.square(
399 | (origin_real_tgt_data[:, :, 1] - infer_real_tgt_feature[:, :, 1]) - real_test_loss_DBP_MD)))
400 | real_test_loss_MBP_SD1 = np.sqrt(
401 | np.mean(np.square(
402 | (origin_real_tgt_data[:, :, 2] - infer_real_tgt_feature[:, :, 2]) - real_test_loss_MBP_MD)))
403 | # SD2: the standard deviation of predicted value itself
404 | real_test_loss_SBP_SD2 = np.std(infer_real_tgt_feature[:, :, 0])
405 | real_test_loss_DBP_SD2 = np.std(infer_real_tgt_feature[:, :, 1])
406 | real_test_loss_MBP_SD2 = np.std(infer_real_tgt_feature[:, :, 2])
407 | name_collection = ['real_test_loss_SBP_RMSE', 'real_test_loss_DBP_RMSE', 'real_test_loss_MBP_RMSE',
408 | 'real_test_loss_SBP_MAD', 'real_test_loss_DBP_MAD', 'real_test_loss_MBP_MAD',
409 | 'real_test_loss_SBP_MD', 'real_test_loss_DBP_MD', 'real_test_loss_MBP_MD',
410 | 'real_test_loss_SBP_SD1', 'real_test_loss_DBP_SD1', 'real_test_loss_MBP_SD1',
411 | 'real_test_loss_SBP_SD2', 'real_test_loss_DBP_SD2', 'real_test_loss_MBP_SD2']
412 | metric_collection = [real_test_loss_SBP_RMSE, real_test_loss_DBP_RMSE, real_test_loss_MBP_RMSE,
413 | real_test_loss_SBP_MAD, real_test_loss_DBP_MAD, real_test_loss_MBP_MAD,
414 | real_test_loss_SBP_MD, real_test_loss_DBP_MD, real_test_loss_MBP_MD,
415 | real_test_loss_SBP_SD1, real_test_loss_DBP_SD1, real_test_loss_MBP_SD1,
416 | real_test_loss_SBP_SD2, real_test_loss_DBP_SD2, real_test_loss_MBP_SD2]
417 |
418 | with open(os.path.join(step_result_dir, 'step_' + str(global_step) + '_performance_metrics.txt'),
419 | 'a') as result_fo:
420 | for i in range(len(name_collection)):
421 | if i % 3 == 0:
422 | print('\n')
423 | result_fo.write('\n')
424 | print(data_name + '_' + name_collection[i], ':', metric_collection[i])
425 | result_fo.write('%s_%s:%f\n' % (data_name, name_collection[i], metric_collection[i]))
426 |
--------------------------------------------------------------------------------
/code/readme.md:
--------------------------------------------------------------------------------
1 | ## Main training file
2 | train.py
3 |
4 | ## Model definition
5 | model.py
6 |
--------------------------------------------------------------------------------
/code/train.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 |
3 | import argparse
4 | import os
5 | import codecs
6 | import json
7 |
8 | import tensorflow as tf
9 |
10 | import utils
11 | import process
12 | import preprocess
13 |
14 |
15 | def add_arguments(parser):
16 | """Build ArgumentParser."""
17 | parser.register("type", "bool", lambda v: v.lower() == "true")
18 | """
19 | # for ordinary lstm
20 | parser.add_argument("--num_layers", type=int, default=4, help="number of layers")
21 | parser.add_argument("--num_residual_layers", type=int, default=2,
22 | help="number of residual layers of encoder, should be not greater than the number of encoder layers.")
23 | parser.add_argument("--lstm_type", type=str, default="uni", help="\
24 | uni | bi | gnmt.
25 | For bi, we build num_encoder_layers/2 bi-directional layers.
26 | For gnmt, we build 1 bi-directional layer, and (num_encoder_layers - 1)
27 | uni-directional layers.\
28 | ")
29 | """
30 |
31 | # network
32 | parser.add_argument("--architecture", type=str, default="deepRNN", help="""deepRNN""")
33 | ## layer(for peng's model)
34 | parser.add_argument("--num_bi_layers", type=int, default=1,
35 | help="number of bidirectional layers, 1 layer acually represent 2 directions")
36 | parser.add_argument("--num_uni_layers", type=int, default=3,
37 | help="number of uni layers stacked on the bi layers, "
38 | "with uni_residual_layer=uni_layers-1 setting as default .")
39 | ## cell
40 | parser.add_argument("--unit_type", type=str, default="lstm", help="lstm")
41 | parser.add_argument("--num_units", type=int, default=128, help="the hidden size of each cell.")
42 | parser.add_argument("--forget_bias", type=float, default=1.0,
43 | help="Forget bias for BasicLSTMCell.")
44 | parser.add_argument("--dropout", type=float, default=0,
45 | help="Dropout rate (not keep_prob)")
46 | ## initializer (uniform initializer as default)
47 | parser.add_argument("--init_weight", type=float, default=0.1,
48 | help=("for uniform init_op, initialize weights "
49 | "between [-this, this]."))
50 | ## optimizer
51 | parser.add_argument("--optimizer", type=str, default="adam", help="the type of optimizer")
52 | parser.add_argument("--learning_rate", type=float, default=1e-3,
53 | help="Learning rate. Adam: 0.001 | 0.0001")
54 |
55 | # data
56 | ## data source
57 | parser.add_argument('--data_source_dir', type=str,
58 | default='/path/to/data/',
59 | help='the path to load training data')
60 |
61 | ## data detail
62 | parser.add_argument("--src_len", type=int, default=64,
63 | help="length of src sequences during training. "
64 | "the input are all in same length")
65 | parser.add_argument("--tgt_len", type=int, default=64,
66 | help="length of tgt sequences during training.")
67 | parser.add_argument("--src_feature_size", type=int, default=7,
68 | help="feature size of src")
69 | parser.add_argument("--tgt_feature_size", type=int, default=3,
70 | help="feature size of tgt")
71 |
72 | # expriment
73 | parser.add_argument("--exprs_dir", type=str,
74 | default='/path/to/save/experiments',
75 | help="the dir to store experiment.")
76 | parser.add_argument("--expr_name", type=str,
77 | default='deepRNN',
78 | help='the name of dir to store config, model, data, figure, result')
79 | parser.add_argument('--train_ratio', default=0.7, type=float,
80 | help='the ratio of data for trainning')
81 | parser.add_argument('--eval_ratio', default=0.1, type=float,
82 | help='the ratio of data for validation')
83 | parser.add_argument("--batch_size", type=int, default=32, help="Batch size.")
84 | parser.add_argument("--random_seed", type=int, default=1234,
85 | help="Random seed (>0, set a specific seed).")
86 | parser.add_argument("--num_train_steps", type=int, default=100000, help="Num steps to train.")
87 | parser.add_argument('--backward_enable', default=False, type="bool",
88 | help="if enable the backward training")
89 | parser.add_argument('--forward_directions', default=["src2tgt"], type=list,
90 | help="the directions for forward phase of traning"
91 | " can only be 'src2tgt'")
92 | parser.add_argument("--steps_per_stats", type=int, default=1,
93 | help=("How many training steps to do per stats logging."
94 | "Save checkpoint every epoch"))
95 |
96 |
97 | def create_dirs(hparams):
98 | """create directories"""
99 | assert hparams.exprs_dir
100 | exprs_dir = hparams.exprs_dir
101 | if not tf.gfile.Exists(exprs_dir):
102 | tf.gfile.MakeDirs(exprs_dir)
103 | utils.print_out("# experiments directory: %s " % exprs_dir)
104 | expr_dir = os.path.join(exprs_dir, hparams.expr_name)
105 | if not tf.gfile.Exists(expr_dir):
106 | tf.gfile.MakeDirs(expr_dir)
107 | utils.print_out("# -experiment directory: %s " % expr_dir)
108 | config_dir = os.path.join(expr_dir, 'config')
109 | if not tf.gfile.Exists(config_dir):
110 | tf.gfile.MakeDirs(config_dir)
111 | utils.print_out("# --config directory: %s " % config_dir)
112 | log_dir = os.path.join(expr_dir, 'log')
113 | if not tf.gfile.Exists(log_dir):
114 | tf.gfile.MakeDirs(log_dir)
115 | utils.print_out("# --log directory: %s " % log_dir)
116 | data_dir = os.path.join(expr_dir, 'data')
117 | if not tf.gfile.Exists(data_dir):
118 | tf.gfile.MakeDirs(data_dir)
119 | utils.print_out("# --data directory: %s " % data_dir)
120 | model_dir = os.path.join(expr_dir, 'model')
121 | if not tf.gfile.Exists(model_dir):
122 | tf.gfile.MakeDirs(model_dir)
123 | utils.print_out("# --model directory: %s " % model_dir)
124 | figure_dir = os.path.join(expr_dir, 'figure')
125 | if not tf.gfile.Exists(figure_dir):
126 | tf.gfile.MakeDirs(figure_dir)
127 | utils.print_out("# --figure directory: %s " % figure_dir)
128 | result_dir = os.path.join(expr_dir, 'result')
129 | if not tf.gfile.Exists(result_dir):
130 | tf.gfile.MakeDirs(result_dir)
131 | utils.print_out("# --result directory: %s " % result_dir)
132 | return expr_dir, config_dir, log_dir, data_dir, model_dir, figure_dir, result_dir
133 |
134 |
135 | def check_and_save_hparams(out_dir, hparams):
136 | """Save hparams."""
137 | hparams_file = os.path.join(out_dir, "hparams")
138 | if tf.gfile.Exists(hparams_file):
139 | with codecs.getreader("utf-8")(tf.gfile.GFile(hparams_file, "rb")) as f:
140 | origin_hparams = json.load(f)
141 | origin_hparams = tf.contrib.training.HParams(**origin_hparams)
142 | wrong_keys = []
143 | keys = set(list(hparams.values().keys()) + (list(origin_hparams.values().keys())))
144 | for key in keys:
145 | if (hparams.values().get(key, None) != origin_hparams.values().get(key, None) or
146 | hparams.values().get(key, None) == None or
147 | hparams.values().get(key, None) == None):
148 | wrong_keys.append(key)
149 | try:
150 | assert origin_hparams.values() == hparams.values()
151 | utils.print_out("using the same hparams of old %s" % hparams_file)
152 | except:
153 | utils.print_out('new hparams not the same with the existed one')
154 | for wrong_key in wrong_keys:
155 | utils.print_out(" keys: %s, \norigin_value: %s, \nnew_value: %s\n" % (
156 | wrong_key, origin_hparams.values()[wrong_key], hparams.values()[wrong_key]))
157 | raise ValueError
158 | else:
159 | utils.print_out(" not old hparams found, create new hparams to %s" % hparams_file)
160 | with codecs.getwriter("utf-8")(tf.gfile.GFile(hparams_file, "wb")) as f:
161 | f.write(hparams.to_json(indent=4))
162 |
163 |
164 | def main():
165 | parser = argparse.ArgumentParser(description='Deep BiLSTM with Residual')
166 | add_arguments(parser)
167 | args = parser.parse_args()
168 | print(args)
169 | hparams = tf.contrib.training.HParams(**vars(args))
170 | # check GPU device
171 | utils.print_out("# Devices visible to TensorFlow: %s" % repr(tf.Session().list_devices()))
172 | # create dirs
173 | expr_dir, config_dir, log_dir, data_dir, model_dir, figure_dir, result_dir = create_dirs(hparams)
174 | # save hyperameter
175 | check_and_save_hparams(config_dir, hparams)
176 |
177 | stage = 'test' # preprocess','train_eval', or 'test'
178 | assert stage in [, 'train_eval', 'test'], 'stage not recognized'
179 | utils.print_out('stage: %s' % stage)
180 | # if stage == 'preprocess':
181 | # preprocess.preprocess(hparams, data_dir)
182 | # the data are stored in the data_dir for the training step
183 | if stage == 'train_eval':
184 | process.train_eval(hparams, data_dir, model_dir, log_dir)
185 | if stage == 'test':
186 | process.infer(hparams, data_dir, model_dir, result_dir)
187 |
188 |
189 | if __name__ == '__main__':
190 | main()
191 |
--------------------------------------------------------------------------------
/code/utils.py:
--------------------------------------------------------------------------------
1 | """Generally useful utility functions."""
2 | from __future__ import print_function
3 |
4 | import sys
5 |
6 | import tensorflow as tf
7 |
8 |
9 | def print_out(s, f=None, new_line=True):
10 | """Similar to print but with support to flush and output to a file."""
11 | if isinstance(s, bytes):
12 | s = s.decode("utf-8")
13 |
14 | if f:
15 | f.write(s.encode("utf-8"))
16 | if new_line:
17 | f.write(b"\n")
18 |
19 | # stdout
20 | out_s = s.encode("utf-8")
21 | if not isinstance(out_s, str):
22 | out_s = out_s.decode("utf-8")
23 | print(out_s, end="", file=sys.stdout)
24 |
25 | if new_line:
26 | sys.stdout.write("\n")
27 | sys.stdout.flush()
28 |
29 | def test_global_variables():
30 | params = tf.global_variables()
31 | print_out("# global variables")
32 | print_out("Format: , , <(soft) device placement>")
33 | for param in params:
34 | print_out(" %s, %s, %s" % (param.name, str(param.get_shape()), param.op.device))
35 |
36 | def test_trainable_variables():
37 | params = tf.trainable_variables()
38 | print_out("# trainable variables")
39 | print_out("Format: , , <(soft) device placement>")
40 | for param in params:
41 | print_out(" %s, %s, %s" % (param.name, str(param.get_shape()), param.op.device))
--------------------------------------------------------------------------------
/data/readme.md:
--------------------------------------------------------------------------------
1 |
2 | The data was provided by another institution. Due to the IP issue, we cannot share it now.
3 | But we highly recommended [MMIC](https://mimic.physionet.org/) dataset for validation use.
4 |
--------------------------------------------------------------------------------
/deepRnn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psu1/DeepRNN/65ac0c84f5ca85d498e3028902265e48845024e0/deepRnn.png
--------------------------------------------------------------------------------
/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psu1/DeepRNN/65ac0c84f5ca85d498e3028902265e48845024e0/demo.png
--------------------------------------------------------------------------------
/plotResult.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psu1/DeepRNN/65ac0c84f5ca85d498e3028902265e48845024e0/plotResult.png
--------------------------------------------------------------------------------
/table1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psu1/DeepRNN/65ac0c84f5ca85d498e3028902265e48845024e0/table1.png
--------------------------------------------------------------------------------