svm-models.md
291 lines · skills/scikit-survival/references/svm-models.md at 5f2bc03
What survival SVMs predict
Survival SVMs optimize ranking, regression, or a mixture. They generally return a scalar score, not a baseline survival function or cumulative hazard function. Therefore:
- use concordance or cumulative/dynamic AUC only after confirming score direction;
- do not pass SVM output to Brier metrics;
- do not convert a margin to event probability without a separately validated calibration model and protocol.
Fast linear survival SVM
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sksurv.svm import FastSurvivalSVM
model = make_pipeline(
StandardScaler(),
FastSurvivalSVM(
alpha=1.0,
rank_ratio=1.0,
max_iter=1000,
tol=1e-5,
random_state=20260723,
),
)
model.fit(X_train, y_train)
risk = model.predict(X_test)Current signature:
FastSurvivalSVM(
alpha=1, *,
rank_ratio=1.0,
fit_intercept=False,
max_iter=20,
verbose=False,
tol=None,
optimizer=None,
random_state=None,
timeit=False,
)Key semantics:
rank_ratio=1.0: ranking-only objective; higher predictions indicate shorter survival/higher event risk.0 < rank_ratio < 1: mixed ranking and regression.rank_ratio=0.0: regression-only objective.- When
rank_ratio < 1,predict()exponentiates the log-time predictor and returns original-time values: lower prediction means shorter survival. For a metric requiring higher event risk, use-predictionand document the conversion. alphaweights the data-fit/ranking objective relative to the penalty, so larger values mean less regularization; tune it within inner CV.
Do not use an arbitrary sign simply because a C-index improves. The sign follows the model objective and target interpretation.
Raw lines 16 to 67Fast kernel survival SVM
from sksurv.svm import FastKernelSurvivalSVM
model = FastKernelSurvivalSVM(
alpha=1.0,
rank_ratio=1.0,
kernel="rbf",
gamma=0.05,
max_iter=100,
tol=1e-5,
random_state=20260723,
)
model.fit(X_train_scaled, y_train)
risk = model.predict(X_test_scaled)Current signature:
FastKernelSurvivalSVM(
alpha=1, *,
rank_ratio=1.0,
fit_intercept=False,
kernel="rbf",
gamma=None,
degree=3,
coef0=1,
kernel_params=None,
max_iter=20,
verbose=False,
tol=None,
optimizer=None,
random_state=None,
timeit=False,
)Kernel choices follow scikit-learn pairwise-kernel behavior, including "linear", "poly", "rbf", "sigmoid", callable kernels, and "precomputed" where supported. Do not copy older examples that use gamma="scale" without checking the current API; the current scikit-survival parameter default is None.
Kernel fitting and prediction depend on training rows and can require quadratic-size kernel matrices. Enforce row/memory bounds before fitting.
Raw lines 68 to 114Hinge, Minlip, and naive formulations
Current additional estimators:
HingeLossSurvivalSVM(alpha=1.0, solver="ecos", kernel="linear", pairs="all", ...)MinlipSurvivalAnalysis(alpha=1.0, solver="ecos", kernel="linear", pairs="nearest", ...)NaiveSurvivalSVM(penalty="l2", loss="squared_hinge", dual=False, alpha=1.0, ...)
Important current differences:
HingeLossSurvivalSVMandMinlipSurvivalAnalysisdo not have arandom_stateconstructor parameter.- Their convex optimization defaults to the ECOS solver.
- Pair construction and kernel matrices can become expensive.
NaiveSurvivalSVMuses a linear-SVM-style formulation and is mainly useful for small comparisons; it is not the fast implementation.
Use the exact current signature rather than transferring parameters across SVM classes.
Raw lines 115 to 136Scaling and explicit preprocessing
Scale continuous features and fit scaling only on training folds:
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
preprocess = ColumnTransformer(
[
(
"num",
make_pipeline(SimpleImputer(strategy="median"), StandardScaler()),
numeric_columns,
),
(
"cat",
make_pipeline(
SimpleImputer(strategy="most_frequent"),
OneHotEncoder(
drop="first",
handle_unknown="ignore",
sparse_output=False,
),
),
categorical_columns,
),
],
sparse_threshold=0.0,
)
model = make_pipeline(
preprocess,
FastSurvivalSVM(rank_ratio=1.0, random_state=20260723),
)Do not call StandardScaler.fit_transform(X) before cross-validation. Keeping it inside the pipeline makes every inner and outer fold independent.
Kernel preprocessing
For "precomputed", training input must be a square (n_train, n_train) kernel matrix; test input must be (n_test, n_train) with columns in the identical training order.
sksurv.kernels.ClinicalKernelTransform and clinical_kernel() support mixed continuous, ordinal, and nominal DataFrame columns, including pandas/Polars in 0.28. They do not accept an ad hoc list of (clinical, molecular) tuples as a special API. Fit the transform on a clearly typed training DataFrame or explicitly precompute the kernel:
from sksurv.kernels import ClinicalKernelTransform
from sksurv.svm import FastKernelSurvivalSVM
kernel = ClinicalKernelTransform()
kernel_train = kernel.fit_transform(X_train_typed)
kernel_test = kernel.transform(X_test_typed)
model = FastKernelSurvivalSVM(
kernel="precomputed",
rank_ratio=1.0,
random_state=20260723,
)
model.fit(kernel_train, y_train)
risk = model.predict(kernel_test)The one-shot clinical_kernel(X_test, X_train) infers numeric ranges from both arguments, changing normalization with the test cohort. Prefer the fitted transform above. The released one-shot Cython routine also allocates its range buffer by row count while writing one value per numeric feature: avoid that routine when numeric features outnumber rows (source finding; unsafe case not executed). Put ClinicalKernelTransform() inside the pipeline for CV so its ranges and reference rows are learned afresh in every fold. Out-of-training-range values can produce negative similarities in the native transform; inspect support and define any clipping/extrapolation policy before evaluating.
Any data-dependent kernel typing, scaling, or parameter choice belongs inside the training/CV protocol.
Raw lines 178 to 219Nested tuning
Tune at least alpha; for kernel models also tune kernel and its parameters. Keep the grid bounded:
from sklearn.model_selection import GridSearchCV
search = GridSearchCV(
pipeline,
{
"fastkernelsurvivalsvm__alpha": [0.1, 1.0, 10.0],
"fastkernelsurvivalsvm__gamma": [0.01, 0.05, 0.2],
},
cv=inner_splits,
error_score="raise",
n_jobs=1,
)
search.fit(X_outer_train, y_outer_train)The actual parameter prefix depends on pipeline step names. Run this search within each outer fold for a nested-CV performance estimate. Do not:
- fit a scaler or kernel transform before the outer split;
- select the sign, kernel, or horizon on outer-validation results;
- reuse the final test set to choose
alpha/gamma; - compare SVM Brier scores, because SVMs do not output survival probabilities.
For IPCW metrics, fit the censoring distribution on the corresponding outer training outcomes and keep the time grid within that fold's support.
Raw lines 220 to 251Choosing an SVM candidate
- Linear ranking objective: a scalable margin-based discrimination baseline.
- Kernel ranking objective: nonlinear relationships when row count permits.
- Mixed/regression objective: time-oriented score, with different sign semantics.
- Need absolute survival probability: choose a model with
predict_survival_function()or add a separately validated calibration stage. - Need coefficient/hazard-ratio interpretation: use an appropriate Cox model, not an SVM margin.
These are capability distinctions, not guarantees of performance.
Raw lines 252 to 263Interpretation
SVM margins are arbitrary-scale predictions. A high C-index or dynamic AUC says that orderings discriminate under the chosen censoring estimator and horizon; it does not establish:
- probability calibration;
- causal or treatment effects;
- transportability;
- subgroup fairness;
- clinical or decision utility.
Report optimization convergence, score direction, kernel, preprocessing, tuning resamples, and censoring assumptions.
Raw lines 264 to 278