pymongopredict.predictions_api
The predictions API: a $predict aggregation stage backed by prediction models stored in the database.
You define a model once on a collection, as you would a vector search index, then call it by name as the first stage
of an aggregation pipeline; later stages read its output through $meta. No server changes are needed.
$vectorSearch |
$predict |
|
|---|---|---|
| defined with | create_search_index |
create_prediction_model |
| managed with | list_ / update_ / drop_search_index |
list_ / update_ / drop_prediction_model |
| stage options | index, path, queryVector, filter, limit |
model, filter, limit, batchSize |
| position | first stage only | first stage only |
| output | {"$meta": "vectorSearchScore"} |
{"$meta": "prediction"}, "predictScore", "predictProbabilities" |
| runs | inside Atlas | in your Python program, then on the server |
What you use:
PredictionModelandcreate_prediction_model(): define and train a model; the model is stored in the__mongopredict_modelscollection of the same database.list_prediction_models(),update_prediction_model(),drop_prediction_model(): status, metrics, retraining, deletion.enable()/disable(): letcoll.aggregaterun$predictand make the model functions collection methods.aggregate()andPredictCursor: run a pipeline that starts with$predict, and its result (with timingstats).
Without enable(), call aggregate(coll, pipeline) and the module-level model
functions with the collection as their first argument.
Model definition
The definition given to PredictionModel takes:
target: the dotted path of the field to predict (required).columns,embedFields,excludeFields,embeddingModel,embeddingKwargs,maskTarget,maxTokens,maxJsonChars: the settings of thepymongopredict.embeddings_api.DocumentFeaturizer;featurizerOptionspasses any other of its arguments by its Python name. WithoutembedFieldsthe whole document is embedded.embeddingModel:"voyage-4-nano"(the default, runs locally), any Hugging Face model id (runs locally), or a Voyage API model such as"voyage-4-lite","voyage-4"or"voyage-4-large"(needsVOYAGE_API_KEY);embeddingKwargspasses its settings, e.g.{"output_dimension": 512}for an API model (but notapi_key, which would be stored with the model: setVOYAGE_API_KEYinstead). See the embeddings API documentation for every option.task:"auto","classification"or"regression"."auto"predicts a number when every value of the target is a number (numbers stored as text count; empty strings and text such as"NA","none"or"-"are missing values, while in a field of categories they are labels) and there are at least 8 distinct values, and a category otherwise, so a 0/1 flag or a 1 to 5 star rating stored as numbers is a category.estimator:"auto"(the default: XGBoost with its default settings, for categories and numbers, on at most 16 threads unlessOMP_NUM_THREADSis set),"xgboost"(the same),"logistic","ridge"or"hgb"(histogram gradient boosting), or any scikit-learn estimator passed tocreate_prediction_model().filter: which documents to learn from (documents without the target are always left out);numTrainingDocuments: a cap on their number.testSize: the fraction, or the number, of training documents held out to measure the stored metrics (default 0.2; 0 skips them). The stored model is then refitted on all training documents.
{"$meta": "predictScore"} is the probability of the predicted category and {"$meta": "predictProbabilities"}
maps each category to its probability; both exist only when the estimator has predict_proba (not for numbers or
"ridge").
How a pipeline runs
create_prediction_model()fits the featurizer and the estimator on the documents that have the target, records the held-out metrics, refits on all of them and stores the fitted model (a pickle of at most 15 MB) with its status and version in the__mongopredict_modelscollection of the same database. As with a search index, the previous version keeps answering while a new one trains.aggregate()fetches the documents selected by the stage'sfilterandlimit(only the fields the model reads), predicts them in batches in your program, and uploads only{_id, prediction, score, probabilities}rows to a temporary collection.- The rest of the pipeline runs on the server: the temporary collection is joined back to the full documents
(
$lookupon_id), the$metareferences are rewritten to the joined fields, and the helper field is removed before the output (or before a final$merge/$out). The temporary collection is then dropped.
On the 14 MongoDB sample tasks, $predict returns the same predictions as calling the model directly, at a median
1.39 times the time of that code (reports/01_predictions_api.html).
Details and limits
- Requirements: MongoDB 5.0 or later (5.1 or later when the source collection is sharded); fitting a model or running
$predicton an older server raisesRuntimeError. The tests run on MongoDB 5.0, 6.0, 7.0 and 8.0, and on Atlas (8.0). On 5.0 a time-series collection is a view, socollation=cannot be passed for it there (6.0 and later accept it).$predictneedsfindon the source collection andcreateCollection,insertanddropCollectionin its database (for the temporary__mongopredict_tmp_*collection); training needsfind,insertandupdateon__mongopredict_models(removeto drop a model). Model records and temporary collections are read and written on the primary, even with a secondary read preference. - A killed process can leave a temporary collection behind:
cleanup_temporary_collections()drops those older than 24 hours (older_than). A call whose temporary collection is dropped while it runs raisesRuntimeErrorinstead of returning partial results. A record stillBUILDINGafter 24 hours is flaggedstale: Truebylist_prediction_models(). - Documents are predicted in batches of at most
batchSizedocuments and 64 MB of BSON. Scores and probabilities are computed only when the pipeline readspredictScoreorpredictProbabilities. An embedding cache directory that cannot be used gives a warning, and the embeddings are then computed without a cache. - The source can be a collection, a view or a time-series collection; views and time-series collections are joined
from the source side, through the temporary collection's
_idindex._idvalues must be unique (a view with an$unwindstage is refused). - A class is returned as first stored in the training documents (an
ObjectId, a date, anint), so predictions can be compared with the labels or joined to them with$lookup. ThepredictProbabilitieskeys and the record'sclassesarestr()of those values, sostr(prediction)is always a key. - Model names may not contain
.or$.limitandbatchSizemust be positive integers; withoutlimitevery matching document is predicted. hint=,collation=,let=andsession=passed to the aggregate apply to the query on the source collection, and the temporary collection gets the source collection's default collation, so string matching and sorting behave the same.$metaworks in$sort,sortBy($setWindowFields,$top,$bottom), expressions,$group,$facet,$redact, and after an inclusion$projector a$replaceRoot. It is not available inside$lookup/$unionWithsub-pipelines (add it as a field first and pass it withlet), nor in$merge'sletorwhenMatched.__mongopredictis reserved as a field name.$predictcannot run inside a multi-document transaction, because it creates and drops a temporary collection.- The documents travel to your program and the model runs there; the result is read in full before it is returned;
$predictworks from Python only (not frommongoshor other drivers). - The stored model is a pickle, so only users trusted to run code in your program should be able to write
__mongopredict_models. SetMONGOPREDICT_MODEL_KEY(the same secret where models are trained and where they are used) to sign stored models: a model without a valid signature is then never unpickled. - The record holds the library versions the model was stored with (
versions). Loading it under other scikit-learn, numpy or pymongopredict versions gives a warning; a model that cannot be loaded raises aValueErrorthat says to rebuild it withupdate_prediction_model(). $predictcaches embeddings only whenMONGOPREDICT_PREDICT_CACHE_DIRnames a directory (the trainingcache_diris not used), so a long-running process does not grow a cache of every document it predicts.- Training on a collection streams the documents: only
_idand the target of each are held in memory (if documents are deleted meanwhile, it trains again from a single read). Views and time-series collections are read once into memory, and training on them also needs unique_idvalues.
Examples
Usage:
from pymongopredict import PredictionModel, predictions_api predictions_api.enable() # coll.aggregate() understands $predict coll.create_prediction_model(PredictionModel({"target": "room_type"}, name="room_type")) coll.list_prediction_models() # status, definition, holdout metrics coll.aggregate([{"$predict": {"model": "room_type", "limit": 1000}}, {"$project": {"p": {"$meta": "prediction"}}}])
A prediction model definition, the counterpart of pymongo.operations.SearchIndexModel.
Only the arguments are checked here; the values are checked when the model is trained.
Parameters
definition (dict): How to train the model. All keys are optional except
target:target: dotted path of the field to predict.columns,embedFields,excludeFields,embeddingModel,embeddingKwargs,maskTarget,maxTokens,maxJsonChars: passed topymongopredict.DocumentFeaturizerascolumns,embed_fields,exclude,embedding_model,embedding_kwargs,mask_target,max_tokensandmax_json_chars(embedFieldsomitted means embed the whole document).featurizerOptionsis a dict of any otherDocumentFeaturizerargument by its Python name; it overrides the keys above.task:"classification","regression"or"auto"(default)."auto"picks regression when every target is a number (or numeric text; empty strings are dropped) and there are at leastMIN_REGRESSION_VALUES(8) distinct values; otherwise classification, so a 0/1 flag, 1-5 stars, booleans and text are categories.estimator:"auto"(default) or"xgboost": XGBoost with its default settings (_XGBLabelClassifierfor classification,xgboost.XGBRegressorfor regression);"logistic"(classification only),"ridge"(RidgeClassifierCV/RidgeCV),"hgb"(histogram gradient boosting), or an sklearn estimator object (usually passed as theestimatorargument ofcreate_prediction_model(); it is deep-copied before fitting).filter: query selecting the training documents. Documents without the target are always left out, as are documents whose target is null (and, for regression, not a number).numTrainingDocuments: positive integer cap on training documents (default: all).testSize: holdout fraction in[0, 1), or integer count of documents, used for the stored metrics (default 0.2; 0 skips them). The split is stratified for classification when every class has at least 2 documents. The stored model is refitted on all training documents.
- name (str, default "default"):
Model name, unique per collection. Must be a non-empty string without
.or$. - type (str, default "prediction"):
Model type; only
"prediction"is supported.
Raises
- ValueError: If
nameis invalid,typeis not"prediction",targetis missing, ordefinitionhas a key outsideDEFINITION_KEYS.
Examples
Usage:
PredictionModel({"target": "imdb.rating", "embedFields": ["plot", "genres"]}, name="imdb_rating")
The model as a {"name", "type", "definition"} dict (SearchIndexModel.document).
create_prediction_model() accepts this dict in place of the object.
Train and store a prediction model; the counterpart of Collection.create_search_index.
Training runs synchronously in this process. If a model with this name already exists on
the collection, it is rebuilt (as update_prediction_model would): as with a search index
rebuild, the previous version stays stored and queryable until the new one is written.
The record in MODELS_COLLECTION (_id "<collection>.<name>") holds:
collection,name,typeandlatestDefinition(the definition of the latest build, with values BSON cannot store, such as estimator objects, as theirrepr).status:"BUILDING"while a build runs, then"READY", or"FAILED"withmessageset to the error (an interrupted build, e.g.KeyboardInterrupt, is markedFAILEDtoo). Only a killed process (SIGKILL, out of memory, a lost node) leaves a recordBUILDING;buildStartedAtrecords when the build started andlist_prediction_models()flags such builds asstaleafterSTALE_BUILD.queryable:Falsewhile the first build runs;Trueonce a version is stored. A failed rebuild keeps the previous version and itsqueryablevalue.version: increments with each build started (taken atomically, so concurrent builds get distinct versions);buildId: unique id of the stored build.- After a successful build:
task,classes(empty for regression),numTrainingDocuments,metrics(r2/mae, oraccuracy/auroc, plusnumTestDocuments; empty whentestSizeis 0),modelBytes,updatedAt(UTC) andmodel(the pickled featurizer and estimator). - Internal bookkeeping:
nextVersionandbuildingId(the build allowed to write).
Only the latest build started may write its result. If a newer build started meanwhile, or the model was dropped, this build's result is discarded with a warning.
Parameters
- collection (pymongo.collection.Collection): Collection holding the training documents; the model is registered on it.
- model (PredictionModel or dict):
The model, or its
{"name", "type", "definition"}document (namedefaults to"default",typeto"prediction"). SeePredictionModelfor the definition keys. - estimator (sklearn estimator, optional):
Overrides the definition's
estimatorwith any sklearn estimator object. - progress (bool or None, default None):
Show a progress bar for each training step: embedding the training part, fitting on it,
embedding and scoring the holdout part, embedding all documents (mostly from the cache)
and fitting the final model (only these last two when
testSizeis 0). Embedding steps count documents.Noneshows the bars on a terminal or in a notebook and hides them when stderr is redirected;True/Falseforce them on or off.
Returns
- str: The model name.
Raises
- ValueError: If the definition is invalid (see
PredictionModel), there are fewer than 2 training documents, the estimator name does not fit the task, the pickled model is over 15 MB, or the record is over MongoDB's 16 MB document limit. The record is markedFAILEDbefore any error from training is re-raised.
Warns
- UserWarning: If the build was superseded by a newer one (or the model dropped) and was not stored.
Examples
Usage:
create_prediction_model(coll, PredictionModel({"target": "room_type"}, "room_type")) create_prediction_model(coll, {"name": "price", "definition": {"target": "price"}}, estimator=HistGradientBoostingRegressor())
Retrain an existing model with a new definition (Collection.update_search_index).
The previous version stays queryable while the new one trains, as described in
create_prediction_model().
Parameters
- collection (pymongo.collection.Collection): Collection the model is registered on.
- name (str): Name of the existing model.
- definition (dict):
The new definition; it replaces the old one entirely (see
PredictionModel). - estimator (sklearn estimator, optional):
Overrides the definition's
estimator. - progress (bool or None, default None):
Progress bars for the training steps, as in
create_prediction_model().
Returns
- str: The model name.
Raises
- ValueError: If no model
nameexists on the collection, or for any reasoncreate_prediction_model()raises.
Status, definition and metrics of the models on a collection (list_search_indexes).
Parameters
- collection (pymongo.collection.Collection): Collection the models are registered on.
- name (str, optional): Return only this model (an empty list if it does not exist).
Returns
- list of dict: One record per model, as described in
create_prediction_model(), without the pickledmodeland the internalnextVersion/buildingIdfields. A record stillBUILDINGafterSTALE_BUILDgetsstale: True: its build was most likely killed (the process cannot record that); create the model again.
Delete a stored model and its cached copy (Collection.drop_search_index).
Dropping a model that does not exist is a no-op. A build still running for it is not stored when it finishes.
Parameters
- collection (pymongo.collection.Collection): Collection the model is registered on.
- name (str): Model name.
collection.aggregate that understands a leading $predict stage.
The stage is {"$predict": {"model": <name>, "filter": <query>, "limit": <n>,
"batchSize": <n>}}:
model(required): name of a stored prediction model ofcollectionwith a queryable version (seepymongopredict.predictions_api.load_prediction_model()).filter: any query selecting the documents to predict (default: all).limit: positive integer, the most documents to predict (default: every match).batchSize: positive integer, how many documents are featurized and uploaded at a time (default 512).
Later stages read the predictions with {"$meta": "prediction"},
{"$meta": "predictScore"} and {"$meta": "predictProbabilities"}; see the module
docstring for how the pipeline runs. Pipelines without $predict go straight to the
server unchanged.
Parameters
- collection (pymongo.collection.Collection): Source collection.
- pipeline (list of dict):
Aggregation pipeline;
$predict, if present, must be its first stage and nowhere else. - _aggregate (callable, optional):
Internal:
(collection, pipeline, **kwargs) -> cursorthat runs a plain pipeline;enable()passes the unpatchedCollection.aggregatehere. **kwargs:
aggregateoptions, handled as on the server where possible:session: used for every operation (the query, creating, filling and dropping the temporary collection, the server pipeline). A session in a transaction, or a snapshot session, is refused.hint: applies only to the query on the source collection.collation: applies to the source query and to the user's stages. The_idjoin then runs separately with the source's default collation (into a second temporary collection), so a case-insensitive collation cannot join"a"to"A". Without it, the temporary collection gets the source's default collation, so string matching and sorting behave the same as on the source.comment,maxTimeMS: passed to the source query and to the server pipeline (commentis dropped from the query on backends that do not support it).let: passed to the server pipeline, and to the source query when the stage has afilter(for$$variables in its$expr).- anything else: passed to the server pipeline.
Returns
- PredictCursor or cursor: A
PredictCursorfor a$predictpipeline; otherwise whatevercollection.aggregatereturns.
Raises
- ValueError: If
$predictis not only the first stage, the stage has other fields beside it,modelis missing, the spec has unknown keys,limit/batchSizeare not positive integers, the model does not exist or is not queryable, the session is in a transaction or is a snapshot session, a document contains the reservedMETA_FIELD, a view or time-series source returns an_idtwice, or$metais used where it is not available ($lookup/$unionWithsub-pipelines,$merge'slet/whenMatched). - RuntimeError: If the temporary collection lost rows while the call ran (dropped by
cleanup_temporary_collections()with a shortolder_than).
Examples
Usage:
aggregate(coll, [ {"$predict": {"model": "imdb_rating", "filter": {"year": {"$gte": 2010}}}}, {"$sort": {"p": {"$meta": "prediction"}}}, # a $meta sort key sorts descending {"$limit": 10}, ], maxTimeMS=60_000).to_list()
Let Collection.aggregate run $predict and add the model helpers as methods.
After this, coll.aggregate(pipeline, ...) calls aggregate() (pymongo's positional
session, let and comment arguments are still accepted), and
coll.create_prediction_model, update_prediction_model, list_prediction_models
and drop_prediction_model call the functions of the same name with coll first.
Calling it again for a class already patched does nothing.
Parameters
- *classes (type):
Collection classes to patch (e.g.
mongomock.Collection). Default:pymongo.collection.Collection.
Examples
Usage:
predictions_api.enable() coll.create_prediction_model(PredictionModel({"target": "room_type"}, "room_type")) coll.aggregate([{"$predict": {"model": "room_type"}}]).to_list() predictions_api.disable()
The materialised result of a $predict pipeline.
Behaves like a pymongo CommandCursor over a list: iterate it, or call next(),
try_next(), to_list(), close(), check alive, or use it as a context manager.
Parameters
- docs (list of dict): The pipeline output.
- stats (dict, optional): Timing statistics (default: empty).
Attributes
- stats (dict):
nDocuments: number of documents predicted (after removing duplicates);fetchSeconds: seconds spent fetching them;predictSeconds: featurizing and predicting on the client;uploadSeconds: uploading the predictions;serverSeconds: running the rest of the pipeline on the server.
Drop the temporary collections that $predict calls left behind (a killed process,
or a user without the dropCollection privilege). Returns the dropped names.
Collections created less than older_than ago may belong to a running call and are kept;
names without a creation time (older versions) count as old. A call whose collection is
dropped while it runs raises RuntimeError rather than returning partial results, so keep
older_than above the longest $predict call.