Beispiel #1
0
    def test_set2set(self):
        n_hidden = 10
        set2set = Set2Set(n_hidden=n_hidden)
        out = set2set([self.x, self.index])
        shapes = set2set.compute_output_shape([self.x.shape, self.index.shape])
        self.assertEqual(shapes[-1], n_hidden * 2)

        model = Model(inputs=[self.x, self.index], outputs=out)
        model.compile(loss='mse', optimizer='adam')
        x = np.random.normal(size=(1, 5, 6))
        index = np.array([[0, 0, 0, 1, 1]])
        result = model.predict([x, index])
        self.assertListEqual(list(result.shape), [1, 2, n_hidden * 2])
Beispiel #2
0
    def __init__(self,
                 nfeat_edge,
                 nfeat_global,
                 nfeat_node=None,
                 nblocks=3,
                 lr=1e-3,
                 n1=64,
                 n2=32,
                 n3=16,
                 nvocal=95,
                 embedding_dim=16,
                 npass=3,
                 ntarget=1,
                 act=softplus2,
                 is_classification=False,
                 loss="mse",
                 l2_coef=None,
                 dropout=None,
                 graph_convertor=None,
                 optimizer_kwargs=None
                 ):
        int32 = 'int32'
        if nfeat_node is None:
            x1 = Input(shape=(None,), dtype=int32)  # only z as feature
            x1_ = Embedding(nvocal, embedding_dim)(x1)
        else:
            x1 = Input(shape=(None, nfeat_node))
            x1_ = x1
        x2 = Input(shape=(None, nfeat_edge))
        x3 = Input(shape=(None, nfeat_global))
        x4 = Input(shape=(None,), dtype=int32)
        x5 = Input(shape=(None,), dtype=int32)
        x6 = Input(shape=(None,), dtype=int32)
        x7 = Input(shape=(None,), dtype=int32)

        if l2_coef is not None:
            reg = l2(l2_coef)
        else:
            reg = None

        # two feedforward layers
        def ff(x, n_hiddens=[n1, n2]):
            out = x
            for i in n_hiddens:
                out = Dense(i, activation=act, kernel_regularizer=reg)(out)
            return out

        # a block corresponds to two feedforward layers + one MEGNetLayer layer
        # Note the first block does not contain the feedforward layer since
        # it will be explicitly added before the block
        def one_block(a, b, c, has_ff=True):
            if has_ff:
                x1_ = ff(a)
                x2_ = ff(b)
                x3_ = ff(c)
            else:
                x1_ = a
                x2_ = b
                x3_ = c
            out = MEGNetLayer(
                [n1, n1, n2], [n1, n1, n2], [n1, n1, n2],
                pool_method='mean', activation=act, kernel_regularizer=reg)(
                [x1_, x2_, x3_, x4, x5, x6, x7])

            x1_temp = out[0]
            x2_temp = out[1]
            x3_temp = out[2]
            if dropout:
                x1_temp = Dropout(dropout)(x1_temp)
                x2_temp = Dropout(dropout)(x2_temp)
                x3_temp = Dropout(dropout)(x3_temp)
            return x1_temp, x2_temp, x3_temp

        x1_ = ff(x1_)
        x2_ = ff(x2)
        x3_ = ff(x3)
        for i in range(nblocks):
            if i == 0:
                has_ff = False
            else:
                has_ff = True
            x1_1 = x1_
            x2_1 = x2_
            x3_1 = x3_
            x1_1, x2_1, x3_1 = one_block(x1_1, x2_1, x3_1, has_ff)
            # skip connection
            x1_ = Add()([x1_, x1_1])
            x2_ = Add()([x2_, x2_1])
            x3_ = Add()([x3_, x3_1])

        # set2set for both the atom and bond
        node_vec = Set2Set(T=npass, n_hidden=n3, kernel_regularizer=reg)([x1_, x6])
        edge_vec = Set2Set(T=npass, n_hidden=n3, kernel_regularizer=reg)([x2_, x7])
        # concatenate atom, bond, and global
        final_vec = Concatenate(axis=-1)([node_vec, edge_vec, x3_])
        if dropout:
            final_vec = Dropout(dropout)(final_vec)
        # final dense layers
        final_vec = Dense(n2, activation=act, kernel_regularizer=reg)(final_vec)
        final_vec = Dense(n3, activation=act, kernel_regularizer=reg)(final_vec)

        if is_classification:
            final_act = 'sigmoid'
            loss = 'binary_crossentropy'
        else:
            final_act = None
            loss = loss

        out = Dense(ntarget, activation=final_act)(final_vec)
        model = Model(inputs=[x1, x2, x3, x4, x5, x6, x7], outputs=out)

        opt_params = {'lr': lr}
        if optimizer_kwargs is not None:
            opt_params.update(optimizer_kwargs)
        model.compile(Adam(**opt_params), loss)

        if graph_convertor is None:
            graph_convertor = CrystalGraph(cutoff=4, bond_convertor=GaussianDistance(np.linspace(0, 5, 100), 0.5))

        super().__init__(model=model, graph_convertor=graph_convertor)
Beispiel #3
0
def make_megnet_model(nfeat_edge: int = None,
                      nfeat_global: int = None,
                      nfeat_node: int = None,
                      nblocks: int = 3,
                      n1: int = 64,
                      n2: int = 32,
                      n3: int = 16,
                      nvocal: int = 95,
                      embedding_dim: int = 16,
                      nbvocal: int = None,
                      bond_embedding_dim: int = None,
                      ngvocal: int = None,
                      global_embedding_dim: int = None,
                      npass: int = 3,
                      ntarget: int = 1,
                      act: Callable = softplus2,
                      is_classification: bool = False,
                      l2_coef: float = None,
                      dropout: float = None,
                      dropout_on_predict: bool = False) -> Model:
    """Make a MEGNet Model

    Args:
        nfeat_edge: (int) number of bond features
        nfeat_global: (int) number of state features
        nfeat_node: (int) number of atom features
        nblocks: (int) number of MEGNetLayer blocks
        n1: (int) number of hidden units in layer 1 in MEGNetLayer
        n2: (int) number of hidden units in layer 2 in MEGNetLayer
        n3: (int) number of hidden units in layer 3 in MEGNetLayer
        nvocal: (int) number of total element
        embedding_dim: (int) number of embedding dimension
        nbvocal: (int) number of bond types if bond attributes are types
        bond_embedding_dim: (int) number of bond embedding dimension
        ngvocal: (int) number of global types if global attributes are types
        global_embedding_dim: (int) number of global embedding dimension
        npass: (int) number of recurrent steps in Set2Set layer
        ntarget: (int) number of output targets
        act: (object) activation function
        l2_coef: (float or None) l2 regularization parameter
        is_classification: (bool) whether it is a classification task
        dropout: (float) dropout rate
        dropout_on_predict (bool): Whether to use dropout during prediction and training
    Returns:
        (Model) Keras model, ready to run
    """

    # Get the setting for the training kwarg of Dropout
    dropout_training = True if dropout_on_predict else None

    # Create the input blocks
    int32 = 'int32'
    if nfeat_node is None:
        x1 = Input(shape=(None, ), dtype=int32)  # only z as feature
        x1_ = Embedding(nvocal, embedding_dim)(x1)
    else:
        x1 = Input(shape=(None, nfeat_node))
        x1_ = x1
    if nfeat_edge is None:
        x2 = Input(shape=(None, ), dtype=int32)
        x2_ = Embedding(nbvocal, bond_embedding_dim)(x2)
    else:
        x2 = Input(shape=(None, nfeat_edge))
        x2_ = x2
    if nfeat_global is None:
        x3 = Input(shape=(None, ), dtype=int32)
        x3_ = Embedding(ngvocal, global_embedding_dim)(x3)
    else:
        x3 = Input(shape=(None, nfeat_global))
        x3_ = x3
    x4 = Input(shape=(None, ), dtype=int32)
    x5 = Input(shape=(None, ), dtype=int32)
    x6 = Input(shape=(None, ), dtype=int32)
    x7 = Input(shape=(None, ), dtype=int32)
    if l2_coef is not None:
        reg = l2(l2_coef)
    else:
        reg = None

    # two feedforward layers
    def ff(x, n_hiddens=[n1, n2]):
        out = x
        for i in n_hiddens:
            out = Dense(i, activation=act, kernel_regularizer=reg)(out)
        return out

    # a block corresponds to two feedforward layers + one MEGNetLayer layer
    # Note the first block does not contain the feedforward layer since
    # it will be explicitly added before the block
    def one_block(a, b, c, has_ff=True):
        if has_ff:
            x1_ = ff(a)
            x2_ = ff(b)
            x3_ = ff(c)
        else:
            x1_ = a
            x2_ = b
            x3_ = c
        out = MEGNetLayer(
            [n1, n1, n2], [n1, n1, n2], [n1, n1, n2],
            pool_method='mean',
            activation=act,
            kernel_regularizer=reg)([x1_, x2_, x3_, x4, x5, x6, x7])

        x1_temp = out[0]
        x2_temp = out[1]
        x3_temp = out[2]
        if dropout:
            x1_temp = Dropout(dropout)(x1_temp, training=dropout_training)
            x2_temp = Dropout(dropout)(x2_temp, training=dropout_training)
            x3_temp = Dropout(dropout)(x3_temp, training=dropout_training)
        return x1_temp, x2_temp, x3_temp

    x1_ = ff(x1_)
    x2_ = ff(x2_)
    x3_ = ff(x3_)
    for i in range(nblocks):
        if i == 0:
            has_ff = False
        else:
            has_ff = True
        x1_1 = x1_
        x2_1 = x2_
        x3_1 = x3_
        x1_1, x2_1, x3_1 = one_block(x1_1, x2_1, x3_1, has_ff)
        # skip connection
        x1_ = Add()([x1_, x1_1])
        x2_ = Add()([x2_, x2_1])
        x3_ = Add()([x3_, x3_1])
    # set2set for both the atom and bond
    node_vec = Set2Set(T=npass, n_hidden=n3, kernel_regularizer=reg)([x1_, x6])
    edge_vec = Set2Set(T=npass, n_hidden=n3, kernel_regularizer=reg)([x2_, x7])
    # concatenate atom, bond, and global
    final_vec = Concatenate(axis=-1)([node_vec, edge_vec, x3_])
    if dropout:
        final_vec = Dropout(dropout)(final_vec, training=dropout_training)
    # final dense layers
    final_vec = Dense(n2, activation=act, kernel_regularizer=reg)(final_vec)
    final_vec = Dense(n3, activation=act, kernel_regularizer=reg)(final_vec)
    if is_classification:
        final_act = 'sigmoid'
    else:
        final_act = None
    out = Dense(ntarget, activation=final_act)(final_vec)
    model = Model(inputs=[x1, x2, x3, x4, x5, x6, x7], outputs=out)
    return model
Beispiel #4
0
def make_megnet_model(
    nfeat_edge: int = None,
    nfeat_global: int = None,
    nfeat_node: int = None,
    nblocks: int = 3,
    n1: int = 64,
    n2: int = 32,
    n3: int = 16,
    nvocal: int = 95,
    embedding_dim: int = 16,
    nbvocal: int = None,
    bond_embedding_dim: int = None,
    ngvocal: int = None,
    global_embedding_dim: int = None,
    npass: int = 3,
    ntarget: int = 1,
    act: Callable = softplus2,
    is_classification: bool = False,
    l2_coef: float = None,
    dropout: float = None,
    dropout_on_predict: bool = False,
    **kwargs,
) -> Model:
    """Make a MEGNet Model
    Args:
        nfeat_edge: (int) number of bond features
        nfeat_global: (int) number of state features
        nfeat_node: (int) number of atom features
        nblocks: (int) number of MEGNetLayer blocks
        n1: (int) number of hidden units in layer 1 in MEGNetLayer
        n2: (int) number of hidden units in layer 2 in MEGNetLayer
        n3: (int) number of hidden units in layer 3 in MEGNetLayer
        nvocal: (int) number of total element
        embedding_dim: (int) number of embedding dimension
        nbvocal: (int) number of bond types if bond attributes are types
        bond_embedding_dim: (int) number of bond embedding dimension
        ngvocal: (int) number of global types if global attributes are types
        global_embedding_dim: (int) number of global embedding dimension
        npass: (int) number of recurrent steps in Set2Set layer
        ntarget: (int) number of output targets
        act: (object) activation function
        l2_coef: (float or None) l2 regularization parameter
        is_classification: (bool) whether it is a classification task
        dropout: (float) dropout rate
        dropout_on_predict (bool): Whether to use dropout during prediction and training
        kwargs (dict): in the case where bond inputs are pure distances (not the expanded
                distances nor integers for embedding, i.e., nfeat_edge=None and bond_embedding_dim=None),
                kwargs can take additional inputs for expand the distance using Gaussian basis.

                centers (np.ndarray): array for defining the Gaussian expansion centers
                width (float): width for the Gaussian basis
    Returns:
        (Model) Keras model, ready to run
    """

    # Get the setting for the training kwarg of Dropout
    dropout_training = True if dropout_on_predict else None

    # atom inputs

    if nfeat_node is None:
        # only z as feature
        x1 = Input(shape=(None, ),
                   dtype=DataType.tf_int,
                   name="atom_int_input")
        x1_ = Embedding(nvocal, embedding_dim, name="atom_embedding")(x1)
    else:
        x1 = Input(shape=(None, nfeat_node), name="atom_feature_input")
        x1_ = x1

    # bond inputs
    if nfeat_edge is None:
        if bond_embedding_dim is not None:
            # bond attributes are integers for embedding
            x2 = Input(shape=(None, ),
                       dtype=DataType.tf_int,
                       name="bond_int_input")
            x2_ = Embedding(nbvocal, bond_embedding_dim,
                            name="bond_embedding")(x2)
        else:
            # the bond attributes are float distance
            x2 = Input(shape=(None, ),
                       dtype=DataType.tf_float,
                       name="bond_float_input")
            centers = kwargs.get("centers", None)
            width = kwargs.get("width", None)
            if centers is None and width is None:
                raise ValueError(
                    "If the bond attributes are single float values, "
                    "we expect the value to be expanded before passing "
                    "to the models. Therefore, `centers` and `width` for "
                    "Gaussian basis expansion are needed")
            x2_ = GaussianExpansion(centers=centers,
                                    width=width)(x2)  # type: ignore
    else:
        x2 = Input(shape=(None, nfeat_edge), name="bond_feature_input")
        x2_ = x2

    # state inputs
    if nfeat_global is None:
        if global_embedding_dim is not None:
            # global state inputs are embedding integers
            x3 = Input(shape=(None, ),
                       dtype=DataType.tf_int,
                       name="state_int_input")
            x3_ = Embedding(ngvocal,
                            global_embedding_dim,
                            name="state_embedding")(x3)
        else:
            # take default vector of two zeros
            x3 = Input(shape=(None, 2),
                       dtype=DataType.tf_float,
                       name="state_default_input")
            x3_ = x3
    else:
        x3 = Input(shape=(None, nfeat_global), name="state_feature_input")
        x3_ = x3
    x4 = Input(shape=(None, ),
               dtype=DataType.tf_int,
               name="bond_index_1_input")
    x5 = Input(shape=(None, ),
               dtype=DataType.tf_int,
               name="bond_index_2_input")
    x6 = Input(shape=(None, ),
               dtype=DataType.tf_int,
               name="atom_graph_index_input")
    x7 = Input(shape=(None, ),
               dtype=DataType.tf_int,
               name="bond_graph_index_input")

    if l2_coef is not None:
        reg = l2(l2_coef)
    else:
        reg = None

    # two feedforward layers
    def ff(x, n_hiddens=[n1, n2], name_prefix=None):
        if name_prefix is None:
            name_prefix = "FF"
        out = x
        for k, i in enumerate(n_hiddens):
            out = Dense(i,
                        activation=act,
                        kernel_regularizer=reg,
                        name=f"{name_prefix}_{k}")(out)
        return out

    # a block corresponds to two feedforward layers + one MEGNetLayer layer
    # Note the first block does not contain the feedforward layer since
    # it will be explicitly added before the block
    def one_block(a, b, c, has_ff=True, block_index=0):
        if has_ff:
            x1_ = ff(a, name_prefix=f"block_{block_index}_atom_ff")
            x2_ = ff(b, name_prefix=f"block_{block_index}_bond_ff")
            x3_ = ff(c, name_prefix=f"block_{block_index}_state_ff")
        else:
            x1_ = a
            x2_ = b
            x3_ = c
        out = MEGNetLayer(
            [n1, n1, n2],
            [n1, n1, n2],
            [n1, n1, n2],
            pool_method="mean",
            activation=act,
            kernel_regularizer=reg,
            name=f"megnet_{block_index}",
        )([x1_, x2_, x3_, x4, x5, x6, x7])

        x1_temp = out[0]
        x2_temp = out[1]
        x3_temp = out[2]
        if dropout:
            x1_temp = Dropout(dropout, name=f"dropout_atom_{block_index}")(
                x1_temp, training=dropout_training)
            x2_temp = Dropout(dropout, name=f"dropout_bond_{block_index}")(
                x2_temp, training=dropout_training)
            x3_temp = Dropout(dropout, name=f"dropout_state_{block_index}")(
                x3_temp, training=dropout_training)
        return x1_temp, x2_temp, x3_temp

    x1_ = ff(x1_, name_prefix="preblock_atom")
    x2_ = ff(x2_, name_prefix="preblock_bond")
    x3_ = ff(x3_, name_prefix="preblock_state")
    for i in range(nblocks):
        if i == 0:
            has_ff = False
        else:
            has_ff = True
        x1_1 = x1_
        x2_1 = x2_
        x3_1 = x3_
        x1_1, x2_1, x3_1 = one_block(x1_1, x2_1, x3_1, has_ff, block_index=i)
        # skip connection
        x1_ = Add(name=f"block_{i}_add_atom")([x1_, x1_1])
        x2_ = Add(name=f"block_{i}_add_bond")([x2_, x2_1])
        x3_ = Add(name=f"block_{i}_add_state")([x3_, x3_1])

    # print(Set2Set(T=npass, n_hidden=n3, kernel_regularizer=reg, name='set2set_atom'
    #             ).compute_output_shape([i.shape for i in [x1_, x6]]))
    # set2set for both the atom and bond
    node_vec = Set2Set(T=npass,
                       n_hidden=n3,
                       kernel_regularizer=reg,
                       name="set2set_atom")([x1_, x6])
    # print('Node vec', node_vec)
    edge_vec = Set2Set(T=npass,
                       n_hidden=n3,
                       kernel_regularizer=reg,
                       name="set2set_bond")([x2_, x7])
    # concatenate atom, bond, and global
    final_vec = Concatenate(axis=-1)([node_vec, edge_vec, x3_])
    if dropout:
        final_vec = Dropout(dropout,
                            name="dropout_final")(final_vec,
                                                  training=dropout_training)
    # final dense layers
    final_vec = Dense(n2,
                      activation=act,
                      kernel_regularizer=reg,
                      name="readout_0")(final_vec)
    final_vec = Dense(n3,
                      activation=act,
                      kernel_regularizer=reg,
                      name="readout_1")(final_vec)
    if is_classification:
        final_act = "sigmoid"
    else:
        final_act = None  # type: ignore
    out = Dense(ntarget, activation=final_act, name="readout_2")(final_vec)
    model = Model(inputs=[x1, x2, x3, x4, x5, x6, x7], outputs=out)
    return model
Beispiel #5
0
def set2set_model(n_feature,
                  n_connect,
                  n_global,
                  n_blocks=3,
                  lr=1e-3,
                  n1=64,
                  n2=32,
                  n3=16,
                  n_pass=3,
                  n_target=1,
                  act=softplus2,
                  dropout=None):
    """
    construct a graph network model with explicit atom features

    :param n_feature: (int) number of atom features
    :param n_connect: (int) number of bond features
    :param n_global: (int) number of state features
    :param n_blocks: (int) number of MEGNet block
    :param lr: (float) learning rate
    :param n1: (int) number of hidden units in layer 1 in MEGNet
    :param n2: (int) number of hidden units in layer 2 in MEGNet
    :param n3: (int) number of hidden units in layer 3 in MEGNet
    :param n_pass: (int) number of recurrent steps in Set2Set layer
    :param n_target: (int) number of output targets
    :param act: (object) activation function
    :param dropout: (float) dropout rate
    :return: keras model object
    """
    int32 = 'int32'
    x1 = Input(shape=(None, n_feature))
    x2 = Input(shape=(None, n_connect))
    x3 = Input(shape=(None, n_global))
    x4 = Input(shape=(None, ), dtype=int32)
    x5 = Input(shape=(None, ), dtype=int32)
    x6 = Input(shape=(None, ), dtype=int32)
    x7 = Input(shape=(None, ), dtype=int32)

    # two feedforward layers
    def ff(x, n_hiddens=[n1, n2]):
        out = x
        for i in n_hiddens:
            out = Dense(i, activation=act)(out)
        return out

    # a block corresponds to two feedforward layers + one MEGNet layer
    # Note the first block does not contain the feedforward layer since
    # it will be explicitly added before the block
    def one_block(a, b, c, has_ff=True):
        if has_ff:
            x1_ = ff(a)
            x2_ = ff(b)
            x3_ = ff(c)
        else:
            x1_ = a
            x2_ = b
            x3_ = c
        out = MEGNet([n1, n1, n2], [n1, n1, n2], [n1, n1, n2],
                     pool_method='mean',
                     activation=act)([x1_, x2_, x3_, x4, x5, x6, x7])

        x1_temp = out[0]
        x2_temp = out[1]
        x3_temp = out[2]
        if dropout:
            x1_temp = Dropout(dropout)(x1_temp)
            x2_temp = Dropout(dropout)(x2_temp)
            x3_temp = Dropout(dropout)(x3_temp)
        return x1_temp, x2_temp, x3_temp

    x1_ = ff(x1)
    x2_ = ff(x2)
    x3_ = ff(x3)
    for i in range(n_blocks):
        if i == 0:
            has_ff = False
        else:
            has_ff = True
        x1_1 = x1_
        x2_1 = x2_
        x3_1 = x3_
        x1_1, x2_1, x3_1 = one_block(x1_1, x2_1, x3_1, has_ff)
        # skip connection
        x1_ = Add()([x1_, x1_1])
        x2_ = Add()([x2_, x2_1])
        x3_ = Add()([x3_, x3_1])

    # set2set for both the atom and bond
    node_vec = Set2Set(T=n_pass, n_hidden=n3)([x1_, x6])
    edge_vec = Set2Set(T=n_pass, n_hidden=n3)([x2_, x7])
    # concatenate atom, bond, and global
    final_vec = Concatenate(axis=-1)([node_vec, edge_vec, x3_])
    if dropout:
        final_vec = Dropout(dropout)(final_vec)
    # final dense layers
    final_vec = Dense(n2, activation=act)(final_vec)
    final_vec = Dense(n3, activation=act)(final_vec)
    out = Dense(n_target)(final_vec)
    model = Model(inputs=[x1, x2, x3, x4, x5, x6, x7], outputs=out)
    model.compile(Adam(lr), mse_scale)
    return model
Beispiel #6
0
def set2set_with_embedding_mp(n_connect,
                              n_global,
                              n_vocal=95,
                              embedding_dim=16,
                              n_blocks=3,
                              lr=1e-3,
                              n1=64,
                              n2=32,
                              n3=16,
                              n_pass=3,
                              n_target=1,
                              act=softplus2,
                              l2_coef=None,
                              is_classification=False):
    """
    construct a graph network model with only Z as atom features

    :param n_connect: (int) number of bond features
    :param n_global: (int) number of state features
    :param n_vocal: (int) number of vocabulary. Max Z number needs to be less than this.
        since we have max Z number 94 in materials project, here the default n_vocal = 95 (the last index is 94)
    :param embedding_dim: (int) embedding vector length
    :param n_blocks: (int) number of MEGNet block
    :param lr: (float) learning rate
    :param n1: (int) number of hidden units in layer 1 in MEGNet
    :param n2: (int) number of hidden units in layer 2 in MEGNet
    :param n3: (int) number of hidden units in layer 3 in MEGNet
    :param n_pass: (int) number of recurrent steps in Set2Set layer
    :param n_target: (int) number of output targets
    :param act: (object) activation function
    :param l2_coef: (float) l2 regularization rate
    :param is_classification: (bool) whether it is a classification problem
    :return: keras model object
    """
    int32 = 'int32'
    x1 = Input(shape=(None, ), dtype=int32)
    x2 = Input(shape=(None, n_connect))
    x3 = Input(shape=(None, n_global))
    x4 = Input(shape=(None, ), dtype=int32)
    x5 = Input(shape=(None, ), dtype=int32)
    x6 = Input(shape=(None, ), dtype=int32)
    x7 = Input(shape=(None, ), dtype=int32)
    if l2_coef is not None:
        reg = l2(l2_coef)
    else:
        reg = None

    def ff(x, n_hiddens=[n1, n2]):
        out = x
        for i in n_hiddens:
            out = Dense(i, activation=act, kernel_regularizer=reg)(out)
        return out

    def one_block(a, b, c, has_ff=True):
        if has_ff:
            x1_ = ff(a)
            x2_ = ff(b)
            x3_ = ff(c)
        else:
            x1_ = a
            x2_ = b
            x3_ = c

        out = MEGNet([n1, n1, n2], [n1, n1, n2], [n1, n1, n2],
                     pool_method='mean',
                     activation=act,
                     kernel_regularizer=reg)([x1_, x2_, x3_, x4, x5, x6, x7])

        return out[0], out[1], out[2]

    x1_ = Embedding(n_vocal, embedding_dim)(x1)
    x1_ = ff(x1_)
    x2_ = ff(x2)
    x3_ = ff(x3)
    for i in range(n_blocks):
        if i == 0:
            has_ff = False
        else:
            has_ff = True
        x1_1 = x1_
        x2_1 = x2_
        x3_1 = x3_
        x1_1, x2_1, x3_1 = one_block(x1_1, x2_1, x3_1, has_ff)
        x1_ = Add()([x1_, x1_1])
        x2_ = Add()([x2_, x2_1])
        x3_ = Add()([x3_, x3_1])
    node_vec = Set2Set(T=n_pass, n_hidden=n3)([x1_, x6])
    edge_vec = Set2Set(T=n_pass, n_hidden=n3)([x2_, x7])
    final_vec = Concatenate(axis=-1)([node_vec, edge_vec, x3_])
    final_vec = Dense(n2, activation=act)(final_vec)
    final_vec = Dense(n3, activation=act)(final_vec)
    if is_classification:
        final_act = 'sigmoid'
        loss = 'binary_crossentropy'
    else:
        final_act = None
        loss = mse_scale

    out = Dense(n_target, activation=final_act)(final_vec)
    model = Model(inputs=[x1, x2, x3, x4, x5, x6, x7], outputs=out)
    model.compile(Adam(lr), loss)
    return model
Beispiel #7
0
def megnet_model(n_connect,
                 n_global,
                 n_feature=None,
                 n_blocks=3,
                 lr=1e-3,
                 n1=64,
                 n2=32,
                 n3=16,
                 n_vocal=95,
                 embedding_dim=16,
                 n_pass=3,
                 n_target=1,
                 act=softplus2,
                 is_classification=False,
                 loss="mse",
                 l2_coef=None,
                 dropout=None,
                 graph_convertor=None,
                 distance_convertor=None):
    """
    construct a graph network model with or without explicit atom features
    if n_feature is specified then a general graph model is assumed, otherwise a crystal graph model with z number as
    atom feature is assumed.

    :param n_connect: (int) number of bond features
    :param n_global: (int) number of state features
    :param n_feature: (int) number of atom features
    :param n_blocks: (int) number of MEGNet block
    :param lr: (float) learning rate
    :param n1: (int) number of hidden units in layer 1 in MEGNet
    :param n2: (int) number of hidden units in layer 2 in MEGNet
    :param n3: (int) number of hidden units in layer 3 in MEGNet
    :param n_vocal: (int) number of total element
    :param embedding_dim: (int) number of embedding dimension
    :param n_pass: (int) number of recurrent steps in Set2Set layer
    :param n_target: (int) number of output targets
    :param act: (object) activation function
    :param l2_coef: (float or None) l2 regularization parameter
    :param is_classification: (bool) whether it is a classifiation task
    :param loss: (object or str) loss function
    :param dropout: (float) dropout rate
    :param graph_convertor: (object) object that exposes a "convert" method for structure to graph conversion
    :param distance_convertor: (object) object that exposes a "convert" method for distance to expanded vector conversion
    :return: keras model object
    """
    int32 = 'int32'
    if n_feature is None:
        x1 = Input(shape=(None, ), dtype=int32)  # only z as feature
        x1_ = Embedding(n_vocal, embedding_dim)(x1)
    else:
        x1 = Input(shape=(None, n_feature))
        x1_ = x1
    x2 = Input(shape=(None, n_connect))
    x3 = Input(shape=(None, n_global))
    x4 = Input(shape=(None, ), dtype=int32)
    x5 = Input(shape=(None, ), dtype=int32)
    x6 = Input(shape=(None, ), dtype=int32)
    x7 = Input(shape=(None, ), dtype=int32)

    if l2_coef is not None:
        reg = l2(l2_coef)
    else:
        reg = None

    # two feedforward layers
    def ff(x, n_hiddens=[n1, n2]):
        out = x
        for i in n_hiddens:
            out = Dense(i, activation=act, kernel_regularizer=reg)(out)
        return out

    # a block corresponds to two feedforward layers + one MEGNet layer
    # Note the first block does not contain the feedforward layer since
    # it will be explicitly added before the block
    def one_block(a, b, c, has_ff=True):
        if has_ff:
            x1_ = ff(a)
            x2_ = ff(b)
            x3_ = ff(c)
        else:
            x1_ = a
            x2_ = b
            x3_ = c
        out = MEGNet([n1, n1, n2], [n1, n1, n2], [n1, n1, n2],
                     pool_method='mean',
                     activation=act)([x1_, x2_, x3_, x4, x5, x6, x7])

        x1_temp = out[0]
        x2_temp = out[1]
        x3_temp = out[2]
        if dropout:
            x1_temp = Dropout(dropout)(x1_temp)
            x2_temp = Dropout(dropout)(x2_temp)
            x3_temp = Dropout(dropout)(x3_temp)
        return x1_temp, x2_temp, x3_temp

    x1_ = ff(x1_)
    x2_ = ff(x2)
    x3_ = ff(x3)
    for i in range(n_blocks):
        if i == 0:
            has_ff = False
        else:
            has_ff = True
        x1_1 = x1_
        x2_1 = x2_
        x3_1 = x3_
        x1_1, x2_1, x3_1 = one_block(x1_1, x2_1, x3_1, has_ff)
        # skip connection
        x1_ = Add()([x1_, x1_1])
        x2_ = Add()([x2_, x2_1])
        x3_ = Add()([x3_, x3_1])

    # set2set for both the atom and bond
    node_vec = Set2Set(T=n_pass, n_hidden=n3)([x1_, x6])
    edge_vec = Set2Set(T=n_pass, n_hidden=n3)([x2_, x7])
    # concatenate atom, bond, and global
    final_vec = Concatenate(axis=-1)([node_vec, edge_vec, x3_])
    if dropout:
        final_vec = Dropout(dropout)(final_vec)
    # final dense layers
    final_vec = Dense(n2, activation=act)(final_vec)
    final_vec = Dense(n3, activation=act)(final_vec)

    if is_classification:
        final_act = 'sigmoid'
        loss = 'binary_crossentropy'
    else:
        final_act = None
        loss = loss

    out = Dense(n_target, activation=final_act)(final_vec)
    model = Model(inputs=[x1, x2, x3, x4, x5, x6, x7],
                  outputs=out,
                  graph_convertor=graph_convertor,
                  distance_convertor=distance_convertor)
    model.compile(Adam(lr), loss)
    return model
Beispiel #8
0
def create_model():
    n_atom_feature = 8
    n_bond_feature = 1
    n_global_feature = 0
    Xavier_init = initializers.VarianceScaling(scale=1.0,
                                               mode='fan_avg',
                                               distribution='normal')

    x1 = Input(shape=(None, n_atom_feature))  # atom feature placeholder
    x2 = Input(shape=(None, n_bond_feature))  # bond feature placeholder
    x3 = Input(shape=(None, n_global_feature))  # global feature placeholder
    x4 = Input(shape=(None, ), dtype='int32')  # bond index1 placeholder
    x5 = Input(shape=(None, ), dtype='int32')  # bond index2 placeholder
    x6 = Input(shape=(None, ), dtype='int32')  # atom_ind placeholder
    x7 = Input(shape=(None, ), dtype='int32')  # bond_ind placeholder
    x8 = Input(shape=(None, ), dtype='int32'
               )  # Ga_index placeholder to gather Ga nodes to a single tensor
    x9 = Input(shape=(None, ), dtype='int32'
               )  # As_index placeholder to gather As nodes to a single tensor
    x10 = Input(
        shape=(None, ), dtype='int32'
    )  # Ga_ind placeholder to sum of the atomic energies of Ga atoms for each single structure
    x11 = Input(
        shape=(None, ), dtype='int32'
    )  # As_ind placeholder to sum of the atomic energies of As atoms for each single structure

    with kb.name_scope("embedding"):
        embed_atom_fea = layers.Dense(16,
                                      name="embedding_atom_fea",
                                      activation='tanh',
                                      kernel_initializer=Xavier_init)
        embed_bond_fea = layers.Dense(16,
                                      name="embedding_bond_fea",
                                      activation='tanh',
                                      kernel_initializer=Xavier_init)
        GN_1 = MEGNetLayer([16, 16], [16, 16], [1],
                           pool_method='mean',
                           activation=softplus2)
        GN_2 = MEGNetLayer([16, 16], [16, 16], [1],
                           pool_method='mean',
                           activation=softplus2)

        x1_ = embed_atom_fea(x1)
        x2_ = embed_bond_fea(x2)

        out1 = GN_1([x1_, x2_, x3, x4, x5, x6, x7])
        x1__ = layers.Add()([x1_, out1[0]])
        x2__ = layers.Add()([x2_, out1[1]])

        out2 = GN_2([x1__, x2__, out1[2], x4, x5, x6, x7])
        x1___ = layers.Add()([x1__, out2[0]])
        x2___ = layers.Add()([x2__, out2[1]])

        Ga_idx = layers.Lambda(lambda x: tf.reshape(x, (-1, )),
                               name="Ga_idx")(x8)
        As_idx = layers.Lambda(lambda x: tf.reshape(x, (-1, )),
                               name="As_idx")(x9)
        Ga = layers.Lambda(lambda x: tf.gather(x, Ga_idx, axis=1),
                           name="Ga")(x1___)
        As = layers.Lambda(lambda x: tf.gather(x, As_idx, axis=1),
                           name="As")(x1___)

        Ga_grp = layers.Lambda(lambda x: tf.reshape(x, (-1, )),
                               name="Ga_grp")(x10)
        As_grp = layers.Lambda(lambda x: tf.reshape(x, (-1, )),
                               name="As_grp")(x11)

        #node = Set2Set(T=3, n_hidden=3)([x1___, x6])
        edge = Set2Set(T=3, n_hidden=3)([x2___, x7])
        edge = layers.Lambda(lambda x: kb.sum(x, axis=2, keepdims=True),
                             name="sum_edge")(edge)
        zero = layers.Lambda(lambda x: tf.zeros_like(x),
                             name="zero_like_edge")(edge)
        zero_edge = layers.Multiply(name="zero_edge")([edge, zero])
        zero_glob = layers.Multiply(name="zero_glob")([out2[2], zero])
        #final = layers.Concatenate(axis=-1)([node, edge, out2[2]])

    with kb.name_scope("Ga"):
        hidden_Ga1 = layers.Dense(10,
                                  name="hidden_Ga1",
                                  activation='tanh',
                                  kernel_initializer=Xavier_init)
        hidden_Ga2 = layers.Dense(10,
                                  name="hidden_Ga2",
                                  activation='tanh',
                                  kernel_initializer=Xavier_init)
        output_Ga = layers.Dense(1,
                                 name="output_Ga",
                                 activation=None,
                                 kernel_initializer=Xavier_init)

        E_Ga = hidden_Ga1(Ga)
        E_Ga = hidden_Ga2(E_Ga)
        E_Ga = output_Ga(E_Ga)

        E_Ga = layers.Lambda(lambda x: tf.reshape(x, (-1, 1)),
                             name="reshape_E_Ga")(E_Ga)
        sum_Ga = layers.Lambda(lambda x: tf.math.segment_sum(x, Ga_grp),
                               name="sum_Ga")(E_Ga)
        #sum_Ga = layers.Lambda(lambda x: tf.reshape(x, glob.shape), name="reshape_sum_Ga")(sum_Ga)
        sum_Ga = layers.Lambda(lambda x: tf.expand_dims(x, axis=0),
                               name="reshape_sum_Ga")(sum_Ga)

    with kb.name_scope("As"):
        hidden_As1 = layers.Dense(10,
                                  name="hidden_As1",
                                  activation='tanh',
                                  kernel_initializer=Xavier_init)
        hidden_As2 = layers.Dense(10,
                                  name="hidden_As2",
                                  activation='tanh',
                                  kernel_initializer=Xavier_init)
        output_As = layers.Dense(1,
                                 name="output_As",
                                 activation=None,
                                 kernel_initializer=Xavier_init)

        E_As = hidden_As1(As)
        E_As = hidden_As2(E_As)
        E_As = output_As(E_As)

        E_As = layers.Lambda(lambda x: tf.reshape(x, (-1, 1)),
                             name="reshape_E_As")(E_As)
        sum_As = layers.Lambda(lambda x: tf.math.segment_sum(x, As_grp),
                               name="sum_As")(E_As)
        #sum_As = layers.Lambda(lambda x: tf.reshape(x, glob.shape), name="reshape_sum_As")(sum_As)
        sum_As = layers.Lambda(lambda x: tf.expand_dims(x, axis=0),
                               name="reshape_sum_As")(sum_As)

    total_E = layers.Add(name="total_E")([sum_Ga, sum_As])
    final_E = layers.Add(name="final_E")([total_E, zero_edge, zero_glob])
    #	total_E = layers.Lambda(lambda x: tf.expand_dims(x, axis=0))(total_E)
    return Model(inputs=[x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11],
                 outputs=final_E)