main/core.py
def build_model(embedding_size=512, lr=0.01, optimizer='adam', depth=2, hidden=0, hidden2=0, loss='mse',
                hidden_activation='tanh', output_activation='linear', dr1=0.0, dr2=0.0, output_size=1,
                molecular_attributes=False, use_fp=None, inner_rep=32, verbose=False):
    '''Generates simple embedding model to use molecular tensor as
    input in order to predict a single-valued output (i.e., yield)

    inputs:
        embedding_size - size of fingerprint for GraphFP layer
        lr - learning rate to use (train_model overwrites this value)
        optimizer - optimization function to use
        depth - depth of the neural fingerprint (i.e., radius)
        hidden - number of hidden tanh nodes after FP (0 is linear)
        hidden2 - number of hidden nodes after "hidden" layer
        hidden_activation - activation function used in hidden layers
        output_activation - activation function for final output nodes
        dr1 - dropout rate after embedding
        dr2 - dropout rate after hidden
        loss - loss function as a string (e.g., 'mse')
        molecular_attributes - whether to include additional molecular 
                    attributes in the atom-level features (recommended)
        use_fp - whether the representation used is actually a fingerprint
                    and not a convolutional network (for benchmarking)

    outputs:
        model - a Keras model'''

    # Graph model
    if type(use_fp) == type(None):
        F_atom, F_bond = sizeAttributeVectors(molecular_attributes = molecular_attributes)
        mat_features = Input(shape=(None, F_atom), name="feature-matrix")  # shape = (n_atoms, n_atom_features)
        mat_adjacency = Input(shape=(None, None), name="adjacency/self-matrix")  # shape = (n_atoms, n_atoms)
        mat_specialbondtypes = Input(shape=(None, F_bond), name="special-bond-types")  # shape = (n_atoms, n_bond_features)

        # Lists to keep track of keras features
        all_mat_features = [mat_features]
        contribs_by_atom = []
        actual_contribs_for_atoms = []
        actual_bond_contribs_for_atoms = []
        output_contribs_byatom = []
        output_contribs = []
        unactivated_features = []
        
        sum_across_atoms       = lambda x: K.sum(x, axis = 1)
        sum_across_atoms_shape = lambda x: (x[0], x[2])

        for d in range(0, depth + 1):
            if verbose:
                print('### DEPTH {}'.format(d))
                print('KERAS SHAPE OF ALL_MAT_FEATURES[d]')  # Get the output contribution using all_mat_features[d]
                print(all_mat_features[d]._keras_shape)
                print('K.ndim of all_mat_features')
                print(K.ndim(all_mat_features[d]))

            output_contribs_byatom.append(
                TimeDistributed(
                    Dense(embedding_size, activation='softmax'),
                    name='d{}-out'.format(d),
                )(all_mat_features[d])
            )
            if verbose: print('Added depth {} output contribution (still atom-wise)'.format(d))

            output_contribs.append(
                Lambda(sum_across_atoms, output_shape=sum_across_atoms_shape, name="d{}-out-sum-across-atoms".format(d))(
                    output_contribs_byatom[d]
                )
            )
            if verbose: print('Added depth {} output contribution (summed across atoms)'.format(d))

            # Update if needed
            if d < depth:
                contribs_by_atom.append(
                    TimeDistributed(
                        Dense(inner_rep, activation = 'linear'), 
                        name="d{}-atom-to-atom".format(d),
                    )(all_mat_features[d])
                )
                if verbose:
                    print('Calculated new atom features for each atom, d {}'.format(d))
                    print('ndim: {}'.format(K.ndim(contribs_by_atom[-1])))
                   
                actual_contribs_for_atoms.append(
                    dot(
                        [mat_adjacency, contribs_by_atom[d]],
                        axes=(0, 0),
                        name="d{}-multiply-atom-contribs-and-adj-mat".format(d)
                    )
                )

                if verbose:
                    print('Multiplied new atom features by adj matrix, d = {}'.format(d))
                    print('ndim: {}'.format(K.ndim(actual_contribs_for_atoms[-1])))

                actual_bond_contribs_for_atoms.append(
                    TimeDistributed(
                        Dense(inner_rep, activation='linear', use_bias=False),
                        name="d{}-get-bond-contributions-to-new-atom-features".format(d),
                    )(mat_specialbondtypes)
                )

                if verbose:
                    print('Calculated bond effects on new atom features d = {}'.format(d))
                    print('ndim: {}'.format(K.ndim(actual_bond_contribs_for_atoms[-1])))

                unactivated_features.append(
                    add(
                        [actual_contribs_for_atoms[d], actual_bond_contribs_for_atoms[d]], 
                        name='d{}-combine-atom-and-bond-contributions-to-new-atom-features'.format(d),
                    )
                )

                if verbose:
                    print('Calculated summed features, unactivated, for d = {}'.format(d))
                    print('ndim: {}'.format(K.ndim(unactivated_features[-1])))

                all_mat_features.append(
                    Activation(hidden_activation, name="d{}-inner-update-activation".format(d))(unactivated_features[d])
                )

                if verbose:
                    print('Added activation layer for new atom features, d = {}'.format(d))
                    print('ndim: {}'.format(K.ndim(all_mat_features[-1])))

        if len(output_contribs) > 1:
            FPs = add(output_contribs, name='pool-across-depths')
        else:
            FPs = output_contribs[0]

    else:
        FPs = Input(shape=(512,), name="input-fingerprint")

    if hidden > 0:
        h1 = Dense(hidden, activation=hidden_activation)(FPs)
        h1d = Dropout(dr1)(h1)
        if verbose: print('    model: added {} Dense layer (-> {})'.format(hidden_activation, hidden))
        if hidden2 > 0:
            h2 = Dense(hidden2, activation=hidden_activation)(h1)
            if verbose: print('    model: added {} Dense layer (-> {})'.format(hidden_activation, hidden2))
            h = Dropout(dr2)(h2)
        else:
            h = h1d
    else:
        h = FPs

    ypred = Dense(output_size, activation = output_activation)(h)
    if verbose: print('    model: added output Dense layer (-> {})'.format(output_size))

    if type(use_fp) == type(None):
        model = Model(inputs=[mat_features, mat_adjacency, mat_specialbondtypes], outputs=[ypred])
    else:
        model = Model(inputs=[FPs], outputs=[ypred])

    if verbose: model.summary()

    # Compile
    if optimizer == 'adam':
        optimizer = Adam(lr=lr)
    elif optimizer == 'rmsprop':
        optimizer = RMSprop(lr=lr)
    elif optimizer == 'adagrad':
        optimizer = Adagrad(lr=lr)
    elif optimizer == 'adadelta':
        optimizer = Adadelta()
    else:
        print('Unrecognized optimizer')
        quit(1)

    # Custom loss to filter out NaN values in multi-task predictions
    if loss == 'custom':
        loss = mse_no_NaN
    elif loss == 'custom2':
        loss = binary_crossnetropy_no_NaN

    if verbose: print('compiling...',)
    model.compile(loss=loss, optimizer=optimizer)
    if verbose: print('done')

    return model
Generates simple embedding model to use molecular tensor as
    input in order to predict a single-valued output (i.e., yield)

    inputs:
        embedding_size - size of fingerprint for GraphFP layer
        lr - learning rate to use (train_model overwrites this value)
        optimizer - optimization function to use
        depth - depth of the neural fingerprint (i.e., radius)
        hidden - number of hidden tanh nodes after FP (0 is linear)
        hidden2 - number of hidden nodes after "hidden" layer
        hidden_activation - activation function used in hidden layers
        output_activation - activation function for final output nodes
        dr1 - dropout rate after embedding
        dr2 - dropout rate after hidden
        loss - loss function as a string (e.g., 'mse')
        molecular_attributes - whether to include additional molecular 
                    attributes in the atom-level features (recommended)
        use_fp - whether the representation used is actually a fingerprint
                    and not a convolutional network (for benchmarking)

    outputs:
        model - a Keras model
Generates simple embedding model to use molecular tensor as
input in order to predict a single-valued output



a Keras model
Generates simple embedding model to use molecular tensor as input in order to predict a single - valued output a Keras model
def build_model(embedding_size=512, lr=0.01, optimizer='adam', depth=2, hidden=0, hidden2=0, loss='mse',
                hidden_activation='tanh', output_activation='linear', dr1=0.0, dr2=0.0, output_size=1,
                molecular_attributes=False, use_fp=None, inner_rep=32, verbose=False):
    if type(use_fp) == type(None):
        F_atom, F_bond = sizeAttributeVectors(molecular_attributes = molecular_attributes)
        mat_features = Input(shape=(None, F_atom), name="feature-matrix")  
        mat_adjacency = Input(shape=(None, None), name="adjacency/self-matrix")  
        mat_specialbondtypes = Input(shape=(None, F_bond), name="special-bond-types")  
        all_mat_features = [mat_features]
        contribs_by_atom = []
        actual_contribs_for_atoms = []
        actual_bond_contribs_for_atoms = []
        output_contribs_byatom = []
        output_contribs = []
        unactivated_features = []
        sum_across_atoms       = lambda x: K.sum(x, axis = 1)
        sum_across_atoms_shape = lambda x: (x[0], x[2])
        for d in range(0, depth + 1):
            if verbose:
                print('### DEPTH {}'.format(d))
                print('KERAS SHAPE OF ALL_MAT_FEATURES[d]')  
                print(all_mat_features[d]._keras_shape)
                print('K.ndim of all_mat_features')
                print(K.ndim(all_mat_features[d]))
            output_contribs_byatom.append(
                TimeDistributed(
                    Dense(embedding_size, activation='softmax'),
                    name='d{}-out'.format(d),
                )(all_mat_features[d])
            )
            if verbose: print('Added depth {} output contribution (still atom-wise)'.format(d))
            output_contribs.append(
                Lambda(sum_across_atoms, output_shape=sum_across_atoms_shape, name="d{}-out-sum-across-atoms".format(d))(
                    output_contribs_byatom[d]
                )
            )
            if verbose: print('Added depth {} output contribution (summed across atoms)'.format(d))
            if d < depth:
                contribs_by_atom.append(
                    TimeDistributed(
                        Dense(inner_rep, activation = 'linear'), 
                        name="d{}-atom-to-atom".format(d),
                    )(all_mat_features[d])
                )
                if verbose:
                    print('Calculated new atom features for each atom, d {}'.format(d))
                    print('ndim: {}'.format(K.ndim(contribs_by_atom[-1])))
                actual_contribs_for_atoms.append(
                    dot(
                        [mat_adjacency, contribs_by_atom[d]],
                        axes=(0, 0),
                        name="d{}-multiply-atom-contribs-and-adj-mat".format(d)
                    )
                )
                if verbose:
                    print('Multiplied new atom features by adj matrix, d = {}'.format(d))
                    print('ndim: {}'.format(K.ndim(actual_contribs_for_atoms[-1])))
                actual_bond_contribs_for_atoms.append(
                    TimeDistributed(
                        Dense(inner_rep, activation='linear', use_bias=False),
                        name="d{}-get-bond-contributions-to-new-atom-features".format(d),
                    )(mat_specialbondtypes)
                )
                if verbose:
                    print('Calculated bond effects on new atom features d = {}'.format(d))
                    print('ndim: {}'.format(K.ndim(actual_bond_contribs_for_atoms[-1])))
                unactivated_features.append(
                    add(
                        [actual_contribs_for_atoms[d], actual_bond_contribs_for_atoms[d]], 
                        name='d{}-combine-atom-and-bond-contributions-to-new-atom-features'.format(d),
                    )
                )
                if verbose:
                    print('Calculated summed features, unactivated, for d = {}'.format(d))
                    print('ndim: {}'.format(K.ndim(unactivated_features[-1])))
                all_mat_features.append(
                    Activation(hidden_activation, name="d{}-inner-update-activation".format(d))(unactivated_features[d])
                )
                if verbose:
                    print('Added activation layer for new atom features, d = {}'.format(d))
                    print('ndim: {}'.format(K.ndim(all_mat_features[-1])))
        if len(output_contribs) > 1:
            FPs = add(output_contribs, name='pool-across-depths')
        else:
            FPs = output_contribs[0]
    else:
        FPs = Input(shape=(512,), name="input-fingerprint")
    if hidden > 0:
        h1 = Dense(hidden, activation=hidden_activation)(FPs)
        h1d = Dropout(dr1)(h1)
        if verbose: print('    model: added {} Dense layer (-> {})'.format(hidden_activation, hidden))
        if hidden2 > 0:
            h2 = Dense(hidden2, activation=hidden_activation)(h1)
            if verbose: print('    model: added {} Dense layer (-> {})'.format(hidden_activation, hidden2))
            h = Dropout(dr2)(h2)
        else:
            h = h1d
    else:
        h = FPs
    ypred = Dense(output_size, activation = output_activation)(h)
    if verbose: print('    model: added output Dense layer (-> {})'.format(output_size))
    if type(use_fp) == type(None):
        model = Model(inputs=[mat_features, mat_adjacency, mat_specialbondtypes], outputs=[ypred])
    else:
        model = Model(inputs=[FPs], outputs=[ypred])
    if verbose: model.summary()
    if optimizer == 'adam':
        optimizer = Adam(lr=lr)
    elif optimizer == 'rmsprop':
        optimizer = RMSprop(lr=lr)
    elif optimizer == 'adagrad':
        optimizer = Adagrad(lr=lr)
    elif optimizer == 'adadelta':
        optimizer = Adadelta()
    else:
        print('Unrecognized optimizer')
        quit(1)
    if loss == 'custom':
        loss = mse_no_NaN
    elif loss == 'custom2':
        loss = binary_crossnetropy_no_NaN
    if verbose: print('compiling...',)
    model.compile(loss=loss, optimizer=optimizer)
    if verbose: print('done')
    return model
def build_model ( embedding_size = 512 , lr = 0.01 , optimizer = 'adam' , depth = 2 , hidden = 0 , hidden2 = 0 , loss = 'mse' , hidden_activation = 'tanh' , output_activation = 'linear' , dr1 = 0.0 , dr2 = 0.0 , output_size = 1 , molecular_attributes = False , use_fp = None , inner_rep = 32 , verbose = False ) : if type ( use_fp ) == type ( None ) : F_atom , F_bond = sizeAttributeVectors ( molecular_attributes = molecular_attributes ) mat_features = Input ( shape = ( None , F_atom ) , name = "feature-matrix" ) mat_adjacency = Input ( shape = ( None , None ) , name = "adjacency/self-matrix" ) mat_specialbondtypes = Input ( shape = ( None , F_bond ) , name = "special-bond-types" ) all_mat_features = [ mat_features ] contribs_by_atom = [ ] actual_contribs_for_atoms = [ ] actual_bond_contribs_for_atoms = [ ] output_contribs_byatom = [ ] output_contribs = [ ] unactivated_features = [ ] sum_across_atoms = lambda x : K . sum ( x , axis = 1 ) sum_across_atoms_shape = lambda x : ( x [ 0 ] , x [ 2 ] ) for d in range ( 0 , depth + 1 ) : if verbose : print ( '### DEPTH {}' . format ( d ) ) print ( 'KERAS SHAPE OF ALL_MAT_FEATURES[d]' ) print ( all_mat_features [ d ] . _keras_shape ) print ( 'K.ndim of all_mat_features' ) print ( K . ndim ( all_mat_features [ d ] ) ) output_contribs_byatom . append ( TimeDistributed ( Dense ( embedding_size , activation = 'softmax' ) , name = 'd{}-out' . format ( d ) , ) ( all_mat_features [ d ] ) ) if verbose : print ( 'Added depth {} output contribution (still atom-wise)' . format ( d ) ) output_contribs . append ( Lambda ( sum_across_atoms , output_shape = sum_across_atoms_shape , name = "d{}-out-sum-across-atoms" . format ( d ) ) ( output_contribs_byatom [ d ] ) ) if verbose : print ( 'Added depth {} output contribution (summed across atoms)' . format ( d ) ) if d < depth : contribs_by_atom . append ( TimeDistributed ( Dense ( inner_rep , activation = 'linear' ) , name = "d{}-atom-to-atom" . format ( d ) , ) ( all_mat_features [ d ] ) ) if verbose : print ( 'Calculated new atom features for each atom, d {}' . format ( d ) ) print ( 'ndim: {}' . format ( K . ndim ( contribs_by_atom [ - 1 ] ) ) ) actual_contribs_for_atoms . append ( dot ( [ mat_adjacency , contribs_by_atom [ d ] ] , axes = ( 0 , 0 ) , name = "d{}-multiply-atom-contribs-and-adj-mat" . format ( d ) ) ) if verbose : print ( 'Multiplied new atom features by adj matrix, d = {}' . format ( d ) ) print ( 'ndim: {}' . format ( K . ndim ( actual_contribs_for_atoms [ - 1 ] ) ) ) actual_bond_contribs_for_atoms . append ( TimeDistributed ( Dense ( inner_rep , activation = 'linear' , use_bias = False ) , name = "d{}-get-bond-contributions-to-new-atom-features" . format ( d ) , ) ( mat_specialbondtypes ) ) if verbose : print ( 'Calculated bond effects on new atom features d = {}' . format ( d ) ) print ( 'ndim: {}' . format ( K . ndim ( actual_bond_contribs_for_atoms [ - 1 ] ) ) ) unactivated_features . append ( add ( [ actual_contribs_for_atoms [ d ] , actual_bond_contribs_for_atoms [ d ] ] , name = 'd{}-combine-atom-and-bond-contributions-to-new-atom-features' . format ( d ) , ) ) if verbose : print ( 'Calculated summed features, unactivated, for d = {}' . format ( d ) ) print ( 'ndim: {}' . format ( K . ndim ( unactivated_features [ - 1 ] ) ) ) all_mat_features . append ( Activation ( hidden_activation , name = "d{}-inner-update-activation" . format ( d ) ) ( unactivated_features [ d ] ) ) if verbose : print ( 'Added activation layer for new atom features, d = {}' . format ( d ) ) print ( 'ndim: {}' . format ( K . ndim ( all_mat_features [ - 1 ] ) ) ) if len ( output_contribs ) > 1 : FPs = add ( output_contribs , name = 'pool-across-depths' ) else : FPs = output_contribs [ 0 ] else : FPs = Input ( shape = ( 512 , ) , name = "input-fingerprint" ) if hidden > 0 : h1 = Dense ( hidden , activation = hidden_activation ) ( FPs ) h1d = Dropout ( dr1 ) ( h1 ) if verbose : print ( '    model: added {} Dense layer (-> {})' . format ( hidden_activation , hidden ) ) if hidden2 > 0 : h2 = Dense ( hidden2 , activation = hidden_activation ) ( h1 ) if verbose : print ( '    model: added {} Dense layer (-> {})' . format ( hidden_activation , hidden2 ) ) h = Dropout ( dr2 ) ( h2 ) else : h = h1d else : h = FPs ypred = Dense ( output_size , activation = output_activation ) ( h ) if verbose : print ( '    model: added output Dense layer (-> {})' . format ( output_size ) ) if type ( use_fp ) == type ( None ) : model = Model ( inputs = [ mat_features , mat_adjacency , mat_specialbondtypes ] , outputs = [ ypred ] ) else : model = Model ( inputs = [ FPs ] , outputs = [ ypred ] ) if verbose : model . summary ( ) if optimizer == 'adam' : optimizer = Adam ( lr = lr ) elif optimizer == 'rmsprop' : optimizer = RMSprop ( lr = lr ) elif optimizer == 'adagrad' : optimizer = Adagrad ( lr = lr ) elif optimizer == 'adadelta' : optimizer = Adadelta ( ) else : print ( 'Unrecognized optimizer' ) quit ( 1 ) if loss == 'custom' : loss = mse_no_NaN elif loss == 'custom2' : loss = binary_crossnetropy_no_NaN if verbose : print ( 'compiling...' , ) model . compile ( loss = loss , optimizer = optimizer ) if verbose : print ( 'done' ) return model
Generates simple embedding model to use molecular tensor as
input in order to predict a single-valued output (i.e., yield)
Generates simple embedding model to use molecular tensor as input in order to predict a single - valued output ( i . e . yield )
'''Generates simple embedding model to use molecular tensor as
    input in order to predict a single-valued output (i.e., yield)

    inputs:
        embedding_size - size of fingerprint for GraphFP layer
        lr - learning rate to use (train_model overwrites this value)
        optimizer - optimization function to use
        depth - depth of the neural fingerprint (i.e., radius)
        hidden - number of hidden tanh nodes after FP (0 is linear)
        hidden2 - number of hidden nodes after "hidden" layer
        hidden_activation - activation function used in hidden layers
        output_activation - activation function for final output nodes
        dr1 - dropout rate after embedding
        dr2 - dropout rate after hidden
        loss - loss function as a string (e.g., 'mse')
        molecular_attributes - whether to include additional molecular 
                    attributes in the atom-level features (recommended)
        use_fp - whether the representation used is actually a fingerprint
                    and not a convolutional network (for benchmarking)

    outputs:
        model - a Keras model''' # Graph model # shape = (n_atoms, n_atom_features) # shape = (n_atoms, n_atoms) # shape = (n_atoms, n_bond_features) # Lists to keep track of keras features # Get the output contribution using all_mat_features[d] # Update if needed # Compile # Custom loss to filter out NaN values in multi-task predictions
embedding_size
lr
optimizer
depth
hidden
hidden2
loss
hidden_activation
output_activation
dr1
dr2
output_size
molecular_attributes
use_fp
inner_rep
verbose