Example #1
0
def get_interpolation_coeffs(directory, data, times, use_noskip, reduced, interpolation_method='cubic'):
    # Create new folder for storing coefficients as datasets (interpolation is expensive)
    if not os.path.exists(directory):
        os.mkdir(directory)
    # Dataset name
    noskip = 'noskip' if use_noskip else ''
    red = 'red' if reduced else ''
    timing = 'eqspaced' if times is None or interpolation_method == 'rectilinear' else 'irrspaced' # quick naming fix. TODO: if another equally spaced time series timestamps is in times it wouldn't name it properly...
    coeffs_filename = f'{interpolation_method}_coeffs{noskip}{red}_{timing}.hdf5'
    absolute_coeffs_filename_path = os.path.join(directory, coeffs_filename)
    
    # Interpolate and save, or load it for use if it exists
    coefficients = {}
    if not os.path.exists(absolute_coeffs_filename_path):
        if interpolation_method == 'cubic':
            coefficients['train_coeffs'] = torchcde.natural_cubic_spline_coeffs(data['train_data'], t=times)
            coefficients['val_coeffs'] = torchcde.natural_cubic_spline_coeffs(data['val_data'], t=times)
            coefficients['test_coeffs'] = torchcde.natural_cubic_spline_coeffs(data['test_data'], t=times)
        
        elif interpolation_method == 'linear':
            coefficients['train_coeffs'] = torchcde.linear_interpolation_coeffs(data['train_data'], t=times)
            coefficients['val_coeffs'] = torchcde.linear_interpolation_coeffs(data['val_data'], t=times)
            coefficients['test_coeffs'] = torchcde.linear_interpolation_coeffs(data['test_data'], t=times)
        
        elif interpolation_method == 'rectilinear': # rectifilinear doesn't work when passing time argument
            if timing == 'irrspaced': print('Warning: will do default equally spaced time array instead, rectifilinear interpolation currently works with it only.')
            coefficients['train_coeffs'] = torchcde.linear_interpolation_coeffs(data['train_data'], rectilinear=0)
            coefficients['val_coeffs'] = torchcde.linear_interpolation_coeffs(data['val_data'], rectilinear=0)
            coefficients['test_coeffs'] = torchcde.linear_interpolation_coeffs(data['test_data'], rectilinear=0)
    
        # Save coefficients in the new directory
        print('Saving interpolation coefficients ...')
        train_nobs, train_ntimes, train_nfeatures = coefficients['train_coeffs'].shape
        val_nobs, val_ntimes, val_nfeatures = coefficients['val_coeffs'].shape
        test_nobs, test_ntimes, test_nfeatures = coefficients['test_coeffs'].shape

        hdf5_coeffs = h5py.File(absolute_coeffs_filename_path , mode='w')
        hdf5_coeffs.create_dataset('train', (train_nobs, train_ntimes, train_nfeatures), np.float, data=coefficients['train_coeffs'])
        hdf5_coeffs.create_dataset('val', (val_nobs, val_ntimes, val_nfeatures), np.float, data=coefficients['val_coeffs'])
        hdf5_coeffs.create_dataset('test', (test_nobs, test_ntimes, test_nfeatures), np.float, data=coefficients['test_coeffs'])
    
    else:
        print('Loading corresponding interpolation coefficients ...')
        coeffs_dataset = h5py.File(absolute_coeffs_filename_path, mode='r')
        coefficients['train_coeffs'] = torch.Tensor(coeffs_dataset['train'][:])
        coefficients['val_coeffs'] = torch.Tensor(coeffs_dataset['val'][:])
        coefficients['test_coeffs'] = torch.Tensor(coeffs_dataset['test'][:])

    train_coeffs = coefficients['train_coeffs']
    val_coeffs = coefficients['val_coeffs'] 
    test_coeffs = coefficients['test_coeffs'] 
    print(f'Train data interpolation coefficients shape: {train_coeffs.shape}')
    print(f'Validation data interpolation coefficients shape: {val_coeffs.shape}')
    print(f'Test data interpolation coefficients shape: {test_coeffs.shape}')
    return coefficients
Example #2
0
def test_grad_paths():
    for method in ('rk4', 'dopri5'):
        for adjoint in (True, False):
            t = torch.linspace(0, 9, 10, requires_grad=True)
            path = torch.rand(1, 10, 3, requires_grad=True)
            coeffs = torchcde.natural_cubic_spline_coeffs(path, t)
            cubic_spline = torchcde.NaturalCubicSpline(coeffs, t)
            z0 = torch.rand(1, 3, requires_grad=True)
            func = _Func(input_size=3, hidden_size=3)
            t_ = torch.tensor([0., 9.], requires_grad=True)

            z = torchcde.cdeint(X=cubic_spline,
                                func=func,
                                z0=z0,
                                t=t_,
                                adjoint=adjoint,
                                method=method,
                                rtol=1e-4,
                                atol=1e-6)
            assert z.shape == (1, 2, 3)
            assert t.grad is None
            assert path.grad is None
            assert z0.grad is None
            assert func.variable.grad is None
            assert t_.grad is None
            z[:, 1].sum().backward()
            assert isinstance(t.grad, torch.Tensor)
            assert isinstance(path.grad, torch.Tensor)
            assert isinstance(z0.grad, torch.Tensor)
            assert isinstance(func.variable.grad, torch.Tensor)
            assert isinstance(t_.grad, torch.Tensor)
def _solve_cde(x):
    # x should be of shape (batch, length, channels)

    batch_size = x.size(0)
    input_channels = x.size(2)
    hidden_channels = 4  # hyperparameter, we can pick whatever we want for this

    coeffs = torchcde.natural_cubic_spline_coeffs(x)
    X = torchcde.NaturalCubicSpline(coeffs)
    z0 = torch.rand(batch_size, hidden_channels)

    class F(torch.nn.Module):
        def __init__(self):
            super(F, self).__init__()
            self.linear = torch.nn.Linear(hidden_channels,
                                          hidden_channels * input_channels)

        def forward(self, t, z):
            return self.linear(z).view(batch_size, hidden_channels, input_channels)

    func = F()
    zt = torchcde.cdeint(X=X, func=func, z0=z0, t=X.interval)
    zT = zt[:, -1]  # get the terminal value of the CDE

    return zT
Example #4
0
def test_specification_and_derivative():
    for _ in range(10):
        for use_t in (False, True):
            for num_batch_dims in (0, 1, 2, 3):
                batch_dims = []
                for _ in range(num_batch_dims):
                    batch_dims.append(torch.randint(low=1, high=3, size=(1,)).item())
                length = torch.randint(low=5, high=10, size=(1,)).item()
                channels = torch.randint(low=1, high=5, size=(1,)).item()
                if use_t:
                    t = torch.linspace(0, 1, length, dtype=torch.float64)
                else:
                    t = torch.linspace(0, length - 1, length, dtype=torch.float64)
                x = torch.rand(*batch_dims, length, channels, dtype=torch.float64)
                coeffs = torchcde.natural_cubic_spline_coeffs(x, t)
                spline = torchcde.NaturalCubicSpline(coeffs, t)
                # Test specification
                for i, point in enumerate(t):
                    evaluate = spline.evaluate(point)
                    xi = x[..., i, :]
                    assert evaluate.allclose(xi, atol=1e-5, rtol=1e-5)
                # Test derivative
                for point in torch.rand(100, dtype=torch.float64):
                    point_with_grad = point.detach().requires_grad_(True)
                    evaluate = spline.evaluate(point_with_grad)
                    derivative = spline.derivative(point)
                    autoderivative = []
                    for elem in evaluate.view(-1):
                        elem.backward(retain_graph=True)
                        with torch.no_grad():
                            autoderivative.append(point_with_grad.grad.clone())
                        point_with_grad.grad.zero_()
                    autoderivative = torch.stack(autoderivative).view(*evaluate.shape)
                    assert derivative.shape == autoderivative.shape
                    assert derivative.allclose(autoderivative, atol=1e-5, rtol=1e-5)
Example #5
0
def test_short():
    for use_t in (False, True):
        if use_t:
            t = torch.tensor([0., 1.])
        else:
            t = None
        values = torch.rand(2, 1)
        coeffs = torchcde.natural_cubic_spline_coeffs(values, t)
        spline = torchcde.NaturalCubicSpline(coeffs, t)
        coeffs2 = torchcde.linear_interpolation_coeffs(values, t)
        linear = torchcde.LinearInterpolation(coeffs2, t)
        batch_dims = []
        num_channels = 1
        _test_equal(batch_dims, num_channels, linear, spline, torch.float32, -1.5, 1.5, 1e-4)
Example #6
0
def main(num_epochs=30):
    train_X, train_y = get_data()

    ######################
    # input_channels=3 because we have both the horizontal and vertical position of a point in the spiral, and time.
    # hidden_channels=8 is the number of hidden channels for the evolving z_t, which we get to choose.
    # output_channels=1 because we're doing binary classification.
    ######################
    model = NeuralCDE(input_channels=3, hidden_channels=8, output_channels=1)
    optimizer = torch.optim.Adam(model.parameters())

    ######################
    # Now we turn our dataset into a continuous path. We do this here via natural cubic spline interpolation.
    # The resulting `train_coeffs` is a tensor describing the path.
    # For most problems, it's probably easiest to save this tensor and treat it as the dataset.
    ######################
    train_coeffs = torchcde.natural_cubic_spline_coeffs(train_X)

    train_dataset = torch.utils.data.TensorDataset(train_coeffs, train_y)
    train_dataloader = torch.utils.data.DataLoader(train_dataset, batch_size=32)
    for epoch in range(num_epochs):
        for batch in train_dataloader:
            batch_coeffs, batch_y = batch
            pred_y = model(batch_coeffs).squeeze(-1)
            loss = torch.nn.functional.binary_cross_entropy_with_logits(pred_y, batch_y)
            loss.backward()
            optimizer.step()
            optimizer.zero_grad()
        print('Epoch: {}   Training loss: {}'.format(epoch, loss.item()))

    test_X, test_y = get_data()
    test_coeffs = torchcde.natural_cubic_spline_coeffs(test_X)
    pred_y = model(test_coeffs).squeeze(-1)
    binary_prediction = (torch.sigmoid(pred_y) > 0.5).to(test_y.dtype)
    prediction_matches = (binary_prediction == test_y).to(test_y.dtype)
    proportion_correct = prediction_matches.sum() / test_y.size(0)
    print('Test Accuracy: {}'.format(proportion_correct))
Example #7
0
def test_interp():
    for _ in range(3):
        for use_t in (True, False):
            for drop in (False, True):
                num_points = torch.randint(low=5, high=100, size=(1,)).item()
                half_num_points = num_points // 2
                num_points = 2 * half_num_points + 1
                if use_t:
                    times1 = torch.rand(half_num_points, dtype=torch.float64) - 1
                    times2 = torch.rand(half_num_points, dtype=torch.float64)
                    t = torch.cat([times1, times2, torch.tensor([0.], dtype=torch.float64)]).sort().values
                    t_ = t
                    start, end = -1.5, 1.5
                    del times1, times2
                else:
                    t = torch.linspace(0, num_points - 1, num_points, dtype=torch.float64)
                    t_ = None
                    start = 0
                    end = num_points - 0.5
                num_channels = torch.randint(low=1, high=3, size=(1,)).item()
                num_batch_dims = torch.randint(low=0, high=3, size=(1,)).item()
                batch_dims = []
                for _ in range(num_batch_dims):
                    batch_dims.append(torch.randint(low=1, high=3, size=(1,)).item())
                if use_t:
                    cubic = _Cubic(batch_dims, num_channels, start=t[0], end=t[-1])
                    knot = 0
                else:
                    cubic = _Offset(batch_dims, num_channels, start=t[0], end=t[-1], offset=t[1] - t[0])
                    knot = t[1] - t[0]
                values = cubic.evaluate(t)
                if drop:
                    for values_slice in values.unbind(dim=-1):
                        num_drop = int(num_points * torch.randint(low=1, high=4, size=(1,)).item() / 10)
                        num_drop = min(num_drop, num_points - 4)
                        # don't drop first or last
                        to_drop = torch.randperm(num_points - 2)[:num_drop] + 1
                        to_drop = [x for x in to_drop if x != knot]
                        values_slice[..., to_drop] = float('nan')
                        del num_drop, to_drop, values_slice
                coeffs = torchcde.natural_cubic_spline_coeffs(values, t_)
                spline = torchcde.NaturalCubicSpline(coeffs, t_)
                _test_equal(batch_dims, num_channels, cubic, spline, torch.float64, start, end, 1e-3)
Example #8
0
def test_linear():
    for use_t in (False, True):
        start = torch.rand(1).item() * 5 - 2.5
        end = torch.rand(1).item() * 5 - 2.5
        start, end = min(start, end), max(start, end)
        num_points = torch.randint(low=2, high=10, size=(1,)).item()
        num_channels = torch.randint(low=1, high=4, size=(1,)).item()
        m = torch.rand(num_channels) * 5 - 2.5
        c = torch.rand(num_channels) * 5 - 2.5
        if use_t:
            t = torch.linspace(start, end, num_points)
            t_ = t
        else:
            t = torch.linspace(0, num_points - 1, num_points)
            t_ = None
        values = m * t.unsqueeze(-1) + c
        coeffs = torchcde.natural_cubic_spline_coeffs(values, t_)
        spline = torchcde.NaturalCubicSpline(coeffs, t_)
        coeffs2 = torchcde.linear_interpolation_coeffs(values, t_)
        linear = torchcde.LinearInterpolation(coeffs2, t_)
        batch_dims = []
        _test_equal(batch_dims, num_channels, linear, spline, torch.float32, -1.5, 1.5, 1e-4)
Example #9
0
def test_shape():
    for method in ('rk4', 'dopri5'):
        for _ in range(10):
            num_points = torch.randint(low=5, high=100, size=(1,)).item()
            num_channels = torch.randint(low=1, high=3, size=(1,)).item()
            num_hidden_channels = torch.randint(low=1, high=5, size=(1,)).item()
            num_batch_dims = torch.randint(low=0, high=3, size=(1,)).item()
            batch_dims = []
            for _ in range(num_batch_dims):
                batch_dims.append(torch.randint(low=1, high=3, size=(1,)).item())

            t = torch.rand(num_points).sort().values
            values = torch.rand(*batch_dims, num_points, num_channels)

            coeffs = torchcde.natural_cubic_spline_coeffs(values, t)
            spline = torchcde.NaturalCubicSpline(coeffs, t)

            class _Func(torch.nn.Module):
                def __init__(self):
                    super(_Func, self).__init__()
                    self.variable = torch.nn.Parameter(torch.rand(*[1 for _ in range(num_batch_dims)], 1, num_channels))

                def forward(self, t, z):
                    return z.sigmoid().unsqueeze(-1) + self.variable

            f = _Func()
            z0 = torch.rand(*batch_dims, num_hidden_channels)

            num_out_times = torch.randint(low=2, high=10, size=(1,)).item()
            out_times = torch.rand(num_out_times, dtype=torch.float64).sort().values * (t[-1] - t[0]) + t[0]

            options = {}
            if method == 'rk4':
                options['step_size'] = 1. / num_points
            out = torchcde.cdeint(spline, f, z0, out_times, method=method, options=options, rtol=1e-4, atol=1e-6)
            assert out.shape == (*batch_dims, num_out_times, num_hidden_channels)
Example #10
0
 def interp_():
     coeffs = torchcde.natural_cubic_spline_coeffs(path)
     yield torchcde.NaturalCubicSpline(coeffs)
     coeffs = torchcde.linear_interpolation_coeffs(path)
     yield torchcde.LinearInterpolation(coeffs, reparameterise='bump')