Skip to content

AI: Allow configuration of custom classification models - #5011

Merged
lastzero merged 26 commits into
photoprism:developfrom
raystlin:feature/custom-tf-model-127
Aug 4, 2025
Merged

AI: Allow configuration of custom classification models#5011
lastzero merged 26 commits into
photoprism:developfrom
raystlin:feature/custom-tf-model-127

Conversation

@raystlin

@raystlin raystlin commented May 14, 2025

Copy link
Copy Markdown
Contributor

This commit adds the possibility of using custom models for classification via vision.yml configuration file.
The code will inspect the models and try to find their variable layout, inputs, number of outputs and so on, however, if the model returns logits instead of probabilities it must be specified on the configuration to add a softmax layer after the last layer of the model.

Notes

  • NSFW and Faces can be configured and replaced, but the rest of the code is not really prepared for them.
  • Please, take into account that download-nasnet.sh script has been modified to include labels21k.txt to nasnet path, so as to have a default implementation of 21k labels, although the rules for them have not been written yet.
  • Also the test case internal/ai/classify/model_external_test.go has been set as optional via an environment var, as it is not an acceptance but one to perform on test photos against external models.

Related Issues

raystlin added 17 commits April 11, 2025 14:30
These new functions allows us to inspect the saved models, get the tags
and try to guess the inputs and outputs.
By inspecting existing models we saw that many times logits are returned
instead of probabilities. Photoprism uses probabilites to rank the
quality of the results, so we need to transform those logits. Our
approach is to add a new layer at runtime to the graph that performs the
softmax operation.
EnsureShape operation may seem unnecesary, but PredictCosts fails it it
is not added to the graph.
Vision input parameters have also been changed to support the new
parameters needed for the models.
nsfw/model.go and vision/model.go modified to make it work. Testing
remains undone.
Now when loading labels internal/ai/tensorflow package will try to look
for all the files that match the glob label*.txt and will return the
labels that match the expected number. Some models add a first label
called background, which is a bias.
Also, a new parameter has been added to models to allow a second path to
look for the label files. This path is set to nasnet asset on
internal/ai/vision.
They were broken when the constructors changed to include metadata.
The tests has a envar check to avoid running it by default, as it
downloads the models and generates a lot of errors.
This parameter allows us to rescale the input of the models because some
of them need values between [0, 1] and other between [-1, 1].
The definition of the models now contain the expected resolution. This
has improved the results for efficientnet-m.
As we are decompressing, we have to check the paths before writing to
disk.
It seems to be standarized, so it is now used as an additional check for
input signatures.
* vision_list command failed silently for models without meta.
* face model panicked for models without meta.
The test was broken because of the new parameters
MODEL_HASH="f18b801354e95cade497b4f12e8d2537d04c04f6 $MODEL_ZIP"
MODEL_VERSION="$MODEL_PATH/version.txt"
MODEL_BACKUP="storage/backup/nasnet-$TODAY"
MODEL_21K_LABELS_URL="https://dl.photoprism.app/tensorflow/vision/labels-imagenet21k.txt"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It probably makes sense to comment this out (or remove it) until 21k labels are actually supported? As I mentioned in my last issue comment, I believe it would be easier to generate CLIP embeddings and implement a search with those than to generate 21,000 different labels suitable only for regular text-based searches?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right. I modified the script so as to have some kind of classification (although the rules were not being applied), but if we are thinking of alternative ways of doing the ruling, it makes more sense to remove this.

@lastzero

Copy link
Copy Markdown
Member

@raystlin Thanks a lot! That's awesome :)

After taking a (very) quick look at your changes, I have a few questions regarding their intended scope and functionality:

  • It seems like the newly added PhotoInput abstracts the input thumbnail configuration, right?
  • When you pass an image: Are there any constraints regarding size or formats?
  • Should it automatically resize the image to match the configured dimensions?
  • If the input image is not square or does not match the configured size or aspect ratio, can/should multiple crops automatically be created to cover the entire area?

Also, I'd like to ask the following just to be sure:

  • When you run make test-go in a terminal, do all the existing and new unit tests pass? Have you observed any instabilities or performance issues?
  • How is the test coverage, especially with regard to the new functionality? Should more tests be added before or after these changes are merged?
  • Did you run make fmt-go to reformat the source files as needed?

Version string `yaml:"Version,omitempty" json:"version,omitempty"`
Prompt string `yaml:"Prompt,omitempty" json:"prompt,omitempty"`
Resolution int `yaml:"Resolution,omitempty" json:"resolution,omitempty"`
Meta *tensorflow.ModelInfo `yaml:"Meta,omitempty" json:"meta,omitempty"`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a vision.yml configuration example with your changes applied could help with testing. This would clarify what it should look like and enable us to easily compare it with the existing format and available options.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course. I missed this one. This is the example I used for the transformer one, but I have to change it on the PR.

---
Models:
- Type: labels
  Name: transformer
  Version: Mobile
  Resolution: 224
  Meta:
    Input:
        Interval:
            Start: -1.0
            End: 1.0
    Output:
      OutputsLogits: true
- Type: nsfw
  Name: Nsfw
  Resolution: 224
- Type: face
  Name: FaceNet
  Resolution: 160
- Type: caption
  Resolution: 224
  Service:
    Uri: http://photoprism-vision:5000/api/v1/vision/caption
    FileScheme: https
    RequestFormat: url
    ResponseFormat: vision
Thresholds:
  Confidence: 10

@lastzero lastzero added ai Artificial Intelligence, Machine Learning (ML) awesome This is pretty awesome! in-progress Somebody is working on this labels May 17, 2025
@lastzero lastzero moved this to Development 🐝 in Roadmap πŸš€βœ¨ May 17, 2025
@raystlin

Copy link
Copy Markdown
Contributor Author
  • PhotoInput abstracts the entry needed by the model: the input layer name, the expected height and width, the expected interval for input values ([0,1] or [-1,1] or whatever), the index of the output and so on. I try to deduce as much as I can from the model deserialization, but those assumptions can be overridden via vision.yml. Please, also notice that I left resolution value at Models level, this is because I assumed by the comment Labels: Load and use a custom TensorFlow modelΒ #127 (comment) that this parameter was going to be used to generate thumbnails in the exact size needed by the model. I think I misunderstood it, but we can always do a transformation like the one done on NSFW to solve it.
  • Supported file formats are the same ones previously supported: jpg and png (I did not change the image decoding part of the code).
  • Constraints are not enforced. As I previously said, I thought the input was being enforced to its correct size during the thumbnail generation. If this assumption is wrong, I am afraid I have to work on that part.
  • make test-go runs and passes the tests. I have not seen performance or stability issues.
  • I have added the file internal/ai/classify/model_external_test.go to test/compare the new model against the test data used for nasnet. The test downloads the models (which is really slow), runs them and, sometimes, the labels generated for some tests are not the expected ones. Because of this, I added an enviroment variable as a condition to run them.
  • I ran make fmt-go but I missed one file: internal/ai/classify/model_external_test.go. I will add it to the commit.

raystlin added 2 commits May 18, 2025 11:06
make fmt-go was applied
This version adds the new fields to the test configuration
@lastzero

Copy link
Copy Markdown
Member

@raystlin Sorry for not getting back to you sooner! I was traveling, but am now back in the office. Please let me know if you consider this stable enough for release. I will then merge your changes, perform final testing, and create a new preview build :)

@lastzero
lastzero changed the base branch from feature/custom-tf-model-127 to develop August 4, 2025 07:31
@lastzero

lastzero commented Aug 4, 2025

Copy link
Copy Markdown
Member

@raystlin All the unit tests are green, so that's great!

As a general note, the inline comments should end with a period because they are used to automatically generate API documentation (not a big issue, though, because I can easily update them):

Screenshot from 2025-08-04 09-39-21

For our end user and developer documentation, it would also be helpful to have more information on the newly added configuration options, as well as a list of the models that you have tested and found to work?

Thank you very much! πŸ€—

lastzero added a commit that referenced this pull request Aug 4, 2025
Signed-off-by: Michael Mayer <michael@photoprism.app>
@lastzero
lastzero merged commit aeba5bf into photoprism:develop Aug 4, 2025
1 check passed
lastzero added a commit that referenced this pull request Aug 4, 2025
Signed-off-by: Michael Mayer <michael@photoprism.app>
lastzero added a commit that referenced this pull request Aug 4, 2025
Signed-off-by: Michael Mayer <michael@photoprism.app>
lastzero added a commit that referenced this pull request Aug 4, 2025
Signed-off-by: Michael Mayer <michael@photoprism.app>
lastzero added a commit that referenced this pull request Aug 4, 2025


Signed-off-by: Michael Mayer <michael@photoprism.app>
@lastzero

lastzero commented Aug 4, 2025

Copy link
Copy Markdown
Member

@raystlin I've merged your changes with a few edits for further testing and documentation. Thank you so much for working on this! ❀️

It would be good to have some specific configuration and usage examples ready for end users before releasing these improvements in the stable version. @graciousgrey and @omerdduran are happy to help with this.

@lastzero lastzero added please-test Ready for manual testing & feedback and removed in-progress Somebody is working on this labels Aug 4, 2025
@raystlin

raystlin commented Aug 4, 2025 via email

Copy link
Copy Markdown
Contributor Author

@lastzero lastzero moved this from Development 🐝 to Preview 🐳 in Roadmap πŸš€βœ¨ Aug 4, 2025
@lastzero

lastzero commented Aug 4, 2025

Copy link
Copy Markdown
Member

lastzero added a commit that referenced this pull request Aug 4, 2025
Signed-off-by: Michael Mayer <michael@photoprism.app>
lastzero added a commit that referenced this pull request Aug 4, 2025
Signed-off-by: Michael Mayer <michael@photoprism.app>
lastzero added a commit that referenced this pull request Sep 1, 2025
This also renames the Meta option to TensorFlow so it is clear these
values are to configure TensorFlow models only.

Signed-off-by: Michael Mayer <michael@photoprism.app>
lastzero added a commit that referenced this pull request Sep 1, 2025
Signed-off-by: Michael Mayer <michael@photoprism.app>
lastzero added a commit that referenced this pull request Sep 1, 2025
Signed-off-by: Michael Mayer <michael@photoprism.app>
lastzero added a commit that referenced this pull request Sep 1, 2025
Signed-off-by: Michael Mayer <michael@photoprism.app>
lastzero added a commit that referenced this pull request Oct 30, 2025


Signed-off-by: Michael Mayer <michael@photoprism.app>
@graciousgrey graciousgrey added tested Changes have been tested successfully and removed please-test Ready for manual testing & feedback labels Nov 25, 2025
@lastzero lastzero moved this from Preview 🐳 to Release 🌈 in Roadmap πŸš€βœ¨ Dec 1, 2025
@graciousgrey graciousgrey added released Available in a stable release and removed tested Changes have been tested successfully labels Dec 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai Artificial Intelligence, Machine Learning (ML) awesome This is pretty awesome! labels Related to labels assigned manually or from metadata released Available in a stable release

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

4 participants