Commit 5d4a6f9
authored
The analytic Hessian was -2*grad/h; also fix cosine NW gradient and scale-dependent bandwidths (#10)
* Fix analytic Hessians, cosine NW weight derivative, and scale-dependent tolerance
Three defects, each verified against central finite differences with a
step-size convergence study before and after.
1. None of lscv, lscv_mv, loocv_mse or loocv_mse_mv returned a Hessian.
In 1-D, d2/dh2 [h^-1 f(delta/h)] is h^-3 (2f + 4u f' + u^2 f''). The code
used h^-2 (2f' + u f'') with a minus sign. Both summands of that term are
odd in u, so it vanished exactly over the symmetric pair matrix, leaving
hess == -2 * grad / h identically -- not a second derivative at all. For the
Gaussian kernel the returned value was 5.879e-02 where the true second
derivative is 2.563e-01.
The multivariate versions had the analogous error: the bracket needs
d(d+1) + 2(d+1)S + S^2 + sum_k U_k^2 (f''/f - (f'/f)^2), and the whole term
belongs over h^(d+2), not h^(d+1).
Same for the Nadaraya-Watson weight second derivative, w'' = (2K + 4uK' +
u^2 K'')/h^3. It was wrong for gauss, biweight, triweight and cosine; epan
was already right, and the Numba epan and unif kernels already used the
correct general form.
Finite differences now converge quadratically to the analytic value in every
case: gauss lscv relative error 3.9e-04 -> 3.9e-06 -> 4.2e-08 as eps goes
1e-2 -> 1e-3 -> 1e-4.
2. The cosine NW weight first derivative was wrong. It read
(pi/4)[(pi^2 u^2/4 - 1)cos - (pi u/2)sin]/h^2; the derivative of K(u)/h is
(pi/4)[-cos + (pi u/2)sin]/h^2 -- a spurious quadratic term and the wrong
sign on the sine. The LOOCV-MSE gradient was 1.833e-03 against a
finite-difference value of -2.135e-03: wrong sign, so nw_bandwidth with
kernel="cosine" walked the wrong way and returned Silverman's rule.
3. Bandwidths depended on the units of the data. The LSCV criterion is exactly
scale-equivariant (c*LSCV(c*x, c*h) == LSCV(x, h) to 1.8e-16), so its
minimiser must satisfy h(c*x) == c*h(x). Its gradient, however, scales like
1/c^2, and convergence was tested against an absolute tol=1e-5. For c >= 100
every kernel except unif reported convergence at the starting guess and
returned Silverman's rule untouched -- 172% off for gauss. Tested on the
dimensionless ratio |g|*h/|f| instead, which is invariant for both criteria.
Equivariance now holds to 6e-12 or better across all kernels.
Two consequences of the Hessian fix had to be handled:
- With honest curvature the Newton step is dominated by the kinks that the C0
kernels put in the criterion, so the iterate crawled and the iteration cap
bound long before the minimum: kde_bandwidth_mv with kernel="epan" fell to
0.47 against a true minimiser of 0.89. The line search now forward-tracks
(doubles while the score keeps falling) as well as backtracking. Near a
genuine quadratic minimum the first doubling is rejected, so local
convergence is unaffected.
- The Numba LOOCV scorers skipped any point whose weights had all underflowed,
so a bandwidth small enough to underflow every weight scored exactly 0 -- a
fake global minimum that the new line search could reach. They now agree with
the NumPy reference (m = 0 there).
Tests: each new test was confirmed to fail on the unfixed source.
Suite: 77 passed before, 89 passed after.
* Let a criterion that passes through zero converge
_grad_converged scaled the gradient test by abs(f) and returned False
outright when that scale was zero. An iterate sitting at f == 0 with a
negligible gradient could therefore never converge, and burned the whole
iteration budget with nothing left to gain. It now falls back to an
absolute test at that single point.
Also records, in _line_search's own docstring, that the accepted point
is the last one that improved and so can lie past the nearest minimum --
the reason 1-D cosine settles on a local minimum 0.03% worse in
objective than a dense grid.
Refining that bracket was tried and rejected. Golden-sectioning it does
find the minimum on a quadratic (1.2 against a true 1.0 becomes 1.004),
but every variant needed an absolute floor or tolerance somewhere -- in
the termination test, then in the h_floor clamp -- and each one broke
h(c*x) == c*h(x) for unif at c=100 and cosine at c=1000. Reintroducing
the scale dependence this branch exists to remove, in order to improve a
line search that already returns a valid decrease, is a bad trade. The
docstring now says what the invariant is for anyone who revisits it.
Both raised by an independent review of the branch. Suite 89 -> 93.
* Make optimizer scale invariant in both directions1 parent fdab461 commit 5d4a6f9
7 files changed
Lines changed: 660 additions & 246 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
208 | 208 | | |
209 | 209 | | |
210 | 210 | | |
211 | | - | |
| 211 | + | |
212 | 212 | | |
213 | 213 | | |
214 | 214 | | |
| |||
235 | 235 | | |
236 | 236 | | |
237 | 237 | | |
238 | | - | |
| 238 | + | |
239 | 239 | | |
240 | 240 | | |
241 | 241 | | |
242 | 242 | | |
243 | | - | |
| 243 | + | |
244 | 244 | | |
245 | 245 | | |
246 | 246 | | |
| |||
256 | 256 | | |
257 | 257 | | |
258 | 258 | | |
259 | | - | |
260 | | - | |
261 | | - | |
262 | | - | |
263 | | - | |
264 | | - | |
| 259 | + | |
265 | 260 | | |
266 | 261 | | |
267 | 262 | | |
| |||
314 | 309 | | |
315 | 310 | | |
316 | 311 | | |
317 | | - | |
| 312 | + | |
318 | 313 | | |
319 | 314 | | |
320 | 315 | | |
321 | 316 | | |
322 | | - | |
| 317 | + | |
323 | 318 | | |
324 | 319 | | |
325 | 320 | | |
| |||
335 | 330 | | |
336 | 331 | | |
337 | 332 | | |
338 | | - | |
339 | | - | |
340 | | - | |
341 | | - | |
342 | | - | |
343 | | - | |
| 333 | + | |
344 | 334 | | |
345 | 335 | | |
346 | 336 | | |
| |||
393 | 383 | | |
394 | 384 | | |
395 | 385 | | |
396 | | - | |
| 386 | + | |
397 | 387 | | |
398 | 388 | | |
399 | 389 | | |
400 | 390 | | |
401 | | - | |
| 391 | + | |
402 | 392 | | |
403 | 393 | | |
404 | 394 | | |
| |||
414 | 404 | | |
415 | 405 | | |
416 | 406 | | |
417 | | - | |
418 | | - | |
419 | | - | |
420 | | - | |
421 | | - | |
422 | | - | |
| 407 | + | |
423 | 408 | | |
424 | 409 | | |
425 | 410 | | |
| |||
471 | 456 | | |
472 | 457 | | |
473 | 458 | | |
474 | | - | |
| 459 | + | |
475 | 460 | | |
476 | 461 | | |
477 | 462 | | |
478 | 463 | | |
479 | | - | |
| 464 | + | |
480 | 465 | | |
481 | 466 | | |
482 | 467 | | |
| |||
492 | 477 | | |
493 | 478 | | |
494 | 479 | | |
495 | | - | |
496 | | - | |
497 | | - | |
498 | | - | |
499 | | - | |
500 | | - | |
| 480 | + | |
501 | 481 | | |
502 | 482 | | |
503 | 483 | | |
| |||
559 | 539 | | |
560 | 540 | | |
561 | 541 | | |
562 | | - | |
| 542 | + | |
563 | 543 | | |
564 | 544 | | |
565 | 545 | | |
566 | 546 | | |
567 | | - | |
| 547 | + | |
568 | 548 | | |
569 | 549 | | |
570 | 550 | | |
| |||
580 | 560 | | |
581 | 561 | | |
582 | 562 | | |
583 | | - | |
584 | | - | |
585 | | - | |
586 | | - | |
587 | | - | |
588 | | - | |
| 563 | + | |
589 | 564 | | |
590 | 565 | | |
591 | 566 | | |
| |||
649 | 624 | | |
650 | 625 | | |
651 | 626 | | |
652 | | - | |
| 627 | + | |
653 | 628 | | |
654 | 629 | | |
655 | 630 | | |
656 | 631 | | |
657 | | - | |
| 632 | + | |
658 | 633 | | |
659 | 634 | | |
660 | 635 | | |
| |||
670 | 645 | | |
671 | 646 | | |
672 | 647 | | |
673 | | - | |
674 | | - | |
675 | | - | |
676 | | - | |
677 | | - | |
678 | | - | |
| 648 | + | |
679 | 649 | | |
680 | 650 | | |
681 | 651 | | |
| |||
751 | 721 | | |
752 | 722 | | |
753 | 723 | | |
754 | | - | |
| 724 | + | |
755 | 725 | | |
756 | 726 | | |
757 | 727 | | |
758 | 728 | | |
759 | | - | |
| 729 | + | |
760 | 730 | | |
761 | 731 | | |
762 | 732 | | |
763 | 733 | | |
764 | | - | |
| 734 | + | |
765 | 735 | | |
766 | 736 | | |
767 | 737 | | |
768 | 738 | | |
769 | 739 | | |
770 | 740 | | |
771 | | - | |
| 741 | + | |
772 | 742 | | |
773 | 743 | | |
774 | 744 | | |
| |||
786 | 756 | | |
787 | 757 | | |
788 | 758 | | |
789 | | - | |
790 | | - | |
791 | | - | |
792 | | - | |
793 | | - | |
794 | | - | |
| 759 | + | |
795 | 760 | | |
796 | 761 | | |
797 | 762 | | |
| |||
0 commit comments