def conv1d_sd(input, filters, image_shape, filter_shape, border_mode='valid', subsample=(1,), filter_flip=True): """ using a single dot product """ if border_mode not in ('valid', 0, (0,)): raise RuntimeError("Unsupported border_mode for conv1d_sd: " "%s" % border_mode) batch_size, num_input_channels, input_length = image_shape num_filters, num_input_channels_, filter_length = filter_shape stride = subsample[0] if filter_length % stride > 0: raise RuntimeError("Filter length (%d) is not a multiple of the " "stride (%d)" % (filter_length, stride)) num_steps = filter_length // stride output_length = (input_length - filter_length + stride) // stride # pad the input so all the shifted dot products fit inside. # shape is (b, c, l) padded_length = ((input_length // filter_length) * filter_length + (num_steps - 1) * stride) # at this point, it is possible that the padded_length is SMALLER than the # input size. so then we have to truncate first. truncated_length = min(input_length, padded_length) input_truncated = input[:, :, :truncated_length] input_padded_shape = (batch_size, num_input_channels, padded_length) input_padded = T.zeros(input_padded_shape) input_padded = T.set_subtensor(input_padded[:, :, :truncated_length], input_truncated) inputs = [] for num in range(num_steps): shift = num * stride length = (padded_length - shift) // filter_length r_input_shape = (batch_size, num_input_channels, length, filter_length) r_input = input_padded[ :, :, shift:length * filter_length + shift].reshape(r_input_shape) inputs.append(r_input) inputs_stacked = T.stack(*inputs) # shape is (n, b, c, w, f) filters_flipped = filters[:, :, ::-1] if filter_flip else filters r_conved = T.tensordot(inputs_stacked, filters_flipped, np.asarray([[2, 4], [1, 2]])) # resulting shape is (n, b, w, n_filters) # output needs to be (b, n_filters, w * n) r_conved = r_conved.dimshuffle(1, 3, 2, 0) # (b, n_filters, w, n) conved = r_conved.reshape((r_conved.shape[0], r_conved.shape[1], r_conved.shape[2] * r_conved.shape[3])) # result is (b, n_f, l) # remove padding return conved[:, :, :output_length]
def unroll_scan(fn, sequences, outputs_info, non_sequences, n_steps, go_backwards=False): """ Helper function to unroll for loops. Can be used to unroll theano.scan. The parameter names are identical to theano.scan, please refer to here for more information. Note that this function does not support the truncate_gradient setting from theano.scan. Parameters ---------- fn : function Function that defines calculations at each step. sequences : TensorVariable or list of TensorVariables List of TensorVariable with sequence data. The function iterates over the first dimension of each TensorVariable. outputs_info : list of TensorVariables List of tensors specifying the initial values for each recurrent value. non_sequences: list of TensorVariables List of theano.shared variables that are used in the step function. n_steps: int Number of steps to unroll. go_backwards: bool If true the recursion starts at sequences[-1] and iterates backwards. Returns ------- List of TensorVariables. Each element in the list gives the recurrent values at each time step. """ if not isinstance(sequences, (list, tuple)): sequences = [sequences] # When backwards reverse the recursion direction counter = range(n_steps) if go_backwards: counter = counter[::-1] output = [] prev_vals = outputs_info for i in counter: step_input = [s[i] for s in sequences] + prev_vals + non_sequences out_ = fn(*step_input) # The returned values from step can be either a TensorVariable, # a list, or a tuple. Below, we force it to always be a list. if isinstance(out_, T.TensorVariable): out_ = [out_] if isinstance(out_, tuple): out_ = list(out_) output.append(out_) prev_vals = output[-1] # iterate over each scan output and convert it to same format as scan: # [[output11, output12,...output1n], # [output21, output22,...output2n],...] output_scan = [] for i in range(len(output[0])): l = map(lambda x: x[i], output) output_scan.append(T.stack(*l)) return output_scan