rank_obj.cu

1.xgboost训练流程图


xgboost训练流程

二.利用xgboost进行pairwise排序


rank_obj.cu UML类图
rank:pairwise相关数据类型
/*! \brief helper information in a list */ 

struct ListEntry {

 /*! \brief the predict score we in the data */ 

 bst_float pred;  

 /*! \brief the actual label of the entry */ 

 bst_float label;  

 /*! \brief row index in the data matrix */ 

 unsigned rindex;  

 // constructor 

 ListEntry(bst_float pred, bst_float label, unsigned rindex)  

   : pred(pred), label(label), rindex(rindex) {}  

 // comparator by prediction 

 inline  static  bool CmpPred(const ListEntry &a, const ListEntry &b) {

   return a.pred > b.pred;

 }  

 // comparator by label 

 inline  static  bool CmpLabel(const ListEntry &a, const ListEntry &b) {

   return a.label > b.label;

 }  

};  

/*! \brief a pair in the lambda rank */  //pos_index neg_index weights 

struct LambdaPair {

 /*! \brief positive index: this is a position in the list */ 

 unsigned pos_index;  

 /*! \brief negative index: this is a position in the list */ 

 unsigned neg_index;  

 /*! \brief weight to be filled in */ 

 bst_float weight;  

 // constructor 

 LambdaPair(unsigned pos_index, unsigned neg_index)  

   : pos_index(pos_index), neg_index(neg_index), weight(1.0f) {}  

 // constructor 

 LambdaPair(unsigned pos_index, unsigned neg_index, bst_float weight)  

   : pos_index(pos_index), neg_index(neg_index), weight(weight) {}  

};  

pairwise loss 计算相关
template <typename LambdaWeightComputerT>

class LambdaRankObj : public ObjFunction {

public:  

 void GetGradient(const HostDeviceVector<bst_float>& preds,

                  const MetaInfo& info,

                  int iter,

                  HostDeviceVector<GradientPair>* out_gpair) override {  

CHECK_EQ(preds.Size(), info.labels_.Size()) << "label size predict size not match";  

   // quick consistency when group is not available 

std::vector<unsigned> tgptr(2, 0); tgptr[1] = static_cast<unsigned>(info.labels_.Size());  

   const std::vector<unsigned> &gptr = info.group_ptr_.size() == 0 ? tgptr : info.group_ptr_;

   CHECK(gptr.size() != 0 && gptr.back() == info.labels_.Size())  

<< "group structure not consistent with #rows" << ", " 

<< "group ponter size: " << gptr.size() << ", " 

<< "labels size: " << info.labels_.Size() << ", " 

<< "group pointer back: " << (gptr.size() == 0 ? 0 : gptr.back());

#if defined(__CUDACC__) 

   // Check if we have a GPU assignment; else, revert back to CPU 

   auto device = tparam_->gpu_id;  

   if (device >= 0) {

     ComputeGradientsOnGPU(preds, info, iter, out_gpair, gptr);  

} else {

     // Revert back to CPU 

#endif 

     ComputeGradientsOnCPU(preds, info, iter, out_gpair, gptr);  

#if defined(__CUDACC__) 

   }  

#endif 

 }  

private:  

 bst_float ComputeWeightNormalizationFactor(const MetaInfo& info,

                                            const std::vector<unsigned> &gptr) {

   const auto ngroup = static_cast<bst_omp_uint>(gptr.size() - 1);  

   bst_float sum_weights = 0;  

   for (bst_omp_uint k = 0; k < ngroup; ++k) {

     sum_weights += info.GetWeight(k);  

   }  

   return ngroup / sum_weights;

 }  
取pair举例

Lambda汇总

2.1利用cpu计算梯度值

 void ComputeGradientsOnCPU(const HostDeviceVector<bst_float>& preds,

                            const MetaInfo& info,

                            int iter,

                            HostDeviceVector<GradientPair>* out_gpair,  

                            const std::vector<unsigned> &gptr) {

LOG(DEBUG) << "Computing " << LambdaWeightComputerT::Name() << " gradients on CPU.";  

   bst_float weight_normalization_factor = ComputeWeightNormalizationFactor(info, gptr);  

   const auto& preds_h = preds.HostVector();

   const auto& labels = info.labels_.HostVector();

   std::vector<GradientPair>& gpair = out_gpair->HostVector();  

   const auto ngroup = static_cast<bst_omp_uint>(gptr.size() - 1);  

   out_gpair->Resize(preds.Size());  

   #pragma omp parallel 

   {  

     // parallel construct, declare random number generator here, so that each 

     // thread use its own random number generator, seed by thread id and current iteration 

     std::minstd_rand rnd((iter + 1) * 1111);  

     std::vector<LambdaPair> pairs;  

     std::vector<ListEntry>  lst;  

     std::vector< std::pair<bst_float, unsigned> > rec;  

     //gpair存的是相对应的一二阶梯度 

     #pragma omp for schedule(static) 

     for (bst_omp_uint k = 0; k < ngroup; ++k) {//在同一个group里进行这个操作 

       lst.clear(); pairs.clear();

       for (unsigned j = gptr[k]; j < gptr[k+1]; ++j) {

         lst.emplace_back(preds_h[j], labels[j], j);  

         gpair[j] = GradientPair(0.0f, 0.0f);  

       }//初始化梯度对 

       std::stable_sort(lst.begin(), lst.end(), ListEntry::CmpPred);//按预测值排序

       rec.resize(lst.size());  

       for (unsigned i = 0; i < lst.size(); ++i) {

         rec[i] = std::make_pair(lst[i].label, i);  

       }//rec存储预测值对应的label以及在预测值中对应的序号 

       std::stable_sort(rec.begin(), rec.end(), common::CmpFirst);  

       // enumerate buckets with same label, for each item in the lst, grab another sample randomly 
 for (unsigned i = 0; i < rec.size(); ) {

         unsigned j = i + 1;  

         while (j < rec.size() && rec[j].first == rec[i].first) ++j;//桶内元素是否增加 

         // bucket in [i,j), get a sample outside bucket 

unsigned nleft = i, nright = static_cast<unsigned>(rec.size() - j);  

         if (nleft + nright != 0) {

           int nsample = param_.num_pairsample;

           while (nsample --) {//一个label取nsample个样本 

             for (unsigned pid = i; pid < j; ++pid) {//出现同一个label的情况下遍历桶内的每个点 

               unsigned ridx = std::uniform_int_distribution<unsigned>(0, nleft + nright - 1)(rnd);//在0到数组长度减桶长之间产生随机数 

               if (ridx < nleft) {

                 pairs.emplace_back(rec[ridx].second, rec[pid].second,//小于的话不变 

                     info.GetWeight(k) * weight_normalization_factor);  

} else {

                 pairs.emplace_back(rec[pid].second, rec[ridx+j-i].second,//大于的话加上桶长 

                     info.GetWeight(k) * weight_normalization_factor);  

               }  

             }  

           }  

         }  

         i = j;  

       }  

       // get lambda weight for the pairs 

       LambdaWeightComputerT::GetLambdaWeight(lst, &pairs);  

       // rescale each gradient and hessian so that the lst have constant weighted 

       float scale = 1.0f / param_.num_pairsample;

       if (param_.fix_list_weight != 0.0f) {

         scale *= param_.fix_list_weight / (gptr[k + 1] - gptr[k]);  

       }  

       for (auto & pair : pairs) {

         const ListEntry &pos = lst[pair.pos_index];

         const ListEntry &neg = lst[pair.neg_index];

         const bst_float w = pair.weight * scale;

         const  float eps = 1e-16f;

         bst_float p = common::Sigmoid(pos.pred - neg.pred);  

         bst_float g = p - 1.0f;  

         bst_float h = std::max(p * (1.0f - p), eps);  

         // accumulate gradient and hessian in both pid, and nid 

         gpair[pos.rindex] += GradientPair(g * w, 2.0f*w*h);  

         gpair[neg.rindex] += GradientPair(-g * w, 2.0f*w*h);  

       }  

     }  

   }  

 }  

};  

class PairwiseLambdaWeightComputer {

public:  

 /*!

  * \brief get lambda weight for existing pairs - for pairwise objective

  * \param list a list that is sorted by pred score

  * \param io_pairs record of pairs, containing the pairs to fill in weights

  */ 

 static  void GetLambdaWeight(const std::vector<ListEntry> &sorted_list,

                             std::vector<LambdaPair> *io_pairs) {}  

 static  char  const* Name() {  

   return  "rank:pairwise";  

 }  

#if defined(__CUDACC__) 

 PairwiseLambdaWeightComputer(const bst_float *dpreds,

                              const bst_float *dlabels,

                              const dh::SegmentSorter<float> &segment_label_sorter) {}  

 class PairwiseLambdaWeightMultiplier {

  public:  

   // Adjust the items weight by this value 

__device__ __forceinline__ bst_float GetWeight(uint32_t gidx, int pidx, int nidx) const {

     return 1.0f;

   }  

 };  

 inline  const PairwiseLambdaWeightMultiplier GetWeightMultiplier() const {

   return {};

 }  

#endif 

};  

Class Diagram1.png
void GetGradient(const HostDeviceVector<bst_float>& preds,

                  const MetaInfo& info,

                  int iter,

                  HostDeviceVector<GradientPair>* out_gpair) override {  

CHECK_EQ(preds.Size(), info.labels_.Size()) << "label size predict size not match";  

   // quick consistency when group is not available 

std::vector<unsigned> tgptr(2, 0); tgptr[1] = static_cast<unsigned>(info.labels_.Size());  

   const std::vector<unsigned> &gptr = info.group_ptr_.size() == 0 ? tgptr : info.group_ptr_;

   CHECK(gptr.size() != 0 && gptr.back() == info.labels_.Size())  

<< "group structure not consistent with #rows" << ", " 

<< "group ponter size: " << gptr.size() << ", " 

<< "labels size: " << info.labels_.Size() << ", " 

<< "group pointer back: " << (gptr.size() == 0 ? 0 : gptr.back());

#if defined(__CUDACC__) 

   // Check if we have a GPU assignment; else, revert back to CPU 

   auto device = tparam_->gpu_id;  

   if (device >= 0) {

     ComputeGradientsOnGPU(preds, info, iter, out_gpair, gptr);  

} else {

     // Revert back to CPU 

#endif 

     ComputeGradientsOnCPU(preds, info, iter, out_gpair, gptr);  

#if defined(__CUDACC__) 

   }  

#endif 

 }  

2.2利用gpu计算梯度值

#if defined(__CUDACC__) 

 void ComputeGradientsOnGPU(const HostDeviceVector<bst_float>& preds,

                            const MetaInfo& info,

                            int iter,

                            HostDeviceVector<GradientPair>* out_gpair,  

                            const std::vector<unsigned> &gptr) {

LOG(DEBUG) << "Computing " << LambdaWeightComputerT::Name() << " gradients on GPU.";  

   auto device = tparam_->gpu_id;  

   dh::safe_cuda(cudaSetDevice(device));  

   bst_float weight_normalization_factor = ComputeWeightNormalizationFactor(info, gptr);  

   // Set the device ID and copy them to the device 

   out_gpair->SetDevice(device);  

   info.labels_.SetDevice(device);  

   preds.SetDevice(device);  

   info.weights_.SetDevice(device);  

   out_gpair->Resize(preds.Size());  

   auto d_preds = preds.ConstDevicePointer();  

   auto d_gpair = out_gpair->DevicePointer();  

   auto d_labels = info.labels_.ConstDevicePointer();  

   SortedLabelList slist(param_);  

   // Sort the labels within the groups on the device 

   slist.Sort(info.labels_, gptr);  

   // Initialize the gradients next 

   out_gpair->Fill(GradientPair(0.0f, 0.0f));  

   // Finally, compute the gradients 

   slist.ComputeGradients<LambdaWeightComputerT>  

     (d_preds, d_labels, info.weights_, iter, d_gpair, weight_normalization_factor);  

 }  

#endif 

#if defined(__CUDACC__) 

class SortedLabelList : dh::SegmentSorter<float> {  

private:  

 const LambdaRankParam ¶m_;// Objective configuration 

public:  

 explicit SortedLabelList(const LambdaRankParam ¶m)

   : param_(param) {}  

 // Sort the labels that are grouped by 'groups' 

 void Sort(const HostDeviceVector<bst_float> &dlabels, const std::vector<uint32_t> &groups) {

   this->SortItems(dlabels.ConstDevicePointer(), dlabels.Size(), groups);  

 }  
Class Diagram 4.png
 // This kernel can only run *after* the kernel in sort is completed, as they 

 // use the default stream 

 template <typename LambdaWeightComputerT>

 void ComputeGradients(const bst_float *dpreds,// Unsorted predictions 

                       const bst_float *dlabels,// Unsorted labels 

                       const HostDeviceVector<bst_float> &weights,

                       int iter,

                       GradientPair *out_gpair,  

                       float weight_normalization_factor) {

   // Group info on device 

   const auto &dgroups = this->GetGroupsSpan();  

uint32_t ngroups = this->GetNumGroups() + 1;  

uint32_t total_items = this->GetNumItems();  

   uint32_t niter = param_.num_pairsample * total_items;  

   float fix_list_weight = param_.fix_list_weight;

   const auto &original_pos = this->GetOriginalPositionsSpan();  

   uint32_t num_weights = weights.Size();  

   auto dweights = num_weights ? weights.ConstDevicePointer() : nullptr;  

   const auto &sorted_labels = this->GetItemsSpan();  

   // This is used to adjust the weight of different elements based on the different ranking 

   // objective function policies 

   LambdaWeightComputerT weight_computer(dpreds, dlabels, *this);  

   auto wmultiplier = weight_computer.GetWeightMultiplier();  

   int device_id = -1;

   dh::safe_cuda(cudaGetDevice(&device_id));  

   // For each instance in the group, compute the gradient pair concurrently 

   dh::LaunchN(device_id, niter, nullptr, [=] __device__(uint32_t idx) {  

     // First, determine the group 'idx' belongs to 

     uint32_t item_idx = idx % total_items;  

     uint32_t group_idx =  

         thrust::upper_bound(thrust::seq, dgroups.begin(),  

                             dgroups.begin() + ngroups, item_idx) -  

         dgroups.begin();  

     // Span of this group within the larger labels/predictions sorted tuple 

     uint32_t group_begin = dgroups[group_idx - 1];  

     uint32_t group_end = dgroups[group_idx];  

     uint32_t total_group_items = group_end - group_begin;  

     // Are the labels diverse enough? If they are all the same, then there is nothing to pick 

     // from another group - bail sooner 

     if (sorted_labels[group_begin] == sorted_labels[group_end - 1]) return;  

     // Find the number of labels less than and greater than the current label 

     // at the sorted index position item_idx 

     uint32_t nleft  = CountNumItemsToTheLeftOf(  

       sorted_labels.data() + group_begin, item_idx - group_begin + 1, sorted_labels[item_idx]);  

     uint32_t nright = CountNumItemsToTheRightOf(  

       sorted_labels.data() + item_idx, group_end - item_idx, sorted_labels[item_idx]);  

     // Create a minstd_rand object to act as our source of randomness 

     thrust::minstd_rand rng((iter + 1) * 1111);  

     rng.discard(((idx / total_items) * total_group_items) + item_idx - group_begin);  

     // Create a uniform_int_distribution to produce a sample from outside of the 

     // present label group 

     thrust::uniform_int_distribution<int> dist(0, nleft + nright - 1);  

     int sample = dist(rng);

     int pos_idx = -1;// Bigger label 

     int neg_idx = -1;// Smaller label 

     // Are we picking a sample to the left/right of the current group? 

     if (sample < nleft) {

       // Go left 

       pos_idx = sample + group_begin;  

       neg_idx = item_idx;  

} else {

       pos_idx = item_idx;  

       uint32_t items_in_group = total_group_items - nleft - nright;  

       neg_idx = sample + items_in_group + group_begin;  

     }  

     // Compute and assign the gradients now 

     const  float eps = 1e-16f;

     bst_float p = common::Sigmoid(dpreds[original_pos[pos_idx]] - dpreds[original_pos[neg_idx]]);  

     bst_float g = p - 1.0f;  

     bst_float h = thrust::max(p * (1.0f - p), eps);  

     // Rescale each gradient and hessian so that the group has a weighted constant 

     float scale = __frcp_ru(niter / total_items);

     if (fix_list_weight != 0.0f) {

       scale *= fix_list_weight / total_group_items;  

     }  

     float weight = num_weights ? dweights[group_idx - 1] : 1.0f;

     weight *= weight_normalization_factor;  

     weight *= wmultiplier.GetWeight(group_idx - 1, pos_idx, neg_idx);  

     weight *= scale;  

     // Accumulate gradient and hessian in both positive and negative indices 

     const GradientPair in_pos_gpair(g * weight, 2.0f * weight * h);

     dh::AtomicAddGpair(&out_gpair[original_pos[pos_idx]], in_pos_gpair);  

     const GradientPair in_neg_gpair(-g * weight, 2.0f * weight * h);

     dh::AtomicAddGpair(&out_gpair[original_pos[neg_idx]], in_neg_gpair);  

   });  

   // Wait until the computations done by the kernel is complete 

   dh::safe_cuda(cudaStreamSynchronize(nullptr));  

 }  

};  

#endif 

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354