xLearn源码分析之FM的CalcScore实现

写在前面

xLearn是由Chao Ma实现的一个高效的机器学习算法库,这里附上github地址:

https://github.com/aksnzhy/xlearn

FM是机器学习中一个在CTR领域中表现突出的模型,最早由Konstanz大学Steffen Rendle(现任职于Google)于2010年最早提出。

FM模型

FM的模型方程为:

y(x)=w0+i=1nwixi+i=1nj=i+1nvi,vjxixj

直观上看,FM的复杂度是 O(kn2),但是,FM的二次项可以化简,其复杂度可以优化到 O(kn)。论文中简化如下式:

i=1nj=i+1nvi,vjxixj=12f=1k((i=1nvi,fxi)2i=1nvi,f2xi2)

这里记录一下具体推导过程:

i=1nj=i+1nvi,vjxixj

=12(i=1nj=1nvi,vjxixji=1nvi,vixixi)

=12(i=1nj=1nf=1kvifvjfxixji=1nf=1kvifvjfxixi)

=12f=1k(i=1nj=1nvifvjfxixji=1nvifvifxixi)

=12f=1k((i=1nvifxi)(j=1nvjfxj)i=1n(vifxi)2)

=12f=1k((i=1nvifxi)2i=1n(vifxi)2)

=12f=1k((i=1nvi,fxi)2i=1nvi,f2xi2)

xLearn的CalcScore实现

// y = sum( (V_i*V_j)(x_i * x_j) )
// Using SSE to accelerate vector operation.
// row为libsvm格式的样本,采用vector稀疏存储的Node,Node里有feat_id及feat_val
// model为模型
real_t FMScore::CalcScore(const SparseRow* row,
                          Model& model,
                          real_t norm) {
  /*********************************************************
   *  linear term and bias term                            *
   *********************************************************/
  real_t sqrt_norm = sqrt(norm);
  real_t *w = model.GetParameter_w();
  index_t num_feat = model.GetNumFeature();
  real_t t = 0;
  index_t aux_size = model.GetAuxiliarySize();
  for (SparseRow::const_iterator iter = row->begin();
       iter != row->end(); ++iter) {
    index_t feat_id = iter->feat_id;
    // To avoid unseen feature in Prediction
    if (feat_id >= num_feat) continue;
    //计算线性部分x_i*w_i,求和到t
    t += (iter->feat_val * w[feat_id*aux_size] * sqrt_norm);
  }
  // bias
  // 偏置w_0,加到t
  w = model.GetParameter_b();
  t += w[0];
  /*********************************************************
   *  latent factor                                        *
   *********************************************************/
  //隐向量长度调整为4的整数倍aligned_k
  index_t aligned_k = model.get_aligned_k();
  index_t align0 = model.get_aligned_k() * aux_size;
  std::vector<real_t> sv(aligned_k, 0);
  real_t* s = sv.data();
  for (SparseRow::const_iterator iter = row->begin();
       iter != row->end(); ++iter) {
    index_t j1 = iter->feat_id;
    // To avoid unseen feature in Prediction
    if (j1 >= num_feat) continue;
    real_t v1 = iter->feat_val;//x_i
    real_t *w = model.GetParameter_v() + j1 * align0;//v_i
    //SSE指令,x_i存储于128位的寄存器中
    __m128 XMMv = _mm_set1_ps(v1*norm);//x_i
    //循环每次移动4个长度,4个float正好128位,一次循环计算4个浮点数
    for (index_t d = 0; d < aligned_k; d += kAlign) {
      __m128 XMMs = _mm_load_ps(s+d);
      __m128 const XMMw = _mm_load_ps(w+d);//v_i
      //计算v_if * x_i,并按i求和,结果是一个k维向量sv
      XMMs = _mm_add_ps(XMMs, _mm_mul_ps(XMMw, XMMv));
      _mm_store_ps(s+d, XMMs);
    }
  }
  __m128 XMMt = _mm_set1_ps(0.0f);
  for (SparseRow::const_iterator iter = row->begin();
       iter != row->end(); ++iter) {
    index_t j1 = iter->feat_id;
    // To avoid unseen feature in Prediction
    if (j1 >= num_feat) continue;
    real_t v1 = iter->feat_val;//x_i
    real_t *w = model.GetParameter_v() + j1 * align0;//v_i
    //SSE指令,x_i存储于128位的寄存器中
    __m128 XMMv = _mm_set1_ps(v1*norm);
    for (index_t d = 0; d < aligned_k; d += kAlign) {
      __m128 XMMs = _mm_load_ps(s+d);
      __m128 XMMw = _mm_load_ps(w+d);//v_i
      __m128 XMMwv = _mm_mul_ps(XMMw, XMMv);//v_if * x_i
      XMMt = _mm_add_ps(XMMt,
         _mm_mul_ps(XMMwv, _mm_sub_ps(XMMs, XMMwv)));
    }
  }
  XMMt = _mm_hadd_ps(XMMt, XMMt);
  XMMt = _mm_hadd_ps(XMMt, XMMt);
  real_t t_all;
  _mm_store_ss(&t_all, XMMt);
  t_all *= 0.5;
  t_all += t;
  return t_all;
}

在xLearn中的实现,并非是论文的简化后的公式,具体如下:

i=1nj=i+1nvi,vjxixj

=12(i=1nj=1nvi,vjxixji=1nvi,vixixi)

=12(i=1nj=1nf=1kvifvjfxixji=1nf=1kvifvjfxixi)

=12f=1k(i=1nj=1nvifvjfxixji=1nvifvifxixi)

=12f=1k(i=1nvifxi(j=1nvjfxjvifxi))

第一个for循环是计算j=1nvjfxj,注意下标是j,结果存于sv的vector中,内层嵌套for循环并没有做求和操作,只是遍历的隐向量。

第二个for循环是计算i=1n,注意下标是i,内层嵌套for循环是计算f=1k,被两层循环计算求和的单元是

vifxi(j=1nvjfxjvifxi)

用到了前面循环的中间结果,结果存于XMMt,由于内层循环是以4个浮点数同时做计算,所以结果最后需要将这个四个浮点数加起来,调用了两次_mm_hadd_ps实现。

参考链接:

http://www.algo.uni-konstanz.de/members/rendle/pdf/Rendle2010FM.pdf

https://tech.meituan.com/2016/03/03/deep-understanding-of-ffm-principles-and-practices.html

posted @   The_Matrix  阅读(1217)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示