本文是根据sklean官方文档进行翻译和学习的,如果理解有误欢迎留言指正,谢谢。
def fit(self, X, y, sample_weight=None):
"""根据给定的训练数据拟合模型.
参数
----------
X : {array-like, sparse matrix}, shape (n_samples, n_features)
训练向量, 其中n_samples 表示样本数量,n_features特征数量.
y : array-like, shape (n_samples,)
相对于X的目标向量
sample_weight : array-like, shape (n_samples,) optional
分配给单个样本的权重数组。
如果没有提供,那么每个样本都具有单位权重
返回值
-------
self : object
Returns self.
"""
#判断self.C的是否是正数。self.C是正则项系数的倒数,一个常数C,默认:C=1.0
if not isinstance(self.C, numbers.Number) or self.C < 0:
raise ValueError("Penalty term must be positive; got (C=%r)"
% self.C)
#判断self.max_iter是否是正数。self.max_iter是算法收敛时的最大迭代次数
if not isinstance(self.max_iter, numbers.Number) or self.max_iter < 0:
raise ValueError("Maximum number of iteration must be positive;"
" got (max_iter=%r)" % self.max_iter)
#判断self.tol是否是正数。self.tol是容差
if not isinstance(self.tol, numbers.Number) or self.tol < 0:
raise ValueError("Tolerance for stopping criteria must be "
"positive; got (tol=%r)" % self.tol)
#self.solver参数决定了我们对逻辑回归损失函数的优化方法,有四种算法可以选择
liblinear:使用了开源的liblinear库实现,内部使用了坐标轴下降法来迭代优化损失函数。
lbfgs:拟牛顿法的一种,利用损失函数二阶导数矩阵即海森矩阵来迭代优化损失函数。
newton-cg:也是牛顿法家族的一种,利用损失函数二阶导数矩阵即海森矩阵来迭代优化损失函数。
sag:即随机平均梯度下降,是梯度下降法的变种,和普通梯度下降法的区别是每次迭代仅仅用一部分的样本来计算梯度
#定义数据类型
if self.solver in ['newton-cg']:
_dtype = [np.float64, np.float32]
else:
_dtype = np.float64
#标准estimator器的输入验证
X, y = check_X_y(X, y, accept_sparse='csr', dtype=_dtype,
order="C")
#验证目标y属于非回归类型.
check_classification_targets(y)
#去除y中的重复数字,并排序
self.classes_ = np.unique(y)
#输入数据的样本数和维度
n_samples, n_features = X.shape
#验证算法的相关选项是否符合要求
_check_solver_option(self.solver, self.multi_class, self.penalty,
self.dual)
#根据不同的算法迭代训练模型
if self.solver == 'liblinear':
if self.n_jobs != 1:
warnings.warn("'n_jobs' > 1 does not have any effect when"
" 'solver' is set to 'liblinear'. Got 'n_jobs'"
" = {}.".format(self.n_jobs))
self.coef_, self.intercept_, n_iter_ = _fit_liblinear(
X, y, self.C, self.fit_intercept, self.intercept_scaling,
self.class_weight, self.penalty, self.dual, self.verbose,
self.max_iter, self.tol, self.random_state,
sample_weight=sample_weight)
self.n_iter_ = np.array([n_iter_])
return self
if self.solver in ['sag', 'saga']:
max_squared_sum = row_norms(X, squared=True).max()
else:
max_squared_sum = None
n_classes = len(self.classes_)
classes_ = self.classes_
if n_classes < 2:
raise ValueError("This solver needs samples of at least 2 classes"
" in the data, but the data contains only one"
" class: %r" % classes_[0])
if len(self.classes_) == 2:
n_classes = 1
classes_ = classes_[1:]
if self.warm_start:
warm_start_coef = getattr(self, 'coef_', None)
else:
warm_start_coef = None
if warm_start_coef is not None and self.fit_intercept:
warm_start_coef = np.append(warm_start_coef,
self.intercept_[:, np.newaxis],
axis=1)
self.coef_ = list()
self.intercept_ = np.zeros(n_classes)
# Hack so that we iterate only once for the multinomial case.
if self.multi_class == 'multinomial':
classes_ = [None]
warm_start_coef = [warm_start_coef]
if warm_start_coef is None:
warm_start_coef = [None] * n_classes
path_func = delayed(logistic_regression_path)
# The SAG solver releases the GIL so it's more efficient to use
# threads for this solver.
if self.solver in ['sag', 'saga']:
backend = 'threading'
else:
backend = 'multiprocessing'
fold_coefs_ = Parallel(n_jobs=self.n_jobs, verbose=self.verbose,
backend=backend)(
path_func(X, y, pos_class=class_, Cs=[self.C],
fit_intercept=self.fit_intercept, tol=self.tol,
verbose=self.verbose, solver=self.solver,
multi_class=self.multi_class, max_iter=self.max_iter,
class_weight=self.class_weight, check_input=False,
random_state=self.random_state, coef=warm_start_coef_,
penalty=self.penalty,
max_squared_sum=max_squared_sum,
sample_weight=sample_weight)
for class_, warm_start_coef_ in zip(classes_, warm_start_coef))
fold_coefs_, _, n_iter_ = zip(*fold_coefs_)
self.n_iter_ = np.asarray(n_iter_, dtype=np.int32)[:, 0]
if self.multi_class == 'multinomial':
self.coef_ = fold_coefs_[0][0]
else:
self.coef_ = np.asarray(fold_coefs_)
self.coef_ = self.coef_.reshape(n_classes, n_features +
int(self.fit_intercept))
if self.fit_intercept:
self.intercept_ = self.coef_[:, -1]
self.coef_ = self.coef_[:, :-1]
return self
def check_X_y(X, y, accept_sparse=False, dtype="numeric", order=None,
copy=False, force_all_finite=True, ensure_2d=True,
allow_nd=False, multi_output=False, ensure_min_samples=1,
ensure_min_features=1, y_numeric=False,
warn_on_dtype=False, estimator=None):
"""标准estimator器的输入验证。
检查X和y的长度, 让X是2维的y是1维的.
标准输入检查只适用于y,比如检查y中是否含有np.nan或者np.inf这类数据
对于多标记类型的y,要通过设置multi_output=True才可以允许使用2维和稀疏数据y.
如果X的类型是object,尝试转换为浮点型或者引发异常
Parameters
----------
X : nd-array, list or sparse matrix
输入数据.
y : nd-array, list or sparse matrix
标签.
accept_sparse : string, boolean or list of string (default=False)
字符串[s]表示允许的稀疏矩阵格式。比如'csc','csr', 等.
如果输入是稀疏的,但不是允许的格式,它将被转换为第一个列出的格式。
True 表示允许输入为任何格式
False 表示如果输入是稀疏的则会引发异常
dtype : string, type, list of types or None (default="numeric")
结果集的数据类型.
如果为None,将会保留输入时的数据类型.
如果为"numeric", 数据类型将被保留除非array.dtype是object.
如果是一个类型的list, 如果输入的dtype不在列表中,则会且只会转换为列表中的第一个类型。
order : 'F', 'C' or None (default=None)
数组是否被强制转换为fortran或c风格
copy : boolean (default=False)
是否触发强制复制
如果copy=False, 一次转换就会触发一次复制
force_all_finite : boolean or 'allow-nan', (default=True)
是否对输入数据x中的np.inf 和 np.nan引发异常。
这个参数不影响y是否具有np.inf 和 np.nan值。
允许的参数有:
- True: 使所有X的值都是有限的
- False: 允许X中有np.inf和np.nan值
- 'allow-nan': 只允许X中有np.nan值. 不允许数据是无穷的
ensure_2d : boolean (default=True)
是否使X至少为2维。
allow_nd : boolean (default=False)
是否允许X的维度大于2.
multi_output : boolean (default=False)
是否允许输出y是2维的(数组或者稀疏矩阵).
如果为false, 就会验证y是不是一个向量.
y中不可以有np.nan或者 np.inf 这样的值如果multi_output=True.
ensure_min_samples : int (default=1)
确保X在第一个轴中有最小数量的样本。(行是2D数组).
ensure_min_features : int (default=1)
确保2D数组有一些最小的特性(列)。默认值1拒绝空数据集
Make sure that the 2D array has some minimum number of features
y_numeric : boolean (default=False)
是否确保y具有数值类型。如果y的类型是object,则将其转换为float64.
只用于回归算法中.
warn_on_dtype : boolean (default=False)
如果输入数据结构的类型和要求的类型不一致,则给出警告DataConversionWarning
estimator : str or estimator instance (default=None)
如果忽略,请在警告消息中包含estimator的名称。
返回值
-------
X_converted : object
转换和验证后的X
y_converted : object
转换和验证后的y.
"""
X = check_array(X, accept_sparse, dtype, order, copy, force_all_finite,
ensure_2d, allow_nd, ensure_min_samples,
ensure_min_features, warn_on_dtype, estimator)
if multi_output:
y = check_array(y, 'csr', force_all_finite=True, ensure_2d=False,
dtype=None)
else:
y = column_or_1d(y, warn=True)
_assert_all_finite(y)
if y_numeric and y.dtype.kind == 'O':
y = y.astype(np.float64)
check_consistent_length(X, y)
return X, y
def check_classification_targets(y):
"""确保目标y属于非回归类型.
只有以下目标类型(在type_of_target中定义的)被允许:
'binary', 'multiclass', 'multiclass-multioutput',
'multilabel-indicator', 'multilabel-sequences'
参数
----------
y : array-like
"""
y_type = type_of_target(y)
if y_type not in ['binary', 'multiclass', 'multiclass-multioutput',
'multilabel-indicator', 'multilabel-sequences']:
raise ValueError("Unknown label type: %r" % y_type)
def _check_solver_option(solver, multi_class, penalty, dual):
"""检查算法的相关参数"""
if solver not in ['liblinear', 'newton-cg', 'lbfgs', 'sag', 'saga']:
raise ValueError("Logistic Regression supports only liblinear, "
"newton-cg, lbfgs, sag and saga solvers, got %s"
% solver)
if multi_class not in ['multinomial', 'ovr']:
raise ValueError("multi_class should be either multinomial or "
"ovr, got %s" % multi_class)
if multi_class == 'multinomial' and solver == 'liblinear':
raise ValueError("Solver %s does not support "
"a multinomial backend." % solver)
if solver not in ['liblinear', 'saga']:
if penalty != 'l2':
raise ValueError("Solver %s supports only l2 penalties, "
"got %s penalty." % (solver, penalty))
if solver != 'liblinear':
if dual:
raise ValueError("Solver %s supports only "
"dual=False, got dual=%s" % (solver, dual))
def _fit_liblinear(X, y, C, fit_intercept, intercept_scaling, class_weight,
penalty, dual, verbose, max_iter, tol,
random_state=None, multi_class='ovr',
loss='logistic_regression', epsilon=0.1,
sample_weight=None):
"""在使用Logistic Regression (and CV) 和LinearSVC是需要该方法.
在将其提供给liblinear之前,先进行预处理
Parameters
----------
X : {array-like, sparse matrix}, shape (n_samples, n_features)
训练向量
y : array-like, shape (n_samples,)
与X对应的目标向量y
C : float
交叉验证参数的倒数
fit_intercept : bool
是否训练截距
intercept_scaling : float
class_weight : {dict, 'balanced'}, optional
penalty : str, {'l1', 'l2'}
在正规化中使用的规范
dual : bool
verbose : int
将verbose设置为任意正数.
max_iter : int
迭代次数
tol : float
停止条件.
random_state : int, RandomState instance or None, optional (default=None)
multi_class : str, {'ovr', 'crammer_singer'}
loss : str, {'logistic_regression', 'hinge', 'squared_hinge',
'epsilon_insensitive', 'squared_epsilon_insensitive}
用于拟合模型的损失函数。
epsilon : float, optional (default=0.1)
Epsilon parameter in the epsilon-insensitive loss function.
sample_weight : array-like, optional
分配给每个样本的权重。
Returns
-------
coef_ : ndarray, shape (n_features, n_features + 1)
通过最小化目标函数得到系数向量。
intercept_ : float
向量的截距项
n_iter_ : int
所有类的最大迭代次数
"""
if loss not in ['epsilon_insensitive', 'squared_epsilon_insensitive']:
#在0和n_class -1之间对标签进行编码
enc = LabelEncoder()
#训练标签编码器和返回训练后的编码标签
y_ind = enc.fit_transform(y)
#为每个类保存标签
classes_ = enc.classes_
if len(classes_) < 2:
raise ValueError("This solver needs samples of at least 2 classes"
" in the data, but the data contains only one"
" class: %r" % classes_[0])
#估算不平衡数据集的类权重
class_weight_ = compute_class_weight(class_weight, classes_, y)
else:
class_weight_ = np.empty(0, dtype=np.float64)
y_ind = y
liblinear.set_verbosity_wrap(verbose)
rnd = check_random_state(random_state)
if verbose:
print('[LibLinear]', end='')
# LinearSVC breaks when intercept_scaling is <= 0
bias = -1.0
if fit_intercept:
if intercept_scaling <= 0:
raise ValueError("Intercept scaling is %r but needs to be greater than 0."
" To disable fitting an intercept,"
" set fit_intercept=False." % intercept_scaling)
else:
bias = intercept_scaling
libsvm.set_verbosity_wrap(verbose)
libsvm_sparse.set_verbosity_wrap(verbose)
liblinear.set_verbosity_wrap(verbose)
# LibLinear wants targets as doubles, even for classification
y_ind = np.asarray(y_ind, dtype=np.float64).ravel()
if sample_weight is None:
sample_weight = np.ones(X.shape[0])
else:
sample_weight = np.array(sample_weight, dtype=np.float64, order='C')
check_consistent_length(sample_weight, X)
solver_type = _get_liblinear_solver_type(multi_class, penalty, loss, dual)
raw_coef_, n_iter_ = liblinear.train_wrap(
X, y_ind, sp.isspmatrix(X), solver_type, tol, bias, C,
class_weight_, max_iter, rnd.randint(np.iinfo('i').max),
epsilon, sample_weight)
# Regarding rnd.randint(..) in the above signature:
# seed for srand in range [0..INT_MAX); due to limitations in Numpy
# on 32-bit platforms, we can't get to the UINT_MAX limit that
# srand supports
n_iter_ = max(n_iter_)
if n_iter_ >= max_iter:
warnings.warn("Liblinear failed to converge, increase "
"the number of iterations.", ConvergenceWarning)
if fit_intercept:
coef_ = raw_coef_[:, :-1]
intercept_ = intercept_scaling * raw_coef_[:, -1]
else:
coef_ = raw_coef_
intercept_ = 0.
return coef_, intercept_, n_iter_
备注:还没学习完,等有时间了再更新本文。