def __init__(self, in_channels, hidden_channels1, hidden_channels2, kernel_size, include_original, include_time, sig_depth, out_channels, batch_norm=False): """ Inputs: in_channels: As SignatureModel. hidden_channels1: How large to make certain hidden channels within the model. hidden_channels2: How large to make certain hidden channels within the model. kernel_size: How far to look back in time. include_original: As SignatureModel. include_time: As SignatureModel. sig_depth: As SignatureModel. out_channels: As SignatureModel. """ super().__init__() self.kernel_size = kernel_size self.batch_norm = batch_norm self.padding1 = torch.nn.ConstantPad1d((kernel_size - 1, 0), 0) self.augment1 = signatory.Augment(in_channels=in_channels, layer_sizes=(hidden_channels1, hidden_channels1, hidden_channels2), kernel_size=kernel_size, include_original=include_original, include_time=include_time) self.signature1 = signatory.Signature(depth=sig_depth, stream=True) sig_hidden_channels = hidden_channels2 if include_original: sig_hidden_channels += in_channels if include_time: sig_hidden_channels += 1 sig_channels1 = signatory.signature_channels( channels=sig_hidden_channels, depth=sig_depth) self.padding2 = torch.nn.ConstantPad1d((kernel_size - 1, 0), 0) self.augment2 = signatory.Augment(in_channels=sig_channels1, layer_sizes=(hidden_channels1, hidden_channels1, hidden_channels2), kernel_size=kernel_size, include_original=False, include_time=False) self.signature2 = signatory.Signature(depth=sig_depth, stream=False) sig_channels2 = signatory.signature_channels(channels=hidden_channels2, depth=sig_depth) self.linear = torch.nn.Linear(sig_channels2, out_channels) if self.batch_norm: self.bn1 = nn.BatchNorm1d(num_features=sig_channels1) self.bn2 = nn.BatchNorm1d(num_features=sig_channels2)
def setup(obj): torch.set_num_threads(1) obj.path = torch.rand(obj.size, dtype=torch.float, requires_grad=True) shape = obj.size[-3], signatory.signature_channels(obj.size[-1], obj.depth) obj.grad = torch.rand(shape) obj.signature = signatory.signature(obj.path, obj.depth)
def _rescale_signature(signature, channels, depth, length, by_length=False): sigtensor_channels = signature.size(-1) if signatory.signature_channels(channels, depth) != sigtensor_channels: raise ValueError( "Given a sigtensor with {} channels, a path with {} channels and a depth of {}, which are " "not consistent.".format(sigtensor_channels, channels, depth)) if by_length: length_reciprocal = length.reciprocal() for i in range(len(signature.shape) - len(length.shape)): length_reciprocal.unsqueeze_(-1) end = 0 term_length = 1 val = 1 terms = [] for d in range(1, depth + 1): start = end term_length *= channels end = start + term_length val *= d if by_length: val *= length_reciprocal terms.append(signature[..., start:end] * val) return torch.cat(terms, dim=-1)
def __init__(self, in_channels, out_dimension, sig_depth): super(SigNet, self).__init__() self.augment = signatory.Augment(in_channels=in_channels, layer_sizes=(), kernel_size=1, include_original=True, include_time=True) self.signature = signatory.Signature(depth=sig_depth) # +1 because signatory.Augment is used to add time as well sig_channels = signatory.signature_channels(channels=in_channels + 1, depth=sig_depth) self.linear = torch.nn.Linear(sig_channels, out_dimension)
def X_limit(N, depth, time): """Returns limiting rough path in p-var top for the pure area path problem (with signatory)""" X_inf = torch.zeros((1, N, signatory.signature_channels(2, depth))) #.cuda() for k in range(N): X_inf[0, k, 0] = 0. X_inf[0, k, 1] = 0. X_inf[0, k, 2] = 0. X_inf[0, k, 3] = time[k] X_inf[0, k, 4] = -time[k] X_inf[0, k, 5] = 0. return signatory.Path(X_inf, 1, basepoint=True)
def __init__(self, in_channels, out_dimension, sig_depth): super(SigNet3, self).__init__() self.augment1 = signatory.Augment(in_channels=in_channels, layer_sizes=(8, 8, 4), kernel_size=4, include_original=True, include_time=True) self.signature1 = signatory.Signature(depth=sig_depth, stream=True) # +5 because self.augment1 is used to add time, and 4 other # channels, as well sig_channels1 = signatory.signature_channels(channels=in_channels + 5, depth=sig_depth) self.augment2 = signatory.Augment(in_channels=sig_channels1, layer_sizes=(8, 8, 4), kernel_size=4, include_original=False, include_time=False) self.signature2 = signatory.Signature(depth=sig_depth, stream=False) # 4 because that's the final layer size in self.augment2 sig_channels2 = signatory.signature_channels(channels=4, depth=sig_depth) self.linear = torch.nn.Linear(sig_channels2, out_dimension)
def __init__(self, input_dim, depth, logsig, basepoint=True): super().__init__() self.basepoint = basepoint self.depth = depth self.logsig = logsig self.sig_func = signatory.logsignature if self.logsig else signatory.signature self.terms = signatory.logsignature_channels( input_dim, self.depth) if self.logsig else signatory.signature_channels( input_dim, self.depth) if self.terms >= TERMS_LIMIT: raise ImportError( "Number of signature terms {} is greater than {}..".format( self.terms, TERMS_LIMIT))
def __init__(self, input_dim, latent_dim, gru_n_layers = 2, augment_chs = 8): super().__init__() self.latent_dim = latent_dim self.augment1 = signatory.Augment(in_channels=input_dim, layer_sizes=(32, augment_chs), kernel_size=1, include_original=True, include_time=True) self.signature1 = signatory.Signature(depth=2, stream=True) sig_channels1 = signatory.signature_channels(channels=input_dim + augment_chs + 1, depth=2) self.gru = nn.GRU(input_size = sig_channels1, hidden_size = latent_dim, num_layers = gru_n_layers, batch_first = True)
def __init__(self, d: int, mu: float, depth: int, rnn_hidden: int, ffn_hidden: List[int]): super().__init__() self.d = d self.mu = mu # risk free rate self.depth = depth self.augmentations = (LeadLag(with_time=False), ) self.sig_channels = signatory.signature_channels( channels=2 * d, depth=depth) # x2 because we do lead-lag self.f = RNN(rnn_in=self.sig_channels + 1, rnn_hidden=rnn_hidden, ffn_sizes=ffn_hidden + [1]) # +1 is for time self.dfdx = RNN(rnn_in=self.sig_channels + 1, rnn_hidden=rnn_hidden, ffn_sizes=ffn_hidden + [d]) # +1 is for time
def invert_signature(signature, depth, channels, initial_position=None): """Reconstruct the path from its signature Arguments: signature: torch.Tensor, shape (batch, channels + channels^2 + ... + channels^depth) Batch of signatures truncated at order depth: output of signatory.signature(path, depth), where path is a tensor of shape (batch, length, channels) depth: int Depth of the signature. channels: int The number of channels of the paths that were used to compute signature. initial_position: optional, torch.Tensor, shape (batch, channels) Initial point of the paths. If None, the reconstructed paths are set to begin at zero. Returns: path: torch.Tensor, shape (batch, depth+1, channels) Batch of inverted paths. """ if signatory.signature_channels(channels, depth) != signature.shape[1]: raise ValueError( "channels and depth do not correspond to signature shape.") batch = signature.shape[0] path_derivatives = torch.zeros((batch, depth, channels)) path = torch.zeros((batch, depth + 1, channels)) if initial_position is not None: path[:, 0, :] = initial_position if depth == 1: path[:, 1, :] = path[:, 0, :] + signature else: for insertion_position in torch.arange(1, depth + 1): x_optimal = solve_optimization_problem(signature, insertion_position, depth, channels) path_derivatives[:, insertion_position - 1, :] = x_optimal path[:, insertion_position, :] = ( path[:, insertion_position - 1, :] + path_derivatives[:, insertion_position - 1, :] * (1 / depth)) return path
def transform(self, sequence) -> torch.Tensor: embedded = sequence['embedded'] sig_func = signatory.logsignature if self.logsig else signatory.signature terms = signatory.logsignature_channels( embedded.shape[-1], self.depth) if self.logsig else signatory.signature_channels( embedded.shape[-1], self.depth) if terms >= TERMS_LIMIT: raise ImportError( "Number of signature terms is greater than {}..".format( TERMS_LIMIT)) signature_terms = sig_func(path=embedded, depth=self.depth, basepoint=self.basepoint) return signature_terms
def __init__(self, latent_dim, output_dim, decoder_hidden_units = 64): super().__init__() self.latent_dim = latent_dim self.decoder_hidden_units = decoder_hidden_units self.augment2 = signatory.Augment(in_channels=latent_dim, layer_sizes=(64, 32), kernel_size=1, include_original=False, include_time=False) self.signature2 = signatory.Signature(depth=2, stream=True) sig_channels2 = signatory.signature_channels(channels= 32, depth=2) self.linear1 = nn.Linear(in_features = sig_channels2, out_features = decoder_hidden_units) self.linear2 = nn.Linear(in_features = decoder_hidden_units, out_features = output_dim)
def __init__(self, in_channels, extra_channels, channel_groups, include_original, include_time, sig_depth, out_channels, final_network): """ Inputs: in_channels: An integer specifying the number of input channels in the data. extra_channels: How many channels to learn before the signature. Can be set to 0 to not learn any extra channels. channel_groups: We compute the signature on channel_groups many paths. Note that this is only worth setting to something other than 1 if extra_channels is nonzero. include_original: Whether to pass the original data to the signature. If True then the original data and the extra learnt channels will be concatenated. (So you probably want True if the number of channels in the original data is small). If False then the original data will not be included (so you probably want False if the number of channels in the original data is large - then you get just the learnt channels.) include_time: Whether to include a time parameter in the augmentation. You probably want to set this to True. sig_depth: What depth of signature to calculate. Careful - you'll get exponentially many more parameters as this number is increased. Reducing the value of extra_channels or toggling off include_original will also help reduce the number of parameters. out_channels: How many channels to output. final_network: What kind of network to use on the result of the signature. Should be a tuple or scalars, representing the sizes of hidden layers in a small feedforward neural network. ReLU nonlinearities will be placed in between. For example, an empty tuple represents no hidden layers; i.e. just a linear map. Examples: extra_channels=0, include_original=True, channel_groups=1: This corresponds to shallow signature models, without any learnt transformation before the signature. extra_channels=10, incldue_original=False: This corresponds to the simplest possible deep signature model, just learning a simple transformation before the signature. """ super().__init__() self.channel_groups = channel_groups self.sig_depth = sig_depth layer_sizes = () if extra_channels == 0 else (extra_channels, ) self.augments = nn.ModuleList( signatory.Augment( in_channels=in_channels, layer_sizes=layer_sizes, # IMPORTANT. We rely on kernel_size=1 to make trick for handling # variable-length inputs work. kernel_size=1, include_original=include_original, include_time=include_time) for _ in range(channel_groups)) in_sig_channels = extra_channels if include_original: in_sig_channels += in_channels if include_time: in_sig_channels += 1 sig_channels = signatory.signature_channels(in_sig_channels, sig_depth) sig_channels *= channel_groups layers = [] prev_size = sig_channels for size in final_network: layers.append(nn.Linear(prev_size, size)) layers.append(nn.ReLU()) prev_size = size layers.append(nn.Linear(prev_size, out_channels)) self.neural = nn.Sequential(*layers)
def __init__(self, in_channels, extra_channels, channel_groups, include_original, include_time, sig_depth, step, length, rnn_channels, out_channels, rnn_type): """ Inputs: in_channels: As SignatureModel. extra_channels: As SignatureModel. channel_groups: As SignatureModel. include_original: As SignatureModel. include_time: As SignatureModel. sig_depth: As SignatureModel. step: The number of indices to move the sliding window forward by. length: The length of the sliding window that a signature is taken over. rnn_channels: The size of the hidden state of the GRU. out_channels: As SignatureModel. rnn_type: Either 'gru' or 'lstm'. Note: Unless step, length, and the length of the input stream all suitably line up, then the final pieces of data in the input stream may not end up being used, because the next sliding window would go 'off the end' of the data. This can be avoided by setting the parameters appropriately, e.g. by taking step=1. """ super().__init__() self.channel_groups = channel_groups self.sig_depth = sig_depth self.step = step self.length = length self.rnn_channels = rnn_channels self.out_channels = out_channels self.rnn_type = rnn_type layer_sizes = () if extra_channels == 0 else (extra_channels, ) self.augments = nn.ModuleList( signatory.Augment( in_channels=in_channels, layer_sizes=layer_sizes, # IMPORTANT. We rely on kernel_size=1 to make trick for handling # variable-length inputs work. kernel_size=1, include_original=include_original, include_time=include_time) for _ in range(channel_groups)) in_sig_channels = extra_channels if include_original: in_sig_channels += in_channels if include_time: in_sig_channels += 1 sig_channels = signatory.signature_channels(in_sig_channels, sig_depth) sig_channels *= channel_groups if rnn_type == 'gru': self.rnn_cell = nn.GRUCell(sig_channels, rnn_channels) elif rnn_type == 'lstm': self.rnn_cell = nn.LSTMCell(sig_channels, rnn_channels) else: raise ValueError( 'rnn_type of value "{}" not understood'.format(rnn_type)) self.linear = nn.Linear(rnn_channels, out_channels)