pip install scikit-surprise★Needs NumPy + a C compiler.conda install -c conda-forge scikit-surpriseEasiest route on Windows.from surprise import SVD, Dataset, Reader★Algorithms live in the top-level namespace.from surprise import accuracy, dumpMetrics + model persistence.from surprise.model_selection import cross_validateCV, splitters & search live here.
reader = Reader(rating_scale=(1, 5))★Declare the rating range.Dataset.load_from_df(df[[u,i,r]], reader)★From a DataFrame — 3 cols, in that order.Reader(line_format="user item rating", sep="\t")Describe a raw text/CSV layout.Dataset.load_from_file(path, reader)From a CSV/TSV on disk.Dataset.load_builtin("ml-100k")Auto-download MovieLens / Jester.data.build_full_trainset()★Use all ratings as one trainset.
data = Dataset.load_builtin("ml-100k")Get some ratings.algo = SVD()★Pick an algorithm.cross_validate(algo, data, cv=5)★Benchmark it — folds + metrics in one call.tr, te = train_test_split(data, test_size=.25)Or split once for a quick holdout.algo.fit(tr).test(te)★Train + predict in a single chain.
SVD()★Funk matrix factorization — the go-to.SVDpp()SVD++ folds in implicit signal; slower, often best.NMF()Non-negative matrix factorization.KNNBaseline()★Neighborhood + baseline — strongest KNN.BaselineOnly()μ + bᵘ + bᵢ. Surprisingly hard to beat.SlopeOne() / CoClustering()Lightweight alternatives.NormalPredictor()Random baseline — your sanity floor.
SVD(n_factors=100, n_epochs=20)★Latent dimensions & SGD passes.SVD(lr_all=.005, reg_all=.02)Learning rate & L2 regularization.SVD(biased=False)Drops bias terms → plain PMF.SVDpp(n_factors=20)Uses implicit feedback; noticeably slower.NMF(n_factors=15, n_epochs=50)Non-negative;reg_pu/reg_qi.random_state=42Pin the seed for reproducibility.
KNNBasic(k=40, min_k=1)Weighted average overkneighbors.KNNWithMeans()★Subtracts each user/item mean first.KNNWithZScore()z-score normalizes before averaging.KNNBaseline()★Baseline-corrected — usually the best KNN.sim_options={"user_based": False}Item–item; often faster & better.
BaselineOnly(bsl_options=...)Just μ + bᵘ + bᵢ — no interactions.NormalPredictor()Samples from N(μ, σ) of the trainset.SlopeOne()Item-based, parameter-free, quick.CoClustering(n_cltr_u=3, n_cltr_i=3)Co-clusters users × items.
algo.fit(trainset)★Learn from aTrainset.preds = algo.test(testset)★Predict a whole list of (u, i) pairs.algo.predict(uid, iid, r_ui=4)★One rating — raw ids, as strings.pred.estThe estimate r̂ₕᵢ on a Prediction.algo.fit(tr).test(te)Chain them in one line.pred.details["was_impossible"]True when the pair couldn't be scored.
train_test_split(data, test_size=.25)★Quick single holdout.cross_validate(algo, data, cv=5, measures=[...])★k-fold + metrics, fully automatic.kf = KFold(n_splits=5)Manual:for tr, te in kf.split(data).ShuffleSplit() / RepeatedKFold()Randomized / repeated variants.LeaveOneOut()One held-out rating per user.PredefinedKFold()Folds already split into files on disk.
gs = GridSearchCV(SVD, grid, cv=3)★Pass the class, not an instance.grid = {"n_epochs":[5,10], "lr_all":[.002,.005]}Every combination is tried.gs.fit(data)★Runs the full sweep.gs.best_params["rmse"]Winning combo (+best_score).gs.best_estimator["rmse"]Ready-to-fit tuned algorithm.RandomizedSearchCV(SVD, distr, n_iter=10)Sample the space instead of gridding it.
accuracy.rmse(preds)★Root mean squared error.accuracy.mae(preds)★Mean absolute error.accuracy.mse(preds)Mean squared error.accuracy.fcp(preds)Fraction of concordant pairs (ranking-ish).# precision@k / recall@kNot built in — roll your own frompreds.
"name": "msd"Default; alsocosine,pearson,pearson_baseline."user_based": True★Users vs items — huge impact on speed & error."min_support": 5Below this many co-ratings → sim = 0."shrinkage": 100pearson_baselineonly.KNNBasic(sim_options=sim_options)Feed it to any KNN algorithm.
"method": "als"★ALS (default) or"sgd".als: "reg_i":10, "reg_u":15, "n_epochs":10Item / user reg & iterations.sgd: "learning_rate":.005, "reg":.02Gradient-descent variant.BaselineOnly(bsl_options=bsl_options)Also used byKNNBaseline& pearson_baseline.
trainset = data.build_full_trainset()Train on everything first.anti = trainset.build_anti_testset()★Every (u, i) pair the user hasn't rated.preds = algo.test(anti)Score all the blanks.sort preds by .est per uid → take nGroup by user, keep the top few (DIY helper).len(anti) ≈ users × itemsmemoryAnti-testset can be enormous — batch it.
dump.dump(fname, predictions=preds, algo=algo)Save model + predictions to disk._, algo = dump.load(fname)Reload later.trainset.n_users, .n_items, .global_meanQuick dataset stats.trainset.to_inner_uid(raw)Raw→inner id (andto_raw_uidback).trainset.ur[inner_uid]A user's (item, rating) list.
GridSearchCV(SVD)classPass the class, notSVD().predict("196", "302")raw idsRaw ids are the strings from your file.explicit ratings onlyscopeNo implicit clicks, no content features.unknown user/itemfallbackFalls back to global mean; flagswas_impossible.user_based=True defaultslowItem-based is often faster & more accurate.
Prediction(uid, iid, r_ui, est, details)The named tuple you get per pair.uid · iidRaw user & item ids.r_uiTrue rating if known, elseNone.estThe estimate r̂ₕᵢ — what you rank on.details["was_impossible"]Whether it could actually be scored.raw id = file (str) · inner id = intSurprise indexes internally with inner ids.