昇腾AI原生创新算子挑战赛(S1赛季)复盘-Spence算子
收藏回复举报
昇腾AI原生创新算子挑战赛(S1赛季)复盘-Spence算子
发表于2024-05-14 16:22:12
0 查看
  • 正文前感谢昇腾各位工作人员,没有你们的辛勤就没有我们的进步
  • 本文立意交流大赛Spence算子编译过程
  • 这道题难点在于
    • 如何解析Spence公式
    • 如何找到Spence,tensorflow源码
    • 复杂的计算过程如何重新compute出来

cke_4794.png

  • 这个公式看上去一脸懵逼,居然还有积分,去哪找这样的API
  • 那肯定有替代算法,所以必须找到tensorflow相关源码
  • 200DK A2原驱动并不支持tensorflow,需要自行安装,太麻烦
  • 就直接修改scripts/gen_data.py,使用scipy来生成数据
import scipy.special as sci
y = sci.spence(input_x).astype(former_out)

  • 因为装了virtualenv , tensorflow相关源码在相对应的site-packages中
  • 可以直接用VSCODE CTRL+左键单击跳转代码,也可以search代码
  • 最后在 tf\Lib\site-packages\tensorflow\include\tensorflow\core\kernels\special_math 找到源码
  • 看到源码也蒙了,太长了,太多分类了
    • 有大数组A,B
    • 要求一元多次,internal::ppolevl<Scalar, 7>::run(w, A)
    • 多个区间函数
    • 累积函数太多,误差叠加过多,只能转换大类型包含误差,最后再转回来

// Implementation of Spence's Integral based on Cephes.
template <typename Scalar>
struct spence_op {
  EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar
  operator()(const Scalar& x) const {
    const Scalar A[] = {
        Scalar(4.65128586073990045278E-5), Scalar(7.31589045238094711071E-3),
        Scalar(1.33847639578309018650E-1), Scalar(8.79691311754530315341E-1),
        Scalar(2.71149851196553469920E0),  Scalar(4.25697156008121755724E0),
        Scalar(3.29771340985225106936E0),  Scalar(1.00000000000000000126E0),
    };
    const Scalar B[] = {
        Scalar(6.90990488912553276999E-4), Scalar(2.54043763932544379113E-2),
        Scalar(2.82974860602568089943E-1), Scalar(1.41172597751831069617E0),
        Scalar(3.63800533345137075418E0),  Scalar(5.03278880143316990390E0),
        Scalar(3.54771340985225096217E0),  Scalar(9.99999999999999998740E-1),
    };
    const Scalar zero = Scalar(0.0);
    const Scalar one = Scalar(1.0);
    const Scalar three_halves = Scalar(1.5);
    const Scalar two = Scalar(2.0);
    const Scalar half = Scalar(0.5);
    const Scalar nan = Scalar(NumTraits<Scalar>::quiet_NaN());
    // pi**2 / 6.
    const Scalar PI2O6 = Scalar(EIGEN_PI * EIGEN_PI / 6.0);

    if (x < zero) {
      return nan;
    } else if (x == zero) {
      return PI2O6;
    } else if (x == one) {
      return zero;
    }

    Scalar y;
    if (x < two) {
      y = x;
    } else {
      y = one / x;
    }

    Scalar w;
    if (three_halves < y) {
      w = one / y - one;
    } else {
      if (y < half) {
        w = -y;
      } else {
        w = y - one;
      }
    }
    Scalar spence = -w * (internal::ppolevl<Scalar, 7>::run(w, A) /
                          internal::ppolevl<Scalar, 7>::run(w, B));
    Scalar z = numext::log(y);
    if (y < half) {
      spence = -z * numext::log1p(-y) + PI2O6 - spence;
    }
    if (three_halves < x) {
      spence = -half * z * z - spence;
    }
    return spence;
  }
};

  • 这里需要联合使用compare select进行区间函数设计
  • compute代码可以参考中奖团队代码,这里就不贴了,怕丢人哈哈

cke_1618.png

  • 可以先以1.5分割左右两款
  • 再分别以此基础分别分割0.5左侧,2.0右侧
  • 这样就能解决分割问题
  • 最后NAN如何输出呢?这个问题困扰很久
  • 虽然最后测试案例并不要求负数,但是没见过测试案例并不知道不要求负数
  • 单纯使用for循环,逐数据赋值这样是不行的,理论可以,但是测试很多次,发现数据总会缺失或者乱序
  • 能不用for就不用for循环,用API操作最安全,用for真的奔溃到想G,0XCCCCCCC大神是真的牛,预选就能顺利完成
  • 最终使用下列操作就可以实现NaN输出

  • if(this->dt == 0)
        {
          int16_t __NaN = 0x7E00;
          this->NaN=*((half *)&__NaN);
        }else if(this->dt == 1)
        {
          int32_t __NaNF = 0x7FC00000;
          this->NaN=*((float *)&__NaNF);
        } 
    Select(outLocal,tempTensor1,outLocal,this->NaN,SELMODE::VSEL_TENSOR_SCALAR_MODE, this->tileLength);

本帖最后由 匿名用户2024/05/16 09:43:09 编辑

我要发帖子