-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable Tikhonov regularization in BASEX #225
Comments
I mean that in principle Regarding caching, it seems that you load the matrices from disk every time. Have you considered keeping them in memory (as globals in |
Hi Mikhail! Welcome to PyAbel :)
This is an interesting observation! I'm not enough of a computer scientist or mathematician to argue either way about this from first principles. But, I wrote some simple code to compare So, you are correct!
This is a really good point! I think that this will generate some considerable discussion, so I opened a new issue: #226 Back to the issue of including the Tikhonov regularization:
This seems like a reasonable approach, but it has the disadvantage that, if someone wants to re-run their program from scratch, they lose the regularized matrices. And, it would take quite a bit of additional effort to implement. At this point, the easiest thing to implement would be to save the basis sets before including the regularization (like you are proposing), and not worry about caching to RAM. This would create a slower transform, but maybe it's an acceptable trade-off. |
OK, I've just opened a pull request (#227) with this stuff implemented. Here is a speed comparison of Caching, as expected does not help much for practically important cases, but was implemented mostly to avoid recalculating the transform matrices. "reg var" means that the regularization parameter was changed (and thus the regularization reapplied) every time. When it remains constant, the sustained speed is better (more or less equal to "cached", usually better than "old"): In any case, all speed differences are within a factor of 2: |
There might be some more improvements. I did not look closely at the implementation, but I have noticed that |
Thanks for performing these comparisons! Honestly, if we are only talking about factors of 2 in speed, then I would say that we can potentially trade speed improvements for improvements in the readability/useability/hackability of the code. That's interesting about |
Still travelling. The concept of basis memory storage is useful and interesting. Thanks for your work on this @MikhailRyazanov . The implementation is really one of calling the basis generation function Note, ideally, @DanHickstein's idea of a global basis variable is a good one, preferably passed to each method, rather than accessed via
|
@DanHickstein, with no RAM caching it is indeed ~2 times slower: |
@stggh, I do not not what are the use cases for this project and how the architecture decisions were made, but I would probably have the But you probably do not want to refactor everything now, so do whatever fits in the current conventions or tell me how exactly it should look. |
In principle, automatic caching can be done without globals, using some mad skills. |
Well, if there aren't many disadvantages to caching, then we may as well do it.
Unless @rth was involved, any architecture decision were made by myself and @stggh, two experimentalists that don't know all that much about software engineering. So, I'm sure that nothing is perfect :) Our main motivation for making Transform a class so that we could access the various outputs that could be returned using nice "dot notation". You're idea about using classes to ease the implementation of new transform methods sounds very interesting. But yeah, it sounds like it might be some work to implement, so we'd want to do a careful pro/con analysis. If the globals stay confined to the |
I've gone through the BASEX version 2.0 (
|
Important conclusions from my previous comment are:
So I've done 1. and 2., plus combined the horizontal transform into a single matrix. The speed has improved a lot: But, as @DanHickstein has pointed out, this implementation transforms full-width images. This does not give any advantages, but only makes is slower. I think, we should switch to a half-width transform, like in BASEX. |
Well done Mikhail! It is great to know exactly how Basex.exe operates, and I'm very glad that you have figured out the issues with the vertical transform and vertical regularization. At this point, I think that it is clear that you now have an excellent understanding of the basex code. You should feel free to edit the code as extensively as you like. I have never been completely happy with our basex implementation, because the old variable names and weird structures have been transferred from the legacy Matlab code, when it would be much better to directly connect the variable names to the published paper. A few responses to your specific points:
Ha! Well, the current basex.py in PyAbel could probably use more comments as well.
Gosh, that does seem really confusing!
As far as I can tell, the basis sets were generated using Matlab code. You actually sent me this matlab code, as part of a big folder of BASEX-related matlab scripts back in 2011, which spawned PyAbel :). I believe that the relevant script is "basis2.m".
This is great! Should we consider changing our notation to better match the manuscript?
So, with basex.exe, any regularization factor is possible, but reg=20.0 offers enhanced speeds? This is indeed very strange.
Oh, are the BASEX transforms still performed on the full image? Yes, it would be great to switch to the half-width image if possible, since this best matches the other transform methods and should provide the best performance. |
Thanks, @DanHickstein! I'll then proceed to converting everything in By the way, I was thinking about extracting basis sets for smaller images from a larger cached basis, if it is available, which should avoid this ridiculously long basis computation in most cases. I suspect that this can be done simply by slicing the array (discarding the outer part), but even if there are fringe effects, they could be treated much faster than a full basis computation. Moving to half-width operation should also make this easier.
I would like, but there are two problems. First, that transposition (the article convention is bad). Second, I don't like using Z for the image basis (in the r direction!). I would use B and Bp for the basis and its projection, but B is already used in the article for a different purpose. So my thoughts are to leave the current notation (it is at least compatible with the Matlab and C++ implementations) and explain in the comments what is what. If there are no better ideas...
Partially. That script saves them as text files and does not produce |
And a long-promised comparison of regularization effects. As can be seen, reg ~ 200 (in this case) almost completely removes the axial noise from the image and has a negligible effect on the extracted distribution. At While regularization reduces the image noise, it almost does not reduce the speed noise because:
That is, "denoising" the speed distributions requires relatively strong regularization (another example with reg = 1000 can be seen in my dissertation, Fig. 3.10), but for those who needs the images themselves, rather than their integral characteristics, even moderate regularization is a big advantage. |
Well, that's just great news!!
Interesting! If that works, then it would be fantastic. We could simple generate the 1000x1000 baiss set once and then crop all smaller sets from it. Another crazy idea: now that we have scrapped the vertical transform, does that mean that we can generate the basis set for a 1D transform and then somehow copy this into a larger matrix?
I suppose the alternative would be to write our own explanation of the math and follow those conventions. At a minimum, as you say, we should explain in the comments what everything is.
Oh, that's interesting. Kinda sketchy that the origin of the calculated basis sets is completely unknown for basex.exe :)
Fantastic! What colormap did you use? And this is fairly convincing evidence that strong noise suppression is possible with minimal broadening of sharp features. This is clearly a big advantage of the BASEX method, and I'm really happy that we are finally getting this feature into PyAbel. Thank you so much! |
Back from my travels :-( Very nice coding and some clever ideas @MikhailRyazanov! My only concern (discussion point) is that global BTW, 205 elif isinstance(nbf, (int, long)): Any chance that you can implement the anisotropy parameter calculation that was available in the original basex program?
This is neat. I think this approach may also apply for the Dasch method basis calculation. |
Well, if we had With the half-width transform, a 1023×1023 image requires three 512×512 arrays, which is 6 MiB + Python/numpy overhead. I think, this is not a big problem. But I can add a
Do we actually need that stuff? Non-"auto" bases never worked, I do not have plans to implement them (I do not really know the details, and "broad" bases are relatively useless), and it is unlikely that anybody will soon. The removal of the vertical transform has already changed the calling conventions (not the default ones, but it broke some tests), so maybe we should ignore
It was painfully slow (~40 seconds on my computer). I think, calculating it from the reconstructed image using a common procedure is better in every sense. |
Yeah, I agree that it's probably not a huge problem, except if people are running benchmarks or something. I like the idea with
Wider basis sets worked in basex.exe, right? This seems like it might be useful for some people, so I am holding out hope that this eventually gets implemented. But yes, in it's current state, a warning or error should be raised.
Yeah, this also seems fine to me as well. @stggh, in the case of linbasex, it makes sense to extract the anisotropy directly from the functions, since the broad functions closely resemble the photoelectron/photoion distributions. I don't think that we would see the same advantage when using narrow gaussian functions. |
I have updated #227, switching to the half-width approach. Now even for a full image (4 independent quadrants) it works faster than the BASEX program (≲1 s on my computer) and reproduces its results for symmetrized images (within that mystic scale factor). I think, I'm satisfied with this regularization stuff now, so could you please review #227 and run all the tests? (I'm very interested in the efficiency comparison.) Then it would be nice to tidy up and merge it, and close this issue. I'll then handle cache cleanup and basis cropping in #226 separately. I also have the forward transform, but that's another story... |
There was only one "wider basis set", and yes, it worked. But I do not know whether anybody used it. I suspect that using wider on narrower (with regularization, you can have more basis functions than pixels) basis sets is equivalent to resampling the images. Anyway, I'm leaving
If there are such brave people to wait for the basis generation... :–)
In principle, images must have no features sharper than ~2 px (if we consider that Abel = Fourier−1 Hankel, then this is a requirement of the sampling theorem). If
Probably due to something in the basis and its projection. Since the axial pixel is actually a half-pixel, it needs a special treatment, and I'm not sure that this was done correctly. Perhaps, open another issue for implementing |
I'm interested in this story :)
Yeah, my impression is that using a wider basis set was essentially equivalent to blurring the image before processing, but maybe there is some advantage to the larger basis functions. |
Basically, exchanging But let's finish with this issue first. |
@DanHickstein, thanks for merging #227! Perhaps, this issue can be closed now? |
Sounds good. Mission accomplished! |
And a big THANK YOU to @MikhailRyazanov for implementing this! |
In a discussion with Mikhail Ryazanov, I realized that we have disabled the Tikhonov regularization factor in the
basex
algorithm. This occurs on line 184 of basex.py:As I recall, we originally thought that this factor didn't significantly affect the output. However, I think that this parameter can have a large effect on the transform in the presence of noise, greatly suppressing the nose at small r-values.
You can see here the difference between
q_vert, q_horz = 0, 0
(left) andq_vert, q_horz = 100, 100
(right).I think that many users of the old BASEX.exe enjoyed this smoothing, and this is one of the reasons that BASEX became so popular. So, I think that we need to re-activate this feature.
However, in our current implementation, the "basis set" that is saved (pre-cached) already has the tikhonov regularization factor included. So, we would either need to change the naming convention of the saved files to include the tikhonov factor in the file name (in addition to the image size), or we would need to slightly re-work the algorithm to save the basis sets at a slightly earlier stage, and include the tikhonov factor in the computation of the transform (instead of in the computation of the basis sets). This would result in somewhat slower transform times, but allow users the most flexibility to try several tikhonov regularization factors. We should likely investigate how much this slows down the transform before making a decision.
Additionally, Mikhail suggested that we look into using one of the linear algebra "solve" functions instead of matrix inversion and other matrix algebra steps. I think that the appropriate numpy function is this one:
https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.linalg.solve.html
The text was updated successfully, but these errors were encountered: