/** * Shared core for the plain (non-averaged) stochastic gradient descent * estimators (SGDClassifier, SGDRegressor, Perceptron). * * Faithful port of scikit-learn's `_plain_sgd` (sklearn/linear_model/_sgd_fast.pyx): * - per-sample updates over seeded-shuffled epochs; * - learning-rate schedules `constant`, `optimal` (Léon Bottou's t0 * heuristic, exactly as in sklearn's `_init_t`), `invscaling`, `adaptive`; * - L2 regularization by per-step weight scaling (intercept unregularized); * - L1 / elastic-net via the cumulative-penalty trick (Tsuruoka et al. 2009); * - convergence: stop when epoch loss > best - tol for nIterNoChange * consecutive epochs (for `adaptive`, divide eta by 5 instead until * eta <= 1e-6). */ export type SGDPenalty = 'l2' | 'l1' | 'elasticnet' | null; export type SGDLearningRate = 'constant' | 'optimal' | 'invscaling' | 'adaptive'; /** * A pointwise loss. `p` is the model output (decision value / prediction), * `y` the target (±1 for classification losses, real for regression losses). * `dloss` is the (sub)gradient of the loss with respect to `p`. */ export interface SGDLoss { loss(p: number, y: number): number; dloss(p: number, y: number): number; } export type ClassificationLossName = 'hinge' | 'logLoss' | 'modifiedHuber' | 'squaredHinge' | 'perceptron'; export declare function getClassificationLoss(name: ClassificationLossName): SGDLoss; export type RegressionLossName = 'squaredError' | 'huber' | 'epsilonInsensitive' | 'squaredEpsilonInsensitive'; export declare function getRegressionLoss(name: RegressionLossName, epsilon: number): SGDLoss; export interface PlainSGDConfig { X: number[][]; /** Targets: ±1 for classification losses, real values for regression. */ y: number[]; loss: SGDLoss; penalty: SGDPenalty; alpha: number; l1Ratio: number; fitIntercept: boolean; maxIter: number; /** Pass null to disable the convergence check (always run maxIter epochs). */ tol: number | null; shuffle: boolean; /** Fit-local RNG in [0, 1). */ rand: () => number; learningRate: SGDLearningRate; eta0: number; powerT: number; nIterNoChange: number; } export interface PlainSGDResult { weights: number[]; intercept: number; /** Number of epochs actually run. */ nIter: number; } /** In-place Fisher-Yates shuffle driven by the supplied RNG. */ export declare function shuffleIndices(indices: number[], rand: () => number): void; export declare function plainSGD(config: PlainSGDConfig): PlainSGDResult; export declare function validateSGDData(X: number[][], y: number[]): void; export declare function validatePenalty(penalty: unknown): SGDPenalty; export declare function validateSchedule(learningRate: SGDLearningRate, eta0: number, alpha: number): void;