Skip to content

Commit ecef913

Browse files
committed
add neon implementation
1 parent c35326d commit ecef913

File tree

1 file changed

+359
-0
lines changed

1 file changed

+359
-0
lines changed

P1B3/p1b3_baseline_neon.py

Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
from __future__ import division, print_function
2+
3+
import argparse
4+
import logging
5+
6+
import numpy as np
7+
8+
import neon
9+
from neon.util.argparser import NeonArgparser
10+
from neon.callbacks.callbacks import Callbacks
11+
from neon.initializers import Gaussian, GlorotUniform
12+
from neon.layers import GeneralizedCost, Affine, Conv, Dropout, Pooling, Reshape
13+
from neon.models import Model
14+
from neon.optimizers import GradientDescentMomentum
15+
from neon.transforms import Identity
16+
# from neon.transforms import MeanSquared
17+
18+
from neon import transforms
19+
20+
21+
import p1b3
22+
23+
24+
# Model and Training parameters
25+
26+
# Seed for random generation
27+
SEED = 2017
28+
# Size of batch for training
29+
BATCH_SIZE = 100
30+
# Number of training epochs
31+
NB_EPOCH = 20
32+
# Number of data generator workers
33+
NB_WORKER = 1
34+
35+
# Percentage of dropout used in training
36+
DROP = 0.1
37+
# Activation function (options: 'relu', 'tanh', 'sigmoid', 'linear')
38+
ACTIVATION = 'relu'
39+
LOSS = 'mse'
40+
OPTIMIZER = 'sgd'
41+
42+
# Type of feature scaling (options: 'maxabs': to [-1,1]
43+
# 'minmax': to [0,1]
44+
# None : standard normalization
45+
SCALING = 'std'
46+
# Features to (randomly) sample from cell lines or drug descriptors
47+
FEATURE_SUBSAMPLE = 500#0
48+
# FEATURE_SUBSAMPLE = 0
49+
50+
# Number of units in fully connected (dense) layers
51+
D1 = 1000
52+
D2 = 500
53+
D3 = 100
54+
D4 = 50
55+
DENSE_LAYERS = [D1, D2, D3, D4]
56+
57+
# Number of units per locally connected layer
58+
C1 = 10, 10, 5 # nb_filter, filter_length, stride
59+
C2 = 0, 0, 0 # disabled layer
60+
# CONVOLUTION_LAYERS = list(C1 + C2)
61+
CONVOLUTION_LAYERS = [0, 0, 0]
62+
POOL = 10
63+
64+
MIN_LOGCONC = -5.
65+
MAX_LOGCONC = -4.
66+
67+
CATEGORY_CUTOFFS = [0.]
68+
69+
np.set_printoptions(threshold=np.nan)
70+
np.random.seed(SEED)
71+
72+
73+
def get_parser():
74+
params = {'batch_size': BATCH_SIZE, 'epochs': NB_EPOCH}
75+
76+
parser = NeonArgparser(__doc__, default_overrides=params)
77+
78+
# parser = argparse.ArgumentParser(prog='p1b3_baseline',
79+
# formatter_class=argparse.ArgumentDefaultsHelpFormatter)
80+
# parser.add_argument("-v", "--verbose", action="store_true",
81+
# help="increase output verbosity")
82+
parser.add_argument("-a", "--activation", action="store",
83+
default=ACTIVATION,
84+
help="keras activation function to use in inner layers: relu, tanh, sigmoid...")
85+
# parser.add_argument("-b", "--batch_size", action="store",
86+
# default=BATCH_SIZE, type=int,
87+
# help="batch size")
88+
parser.add_argument("--convolution", action="store", nargs='+', type=int,
89+
default=CONVOLUTION_LAYERS,
90+
help="integer array describing convolution layers: conv1_nb_filter, conv1_filter_len, conv1_stride, conv2_nb_filter, conv2_filter_len, conv2_stride ...")
91+
parser.add_argument("--dense", action="store", nargs='+', type=int,
92+
default=DENSE_LAYERS,
93+
help="number of units in fully connected layers in an integer array")
94+
# parser.add_argument("-e", "--epochs", action="store",
95+
# default=NB_EPOCH, type=int,
96+
# help="number of training epochs")
97+
parser.add_argument("--locally_connected", action="store_true",
98+
default=False, # TODO: not currently supported
99+
help="use locally connected layers instead of convolution layers")
100+
parser.add_argument("--optimizer", action="store",
101+
default=OPTIMIZER,
102+
help="keras optimizer to use: sgd, rmsprop, ...")
103+
parser.add_argument("--drop", action="store",
104+
default=DROP, type=float,
105+
help="ratio of dropout used in fully connected layers")
106+
parser.add_argument("--loss", action="store",
107+
default=LOSS,
108+
help="keras loss function to use: mse, ...")
109+
parser.add_argument("--pool", action="store",
110+
default=POOL, type=int,
111+
help="pooling layer length")
112+
parser.add_argument("--scaling", action="store",
113+
default=SCALING,
114+
help="type of feature scaling; 'minabs': to [-1,1]; 'minmax': to [0,1], 'std': standard unit normalization; None: no normalization")
115+
parser.add_argument("--drug_features", action="store",
116+
default="descriptors",
117+
help="use dragon7 descriptors, latent representations from Aspuru-Guzik's SMILES autoencoder, or both, or random features; 'descriptors','latent', 'both', 'noise'")
118+
parser.add_argument("--feature_subsample", action="store",
119+
default=FEATURE_SUBSAMPLE, type=int,
120+
help="number of features to randomly sample from each category (cellline expression, drug descriptors, etc), 0 means using all features")
121+
parser.add_argument("--min_logconc", action="store",
122+
default=MIN_LOGCONC, type=float,
123+
help="min log concentration of dose response data to use: -3.0 to -7.0")
124+
parser.add_argument("--max_logconc", action="store",
125+
default=MAX_LOGCONC, type=float,
126+
help="max log concentration of dose response data to use: -3.0 to -7.0")
127+
parser.add_argument("--subsample", action="store",
128+
default='naive_balancing',
129+
help="dose response subsample strategy; None or 'naive_balancing'")
130+
parser.add_argument("--category_cutoffs", action="store", nargs='+', type=float,
131+
default=CATEGORY_CUTOFFS,
132+
help="list of growth cutoffs (between -1 and +1) seperating non-response and response categories")
133+
parser.add_argument("--train_samples", action="store",
134+
default=0, type=int,
135+
help="overrides the number of training samples if set to nonzero")
136+
parser.add_argument("--val_samples", action="store",
137+
default=0, type=int,
138+
help="overrides the number of validation samples if set to nonzero")
139+
parser.add_argument("--save", action="store",
140+
default='save',
141+
help="prefix of output files")
142+
parser.add_argument("--scramble", action="store_true",
143+
help="randomly shuffle dose response data")
144+
parser.add_argument("--workers", action="store",
145+
default=NB_WORKER, type=int,
146+
help="number of data generator workers")
147+
parser.add_argument("--gpus", action="store", nargs='*',
148+
default=[], type=int,
149+
help="set IDs of GPUs to use")
150+
151+
return parser
152+
153+
154+
def extension_from_parameters(args):
155+
"""Construct string for saving model with annotation of parameters"""
156+
ext = '.neon'
157+
ext += '.A={}'.format(args.activation)
158+
ext += '.B={}'.format(args.batch_size)
159+
ext += '.D={}'.format(args.drop)
160+
ext += '.E={}'.format(args.epochs)
161+
if args.feature_subsample:
162+
ext += '.F={}'.format(args.feature_subsample)
163+
if args.convolution:
164+
name = 'LC' if args.locally_connected else 'C'
165+
layer_list = list(range(0, len(args.convolution), 3))
166+
for l, i in enumerate(layer_list):
167+
nb_filter = args.convolution[i]
168+
filter_len = args.convolution[i+1]
169+
stride = args.convolution[i+2]
170+
if nb_filter <= 0 or filter_len <= 0 or stride <= 0:
171+
break
172+
ext += '.{}{}={},{},{}'.format(name, l+1, nb_filter, filter_len, stride)
173+
if args.pool and layer_list[0] and layer_list[1]:
174+
ext += '.P={}'.format(args.pool)
175+
for i, n in enumerate(args.dense):
176+
if n:
177+
ext += '.D{}={}'.format(i+1, n)
178+
ext += '.S={}'.format(args.scaling)
179+
180+
return ext
181+
182+
183+
class ConcatDataIter(neon.NervanaObject):
184+
"""
185+
Data iterator for concatenated features
186+
Modeled after ArrayIterator: https://github.com/NervanaSystems/neon/blob/master/neon/data/dataiterator.py
187+
"""
188+
189+
def __init__(self, data_loader,
190+
partition='train',
191+
ndata=None,
192+
lshape=None,
193+
datatype=np.float32):
194+
"""
195+
During initialization, the input data will be converted to backend tensor objects
196+
(e.g. CPUTensor or GPUTensor). If the backend uses the GPU, the data is copied over to the
197+
device.
198+
"""
199+
super(ConcatDataIter, self).__init__()
200+
self.data = data_loader
201+
self.gen = p1b3.DataGenerator(data_loader, partition=partition, batch_size=self.be.bsz, concat=True)
202+
self.ndata = ndata or self.gen.num_data
203+
assert self.ndata >= self.be.bsz
204+
self.datatype = datatype
205+
self.gen = self.gen.flow()
206+
self.start = 0
207+
self.ybuf = None
208+
self.shape = lshape or data_loader.input_dim
209+
self.lshape = lshape
210+
211+
@property
212+
def nbatches(self):
213+
"""
214+
Return the number of minibatches in this dataset.
215+
"""
216+
return (self.ndata - self.start) // self.be.bsz
217+
218+
def reset(self):
219+
self.start = 0
220+
221+
def __iter__(self):
222+
"""
223+
Returns a new minibatch of data with each call.
224+
225+
Yields:
226+
tuple: The next minibatch which includes both features and labels.
227+
"""
228+
229+
def transpose_gen(z):
230+
return (self.be.array(z), self.be.iobuf(z.shape[1]),
231+
lambda _in, _out: self.be.copy_transpose(_in, _out))
232+
233+
for i1 in range(self.start, self.ndata, self.be.bsz):
234+
bsz = min(self.be.bsz, self.ndata - i1)
235+
# islice1, oslice1 = slice(0, bsz), slice(i1, i1 + bsz)
236+
islice1, oslice1 = slice(0, bsz), slice(0, bsz)
237+
islice2, oslice2 = None, None
238+
if self.be.bsz > bsz:
239+
islice2, oslice2 = slice(bsz, None), slice(0, self.be.bsz - bsz)
240+
self.start = self.be.bsz - bsz
241+
242+
x, y = next(self.gen)
243+
x = np.ascontiguousarray(x).astype(self.datatype)
244+
y = np.ascontiguousarray(y).astype(self.datatype)
245+
246+
X = [x]
247+
y = y.reshape(y.shape + (1,))
248+
249+
self.Xdev, self.Xbuf, self.unpack_func = list(zip(*[transpose_gen(x) for x in X]))
250+
self.dbuf, self.hbuf = list(self.Xdev), list(self.Xbuf)
251+
self.unpack_func = list(self.unpack_func)
252+
253+
self.ydev, self.ybuf, yfunc = transpose_gen(y)
254+
self.dbuf.append(self.ydev)
255+
self.hbuf.append(self.ybuf)
256+
self.unpack_func.append(yfunc)
257+
258+
for buf, dev, unpack_func in zip(self.hbuf, self.dbuf, self.unpack_func):
259+
unpack_func(dev[oslice1], buf[:, islice1])
260+
if oslice2:
261+
unpack_func(dev[oslice2], buf[:, islice2])
262+
263+
inputs = self.Xbuf[0] if len(self.Xbuf) == 1 else self.Xbuf
264+
targets = self.ybuf if self.ybuf else inputs
265+
266+
yield (inputs, targets)
267+
268+
269+
def get_function(name):
270+
mapping = {}
271+
272+
# activation
273+
mapping['relu'] = neon.transforms.activation.Rectlin
274+
mapping['sigmoid'] = neon.transforms.activation.Logistic
275+
mapping['tanh'] = neon.transforms.activation.Tanh
276+
mapping['linear'] = neon.transforms.activation.Identity
277+
278+
# loss
279+
mapping['mse'] = neon.transforms.cost.MeanSquared
280+
mapping['binary_crossentropy'] = neon.transforms.cost.CrossEntropyBinary
281+
mapping['categorical_crossentropy'] = neon.transforms.cost.CrossEntropyMulti
282+
283+
# optimizer
284+
def SGD(learning_rate=0.01, momentum_coef=0.9, gradient_clip_value=5):
285+
return GradientDescentMomentum(learning_rate, momentum_coef, gradient_clip_value)
286+
287+
mapping['sgd'] = SGD
288+
mapping['rmsprop'] = neon.optimizers.optimizer.RMSProp
289+
mapping['adam'] = neon.optimizers.optimizer.Adam
290+
mapping['adagrad'] = neon.optimizers.optimizer.Adagrad
291+
mapping['adadelta'] = neon.optimizers.optimizer.Adadelta
292+
293+
mapped = mapping.get(name)
294+
if not mapped:
295+
raise Exception('No neon function found for "{}"'.format(name))
296+
297+
return mapped
298+
299+
300+
def main():
301+
parser = get_parser()
302+
args = parser.parse_args()
303+
print('Args:', args)
304+
305+
loggingLevel = logging.DEBUG if args.verbose else logging.INFO
306+
logging.basicConfig(level=loggingLevel, format='')
307+
308+
ext = extension_from_parameters(args)
309+
310+
loader = p1b3.DataLoader(feature_subsample=args.feature_subsample,
311+
scaling=args.scaling,
312+
drug_features=args.drug_features,
313+
scramble=args.scramble,
314+
min_logconc=args.min_logconc,
315+
max_logconc=args.max_logconc,
316+
subsample=args.subsample,
317+
category_cutoffs=args.category_cutoffs)
318+
319+
# initializer = Gaussian(loc=0.0, scale=0.01)
320+
initializer = GlorotUniform()
321+
activation = get_function(args.activation)()
322+
323+
layers = []
324+
reshape = None
325+
326+
if args.convolution and args.convolution[0]:
327+
reshape = (1, loader.input_dim, 1)
328+
layer_list = list(range(0, len(args.convolution), 3))
329+
for l, i in enumerate(layer_list):
330+
nb_filter = args.convolution[i]
331+
filter_len = args.convolution[i+1]
332+
stride = args.convolution[i+2]
333+
# print(nb_filter, filter_len, stride)
334+
# fshape: (height, width, num_filters).
335+
layers.append(Conv((1, filter_len, nb_filter), strides={'str_h':1, 'str_w':stride}, init=initializer, activation=activation))
336+
if args.pool:
337+
layers.append(Pooling((1, args.pool)))
338+
339+
for layer in args.dense:
340+
if layer:
341+
layers.append(Affine(nout=layer, init=initializer, activation=activation))
342+
if args.drop:
343+
layers.append(Dropout(keep=(1-args.drop)))
344+
layers.append(Affine(nout=1, init=initializer, activation=neon.transforms.Identity()))
345+
346+
model = Model(layers=layers)
347+
348+
train_iter = ConcatDataIter(loader, ndata=args.train_samples, lshape=reshape, datatype=args.datatype)
349+
val_iter = ConcatDataIter(loader, partition='val', ndata=args.val_samples, lshape=reshape, datatype=args.datatype)
350+
351+
cost = GeneralizedCost(get_function(args.loss)())
352+
optimizer = get_function(args.optimizer)()
353+
callbacks = Callbacks(model, eval_set=val_iter, **args.callback_args)
354+
355+
model.fit(train_iter, optimizer=optimizer, num_epochs=args.epochs, cost=cost, callbacks=callbacks)
356+
357+
358+
if __name__ == '__main__':
359+
main()

0 commit comments

Comments
 (0)