PhaseNet 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- PhaseNet-0.1.0.dist-info/LICENSE +674 -0
- PhaseNet-0.1.0.dist-info/METADATA +30 -0
- PhaseNet-0.1.0.dist-info/RECORD +12 -0
- PhaseNet-0.1.0.dist-info/WHEEL +6 -0
- PhaseNet-0.1.0.dist-info/top_level.txt +1 -0
- phasenet/__init__.py +1 -0
- phasenet/data_reader.py +520 -0
- phasenet/detect_peaks.py +175 -0
- phasenet/model.py +467 -0
- phasenet/predict.py +143 -0
- phasenet/train.py +297 -0
- phasenet/util.py +203 -0
phasenet/detect_peaks.py
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""Detect peaks in data based on their amplitude and other features."""
|
|
2
|
+
|
|
3
|
+
from __future__ import division, print_function
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
__author__ = "Marcos Duarte, https://github.com/demotu/BMC"
|
|
7
|
+
__version__ = "1.0.4"
|
|
8
|
+
__license__ = "MIT"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def detect_peaks(x, mph=None, mpd=1, threshold=0, edge='rising',
|
|
12
|
+
kpsh=False, valley=False, show=False, ax=None):
|
|
13
|
+
|
|
14
|
+
"""Detect peaks in data based on their amplitude and other features.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
x : 1D array_like
|
|
19
|
+
data.
|
|
20
|
+
mph : {None, number}, optional (default = None)
|
|
21
|
+
detect peaks that are greater than minimum peak height.
|
|
22
|
+
mpd : positive integer, optional (default = 1)
|
|
23
|
+
detect peaks that are at least separated by minimum peak distance (in
|
|
24
|
+
number of data).
|
|
25
|
+
threshold : positive number, optional (default = 0)
|
|
26
|
+
detect peaks (valleys) that are greater (smaller) than `threshold`
|
|
27
|
+
in relation to their immediate neighbors.
|
|
28
|
+
edge : {None, 'rising', 'falling', 'both'}, optional (default = 'rising')
|
|
29
|
+
for a flat peak, keep only the rising edge ('rising'), only the
|
|
30
|
+
falling edge ('falling'), both edges ('both'), or don't detect a
|
|
31
|
+
flat peak (None).
|
|
32
|
+
kpsh : bool, optional (default = False)
|
|
33
|
+
keep peaks with same height even if they are closer than `mpd`.
|
|
34
|
+
valley : bool, optional (default = False)
|
|
35
|
+
if True (1), detect valleys (local minima) instead of peaks.
|
|
36
|
+
show : bool, optional (default = False)
|
|
37
|
+
if True (1), plot data in matplotlib figure.
|
|
38
|
+
ax : a matplotlib.axes.Axes instance, optional (default = None).
|
|
39
|
+
|
|
40
|
+
Returns
|
|
41
|
+
-------
|
|
42
|
+
ind : 1D array_like
|
|
43
|
+
indeces of the peaks in `x`.
|
|
44
|
+
|
|
45
|
+
Notes
|
|
46
|
+
-----
|
|
47
|
+
The detection of valleys instead of peaks is performed internally by simply
|
|
48
|
+
negating the data: `ind_valleys = detect_peaks(-x)`
|
|
49
|
+
|
|
50
|
+
The function can handle NaN's
|
|
51
|
+
|
|
52
|
+
See this IPython Notebook [1]_.
|
|
53
|
+
|
|
54
|
+
References
|
|
55
|
+
----------
|
|
56
|
+
.. [1] http://nbviewer.ipython.org/github/demotu/BMC/blob/master/notebooks/DetectPeaks.ipynb
|
|
57
|
+
|
|
58
|
+
Examples
|
|
59
|
+
--------
|
|
60
|
+
>>> from detect_peaks import detect_peaks
|
|
61
|
+
>>> x = np.random.randn(100)
|
|
62
|
+
>>> x[60:81] = np.nan
|
|
63
|
+
>>> # detect all peaks and plot data
|
|
64
|
+
>>> ind = detect_peaks(x, show=True)
|
|
65
|
+
>>> print(ind)
|
|
66
|
+
|
|
67
|
+
>>> x = np.sin(2*np.pi*5*np.linspace(0, 1, 200)) + np.random.randn(200)/5
|
|
68
|
+
>>> # set minimum peak height = 0 and minimum peak distance = 20
|
|
69
|
+
>>> detect_peaks(x, mph=0, mpd=20, show=True)
|
|
70
|
+
|
|
71
|
+
>>> x = [0, 1, 0, 2, 0, 3, 0, 2, 0, 1, 0]
|
|
72
|
+
>>> # set minimum peak distance = 2
|
|
73
|
+
>>> detect_peaks(x, mpd=2, show=True)
|
|
74
|
+
|
|
75
|
+
>>> x = np.sin(2*np.pi*5*np.linspace(0, 1, 200)) + np.random.randn(200)/5
|
|
76
|
+
>>> # detection of valleys instead of peaks
|
|
77
|
+
>>> detect_peaks(x, mph=0, mpd=20, valley=True, show=True)
|
|
78
|
+
|
|
79
|
+
>>> x = [0, 1, 1, 0, 1, 1, 0]
|
|
80
|
+
>>> # detect both edges
|
|
81
|
+
>>> detect_peaks(x, edge='both', show=True)
|
|
82
|
+
|
|
83
|
+
>>> x = [-2, 1, -2, 2, 1, 1, 3, 0]
|
|
84
|
+
>>> # set threshold = 2
|
|
85
|
+
>>> detect_peaks(x, threshold = 2, show=True)
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
x = np.atleast_1d(x).astype('float64')
|
|
89
|
+
if x.size < 3:
|
|
90
|
+
return np.array([], dtype=int)
|
|
91
|
+
if valley:
|
|
92
|
+
x = -x
|
|
93
|
+
# find indices of all peaks
|
|
94
|
+
dx = x[1:] - x[:-1]
|
|
95
|
+
# handle NaN's
|
|
96
|
+
indnan = np.where(np.isnan(x))[0]
|
|
97
|
+
if indnan.size:
|
|
98
|
+
x[indnan] = np.inf
|
|
99
|
+
dx[np.where(np.isnan(dx))[0]] = np.inf
|
|
100
|
+
ine, ire, ife = np.array([[], [], []], dtype=int)
|
|
101
|
+
if not edge:
|
|
102
|
+
ine = np.where((np.hstack((dx, 0)) < 0) & (np.hstack((0, dx)) > 0))[0]
|
|
103
|
+
else:
|
|
104
|
+
if edge.lower() in ['rising', 'both']:
|
|
105
|
+
ire = np.where((np.hstack((dx, 0)) <= 0) & (np.hstack((0, dx)) > 0))[0]
|
|
106
|
+
if edge.lower() in ['falling', 'both']:
|
|
107
|
+
ife = np.where((np.hstack((dx, 0)) < 0) & (np.hstack((0, dx)) >= 0))[0]
|
|
108
|
+
ind = np.unique(np.hstack((ine, ire, ife)))
|
|
109
|
+
# handle NaN's
|
|
110
|
+
if ind.size and indnan.size:
|
|
111
|
+
# NaN's and values close to NaN's cannot be peaks
|
|
112
|
+
ind = ind[np.in1d(ind, np.unique(np.hstack((indnan, indnan-1, indnan+1))), invert=True)]
|
|
113
|
+
# first and last values of x cannot be peaks
|
|
114
|
+
if ind.size and ind[0] == 0:
|
|
115
|
+
ind = ind[1:]
|
|
116
|
+
if ind.size and ind[-1] == x.size-1:
|
|
117
|
+
ind = ind[:-1]
|
|
118
|
+
# remove peaks < minimum peak height
|
|
119
|
+
if ind.size and mph is not None:
|
|
120
|
+
ind = ind[x[ind] >= mph]
|
|
121
|
+
# remove peaks - neighbors < threshold
|
|
122
|
+
if ind.size and threshold > 0:
|
|
123
|
+
dx = np.min(np.vstack([x[ind]-x[ind-1], x[ind]-x[ind+1]]), axis=0)
|
|
124
|
+
ind = np.delete(ind, np.where(dx < threshold)[0])
|
|
125
|
+
# detect small peaks closer than minimum peak distance
|
|
126
|
+
if ind.size and mpd > 1:
|
|
127
|
+
ind = ind[np.argsort(x[ind])][::-1] # sort ind by peak height
|
|
128
|
+
idel = np.zeros(ind.size, dtype=bool)
|
|
129
|
+
for i in range(ind.size):
|
|
130
|
+
if not idel[i]:
|
|
131
|
+
# keep peaks with the same height if kpsh is True
|
|
132
|
+
idel = idel | (ind >= ind[i] - mpd) & (ind <= ind[i] + mpd) \
|
|
133
|
+
& (x[ind[i]] > x[ind] if kpsh else True)
|
|
134
|
+
idel[i] = 0 # Keep current peak
|
|
135
|
+
# remove the small peaks and sort back the indices by their occurrence
|
|
136
|
+
ind = np.sort(ind[~idel])
|
|
137
|
+
|
|
138
|
+
if show:
|
|
139
|
+
if indnan.size:
|
|
140
|
+
x[indnan] = np.nan
|
|
141
|
+
if valley:
|
|
142
|
+
x = -x
|
|
143
|
+
_plot(x, mph, mpd, threshold, edge, valley, ax, ind)
|
|
144
|
+
|
|
145
|
+
return ind, x[ind]
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _plot(x, mph, mpd, threshold, edge, valley, ax, ind):
|
|
149
|
+
"""Plot results of the detect_peaks function, see its help."""
|
|
150
|
+
try:
|
|
151
|
+
import matplotlib.pyplot as plt
|
|
152
|
+
except ImportError:
|
|
153
|
+
print('matplotlib is not available.')
|
|
154
|
+
else:
|
|
155
|
+
if ax is None:
|
|
156
|
+
_, ax = plt.subplots(1, 1, figsize=(8, 4))
|
|
157
|
+
|
|
158
|
+
ax.plot(x, 'b', lw=1)
|
|
159
|
+
if ind.size:
|
|
160
|
+
label = 'valley' if valley else 'peak'
|
|
161
|
+
label = label + 's' if ind.size > 1 else label
|
|
162
|
+
ax.plot(ind, x[ind], '+', mfc=None, mec='r', mew=2, ms=8,
|
|
163
|
+
label='%d %s' % (ind.size, label))
|
|
164
|
+
ax.legend(loc='best', framealpha=.5, numpoints=1)
|
|
165
|
+
ax.set_xlim(-.02*x.size, x.size*1.02-1)
|
|
166
|
+
ymin, ymax = x[np.isfinite(x)].min(), x[np.isfinite(x)].max()
|
|
167
|
+
yrange = ymax - ymin if ymax > ymin else 1
|
|
168
|
+
ax.set_ylim(ymin - 0.1*yrange, ymax + 0.1*yrange)
|
|
169
|
+
ax.set_xlabel('Data #', fontsize=14)
|
|
170
|
+
ax.set_ylabel('Amplitude', fontsize=14)
|
|
171
|
+
mode = 'Valley detection' if valley else 'Peak detection'
|
|
172
|
+
ax.set_title("%s (mph=%s, mpd=%d, threshold=%s, edge='%s')"
|
|
173
|
+
% (mode, str(mph), mpd, str(threshold), edge))
|
|
174
|
+
# plt.grid()
|
|
175
|
+
plt.show()
|
phasenet/model.py
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
import tensorflow as tf
|
|
2
|
+
# tf.compat.v1.disable_eager_execution()
|
|
3
|
+
import numpy as np
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
class ModelConfig:
|
|
7
|
+
|
|
8
|
+
batch_size = 20
|
|
9
|
+
depths = 5
|
|
10
|
+
filters_root = 8
|
|
11
|
+
kernel_size = [7, 1]
|
|
12
|
+
pool_size = [4, 1]
|
|
13
|
+
dilation_rate = [1, 1]
|
|
14
|
+
class_weights = [1.0, 1.0, 1.0]
|
|
15
|
+
loss_type = "cross_entropy"
|
|
16
|
+
weight_decay = 0.0
|
|
17
|
+
optimizer = "adam"
|
|
18
|
+
momentum = 0.9
|
|
19
|
+
learning_rate = 0.01
|
|
20
|
+
decay_step = 1e9
|
|
21
|
+
decay_rate = 0.9
|
|
22
|
+
drop_rate = 0.0
|
|
23
|
+
summary = True
|
|
24
|
+
|
|
25
|
+
X_shape = [3000, 1, 3]
|
|
26
|
+
n_channel = X_shape[-1]
|
|
27
|
+
Y_shape = [3000, 1, 3]
|
|
28
|
+
n_class = Y_shape[-1]
|
|
29
|
+
|
|
30
|
+
def __init__(self, **kwargs):
|
|
31
|
+
for k,v in kwargs.items():
|
|
32
|
+
setattr(self, k, v)
|
|
33
|
+
|
|
34
|
+
def update_args(self, args):
|
|
35
|
+
for k,v in vars(args).items():
|
|
36
|
+
setattr(self, k, v)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def crop_and_concat(net1, net2):
|
|
40
|
+
"""
|
|
41
|
+
the size(net1) <= size(net2)
|
|
42
|
+
"""
|
|
43
|
+
net1_shape = net1.get_shape().as_list()
|
|
44
|
+
net2_shape = net2.get_shape().as_list()
|
|
45
|
+
# print(net1_shape)
|
|
46
|
+
# print(net2_shape)
|
|
47
|
+
# if net2_shape[1] >= net1_shape[1] and net2_shape[2] >= net1_shape[2]:
|
|
48
|
+
offsets = [0, (net2_shape[1] - net1_shape[1]) // 2, (net2_shape[2] - net1_shape[2]) // 2, 0]
|
|
49
|
+
size = [-1, net1_shape[1], net1_shape[2], -1]
|
|
50
|
+
net2_resize = tf.slice(net2, offsets, size)
|
|
51
|
+
return tf.concat([net1, net2_resize], 3)
|
|
52
|
+
# else:
|
|
53
|
+
# offsets = [0, (net1_shape[1] - net2_shape[1]) // 2, (net1_shape[2] - net2_shape[2]) // 2, 0]
|
|
54
|
+
# size = [-1, net2_shape[1], net2_shape[2], -1]
|
|
55
|
+
# net1_resize = tf.slice(net1, offsets, size)
|
|
56
|
+
# return tf.concat([net1_resize, net2], 3)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def crop_only(net1, net2):
|
|
60
|
+
"""
|
|
61
|
+
the size(net1) <= size(net2)
|
|
62
|
+
"""
|
|
63
|
+
net1_shape = net1.get_shape().as_list()
|
|
64
|
+
net2_shape = net2.get_shape().as_list()
|
|
65
|
+
# print(net1_shape)
|
|
66
|
+
# print(net2_shape)
|
|
67
|
+
# if net2_shape[1] >= net1_shape[1] and net2_shape[2] >= net1_shape[2]:
|
|
68
|
+
offsets = [0, (net2_shape[1] - net1_shape[1]) // 2, (net2_shape[2] - net1_shape[2]) // 2, 0]
|
|
69
|
+
size = [-1, net1_shape[1], net1_shape[2], -1]
|
|
70
|
+
net2_resize = tf.slice(net2, offsets, size)
|
|
71
|
+
#return tf.concat([net1, net2_resize], 3)
|
|
72
|
+
return net2_resize
|
|
73
|
+
|
|
74
|
+
class UNet:
|
|
75
|
+
def __init__(self, config=ModelConfig(), input_batch=None, mode='train'):
|
|
76
|
+
self.depths = config.depths
|
|
77
|
+
self.filters_root = config.filters_root
|
|
78
|
+
self.kernel_size = config.kernel_size
|
|
79
|
+
self.dilation_rate = config.dilation_rate
|
|
80
|
+
self.pool_size = config.pool_size
|
|
81
|
+
self.X_shape = config.X_shape
|
|
82
|
+
self.Y_shape = config.Y_shape
|
|
83
|
+
self.n_channel = config.n_channel
|
|
84
|
+
self.n_class = config.n_class
|
|
85
|
+
self.class_weights = config.class_weights
|
|
86
|
+
self.batch_size = config.batch_size
|
|
87
|
+
self.loss_type = config.loss_type
|
|
88
|
+
self.weight_decay = config.weight_decay
|
|
89
|
+
self.optimizer = config.optimizer
|
|
90
|
+
self.learning_rate = config.learning_rate
|
|
91
|
+
self.decay_step = config.decay_step
|
|
92
|
+
self.decay_rate = config.decay_rate
|
|
93
|
+
self.momentum = config.momentum
|
|
94
|
+
self.global_step = tf.compat.v1.get_variable(name="global_step", initializer=0, dtype=tf.int32)
|
|
95
|
+
self.summary_train = []
|
|
96
|
+
self.summary_valid = []
|
|
97
|
+
|
|
98
|
+
self.build(input_batch, mode=mode)
|
|
99
|
+
|
|
100
|
+
def add_placeholders(self, input_batch=None, mode="train"):
|
|
101
|
+
if input_batch is None:
|
|
102
|
+
self.X = tf.compat.v1.placeholder(dtype=tf.float32, shape=[None, self.X_shape[-3], self.X_shape[-2], self.X_shape[-1]], name='X')
|
|
103
|
+
self.Y = tf.compat.v1.placeholder(dtype=tf.float32, shape=[None, self.Y_shape[-3], self.Y_shape[-2], self.n_class], name='y')
|
|
104
|
+
else:
|
|
105
|
+
self.X = input_batch[0]
|
|
106
|
+
if mode in ["train", "valid", "test"]:
|
|
107
|
+
self.Y = input_batch[1]
|
|
108
|
+
self.input_batch = input_batch
|
|
109
|
+
|
|
110
|
+
self.is_training = tf.compat.v1.placeholder(dtype=tf.bool, name="is_training")
|
|
111
|
+
# self.keep_prob = tf.compat.v1.placeholder(dtype=tf.float32, name="keep_prob")
|
|
112
|
+
self.drop_rate = tf.compat.v1.placeholder(dtype=tf.float32, name="drop_rate")
|
|
113
|
+
|
|
114
|
+
def add_prediction_op(self):
|
|
115
|
+
logging.info("Model: depths {depths}, filters {filters}, "
|
|
116
|
+
"filter size {kernel_size[0]}x{kernel_size[1]}, "
|
|
117
|
+
"pool size: {pool_size[0]}x{pool_size[1]}, "
|
|
118
|
+
"dilation rate: {dilation_rate[0]}x{dilation_rate[1]}".format(
|
|
119
|
+
depths=self.depths,
|
|
120
|
+
filters=self.filters_root,
|
|
121
|
+
kernel_size=self.kernel_size,
|
|
122
|
+
dilation_rate=self.dilation_rate,
|
|
123
|
+
pool_size=self.pool_size))
|
|
124
|
+
|
|
125
|
+
if self.weight_decay > 0:
|
|
126
|
+
weight_decay = tf.constant(self.weight_decay, dtype=tf.float32, name="weight_constant")
|
|
127
|
+
self.regularizer = tf.keras.regularizers.l2(l=0.5 * (weight_decay))
|
|
128
|
+
else:
|
|
129
|
+
self.regularizer = None
|
|
130
|
+
|
|
131
|
+
self.initializer = tf.compat.v1.keras.initializers.VarianceScaling(scale=1.0, mode="fan_avg", distribution="uniform")
|
|
132
|
+
|
|
133
|
+
# down sample layers
|
|
134
|
+
convs = [None] * self.depths # store output of each depth
|
|
135
|
+
|
|
136
|
+
with tf.compat.v1.variable_scope("Input"):
|
|
137
|
+
net = self.X
|
|
138
|
+
net = tf.compat.v1.layers.conv2d(net,
|
|
139
|
+
filters=self.filters_root,
|
|
140
|
+
kernel_size=self.kernel_size,
|
|
141
|
+
activation=None,
|
|
142
|
+
padding='same',
|
|
143
|
+
dilation_rate=self.dilation_rate,
|
|
144
|
+
kernel_initializer=self.initializer,
|
|
145
|
+
kernel_regularizer=self.regularizer,
|
|
146
|
+
name="input_conv")
|
|
147
|
+
net = tf.compat.v1.layers.batch_normalization(net,
|
|
148
|
+
training=self.is_training,
|
|
149
|
+
name="input_bn")
|
|
150
|
+
net = tf.nn.relu(net,
|
|
151
|
+
name="input_relu")
|
|
152
|
+
# net = tf.nn.dropout(net, self.keep_prob)
|
|
153
|
+
net = tf.compat.v1.layers.dropout(net,
|
|
154
|
+
rate=self.drop_rate,
|
|
155
|
+
training=self.is_training,
|
|
156
|
+
name="input_dropout")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
for depth in range(0, self.depths):
|
|
160
|
+
with tf.compat.v1.variable_scope("DownConv_%d" % depth):
|
|
161
|
+
filters = int(2**(depth) * self.filters_root)
|
|
162
|
+
|
|
163
|
+
net = tf.compat.v1.layers.conv2d(net,
|
|
164
|
+
filters=filters,
|
|
165
|
+
kernel_size=self.kernel_size,
|
|
166
|
+
activation=None,
|
|
167
|
+
use_bias=False,
|
|
168
|
+
padding='same',
|
|
169
|
+
dilation_rate=self.dilation_rate,
|
|
170
|
+
kernel_initializer=self.initializer,
|
|
171
|
+
kernel_regularizer=self.regularizer,
|
|
172
|
+
name="down_conv1_{}".format(depth + 1))
|
|
173
|
+
net = tf.compat.v1.layers.batch_normalization(net,
|
|
174
|
+
training=self.is_training,
|
|
175
|
+
name="down_bn1_{}".format(depth + 1))
|
|
176
|
+
net = tf.nn.relu(net,
|
|
177
|
+
name="down_relu1_{}".format(depth+1))
|
|
178
|
+
net = tf.compat.v1.layers.dropout(net,
|
|
179
|
+
rate=self.drop_rate,
|
|
180
|
+
training=self.is_training,
|
|
181
|
+
name="down_dropout1_{}".format(depth + 1))
|
|
182
|
+
|
|
183
|
+
convs[depth] = net
|
|
184
|
+
|
|
185
|
+
if depth < self.depths - 1:
|
|
186
|
+
net = tf.compat.v1.layers.conv2d(net,
|
|
187
|
+
filters=filters,
|
|
188
|
+
kernel_size=self.kernel_size,
|
|
189
|
+
strides=self.pool_size,
|
|
190
|
+
activation=None,
|
|
191
|
+
use_bias=False,
|
|
192
|
+
padding='same',
|
|
193
|
+
dilation_rate=self.dilation_rate,
|
|
194
|
+
kernel_initializer=self.initializer,
|
|
195
|
+
kernel_regularizer=self.regularizer,
|
|
196
|
+
name="down_conv3_{}".format(depth + 1))
|
|
197
|
+
net = tf.compat.v1.layers.batch_normalization(net,
|
|
198
|
+
training=self.is_training,
|
|
199
|
+
name="down_bn3_{}".format(depth + 1))
|
|
200
|
+
net = tf.nn.relu(net,
|
|
201
|
+
name="down_relu3_{}".format(depth+1))
|
|
202
|
+
net = tf.compat.v1.layers.dropout(net,
|
|
203
|
+
rate=self.drop_rate,
|
|
204
|
+
training=self.is_training,
|
|
205
|
+
name="down_dropout3_{}".format(depth + 1))
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# up layers
|
|
209
|
+
for depth in range(self.depths - 2, -1, -1):
|
|
210
|
+
with tf.compat.v1.variable_scope("UpConv_%d" % depth):
|
|
211
|
+
filters = int(2**(depth) * self.filters_root)
|
|
212
|
+
net = tf.compat.v1.layers.conv2d_transpose(net,
|
|
213
|
+
filters=filters,
|
|
214
|
+
kernel_size=self.kernel_size,
|
|
215
|
+
strides=self.pool_size,
|
|
216
|
+
activation=None,
|
|
217
|
+
use_bias=False,
|
|
218
|
+
padding="same",
|
|
219
|
+
kernel_initializer=self.initializer,
|
|
220
|
+
kernel_regularizer=self.regularizer,
|
|
221
|
+
name="up_conv0_{}".format(depth+1))
|
|
222
|
+
net = tf.compat.v1.layers.batch_normalization(net,
|
|
223
|
+
training=self.is_training,
|
|
224
|
+
name="up_bn0_{}".format(depth + 1))
|
|
225
|
+
net = tf.nn.relu(net,
|
|
226
|
+
name="up_relu0_{}".format(depth+1))
|
|
227
|
+
net = tf.compat.v1.layers.dropout(net,
|
|
228
|
+
rate=self.drop_rate,
|
|
229
|
+
training=self.is_training,
|
|
230
|
+
name="up_dropout0_{}".format(depth + 1))
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
#skip connection
|
|
234
|
+
net = crop_and_concat(convs[depth], net)
|
|
235
|
+
#net = crop_only(convs[depth], net)
|
|
236
|
+
|
|
237
|
+
net = tf.compat.v1.layers.conv2d(net,
|
|
238
|
+
filters=filters,
|
|
239
|
+
kernel_size=self.kernel_size,
|
|
240
|
+
activation=None,
|
|
241
|
+
use_bias=False,
|
|
242
|
+
padding='same',
|
|
243
|
+
dilation_rate=self.dilation_rate,
|
|
244
|
+
kernel_initializer=self.initializer,
|
|
245
|
+
kernel_regularizer=self.regularizer,
|
|
246
|
+
name="up_conv1_{}".format(depth + 1))
|
|
247
|
+
net = tf.compat.v1.layers.batch_normalization(net,
|
|
248
|
+
training=self.is_training,
|
|
249
|
+
name="up_bn1_{}".format(depth + 1))
|
|
250
|
+
net = tf.nn.relu(net,
|
|
251
|
+
name="up_relu1_{}".format(depth + 1))
|
|
252
|
+
net = tf.compat.v1.layers.dropout(net,
|
|
253
|
+
rate=self.drop_rate,
|
|
254
|
+
training=self.is_training,
|
|
255
|
+
name="up_dropout1_{}".format(depth + 1))
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
# Output Map
|
|
259
|
+
with tf.compat.v1.variable_scope("Output"):
|
|
260
|
+
net = tf.compat.v1.layers.conv2d(net,
|
|
261
|
+
filters=self.n_class,
|
|
262
|
+
kernel_size=(1,1),
|
|
263
|
+
activation=None,
|
|
264
|
+
padding='same',
|
|
265
|
+
#dilation_rate=self.dilation_rate,
|
|
266
|
+
kernel_initializer=self.initializer,
|
|
267
|
+
kernel_regularizer=self.regularizer,
|
|
268
|
+
name="output_conv")
|
|
269
|
+
# net = tf.nn.relu(net,
|
|
270
|
+
# name="output_relu")
|
|
271
|
+
# net = tf.compat.v1.layers.dropout(net,
|
|
272
|
+
# rate=self.drop_rate,
|
|
273
|
+
# training=self.is_training,
|
|
274
|
+
# name="output_dropout")
|
|
275
|
+
# net = tf.compat.v1.layers.batch_normalization(net,
|
|
276
|
+
# training=self.is_training,
|
|
277
|
+
# name="output_bn")
|
|
278
|
+
output = net
|
|
279
|
+
|
|
280
|
+
with tf.compat.v1.variable_scope("representation"):
|
|
281
|
+
self.representation = convs[-1]
|
|
282
|
+
|
|
283
|
+
with tf.compat.v1.variable_scope("logits"):
|
|
284
|
+
self.logits = output
|
|
285
|
+
tmp = tf.compat.v1.summary.histogram("logits", self.logits)
|
|
286
|
+
self.summary_train.append(tmp)
|
|
287
|
+
|
|
288
|
+
with tf.compat.v1.variable_scope("preds"):
|
|
289
|
+
self.preds = tf.nn.softmax(output)
|
|
290
|
+
tmp = tf.compat.v1.summary.histogram("preds", self.preds)
|
|
291
|
+
self.summary_train.append(tmp)
|
|
292
|
+
|
|
293
|
+
def add_loss_op(self):
|
|
294
|
+
if self.loss_type == "cross_entropy":
|
|
295
|
+
with tf.compat.v1.variable_scope("cross_entropy"):
|
|
296
|
+
flat_logits = tf.reshape(self.logits, [-1, self.n_class], name="logits")
|
|
297
|
+
flat_labels = tf.reshape(self.Y, [-1, self.n_class], name="labels")
|
|
298
|
+
if (np.array(self.class_weights) != 1).any():
|
|
299
|
+
class_weights = tf.constant(np.array(self.class_weights, dtype=np.float32), name="class_weights")
|
|
300
|
+
weight_map = tf.multiply(flat_labels, class_weights)
|
|
301
|
+
weight_map = tf.reduce_sum(input_tensor=weight_map, axis=1)
|
|
302
|
+
loss_map = tf.nn.softmax_cross_entropy_with_logits(logits=flat_logits,
|
|
303
|
+
labels=flat_labels)
|
|
304
|
+
|
|
305
|
+
weighted_loss = tf.multiply(loss_map, weight_map)
|
|
306
|
+
loss = tf.reduce_mean(input_tensor=weighted_loss)
|
|
307
|
+
else:
|
|
308
|
+
loss = tf.reduce_mean(input_tensor=tf.nn.softmax_cross_entropy_with_logits(logits=flat_logits,
|
|
309
|
+
labels=flat_labels))
|
|
310
|
+
|
|
311
|
+
elif self.loss_type == "IOU":
|
|
312
|
+
with tf.compat.v1.variable_scope("IOU"):
|
|
313
|
+
eps = 1e-7
|
|
314
|
+
loss = 0
|
|
315
|
+
for i in range(1, self.n_class):
|
|
316
|
+
intersection = eps + tf.reduce_sum(input_tensor=self.preds[:,:,:,i] * self.Y[:,:,:,i], axis=[1,2])
|
|
317
|
+
union = eps + tf.reduce_sum(input_tensor=self.preds[:,:,:,i], axis=[1,2]) + tf.reduce_sum(input_tensor=self.Y[:,:,:,i], axis=[1,2])
|
|
318
|
+
loss += 1 - tf.reduce_mean(input_tensor=intersection / union)
|
|
319
|
+
elif self.loss_type == "mean_squared":
|
|
320
|
+
with tf.compat.v1.variable_scope("mean_squared"):
|
|
321
|
+
flat_logits = tf.reshape(self.logits, [-1, self.n_class], name="logits")
|
|
322
|
+
flat_labels = tf.reshape(self.Y, [-1, self.n_class], name="labels")
|
|
323
|
+
with tf.compat.v1.variable_scope("mean_squared"):
|
|
324
|
+
loss = tf.compat.v1.losses.mean_squared_error(labels=flat_labels, predictions=flat_logits)
|
|
325
|
+
else:
|
|
326
|
+
raise ValueError("Unknown loss function: " % self.loss_type)
|
|
327
|
+
|
|
328
|
+
tmp = tf.compat.v1.summary.scalar("train_loss", loss)
|
|
329
|
+
self.summary_train.append(tmp)
|
|
330
|
+
tmp = tf.compat.v1.summary.scalar("valid_loss", loss)
|
|
331
|
+
self.summary_valid.append(tmp)
|
|
332
|
+
|
|
333
|
+
if self.weight_decay > 0:
|
|
334
|
+
with tf.compat.v1.name_scope('weight_loss'):
|
|
335
|
+
tmp = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
|
|
336
|
+
weight_loss = tf.add_n(tmp, name="weight_loss")
|
|
337
|
+
self.loss = loss + weight_loss
|
|
338
|
+
else:
|
|
339
|
+
self.loss = loss
|
|
340
|
+
|
|
341
|
+
def add_training_op(self):
|
|
342
|
+
if self.optimizer == "momentum":
|
|
343
|
+
self.learning_rate_node = tf.compat.v1.train.exponential_decay(learning_rate=self.learning_rate,
|
|
344
|
+
global_step=self.global_step,
|
|
345
|
+
decay_steps=self.decay_step,
|
|
346
|
+
decay_rate=self.decay_rate,
|
|
347
|
+
staircase=True)
|
|
348
|
+
optimizer = tf.compat.v1.train.MomentumOptimizer(learning_rate=self.learning_rate_node,
|
|
349
|
+
momentum=self.momentum)
|
|
350
|
+
elif self.optimizer == "adam":
|
|
351
|
+
self.learning_rate_node = tf.compat.v1.train.exponential_decay(learning_rate=self.learning_rate,
|
|
352
|
+
global_step=self.global_step,
|
|
353
|
+
decay_steps=self.decay_step,
|
|
354
|
+
decay_rate=self.decay_rate,
|
|
355
|
+
staircase=True)
|
|
356
|
+
|
|
357
|
+
optimizer = tf.compat.v1.train.AdamOptimizer(learning_rate=self.learning_rate_node)
|
|
358
|
+
update_ops = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.UPDATE_OPS)
|
|
359
|
+
with tf.control_dependencies(update_ops):
|
|
360
|
+
self.train_op = optimizer.minimize(self.loss, global_step=self.global_step)
|
|
361
|
+
tmp = tf.compat.v1.summary.scalar("learning_rate", self.learning_rate_node)
|
|
362
|
+
self.summary_train.append(tmp)
|
|
363
|
+
|
|
364
|
+
def add_metrics_op(self):
|
|
365
|
+
with tf.compat.v1.variable_scope("metrics"):
|
|
366
|
+
|
|
367
|
+
Y= tf.argmax(input=self.Y, axis=-1)
|
|
368
|
+
confusion_matrix = tf.cast(tf.math.confusion_matrix(
|
|
369
|
+
labels=tf.reshape(Y, [-1]),
|
|
370
|
+
predictions=tf.reshape(self.preds, [-1]),
|
|
371
|
+
num_classes=self.n_class, name='confusion_matrix'),
|
|
372
|
+
dtype=tf.float32)
|
|
373
|
+
|
|
374
|
+
# with tf.variable_scope("P"):
|
|
375
|
+
c = tf.constant(1e-7, dtype=tf.float32)
|
|
376
|
+
precision_P = (confusion_matrix[1,1] + c) / (tf.reduce_sum(input_tensor=confusion_matrix[:,1]) + c)
|
|
377
|
+
recall_P = (confusion_matrix[1,1] + c) / (tf.reduce_sum(input_tensor=confusion_matrix[1,:]) + c)
|
|
378
|
+
f1_P = 2 * precision_P * recall_P / (precision_P + recall_P)
|
|
379
|
+
|
|
380
|
+
tmp1 = tf.compat.v1.summary.scalar("train_precision_p", precision_P)
|
|
381
|
+
tmp2 = tf.compat.v1.summary.scalar("train_recall_p", recall_P)
|
|
382
|
+
tmp3 = tf.compat.v1.summary.scalar("train_f1_p", f1_P)
|
|
383
|
+
self.summary_train.extend([tmp1, tmp2, tmp3])
|
|
384
|
+
|
|
385
|
+
tmp1 = tf.compat.v1.summary.scalar("valid_precision_p", precision_P)
|
|
386
|
+
tmp2 = tf.compat.v1.summary.scalar("valid_recall_p", recall_P)
|
|
387
|
+
tmp3 = tf.compat.v1.summary.scalar("valid_f1_p", f1_P)
|
|
388
|
+
self.summary_valid.extend([tmp1, tmp2, tmp3])
|
|
389
|
+
|
|
390
|
+
# with tf.variable_scope("S"):
|
|
391
|
+
precision_S = (confusion_matrix[2,2] + c) / (tf.reduce_sum(input_tensor=confusion_matrix[:,2]) + c)
|
|
392
|
+
recall_S = (confusion_matrix[2,2] + c) / (tf.reduce_sum(input_tensor=confusion_matrix[2,:]) + c)
|
|
393
|
+
f1_S = 2 * precision_S * recall_S / (precision_S + recall_S)
|
|
394
|
+
|
|
395
|
+
tmp1 = tf.compat.v1.summary.scalar("train_precision_s", precision_S)
|
|
396
|
+
tmp2 = tf.compat.v1.summary.scalar("train_recall_s", recall_S)
|
|
397
|
+
tmp3 = tf.compat.v1.summary.scalar("train_f1_s", f1_S)
|
|
398
|
+
self.summary_train.extend([tmp1, tmp2, tmp3])
|
|
399
|
+
|
|
400
|
+
tmp1 = tf.compat.v1.summary.scalar("valid_precision_s", precision_S)
|
|
401
|
+
tmp2 = tf.compat.v1.summary.scalar("valid_recall_s", recall_S)
|
|
402
|
+
tmp3 = tf.compat.v1.summary.scalar("valid_f1_s", f1_S)
|
|
403
|
+
self.summary_valid.extend([tmp1, tmp2, tmp3])
|
|
404
|
+
|
|
405
|
+
self.precision = [precision_P, precision_S]
|
|
406
|
+
self.recall = [recall_P, recall_S]
|
|
407
|
+
self.f1 = [f1_P, f1_S]
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def train_on_batch(self, sess, inputs_batch, labels_batch, summary_writer, drop_rate=0.0):
|
|
412
|
+
feed = {self.X: inputs_batch,
|
|
413
|
+
self.Y: labels_batch,
|
|
414
|
+
self.drop_rate: drop_rate,
|
|
415
|
+
self.is_training: True}
|
|
416
|
+
|
|
417
|
+
_, step_summary, step, loss = sess.run([self.train_op,
|
|
418
|
+
self.summary_train,
|
|
419
|
+
self.global_step,
|
|
420
|
+
self.loss],
|
|
421
|
+
feed_dict=feed)
|
|
422
|
+
summary_writer.add_summary(step_summary, step)
|
|
423
|
+
return loss
|
|
424
|
+
|
|
425
|
+
def valid_on_batch(self, sess, inputs_batch, labels_batch, summary_writer):
|
|
426
|
+
feed = {self.X: inputs_batch,
|
|
427
|
+
self.Y: labels_batch,
|
|
428
|
+
self.drop_rate: 0,
|
|
429
|
+
self.is_training: False}
|
|
430
|
+
|
|
431
|
+
step_summary, step, loss, preds = sess.run([self.summary_valid,
|
|
432
|
+
self.global_step,
|
|
433
|
+
self.loss,
|
|
434
|
+
self.preds],
|
|
435
|
+
feed_dict=feed)
|
|
436
|
+
summary_writer.add_summary(step_summary, step)
|
|
437
|
+
return loss, preds
|
|
438
|
+
|
|
439
|
+
def test_on_batch(self, sess, summary_writer):
|
|
440
|
+
feed = {self.drop_rate: 0,
|
|
441
|
+
self.is_training: False}
|
|
442
|
+
step_summary, step, loss, preds, \
|
|
443
|
+
X_batch, Y_batch, fname_batch, \
|
|
444
|
+
itp_batch, its_batch = sess.run([self.summary_valid,
|
|
445
|
+
self.global_step,
|
|
446
|
+
self.loss,
|
|
447
|
+
self.preds,
|
|
448
|
+
self.X,
|
|
449
|
+
self.Y,
|
|
450
|
+
self.input_batch[2],
|
|
451
|
+
self.input_batch[3],
|
|
452
|
+
self.input_batch[4]],
|
|
453
|
+
feed_dict=feed)
|
|
454
|
+
summary_writer.add_summary(step_summary, step)
|
|
455
|
+
return loss, preds, X_batch, Y_batch, fname_batch, itp_batch, its_batch
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def build(self, input_batch=None, mode='train'):
|
|
459
|
+
self.add_placeholders(input_batch, mode)
|
|
460
|
+
self.add_prediction_op()
|
|
461
|
+
if mode in ["train", "valid", "test"]:
|
|
462
|
+
self.add_loss_op()
|
|
463
|
+
self.add_training_op()
|
|
464
|
+
# self.add_metrics_op()
|
|
465
|
+
self.summary_train = tf.compat.v1.summary.merge(self.summary_train)
|
|
466
|
+
self.summary_valid = tf.compat.v1.summary.merge(self.summary_valid)
|
|
467
|
+
return 0
|