LabelPropagation - 如何避免被零除?

pol*_*nsa 18 python numpy machine-learning scikit-learn

当使用LabelPropagation时,我经常遇到这个警告(imho它应该是一个错误,因为它完全无法传播):

/usr/local/lib/python3.5/dist-packages/sklearn/semi_supervised/label_propagation.py:279:运行时警告:在true_divide self.label_distributions_/= normalizer中遇到无效值

因此,在几次尝试使用RBF内核之后,我发现参数gamma有影响.

编辑:

问题来自这些方面:

        if self._variant == 'propagation':
            normalizer = np.sum(
                self.label_distributions_, axis=1)[:, np.newaxis]
            self.label_distributions_ /= normalizer
Run Code Online (Sandbox Code Playgroud)

我不知道label_distributions_如何都是零,特别是当它的定义是:

self.label_distributions_ = safe_sparse_dot(
graph_matrix, self.label_distributions_)
Run Code Online (Sandbox Code Playgroud)

Gamma对graph_matrix有影响(因为graph_matrix是调用内核函数的_build_graph()的结果).好.但仍然.出了点问题

旧帖子(编辑前)

我提醒你如何计算传播的图权重:W = exp(-gamma*D),D是数据集所有点之间的成对距离矩阵.

问题是:np.exp(x) 如果x非常小,返回0.0.
让我们想象一下,我们有两点i也是j如此dist(i, j) = 10.

>>> np.exp(np.asarray(-10*40, dtype=float)) # gamma = 40 => OKAY
1.9151695967140057e-174
>>> np.exp(np.asarray(-10*120, dtype=float)) # gamma = 120 => NOT OKAY
0.0
Run Code Online (Sandbox Code Playgroud)

在实践中,我不是手动设置伽玛,而是使用本文中描述的方法(第2.4节).

那么,如何避免这种划分为零以获得适当的传播?

我能想到的唯一方法是在每个维度中规范化数据集,但是我们会丢失数据集的某些几何/拓扑属性(例如,2x10矩形变为1x1平方)


可重复的例子:

在这个例子中,它是最糟糕的:即使gamma = 20,它也会失败.

In [11]: from sklearn.semi_supervised.label_propagation import LabelPropagation

In [12]: import numpy as np

In [13]: X = np.array([[0, 0], [0, 10]])

In [14]: Y = [0, -1]

In [15]: LabelPropagation(kernel='rbf', tol=0.01, gamma=20).fit(X, Y)
/usr/local/lib/python3.5/dist-packages/sklearn/semi_supervised/label_propagation.py:279: RuntimeWarning: invalid value encountered in true_divide
  self.label_distributions_ /= normalizer
/usr/local/lib/python3.5/dist-packages/sklearn/semi_supervised/label_propagation.py:290: ConvergenceWarning: max_iter=1000 was reached without convergence.
  category=ConvergenceWarning
Out[15]: 
LabelPropagation(alpha=None, gamma=20, kernel='rbf', max_iter=1000, n_jobs=1,
         n_neighbors=7, tol=0.01)

In [16]: LabelPropagation(kernel='rbf', tol=0.01, gamma=2).fit(X, Y)
Out[16]: 
LabelPropagation(alpha=None, gamma=2, kernel='rbf', max_iter=1000, n_jobs=1,
         n_neighbors=7, tol=0.01)

In [17]: 
Run Code Online (Sandbox Code Playgroud)

Dan*_*l F 7

基本上你在做一个softmax功能吧?

防止softmax过度/下溢的一般方法是(从这里)

# Instead of this . . . 
def softmax(x, axis = 0):
    return np.exp(x) / np.sum(np.exp(x), axis = axis, keepdims = True)

# Do this
def softmax(x, axis = 0):
    e_x = np.exp(x - np.max(x, axis = axis, keepdims = True))
    return e_x / e_x.sum(axis, keepdims = True)
Run Code Online (Sandbox Code Playgroud)

这范围e_x在0和1之间,并确保一个值e_x始终为1(即元素np.argmax(x)).这可以防止溢出和下溢(当np.exp(x.max())float64可以处理更大或更小时).

在这种情况下,由于你不能改变算法,我会接受输入D和make,D_ = D - D.min()因为它应该在数值上等同于上面,W.max()应该是-gamma * D.min()(因为你只是翻转符号).做你的算法D_

编辑:

正如下面的@PaulBrodersen所建议的那样,你可以根据这里sklearn实现建立一个"安全"的rbf内核:

def rbf_kernel_safe(X, Y=None, gamma=None): 

      X, Y = sklearn.metrics.pairwise.check_pairwise_arrays(X, Y) 
      if gamma is None: 
          gamma = 1.0 / X.shape[1] 

      K = sklearn.metrics.pairwise.euclidean_distances(X, Y, squared=True) 
      K *= -gamma 
      K -= K.max()
      np.exp(K, K)    # exponentiate K in-place 
      return K 
Run Code Online (Sandbox Code Playgroud)

然后在传播中使用它

LabelPropagation(kernel = rbf_kernel_safe, tol = 0.01, gamma = 20).fit(X, Y)
Run Code Online (Sandbox Code Playgroud)

不幸的是我只有v0.18,它不接受用户定义的内核函数LabelPropagation,所以我无法测试它.

EDIT2:

检查你的来源为什么你有这么大的gamma值让我想知道你是否使用gamma = D.min()/3,这是不正确的.该定义是sigma = D.min()/3同时的定义sigmaw

w = exp(-d**2/sigma**2)  # Equation (1)
Run Code Online (Sandbox Code Playgroud)

这将产生正确的gamma价值1/sigma**29/D.min()**2

  • 为什么不更改实施?您可以继承`LabelPropagation`,并覆盖`_get_kernel`以使用自定义的`rbf_kernel`,在其中,您可以在[第843行]之后添加行K-= K.max()。 /scikit-learn/scikit-learn/blob/f0ab589f/sklearn/metrics/pairwise.py#L843)。免责声明:尚未对此进行任何测试。 (2认同)