ahwillia / affinewarp

An implementation of piecewise linear time warping for multi-dimensional time series alignment
MIT License
162 stars 36 forks source link

Export model to load in separate code #23

Open Antorithms opened 6 months ago

Antorithms commented 6 months ago

Hi everyone, FIrst of all, thanks a lot for developing this amazing package. I was wondering whether there is a way to export models after fitting on data to be able to then load from separate code and use it to transform. Thanks for your help!

ahwillia commented 6 months ago

I think it should be possible using pickle?

Antorithms commented 6 months ago

I tried but i Get the error below

AttributeError: Can't pickle local object 'function..result' Will try the solution at this link and update the issue! https://www.pythonpool.com/cant-pickle-local-object/

Antorithms commented 5 months ago

I tried but without success... Most likely the probelm lies in the code below in _optimizers.py Let me know if you have a fix. Best, Anto

def _construct_template_optimizer(loss):
    if loss == 'quadratic':
        # ------------------------------------------------- #
        # --- Template Update Rule Under Quadratic Loss --- #
        # ------------------------------------------------- #
        def f(x_knots, y_knots, template, data, smoothness_reg_scale, l2_reg_scale):
            K = data.shape[0]
            T = data.shape[1]

            # Initialize WtW with regularization term
            WtW = _diff_gramian(T, smoothness_reg_scale * K, l2_reg_scale * K)

            # Compute gramians.
            WtX = np.zeros((T, data.shape[-1]))
            _fast_template_grams(WtW[-2:], WtX, data, x_knots, y_knots)

            # Solve WtW * template = WtX
            return sci.linalg.solveh_banded(WtW, WtX)

    elif loss == 'poisson':
        # ----------------------------------------------- #
        # --- Template Update Rule Under Poisson Loss --- #
        # ----------------------------------------------- #
        def f(x_knots, y_knots, template, data, smoothness_reg_scale, l2_reg_scale):

            # Initialize template. Otherwise, warm-start from last result.
            if template is None:
                template = np.zeros(data.shape[1:])

            # Create objective.
            obj = PoissonObjective(data, smoothness_reg_scale, l2_reg_scale,
                                   x_knots=x_knots, y_knots=y_knots)

            opt = scipy.optimize.minimize(obj, template.ravel(),
                                          jac=True, method='L-BFGS-B',
                                          options=dict(maxiter=20))

            # # Fit using Newton's method
            # opt = scipy.optimize.minimize(obj, template.ravel(),
            #                               jac=True, hessp=obj.hessp,
            #                               method='newton-cg',
            #                               options=dict(maxiter=10))
            # print(opt.message)

            return (opt.x).reshape(template.shape)

    return f
sjvenditto commented 2 months ago

I got the same error when saving a PiecewiseWarping() model, and I believe the error is from pickle being unable to save nested function definitions, specifically coming from _construct_template_optimizer mentioned above. Un-nesting the functions as follows solved it for me:

def _construct_template_optimizer(loss):
    if loss == 'quadratic':
        return _quadratic_f
    elif loss == 'poisson':
        return _poisson_f

    # return f

def _quadratic_f(x_knots, y_knots, template, data, smoothness_reg_scale, l2_reg_scale):
    # ------------------------------------------------- #
    # --- Template Update Rule Under Quadratic Loss --- #
    # ------------------------------------------------- #

    K = data.shape[0]
    T = data.shape[1]

    # Initialize WtW with regularization term
    WtW = _diff_gramian(T, smoothness_reg_scale * K, l2_reg_scale * K)

    # Compute gramians.
    WtX = np.zeros((T, data.shape[-1]))
    _fast_template_grams(WtW[-2:], WtX, data, x_knots, y_knots)

    # Solve WtW * template = WtX
    return sci.linalg.solveh_banded(WtW, WtX)

def _poisson_f(x_knots, y_knots, template, data, smoothness_reg_scale, l2_reg_scale):
    # ----------------------------------------------- #
    # --- Template Update Rule Under Poisson Loss --- #
    # ----------------------------------------------- #

    # Initialize template. Otherwise, warm-start from last result.
    if template is None:
        template = np.zeros(data.shape[1:])

    # Create objective.
    obj = PoissonObjective(data, smoothness_reg_scale, l2_reg_scale,
                            x_knots=x_knots, y_knots=y_knots)

    opt = scipy.optimize.minimize(obj, template.ravel(),
                                    jac=True, method='L-BFGS-B',
                                    options=dict(maxiter=20))

    # # Fit using Newton's method
    # opt = scipy.optimize.minimize(obj, template.ravel(),
    #                               jac=True, hessp=obj.hessp,
    #                               method='newton-cg',
    #                               options=dict(maxiter=10))
    # print(opt.message)

    return (opt.x).reshape(template.shape)