1

I'm trying to use class weights in a Scikit learn SVM classifier using 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)

I have 4 classes. Now for the class_weight I would like to have random values between 0 and 1 for each of the four classes. It could be done with

'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]]

But this is only for one class and the values are discrete and not just sampled between 0 and 1.

How can I solve this?

Last but not least, does it matter if I'm using values between 0 and 1 or between 1 and 10 (i.e. are the weights rescaled)?

And should the weights of all 4 classes sum up always to the same value (e.g. 1)?

4

2 回答 2

0

我不知道将分布作为字典键传递的可能性。作为对您提出的解决方法的改进,您可以使用:

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到for中的clf__class_weight条目。将此扩展到多类场景或使用不同的分布很简单。请注意,您实际上采样了两次。一次来自真实分布,然后来自该样本。如果您确保在每次调用之前重新生成 fit 或使初始样本足够大,则此解决方法应该适用于您的情况。parametersRandomizedSearchCVRandomizedSearchCVclass_weights


编辑:更好的解决方案是定义您自己的类实现rvs方法。即使不必将现有scipy.stats发行版子类化为:

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。重要的是它们的相对大小,而不是绝对大小。

于 2018-09-27T08:19:31.213 回答
0

您可以尝试列表理解而不是冗长的解决方案它适用于我在 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)]
于 2021-02-16T07:03:30.000 回答