在使用softmax损失时,如何调试和矢量化径向基函数网络的偏导数?

时间:2015-10-19 21:52:19

标签: matlab machine-learning gradient-descent softmax

当我使用标准交叉熵(softmax loss)时,我试图运行(随机或批量)梯度下降:

enter image description here

当使用Radial Basis Function (RBF)网络作为模型时(如果需要,可以观看讲座形式caltech here)扩展到多类分类(通过简单地将RBF网络的输出提供给softmax层。注意P(y=l|x)只是通过将RBF网络的输出传递给每个标签l的softmax层来计算,如下所示:

enter image description here

其中\theta_l索引负责对标签l进行预测的参数。

在这方面,我想通过计算与参数有关的导数来优化我的模型。回想一下,在径向基函数网络中优化的参数是最后一层的权重c和第一层的中心t。我已经实现并调试了如何计算权重c的导数。代码按预期工作,因为偏导数与数值导数匹配。您可以找到单位测试代码为here

我也尝试编写实现中心导数的代码,但我似乎无法将导数匹配的实现与数值导数相提并论。关于我正在尝试实施的中心J的损失衍生物t_k的等式如下:

enter image description here

其中h_{\theta_l}对应于负责预测标签l的RBF的输出。事实上,h_{\theta_l}表达非常简单:

enter image description here

我的主要问题是计算J相对于t_k的导数(上面的等式)。为此,我实现了following function天真计算它而不进行矢量化:

function [ dJ_dt ] = compute_dJ_dt(z,x,y,t,c)
%Computes dJ_dc
%   Input:
%       z = (K x 1)
%       x = data point (D, 1)
%       y = labels (1 x 1)
%       t = centers (D x K)
%       c = weights (K x L)
%   Output:
%       dJ_dc = (D x K)
[D,K] = size(t);
[~, L] = size(c);
dJ_dt = zeros(D, K);
for k=1:K
    dJ_dt_k = zeros(D, 1);
    for l=1:L
        c_l = c(:,l);
        dh_dt_l = compute_dh_dt(z,x,t,c_l); %(D x K)
        delta = (y==l);
        dJ_dt_k = dJ_dt_k + dh_dt_l(:,k) * delta;
    end
    dJ_dt(:,k) = -dJ_dt_k;
end
end

且与the numerical derivatives code不匹配。

我尝试过不同的东西来检查它是否有效,我会在这里解释一下。如果有人有其他想法,请随意分享,我觉得我用了很多新想法试图调试它。

  1. 首先,一个很好的自然问题是,我试图实现的衍生物的数学推导是正确的吗?即使我没有明确地检查过某人的数学推导,我也很有信心它是正确的,因为模型中关于ct的偏导数的推导是相同的,你只是改变了符号\theta到您所涉及的任何参数。由于我已经实现了关于c的导数并且它通过了我的所有导数测试,我认为推导出与t或任何参数\theta相关的导数应该是正确的。我可以在math.stack exchange here中看到我推导出这个等式。
  2. 一个选项可能是compute_dJ_dt实际上没有实现我期望的等式。确实可能是这种情况,并检查我是否独立实施了更多vectorized version of that code以确定我是否实际上实现了我在纸上的方程式。由于方程的两个版本输出相同的导数值,我很有信心它们正在计算,实际上我怀疑的方程式(如果有人有进一步矢量化这个方程的方法,那将是非常棒!我添加了矢量化它是如此微不足道,以至于它看起来并不那么有趣或者大部分性能提升,但它确实会删除一个for循环)。
  3. 由于我在纸上的等式(很有可能)是正确的,并且等式的实现似乎是正确的,因为它的两个版本输出相同的值,然后它使我得出结论可能,数字衍生代码有一个错误。

    1. numerical derivative code非常简单,很难检查它究竟是什么问题。对我来说唯一可能出错的事情是,我softmax cost J的执行可能是错误的,但我对此非常怀疑......我已经为它编写了一个单元测试!另外,我用它检查c的数值导数和c的数值导数总是通过,所以我无法想象J是错误的。
    2. 要检查的最后一件非常重要的事情是正确计算compute_dh_dt。我写过units tests for dh_dt,因为它们在每次运行时都与相应的数值导数匹配,我怀疑代码是正确的。
    3. 此时我并不是100%确定还有什么可以尝试我希望也许有人有个好主意或者可能指出我正在做的愚蠢的事情?我不知道现在该怎么想。感谢您的帮助和时间社区!

1 个答案:

答案 0 :(得分:0)

这是一种反高潮的解决方案,但我想这是可以预料到的,因为这段代码似乎是由工作组件构建的,所以它肯定是一个愚蠢的小错误。错误是在上面的代码我粘贴了我应该使用files = request.files.getlist('files[]') 作为标签的指示和标签的概率之间的差异,但我忘了减去概率。所以上面的代码是:

delta

应该是什么时候:

    delta = (y==l);

所以固定代码现在通过了数值测试,看起来如下:

    prob_y_x_h_x = prob_y_x(h_x); % (L x 1)
    ind_y_l = (y==l);
    delta = ind_y_l - prob_y_x_h_x(l);

我仍然不知道如何进一步对上面的代码进行矢量化,所以我仍然很乐意收到有关该部分问题的反馈!这是我到目前为止的矢量化:

function [ dJ_dt ] = compute_dJ_dt(h_x,z,x,y,t,c)
%Computes dJ_dc
%   Input:
%       z = (K x 1)
%       x = data point (D, 1)
%       y = labels (1 x 1)
%       t = centers (D x K)
%       c = weights (K x L)
%   Output:
%       dJ_dc = (D x K)
[D,K] = size(t);
[~, L] = size(c);
dJ_dt = zeros(D, K);
for k=1:K
    dJ_dt_k = zeros(D, 1);
    for l=1:L
        c_l = c(:,l);
        dh_dt_l = compute_dh_dt(z,x,t,c_l); %(D x K)
        prob_y_x_h_x = prob_y_x(h_x); % (L x 1)
        ind_y_l = (y==l);
        delta = ind_y_l - prob_y_x_h_x(l);
        dJ_dt_k = dJ_dt_k + dh_dt_l(:,k) * delta;
    end
    dJ_dt(:,k) = -dJ_dt_k;
end
end