elastic / eland

Python Client and Toolkit for DataFrames, Big Data, Machine Learning and ETL in Elasticsearch
https://eland.readthedocs.io
Apache License 2.0
628 stars 98 forks source link

`RandomForestClassifier` model cannot be uploaded #639

Closed mundruid closed 5 months ago

mundruid commented 7 months ago

I used to have a RandomForestClassifier model that I was able to upload to my Elastic nodes in the past, but now I can no longer upload it. The versions of Elastic I was able to upload the RandomForestClassifier is: 7.17.1 and 7.17.5. My eland version at this time was 8.3.0.

The error that I am getting seems like an API error and it happens now that my Elastic has been updated to 8.11.1. I am using eland to upload the model and this specific function: MLModel.import_model. When I upload an XGBClassifier to the updated (8.11.1) node, I do not get an error.

The error that I get when I try to upload the RandomForestClassifier is:

BadRequestError: BadRequestError(400, 'illegal_argument_exception', '[1:23040] [trained_model_definition] failed to parse field [trained_model]') 

My eland version is: 8.11.0 and elasticsearch version is 8.11.1 when the above error happens.

Update: I also tried with eland 8.11.1 and I get the same error.

davidkyle commented 7 months ago

Thanks for reporting this @mundruid.

Elasticsearch 8.11 should work with your model. What version of XGBoost did you create the model with?

mundruid commented 7 months ago

I created XGBClassifier with eland 8.11.0 and I can upload to Elastic 8.11.1. I am able to upload the XGBClassifier model.

If I convert the exact same model from XGBClassifier to RandomForestClassifier, I get the bad request error mentioned above.

davidkyle commented 7 months ago

There was as similar error reported in #255 where the model upload failed because it was too big for the HTTP buffer. H/ow big is your model @mundruid?

The resolution was to compress the model definition https://github.com/elastic/eland/issues/255#issuecomment-705589655

mundruid commented 7 months ago

The Random Forest Classifier is 36372 Bytes. I have tried the resolution and I get an error: "Connection #0 to host cas-aggregator.es.us-east-1.aws.elastic-cloud.com left intact {"error":{"root_cause":[{"type":"x_content_parse_exception","reason":"[1:0] [trained_model_definition] Expected START_OBJECT but was: null"}],"type":"x_content_parse_exception","reason":"[1:0] [trained_model_definition] Expected"

Is anyone else able to upload Random Forest Classifier models with these versions of Eland and Elastic? Is there a min/max size?

davidkyle commented 6 months ago

@mundruid there is actually a test that creates a random RandomForestClassifier model and uploads it Elasticsearch, that test passes when I run it locally with pytest tests/ml/test_ml_model_pytest.py::TestMLModel::test_random_forest_classifier and it passes in CI runs.

https://github.com/elastic/eland/blob/main/tests/ml/test_ml_model_pytest.py#L417

The failure you are seeing may be specific to your model, or due to a certain combinations of dependencies.

What version of sklearn do you have installed?

My eland version is: 8.11.0 and elasticsearch version is 8.11.1 when the above error happens. The versions of Elastic I was able to upload the RandomForestClassifier is: 7.17.1 and 7.17.5.

Can you upload the model to Elasticsearch 7.17 with Eland 8.11.1?

mundruid commented 6 months ago

I only have a 7.17.5 cluster left and I tried Eland 8.11.1 and still getting a bad request error.

The model that cannot be uploaded was uploaded using the following method recommended by Elastic. This may help so I am pasting here what I did:

  1. Transform the model to json:
    import json
    from eland.ml.transformers import get_model_transformer
    # (Same inputs as ImportedMLModel)
    transformer = get_model_transformer(
    model,
    feature_names=feature_names,
    classification_labels=classification_labels,
    classification_weights=classification_weights,
    )
    # Put this into the GitHub issue
    print(json.dumps(transformer.transform().to_dict())
  2. Compress:
    python print_ml_model.py > upload.json
    jq -j '{"trained_model": .}' upload.json | gzip | base64 > compressed_definition.txt
    COMPRESSED_DEF=$(cat compressed_definition.txt); FEATURE_NAMES=$(jq '.ensemble.feature_names' upload.json); echo "{\"compressed_definition\": \"$COMPRESSED_DEF\", \"input\": {\"field_names\": $FEATURE_NAMES}, \"inference_config\":{\"classification\":{}}}" > my_model.json
  3. Upload with curl:
    curl -v -X PUT -H "Content-Type: application/json" -d @my_model.json <elasticsearch_endpoint>/_ml/trained_models/<your-model-name>

If you would like me to share the model json please let me know. I do not think I can share it on a public forum but I can send you a PM.

mundruid commented 5 months ago

This issue can be closed. Based on the workaround above I can upload my model. I still cannot upload it with Eland, however based on discussions with Elastic support, this does not seem to be an issue with Eland but with how my model is saved with scikit.