Module dimdrop.models.vasc

Code adapted from https://github.com/wang-research/VASC

Source code
"""
Code adapted from https://github.com/wang-research/VASC
"""
# FIXME if possible reimplement annealing for gumbel approximation in more
# idiomatic keras code

# -*- coding: utf-8 -*-
import h5py
from keras import metrics
from keras.optimizers import RMSprop, Adagrad, Adam
import numpy as np
from keras.utils.layer_utils import print_summary
from keras import regularizers
from keras.models import Model
import keras.backend as K
from keras.layers.merge import concatenate, multiply
from keras.layers import (
    Input,
    Dense,
    Activation,
    Lambda,
    RepeatVector,
    Reshape,
    Layer,
    Dropout,
    BatchNormalization,
    Permute
)
from keras.callbacks import EarlyStopping
from ..losses import VAELoss
from ..util import Transform


class VASC:
    """
    VASC: variational autoencoder for scRNA-seq datasets

    Parameters
    ----------
    in_dim : int
        The input dimension
    out_dim : int
        The output dimension
    layer_sizes : array
        sizes of each layer in the neural network, default is the structure
        proposed in the original paper, namely: `[512, 128, 32]`
    epochs: int, optional
        Maximum number of epochs, default `5000`
    patience: int, optional
        The amount of epochs without improvement before the network stops
        training, default `3`
    batch_size: int, optional
        The batch size for stochastic optimization, default `100`
    lr : float, optional
        The learning rate of the network, default `0.01`
    var: boolean, optional
        Whether to estimate the variance parameters, default `False`
    log: boolean, optional
        Whether log-transformation should be performed, default `False`
    scale: boolean, optional
        Whether scaling (making values within [0,1]) should be performed,
        default `True`
    decay : bool, optional
        Whether to decay the learning rate during training, default `True`.
    verbose : int, optional
        Controls the verbosity of the model, default `0`

    Attributes
    ----------
    ae : keras model
        Partial model used for generating the embbeddings
    vae : keras model
        The variational autoencoder

    References
    ----------
    * Dongfang Wang and Jin Gu. Vasc: dimension reduction and visualization of
      single cell rna sequencing data by deep variational autoencoder.
      *bioRxiv*, 2017.
    """

    def __init__(
            self,
            in_dim,
            out_dim,
            layer_sizes=[512, 128, 32],
            epochs=1000,
            patience=3,
            batch_size=100,
            lr=0.01,
            var=False,
            log=False,
            scale=True,
            decay=True,
            verbose=0):
        self.in_dim = in_dim
        self.out_dim = out_dim
        self.layer_sizes = layer_sizes
        self.epochs = epochs
        self.patience = patience
        self.batch_size = batch_size
        self.lr = lr
        self.var = var
        self.data_transform = Transform(scale, log)
        self.verbose = verbose
        self.decay = decay
        self.__init_network()

    def __init_network(self):
        in_dim = self.in_dim
        expr_in = Input(shape=(self.in_dim,))

        # The first part of model to recover the expr.
        h0 = Dropout(0.5)(expr_in)
        # Encoder layers
        last_layer = h0
        for i, layer_size in enumerate(self.layer_sizes):
            if i == 0:
                last_layer = Dense(
                    units=layer_size,
                    name='encoder_{}'.format(i + 1),
                    kernel_regularizer=regularizers.l1(0.01)
                )(last_layer)
            else:
                last_layer = Dense(
                    units=layer_size,
                    name='encoder_{}'.format(i + 1)
                )(last_layer)
            last_layer = Activation('relu')(last_layer)

        z_mean = Dense(units=self.out_dim, name='z_mean')(last_layer)
        z_log_var = None
        if self.var:
            z_log_var = Dense(units=2, name='z_log_var')(last_layer)
            z_log_var = Activation('softplus')(z_log_var)

        # sampling new samples
            z = Lambda(sampling, output_shape=(
                self.out_dim,))([z_mean, z_log_var])
        else:
            z = Lambda(sampling, output_shape=(self.out_dim,))([z_mean])

        # Decoder layers
        last_layer = z
        for i, layer_size in enumerate(self.layer_sizes[::-1]):
            last_layer = Dense(
                units=layer_size, name='decoder_{}'.format(i + 1))(last_layer)
            last_layer = Activation('relu')(last_layer)

        expr_x = Dense(units=self.in_dim, activation='sigmoid')(
            last_layer)

        expr_x_drop = Lambda(lambda x: -x ** 2)(expr_x)
        expr_x_drop_p = Lambda(lambda x: K.exp(x))(expr_x_drop)
        expr_x_nondrop_p = Lambda(lambda x: 1-x)(expr_x_drop_p)
        expr_x_nondrop_log = Lambda(lambda x: K.log(x+1e-20))(expr_x_nondrop_p)
        expr_x_drop_log = Lambda(lambda x: K.log(x+1e-20))(expr_x_drop_p)
        expr_x_drop_log = Reshape(target_shape=(
            self.in_dim, 1))(expr_x_drop_log)
        expr_x_nondrop_log = Reshape(target_shape=(
            self.in_dim, 1))(expr_x_nondrop_log)
        logits = concatenate(
            [expr_x_drop_log, expr_x_nondrop_log], axis=-1)

        temp_in = Input(shape=(self.in_dim,))
        temp_ = RepeatVector(2)(temp_in)

        temp_ = Permute((2, 1))(temp_)
        samples = Lambda(gumbel_softmax, output_shape=(
            self.in_dim, 2,))([logits, temp_])
        samples = Lambda(lambda x: x[:, :, 1])(samples)
        samples = Reshape(target_shape=(self.in_dim,))(samples)

        out = multiply([expr_x, samples])

        vae = Model(inputs=[expr_in, temp_in], outputs=out)

        loss_func = VAELoss(in_dim, z_log_var, z_mean)

        opt = RMSprop(lr=self.lr, decay=self.lr /
                      self.epochs if self.decay else 0.0)
        vae.compile(optimizer=opt, loss=loss_func)

        ae = Model(inputs=[expr_in, temp_in], outputs=[
                   z_mean, z, samples, out])

        self.vae = vae
        self.ae = ae

        if self.verbose:
            self.vae.summary()

    def fit(self, data):
        """
        Fit the given data to the model.

        Parameters
        ----------
        data : array
            Array of training samples where each sample is of size `in_dim`
        """
        data = self.data_transform(data)

        tau_in = np.ones(data.shape, dtype='float32')

        early_stopping = EarlyStopping(monitor='loss', patience=self.patience)

        self.vae.fit(
            [data, tau_in],
            data,
            epochs=self.epochs,
            batch_size=self.batch_size,
            shuffle=True,
            verbose=self.verbose,
            callbacks=[early_stopping]
        )

    def fit_transform(self, data):
        """
        Fit the given data to the model and return its transformation

        Parameters
        ----------
        data : array
            Array of training samples where each sample is of size `in_dim`

        Returns
        -------
        array
            Transformed samples, where each sample is of size `out_dim`
        """
        self.fit(data)
        return self.transform(data)

    def transform(self, data):
        """
        Transform the given data

        Parameters
        ----------
        data : array
            Array of samples to be transformed, where each sample is of size
            `in_dim`

        Returns
        -------
        array
            Transformed samples, where each sample is of size `out_dim`
        """
        data = self.data_transform(data)
        return self.ae.predict([data, np.ones(data.shape, dtype='float32')])[0]


def sampling(args):
    epsilon_std = 1.0

    if len(args) == 2:
        z_mean, z_log_var = args
        epsilon = K.random_normal(shape=K.shape(z_mean),
                                  mean=0.,
                                  stddev=epsilon_std)

        return z_mean + K.exp(z_log_var / 2) * epsilon
    else:
        z_mean = args[0]
        epsilon = K.random_normal(shape=K.shape(z_mean),
                                  mean=0.,
                                  stddev=epsilon_std)
        return z_mean + K.exp(1.0 / 2) * epsilon


def sampling_gumbel(shape, eps=1e-8):
    u = K.random_uniform(shape)
    return -K.log(-K.log(u+eps)+eps)


def compute_softmax(logits, temp):
    z = logits + sampling_gumbel(K.shape(logits))
    return K.softmax(z / temp)


def gumbel_softmax(args):
    logits, temp = args
    return compute_softmax(logits, temp)

Functions

def compute_softmax(logits, temp)
Source code
def compute_softmax(logits, temp):
    z = logits + sampling_gumbel(K.shape(logits))
    return K.softmax(z / temp)
def gumbel_softmax(args)
Source code
def gumbel_softmax(args):
    logits, temp = args
    return compute_softmax(logits, temp)
def sampling(args)
Source code
def sampling(args):
    epsilon_std = 1.0

    if len(args) == 2:
        z_mean, z_log_var = args
        epsilon = K.random_normal(shape=K.shape(z_mean),
                                  mean=0.,
                                  stddev=epsilon_std)

        return z_mean + K.exp(z_log_var / 2) * epsilon
    else:
        z_mean = args[0]
        epsilon = K.random_normal(shape=K.shape(z_mean),
                                  mean=0.,
                                  stddev=epsilon_std)
        return z_mean + K.exp(1.0 / 2) * epsilon
def sampling_gumbel(shape, eps=1e-08)
Source code
def sampling_gumbel(shape, eps=1e-8):
    u = K.random_uniform(shape)
    return -K.log(-K.log(u+eps)+eps)

Classes

class VASC (in_dim, out_dim, layer_sizes=[512, 128, 32], epochs=1000, patience=3, batch_size=100, lr=0.01, var=False, log=False, scale=True, decay=True, verbose=0)

VASC: variational autoencoder for scRNA-seq datasets

Parameters

in_dim : int
The input dimension
out_dim : int
The output dimension
layer_sizes : array
sizes of each layer in the neural network, default is the structure proposed in the original paper, namely: [512, 128, 32]
epochs : int, optional
Maximum number of epochs, default 5000
patience : int, optional
The amount of epochs without improvement before the network stops training, default 3
batch_size : int, optional
The batch size for stochastic optimization, default 100
lr : float, optional
The learning rate of the network, default 0.01
var : boolean, optional
Whether to estimate the variance parameters, default False
log : boolean, optional
Whether log-transformation should be performed, default False
scale : boolean, optional
Whether scaling (making values within [0,1]) should be performed, default True
decay : bool, optional
Whether to decay the learning rate during training, default True.
verbose : int, optional
Controls the verbosity of the model, default 0

Attributes

ae : keras model
Partial model used for generating the embbeddings
vae : keras model
The variational autoencoder

References

  • Dongfang Wang and Jin Gu. Vasc: dimension reduction and visualization of single cell rna sequencing data by deep variational autoencoder. bioRxiv, 2017.
Source code
class VASC:
    """
    VASC: variational autoencoder for scRNA-seq datasets

    Parameters
    ----------
    in_dim : int
        The input dimension
    out_dim : int
        The output dimension
    layer_sizes : array
        sizes of each layer in the neural network, default is the structure
        proposed in the original paper, namely: `[512, 128, 32]`
    epochs: int, optional
        Maximum number of epochs, default `5000`
    patience: int, optional
        The amount of epochs without improvement before the network stops
        training, default `3`
    batch_size: int, optional
        The batch size for stochastic optimization, default `100`
    lr : float, optional
        The learning rate of the network, default `0.01`
    var: boolean, optional
        Whether to estimate the variance parameters, default `False`
    log: boolean, optional
        Whether log-transformation should be performed, default `False`
    scale: boolean, optional
        Whether scaling (making values within [0,1]) should be performed,
        default `True`
    decay : bool, optional
        Whether to decay the learning rate during training, default `True`.
    verbose : int, optional
        Controls the verbosity of the model, default `0`

    Attributes
    ----------
    ae : keras model
        Partial model used for generating the embbeddings
    vae : keras model
        The variational autoencoder

    References
    ----------
    * Dongfang Wang and Jin Gu. Vasc: dimension reduction and visualization of
      single cell rna sequencing data by deep variational autoencoder.
      *bioRxiv*, 2017.
    """

    def __init__(
            self,
            in_dim,
            out_dim,
            layer_sizes=[512, 128, 32],
            epochs=1000,
            patience=3,
            batch_size=100,
            lr=0.01,
            var=False,
            log=False,
            scale=True,
            decay=True,
            verbose=0):
        self.in_dim = in_dim
        self.out_dim = out_dim
        self.layer_sizes = layer_sizes
        self.epochs = epochs
        self.patience = patience
        self.batch_size = batch_size
        self.lr = lr
        self.var = var
        self.data_transform = Transform(scale, log)
        self.verbose = verbose
        self.decay = decay
        self.__init_network()

    def __init_network(self):
        in_dim = self.in_dim
        expr_in = Input(shape=(self.in_dim,))

        # The first part of model to recover the expr.
        h0 = Dropout(0.5)(expr_in)
        # Encoder layers
        last_layer = h0
        for i, layer_size in enumerate(self.layer_sizes):
            if i == 0:
                last_layer = Dense(
                    units=layer_size,
                    name='encoder_{}'.format(i + 1),
                    kernel_regularizer=regularizers.l1(0.01)
                )(last_layer)
            else:
                last_layer = Dense(
                    units=layer_size,
                    name='encoder_{}'.format(i + 1)
                )(last_layer)
            last_layer = Activation('relu')(last_layer)

        z_mean = Dense(units=self.out_dim, name='z_mean')(last_layer)
        z_log_var = None
        if self.var:
            z_log_var = Dense(units=2, name='z_log_var')(last_layer)
            z_log_var = Activation('softplus')(z_log_var)

        # sampling new samples
            z = Lambda(sampling, output_shape=(
                self.out_dim,))([z_mean, z_log_var])
        else:
            z = Lambda(sampling, output_shape=(self.out_dim,))([z_mean])

        # Decoder layers
        last_layer = z
        for i, layer_size in enumerate(self.layer_sizes[::-1]):
            last_layer = Dense(
                units=layer_size, name='decoder_{}'.format(i + 1))(last_layer)
            last_layer = Activation('relu')(last_layer)

        expr_x = Dense(units=self.in_dim, activation='sigmoid')(
            last_layer)

        expr_x_drop = Lambda(lambda x: -x ** 2)(expr_x)
        expr_x_drop_p = Lambda(lambda x: K.exp(x))(expr_x_drop)
        expr_x_nondrop_p = Lambda(lambda x: 1-x)(expr_x_drop_p)
        expr_x_nondrop_log = Lambda(lambda x: K.log(x+1e-20))(expr_x_nondrop_p)
        expr_x_drop_log = Lambda(lambda x: K.log(x+1e-20))(expr_x_drop_p)
        expr_x_drop_log = Reshape(target_shape=(
            self.in_dim, 1))(expr_x_drop_log)
        expr_x_nondrop_log = Reshape(target_shape=(
            self.in_dim, 1))(expr_x_nondrop_log)
        logits = concatenate(
            [expr_x_drop_log, expr_x_nondrop_log], axis=-1)

        temp_in = Input(shape=(self.in_dim,))
        temp_ = RepeatVector(2)(temp_in)

        temp_ = Permute((2, 1))(temp_)
        samples = Lambda(gumbel_softmax, output_shape=(
            self.in_dim, 2,))([logits, temp_])
        samples = Lambda(lambda x: x[:, :, 1])(samples)
        samples = Reshape(target_shape=(self.in_dim,))(samples)

        out = multiply([expr_x, samples])

        vae = Model(inputs=[expr_in, temp_in], outputs=out)

        loss_func = VAELoss(in_dim, z_log_var, z_mean)

        opt = RMSprop(lr=self.lr, decay=self.lr /
                      self.epochs if self.decay else 0.0)
        vae.compile(optimizer=opt, loss=loss_func)

        ae = Model(inputs=[expr_in, temp_in], outputs=[
                   z_mean, z, samples, out])

        self.vae = vae
        self.ae = ae

        if self.verbose:
            self.vae.summary()

    def fit(self, data):
        """
        Fit the given data to the model.

        Parameters
        ----------
        data : array
            Array of training samples where each sample is of size `in_dim`
        """
        data = self.data_transform(data)

        tau_in = np.ones(data.shape, dtype='float32')

        early_stopping = EarlyStopping(monitor='loss', patience=self.patience)

        self.vae.fit(
            [data, tau_in],
            data,
            epochs=self.epochs,
            batch_size=self.batch_size,
            shuffle=True,
            verbose=self.verbose,
            callbacks=[early_stopping]
        )

    def fit_transform(self, data):
        """
        Fit the given data to the model and return its transformation

        Parameters
        ----------
        data : array
            Array of training samples where each sample is of size `in_dim`

        Returns
        -------
        array
            Transformed samples, where each sample is of size `out_dim`
        """
        self.fit(data)
        return self.transform(data)

    def transform(self, data):
        """
        Transform the given data

        Parameters
        ----------
        data : array
            Array of samples to be transformed, where each sample is of size
            `in_dim`

        Returns
        -------
        array
            Transformed samples, where each sample is of size `out_dim`
        """
        data = self.data_transform(data)
        return self.ae.predict([data, np.ones(data.shape, dtype='float32')])[0]

Methods

def fit(self, data)

Fit the given data to the model.

Parameters

data : array
Array of training samples where each sample is of size in_dim
Source code
def fit(self, data):
    """
    Fit the given data to the model.

    Parameters
    ----------
    data : array
        Array of training samples where each sample is of size `in_dim`
    """
    data = self.data_transform(data)

    tau_in = np.ones(data.shape, dtype='float32')

    early_stopping = EarlyStopping(monitor='loss', patience=self.patience)

    self.vae.fit(
        [data, tau_in],
        data,
        epochs=self.epochs,
        batch_size=self.batch_size,
        shuffle=True,
        verbose=self.verbose,
        callbacks=[early_stopping]
    )
def fit_transform(self, data)

Fit the given data to the model and return its transformation

Parameters

data : array
Array of training samples where each sample is of size in_dim

Returns

array
Transformed samples, where each sample is of size out_dim
Source code
def fit_transform(self, data):
    """
    Fit the given data to the model and return its transformation

    Parameters
    ----------
    data : array
        Array of training samples where each sample is of size `in_dim`

    Returns
    -------
    array
        Transformed samples, where each sample is of size `out_dim`
    """
    self.fit(data)
    return self.transform(data)
def transform(self, data)

Transform the given data

Parameters

data : array
Array of samples to be transformed, where each sample is of size in_dim

Returns

array
Transformed samples, where each sample is of size out_dim
Source code
def transform(self, data):
    """
    Transform the given data

    Parameters
    ----------
    data : array
        Array of samples to be transformed, where each sample is of size
        `in_dim`

    Returns
    -------
    array
        Transformed samples, where each sample is of size `out_dim`
    """
    data = self.data_transform(data)
    return self.ae.predict([data, np.ones(data.shape, dtype='float32')])[0]