RandomizedSearchCV 中 class_weight 的采样值
Sampling values for class_weight in RandomizedSearchCV
我正在尝试使用 RandomizedSearchCV
.
在 Scikit 学习 SVM classifier 中使用 class 权重
clf= svm.SVC(probability=True, random_state=0)
parameters = {'clf__C': scipy.stats.expon(scale=100), 'clf__gamma': scipy.stats.expon(scale=.1),
'clf__kernel': ['rbf'], 'clf__class_weight':['balanced', None]}
search=RandomizedSearchCV(estimator=clf, param_distributions=parameters, scoring='f1_micro',
cv=5, n_iter=100, random_state=0)
search.fit(features,labels)
我有 4 个 class。现在对于 class_weight 我想为四个 class 中的每一个设置 0 到 1 之间的随机值。可以用
来完成
'class_weight':[{0: w} for w in [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9]]
但这仅适用于一个 class 并且值是离散的,而不仅仅是在 0 和 1 之间采样。
我该如何解决这个问题?
最后但同样重要的是,我使用的值介于 0 和 1 之间还是介于 1 和 10 之间(即权重是否重新调整)重要吗?
所有 4 个 class 的权重总和是否应该始终为相同的值(例如 1)?
我不知道将分布作为字典键传递的可能性。作为对您提出的解决方法的改进,您可以使用:
from sklearn.utils.class_weight import compute_class_weight
from scipy.stats import lognorm
class_weight = compute_class_weight("balanced", np.unique(y), y)
class_weights = []
for mltp in lognorm(s = 1, loc = 1, scale = class_weight[0]).rvs(50):
class_weights.append(dict(zip([0, 1], class_weight * [mltp, 1/mltp])))
然后您可以将 class_weights
传递给 parameters
中的 clf__class_weight
条目以供 RandomizedSearchCV
。将其扩展到多 class 场景或使用不同的分布很简单。
请注意,您实际上采样了两次。一次来自真实分布,然后来自此示例的 RandomizedSearchCV
。如果您确保在每次调用 fit 之前重新生成 class_weights
或者您使初始样本足够大,则此解决方法应该适用于您的情况。
编辑:
更好的解决方案是定义您自己的 class 实现 rvs
方法。您甚至无需将现有的 scipy.stats
分配子 class 分配为:
即可执行此操作
class ClassWeights(object):
"""
Draw random variates for cases when parameter is a dict.
Should be personalized as needed.
"""
def __init__(self,y, *args, **kwargs):
self.class_weights = compute_class_weight("balanced", np.unique(y), y)
self._make_dists()
def _make_dists(self):
self.dist0 = gamma(self.class_weights[0])
self.dist1 = gamma(self.class_weights[1])
def rvs(self, *args, **kwargs):
"""override method for drawing random variates"""
ret_val = { 0: self.dist0.rvs(*args, **kwargs),
1: self.dist1.rvs(*args, **kwargs)}
return ret_val
回答你另外两个问题:
权重可以取任何正值(包括 0),它们的总和不必为 1。重要的是它们的相对大小,而不是绝对大小。
你可以尝试列表理解而不是长的解决方案它在 RandomForest 中对我有用,我已经检查了 RandomizedSearchCV
l1 = np.arange(0,1,0.01)
l2 = np.arange(0,1,0.01)
class_weight = [{0:i,1:j} for i,j in zip(l1,l2)]
我正在尝试使用 RandomizedSearchCV
.
clf= svm.SVC(probability=True, random_state=0)
parameters = {'clf__C': scipy.stats.expon(scale=100), 'clf__gamma': scipy.stats.expon(scale=.1),
'clf__kernel': ['rbf'], 'clf__class_weight':['balanced', None]}
search=RandomizedSearchCV(estimator=clf, param_distributions=parameters, scoring='f1_micro',
cv=5, n_iter=100, random_state=0)
search.fit(features,labels)
我有 4 个 class。现在对于 class_weight 我想为四个 class 中的每一个设置 0 到 1 之间的随机值。可以用
来完成'class_weight':[{0: w} for w in [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9]]
但这仅适用于一个 class 并且值是离散的,而不仅仅是在 0 和 1 之间采样。
我该如何解决这个问题?
最后但同样重要的是,我使用的值介于 0 和 1 之间还是介于 1 和 10 之间(即权重是否重新调整)重要吗?
所有 4 个 class 的权重总和是否应该始终为相同的值(例如 1)?
我不知道将分布作为字典键传递的可能性。作为对您提出的解决方法的改进,您可以使用:
from sklearn.utils.class_weight import compute_class_weight
from scipy.stats import lognorm
class_weight = compute_class_weight("balanced", np.unique(y), y)
class_weights = []
for mltp in lognorm(s = 1, loc = 1, scale = class_weight[0]).rvs(50):
class_weights.append(dict(zip([0, 1], class_weight * [mltp, 1/mltp])))
然后您可以将 class_weights
传递给 parameters
中的 clf__class_weight
条目以供 RandomizedSearchCV
。将其扩展到多 class 场景或使用不同的分布很简单。
请注意,您实际上采样了两次。一次来自真实分布,然后来自此示例的 RandomizedSearchCV
。如果您确保在每次调用 fit 之前重新生成 class_weights
或者您使初始样本足够大,则此解决方法应该适用于您的情况。
编辑:
更好的解决方案是定义您自己的 class 实现 rvs
方法。您甚至无需将现有的 scipy.stats
分配子 class 分配为:
class ClassWeights(object):
"""
Draw random variates for cases when parameter is a dict.
Should be personalized as needed.
"""
def __init__(self,y, *args, **kwargs):
self.class_weights = compute_class_weight("balanced", np.unique(y), y)
self._make_dists()
def _make_dists(self):
self.dist0 = gamma(self.class_weights[0])
self.dist1 = gamma(self.class_weights[1])
def rvs(self, *args, **kwargs):
"""override method for drawing random variates"""
ret_val = { 0: self.dist0.rvs(*args, **kwargs),
1: self.dist1.rvs(*args, **kwargs)}
return ret_val
回答你另外两个问题:
权重可以取任何正值(包括 0),它们的总和不必为 1。重要的是它们的相对大小,而不是绝对大小。
你可以尝试列表理解而不是长的解决方案它在 RandomForest 中对我有用,我已经检查了 RandomizedSearchCV
l1 = np.arange(0,1,0.01)
l2 = np.arange(0,1,0.01)
class_weight = [{0:i,1:j} for i,j in zip(l1,l2)]