1- # Author: Peter Prettenhofer <[email protected] >2- # Mathieu Blondel (partial_fit support)
3- # Rob Zinkov (passive-aggressive)
4- # Lars Buitinck
5- #
6- # License: BSD 3 clause
1+ {{py:
72
3+ """
4+ Template file to easily generate fused types consistent code using Tempita
5+ (https://github.com/cython/cython/blob/master/Cython/Tempita/_tempita.py).
86
7+ Generated file: _sgd_fast.pyx
8+
9+ Each relevant function is duplicated for the dtypes float and double.
10+ The keywords between double braces are substituted in setup.py.
11+
12+ Authors: Peter Prettenhofer <
[email protected] >
13+ Mathieu Blondel (partial_fit support)
14+ Rob Zinkov (passive-aggressive)
15+ Lars Buitinck
16+
17+ License: BSD 3 clause
18+ """
19+
20+ # The dtypes are defined as follows (name_suffix, c_type, np_type)
21+ dtypes = [
22+ ("64", "double", "np.float64"),
23+ ("32", "float", "np.float32"),
24+ ]
25+
26+ }}
27+
28+ #------------------------------------------------------------------------------
29+
30+ """
31+ SGD implementation
32+ WARNING: Do not edit .pyx file directly, it is generated from .pyx.tp
33+ """
34+
35+ from cython cimport floating
936import numpy as np
1037from time import time
1138
1239from libc.math cimport exp, log, pow, fabs
1340cimport numpy as cnp
1441from numpy.math cimport INFINITY
1542cdef extern from "_sgd_fast_helpers.h":
16- bint skl_isfinite(double ) nogil
43+ bint skl_isfinite32(float) nogil
44+ bint skl_isfinite64(double) nogil
1745
18- from ..utils._weight_vector cimport WeightVector64 as WeightVector
19- from ..utils._seq_dataset cimport SequentialDataset64 as SequentialDataset
46+ from ..utils._weight_vector cimport WeightVector32, WeightVector64
47+ from ..utils._seq_dataset cimport SequentialDataset32, SequentialDataset64
2048
2149cnp.import_array()
2250
@@ -372,37 +400,48 @@ cdef class SquaredEpsilonInsensitive(Regression):
372400 def __reduce__(self):
373401 return SquaredEpsilonInsensitive, (self.epsilon,)
374402
375-
376- def _plain_sgd (const double[::1] weights ,
377- double intercept ,
378- const double[::1] average_weights ,
379- double average_intercept ,
380- LossFunction loss ,
381- int penalty_type ,
382- double alpha , double C ,
383- double l1_ratio ,
384- SequentialDataset dataset ,
385- const unsigned char[::1] validation_mask ,
386- bint early_stopping , validation_score_cb ,
387- int n_iter_no_change ,
388- unsigned int max_iter , double tol , int fit_intercept ,
389- int verbose , bint shuffle , cnp.uint32_t seed ,
390- double weight_pos , double weight_neg ,
391- int learning_rate , double eta0 ,
392- double power_t ,
393- bint one_class ,
394- double t = 1.0 ,
395- double intercept_decay = 1.0 ,
396- int average = 0 ):
403+ {{for name_suffix, c_type, np_type in dtypes}}
404+
405+ def _plain_sgd{{name_suffix}}(
406+ const {{c_type}}[::1] weights,
407+ double intercept,
408+ const {{c_type}}[::1] average_weights,
409+ double average_intercept,
410+ LossFunction loss,
411+ int penalty_type,
412+ double alpha,
413+ double C,
414+ double l1_ratio,
415+ SequentialDataset{{name_suffix}} dataset,
416+ const unsigned char[::1] validation_mask,
417+ bint early_stopping,
418+ validation_score_cb,
419+ int n_iter_no_change,
420+ unsigned int max_iter,
421+ double tol,
422+ int fit_intercept,
423+ int verbose,
424+ bint shuffle,
425+ cnp.uint32_t seed,
426+ double weight_pos,
427+ double weight_neg,
428+ int learning_rate,
429+ double eta0,
430+ double power_t,
431+ bint one_class,
432+ double t=1.0,
433+ double intercept_decay=1.0,
434+ int average=0,
435+ ):
397436 """SGD for generic loss functions and penalties with optional averaging
398437
399438 Parameters
400439 ----------
401- weights : ndarray[double , ndim=1]
440+ weights : ndarray[{{c_type}} , ndim=1]
402441 The allocated vector of weights.
403442 intercept : double
404443 The initial intercept.
405- average_weights : ndarray[double , ndim=1]
444+ average_weights : ndarray[{{c_type}} , ndim=1]
406445 The average weights as computed for ASGD. Should be None if average
407446 is 0.
408447 average_intercept : double
@@ -489,8 +528,8 @@ def _plain_sgd(const double[::1] weights,
489528 cdef Py_ssize_t n_samples = dataset.n_samples
490529 cdef Py_ssize_t n_features = weights.shape[0]
491530
492- cdef WeightVector w = WeightVector(weights, average_weights)
493- cdef double * x_data_ptr = NULL
531+ cdef WeightVector{{name_suffix}} w = WeightVector{{name_suffix}} (weights, average_weights)
532+ cdef {{c_type}} *x_data_ptr = NULL
494533 cdef int *x_ind_ptr = NULL
495534
496535 # helper variables
@@ -505,9 +544,9 @@ def _plain_sgd(const double[::1] weights,
505544 cdef double score = 0.0
506545 cdef double best_loss = INFINITY
507546 cdef double best_score = -INFINITY
508- cdef double y = 0.0
509- cdef double sample_weight
510- cdef double class_weight = 1.0
547+ cdef {{c_type}} y = 0.0
548+ cdef {{c_type}} sample_weight
549+ cdef {{c_type}} class_weight = 1.0
511550 cdef unsigned int count = 0
512551 cdef unsigned int train_count = n_samples - np.sum(validation_mask)
513552 cdef unsigned int epoch = 0
@@ -520,10 +559,10 @@ def _plain_sgd(const double[::1] weights,
520559 cdef long long sample_index
521560
522561 # q vector is only used for L1 regularization
523- cdef double [::1 ] q = None
524- cdef double * q_data_ptr = NULL
562+ cdef {{c_type}} [::1] q = None
563+ cdef {{c_type}} * q_data_ptr = NULL
525564 if penalty_type == L1 or penalty_type == ELASTICNET:
526- q = np.zeros((n_features,), dtype = np.float64 , order = " c" )
565+ q = np.zeros((n_features,), dtype={{np_type}} , order="c")
527566 q_data_ptr = &q[0]
528567 cdef double u = 0.0
529568
@@ -627,7 +666,7 @@ def _plain_sgd(const double[::1] weights,
627666
628667 if penalty_type == L1 or penalty_type == ELASTICNET:
629668 u += (l1_ratio * eta * alpha)
630- l1penalty(w, q_data_ptr, x_ind_ptr, xnnz, u)
669+ l1penalty{{name_suffix}} (w, q_data_ptr, x_ind_ptr, xnnz, u)
631670
632671 t += 1
633672 count += 1
@@ -694,15 +733,28 @@ def _plain_sgd(const double[::1] weights,
694733 epoch + 1
695734 )
696735
736+ {{endfor}}
737+
738+
739+ cdef inline bint skl_isfinite(floating w) noexcept nogil:
740+ if floating is float:
741+ return skl_isfinite32(w)
742+ else:
743+ return skl_isfinite64(w)
697744
698- cdef bint any_nonfinite(const double * w, int n) noexcept nogil:
745+
746+ cdef inline bint any_nonfinite(const floating *w, int n) noexcept nogil:
699747 for i in range(n):
700748 if not skl_isfinite(w[i]):
701749 return True
702750 return 0
703751
704752
705- cdef double sqnorm(double * x_data_ptr, int * x_ind_ptr, int xnnz) noexcept nogil:
753+ cdef inline double sqnorm(
754+ floating * x_data_ptr,
755+ int * x_ind_ptr,
756+ int xnnz,
757+ ) noexcept nogil:
706758 cdef double x_norm = 0.0
707759 cdef int j
708760 cdef double z
@@ -712,8 +764,15 @@ cdef double sqnorm(double * x_data_ptr, int * x_ind_ptr, int xnnz) noexcept nogi
712764 return x_norm
713765
714766
715- cdef void l1penalty(WeightVector w, double * q_data_ptr,
716- int * x_ind_ptr, int xnnz, double u) noexcept nogil:
767+ {{for name_suffix, c_type, np_type in dtypes}}
768+
769+ cdef void l1penalty{{name_suffix}}(
770+ WeightVector{{name_suffix}} w,
771+ {{c_type}} * q_data_ptr,
772+ int *x_ind_ptr,
773+ int xnnz,
774+ double u,
775+ ) noexcept nogil:
717776 """Apply the L1 penalty to each updated feature
718777
719778 This implements the truncated gradient approach by
@@ -723,7 +782,7 @@ cdef void l1penalty(WeightVector w, double * q_data_ptr,
723782 cdef int j = 0
724783 cdef int idx = 0
725784 cdef double wscale = w.wscale
726- cdef double * w_data_ptr = w.w_data_ptr
785+ cdef {{c_type}} *w_data_ptr = w.w_data_ptr
727786 for j in range(xnnz):
728787 idx = x_ind_ptr[j]
729788 z = w_data_ptr[idx]
@@ -736,3 +795,5 @@ cdef void l1penalty(WeightVector w, double * q_data_ptr,
736795 0.0, w_data_ptr[idx] + ((u - q_data_ptr[idx]) / wscale))
737796
738797 q_data_ptr[idx] += wscale * (w_data_ptr[idx] - z)
798+
799+ {{endfor}}
0 commit comments