1. 程式人生 > >【學習排序】 Learning to Rank中Pointwise關於PRank演算法原始碼實現

【學習排序】 Learning to Rank中Pointwise關於PRank演算法原始碼實現

最近終於忙完了Learning to Rank的作業,同時也學到了很多東西.我準備寫幾篇相關的文章簡單講述自己對它的理解和認識.第一篇準備講述的就是Learning to Rank中Pointwise的認識及PRank演算法的實現.主要從以下四個方面進行講述:     1.學習排序(Learning to Rank)概念     2.基於點的排序演算法(Pointwise)介紹     3.基於順序迴歸(Ordinal Regression-based)的PRank排序演算法     4.PRank演算法Java\C++實現及總結

一. 學習排序(Learning to Rank)概念

    學習排序概念推薦轉載的文章:

機器學習排序之Learning to Rank簡單介紹     1.首先,為什麼會出現學習排序呢?     傳統的排序方法是通過構造一個排序函式實現,在Information Retrieval領域一般按照相關度進行排序。比較典型的是搜尋引擎中一條查詢query,將返回一個相關的文件document,然後根據(query,document)之間的相關度進行排序,再返回給使用者。     而隨著影響相關度的因素(如PageRank)變多,Google目前排序方法考慮了200多種方法。這使得傳統排序方法變得困難,人們就想到通過機器學習來解決這一問題,這就導致了Learning to Rank的誕生。     2.然後是學習排序的基本流程如下圖所示.     很明顯它就是基本步驟就是通過訓練集資料(Train Set)學習得到模型h,然後通過該模型去對測試集資料(Test Set)進行計算和排序,最後得到一個預測的結果.

    3.那麼,學習排序的資料集是怎樣的一個東西呢?也就是上圖中x、y、h分別代表著什麼呢?     資料集可參考微軟136維資料——MSLR-WEB10K 它是2010年的資料.形如:      =============================================================                                       0 qid:1 1:3 2:0 3:2 4:2 ... 135:0 136:0                                        2 qid:1 1:3 2:3 3:0 4:0 ... 135:0 136:0             =============================================================            其資料格式: label qid:id  feaid:feavalue  feaid:feavalue ...     每行表示一個樣本,相同的查詢請求的樣本qid相同,上面就是兩個對qid為“1”的查詢;label表示該樣本和該查詢請求的相關程度,該label等級劃分方式為 {Perfect, Excellent,Good, Fair, Bad} 共五個類別,後面對應的是特徵和特徵值,我們通常使用的<X,Y>即是<特徵量,人工標註>.     同樣你也可以使用比較經典的2007的資料集——

LETOR4.0,它是46維資料.如下圖所示:    它表示每行相當於一個Document(樣本文件),第一行是樣本相關程度,在46維中label共三個值:2-完全相關、1-部分相關、0-不相關;同時qid相同表示同一個查詢對應多行樣本;中間是46維特徵之,最後#相當於註釋解釋.     4.如果你還是不清楚,我換成通俗的例子解釋:

    比如,現在你在Google瀏覽器中輸入"Learning to Rank",它就相當於一個qid.而下面列出的各個連結就是多個樣本集合,其中每一個都有200多種影響因素(如其中一種PageRank).在學習過程中需要找到一個模型來預測新查詢文件的得分,並排序計算出使用者最想要的結果.     PS:這是我的個人理解,如果有錯誤或不足之處,歡迎提出!

二. 基於點的排序演算法(Pointwise)介紹

    機器學習解決排序學習問題可分為3類:     1.基於迴歸排序學習(regression-based algorithms):序列轉為實數     2.基於分類排序學習(classification-based algorithms):二值分類     3.基於順序迴歸排序學習(ordinal regression-based algorithms)     但是這裡我想講述的是最常見的分類,它們應該與上面是交叉的:     1.基於點的LTR演算法——Pointwise Approach     2.基於對的LTR演算法——Pairwise Approach     3.基於列的LTR演算法——Listwise Approach

    Pointwise處理物件是一篇文件,將文件轉化為特徵向量後,機器學習系統根據訓練得出的模型對文件進行打分(注意:訓練集學習出權重模型去給測試集文件打分是LTR中非常經典的用法),打分的順序即為搜尋排序的結果.     Score(x)=w1*F1+w2*F2+w3*F3+...+w136*F136     其中w1-w136為136維對應權重引數,由訓練集訓練得到;F1-F136為測試文件給出136個特徵值.     原資料有5個類標(0-4代表相關程度:Perfect>Excellent>Good>Fair>Bad),則設定5個閾值來區分所得分數的分類.如果得分大於相關閾值,則劃分為相應的類.常見演算法包括:Prank、McRank     下面是我自己畫的一張圖,其中四根紅線是四個閾值,它把這些文件集劃分為了五個不同類.每當一個新的文件來測試,它都會根據已有模型計算出相應分數,再根據分數和閾值劃分類即可.

 

三. PRank演算法介紹

    PRank演算法是基於點的排序學習,順序迴歸學習問題.其演算法主要參考Kolby Crammer & Yoram Singer(From:The HeBrew University,以色列希伯來大學)論文《Pranking with Ranking》.網址如下:http://papers.nips.cc/paper/2023-pranking-with-ranking.pdf演算法過程如下:

    演算法描述:(感覺演算法一目瞭然,但是我功力不夠描述不清楚)     對於46維資料而言,它存在3個類標(0-2).故上述演算法中初始閾值b[0]=b[1]=b[2]=0,b[3]=正無窮.     注意它只有一層迴圈For(1...T)表示樣本集的總行數,而沒有進行迭代(CSDN三國那個例子含迭代錯誤);它主要是通過預測標號y~和實際標號y進行對比,來更新權重和閾值.     在H排序決策函式中,它通過K個閾值b把空間劃分為K個連續的子空間,每個子空間對應一個序列號,即滿足所有的樣本x都有相同的排序結果.對每個樣本,先計算權重w與xi的內積w·x,找出所有滿足w·x-br中最小的br,並將此br對應的序標號xi作為排序模型對樣本的預測排序結果.     推薦中文資料:南開大學論文《基於PRank演算法的主動排序學習演算法》

四. PRank演算法Java\C++實現及總結

    1.Java程式碼實現     程式碼中有詳細註釋,每個步驟都是按照上面的演算法進行設計的.左圖是主函式,它主要包括:讀取檔案並解析資料、寫資料(該函式可註釋掉,它是我用於驗證讀取是否正確時寫的)、學習排序模型和打分預測.右圖是預測排序結果的演算法.

   程式碼如下:

  1. package com.example.pointwise;

  2. import java.io.BufferedReader;

  3. import java.io.File;

  4. import java.io.FileInputStream;

  5. import java.io.FileReader;

  6. import java.io.FileWriter;

  7. import java.io.IOException;

  8. import java.io.InputStreamReader;

  9. import java.util.ArrayList;

  10. import java.util.List;

  11. /**

  12. * Pointwise基於點學習排序(Learning to Rank)的Prank演算法

  13. * @author Eastmount YXZ

  14. * 參考資料

  15. * 該演算法從136維資料集改成46維資料集,中間可能有註釋不一致現象

  16. * (原始論文) http://papers.nips.cc/paper/2023-pranking-with-ranking.pdf

  17. * (新浪) http://blog.sina.com.cn/s/blog_4c98b960010008xn.html

  18. * (CSDN)http://blog.csdn.net/pennyliang/article/details/17333373

  19. */

  20. public class Prank {

  21. public int RANK_NUM = 10000; //記錄總樣本數 (總行數)

  22. public int RANK_CATA = 46; //排序的特徵維數 (資料集136維 後改為46維)

  23. public int RANK_ITER = 1; //排序的迭代次數 (原文迭代1次)

  24. public int RANK_LABEL= 3; //排序劃分的閾值 (微軟資料集劃分5類 0-4) 3維全相關,部分相關,不相關

  25. //採用該方法實現動態陣列新增資料

  26. List<Float> weight = null; //特徵值的權重向量 (46個 136個)

  27. //訓練集資料 每行共48個數據 (46個特徵值 二維陣列-feature[行號][46] + 真實Label值0-2 + qid值)

  28. List<List<Float>> x = null;

  29. Float [] b = null; //閾值數 K+1個(RANK_LABEL+1)

  30. public int sumLabel = 0; //檔案總行數 (標記數)

  31. /**

  32. * 函式功能 讀取檔案

  33. * 引數 String filePath 檔案路徑

  34. */

  35. public void ReadTxtFile(String filePath) throws IOException {

  36. String encoding="GBK";

  37. File file = new File(filePath); //檔案

  38. BufferedReader bufferedReader = null;

  39. try {

  40. //判斷檔案是否存在

  41. if(file.isFile() && file.exists()) {

  42. //輸入流

  43. InputStreamReader read = new InputStreamReader(new FileInputStream(file), encoding);

  44. bufferedReader = new BufferedReader(read);

  45. String lineTxt = null;

  46. sumLabel =0; //記錄總樣本數

  47. x = new ArrayList<List<Float>> ();

  48. //按行讀取資料並分解資料

  49. while((lineTxt = bufferedReader.readLine()) != null) {

  50. String str = null;

  51. int lengthLine = lineTxt.length();

  52. List<Float> subList=new ArrayList<Float>();

  53. x.add(subList);

  54. //獲取資料 字串空格分隔

  55. String arrays[] = lineTxt.split(" ");

  56. for(int i=2; i<arrays.length; i++) {

  57. if(i>=48) { //#號後跳出 後面註釋不進行讀取

  58. continue;

  59. }

  60. //獲取特徵:特徵值 如1:0.0004

  61. String subArrays[] = arrays[i].split(":");

  62. int number = Integer.parseInt(subArrays[0]); //判斷特徵

  63. float value = Float.parseFloat(subArrays[1]);

  64. subList.add(value);

  65. }

  66. //獲取每行樣本的Label值 i=0 (五個等級0-4)

  67. subList.add(Float.parseFloat(arrays[0]));

  68. //獲取qid值 i=1

  69. String subArrays[] = arrays[1].split(":");

  70. subList.add(Float.parseFloat(subArrays[1]));

  71. //總行數+1

  72. sumLabel++;

  73. } //End 按行讀取

  74. read.close();

  75. } else {

  76. System.out.println("找不到指定的檔案\n");

  77. }

  78. } catch (Exception e) {

  79. System.out.println("讀取檔案內容出錯");

  80. e.printStackTrace();

  81. } finally {

  82. bufferedReader.close();

  83. }

  84. }

  85. /**

  86. * 函式 寫檔案

  87. * 引數 String filePath 檔案路徑

  88. * 注意 該函式還是136維資料,但演算法該成46維 故不使用該函式

  89. */

  90. public void WriteTxtFile(String filePath) {

  91. try {

  92. System.out.println("檔案輸出");

  93. String encoding = "GBK";

  94. FileWriter fileWriter = new FileWriter(filePath);

  95. //按行寫檔案

  96. for(int i=0; i<sumLabel; i++) {

  97. fileWriter.write("樣本行數"+i+"\r\n");

  98. fileWriter.flush();

  99. String value;

  100. //寫資料特徵值 136

  101. for(int j=0;j<136;j++) {

  102. value = String.valueOf(x.get(i).get(j)); //輸出第i行 第j個特徵值

  103. fileWriter.write(value+" ");

  104. }

  105. //label等級 qid

  106. fileWriter.write("\r\n");

  107. value = String.valueOf(x.get(i).get(136)); //label

  108. fileWriter.write(value+" ");

  109. value = String.valueOf(x.get(i).get(137));

  110. fileWriter.write(value+" ");

  111. fileWriter.write("\r\n");

  112. }

  113. fileWriter.close();

  114. } catch(Exception e) {

  115. e.printStackTrace();

  116. }

  117. }

  118. /**

  119. * 學習排序

  120. * 主要功能計算136維權重w和劃分五個等級的閾值b

  121. */

  122. public void LearningToRank() {

  123. int realRank; //真實Label等級

  124. int predictRank; //預測Label等級

  125. Float[] y= new Float[RANK_LABEL+1]; //new label

  126. Float tao [] = new Float[RANK_LABEL+1];

  127. //初始化權重 全為0

  128. weight = new ArrayList<Float>();

  129. for(int i=0; i< RANK_CATA; i++){ //特徵向量的維數

  130. weight.add((float) 0.0);

  131. }

  132. //初始化閾值 b[0]=b[1]=[2]=0 b[3]=正無窮大

  133. b=new Float[RANK_LABEL+1];

  134. for(int i=0; i<RANK_LABEL; i++){ //b[0] b[1] b[2]

  135. b[i] = (float) 0.0;

  136. }

  137. b[RANK_LABEL] = Float.POSITIVE_INFINITY; //b[3]

  138. /*

  139. * 開始計算權重

  140. * 注意:迭代主要參照CSDN部落格,它沒有退出.同時沒有損失計算,其結果差別不大

  141. * 同時原論文中Loop 1...T是總行數 並沒有講述迭代

  142. */

  143. for(int iter = 0; iter < RANK_ITER; iter++){ //總的迭代次數 RANK_ITER=1

  144. for(int i=0; i< RANK_NUM; i++){ //總樣本數 可以設定讀取txt中部分

  145. //測試順序

  146. predictRank = 1;

  147. //權重*特徵向量-閾值

  148. float sumWX = (float) 0.0;

  149. for(int z=0; z<46; z++) {

  150. sumWX += weight.get(z)*x.get(i).get(z);

  151. }

  152. //預測排名

  153. for(int r=1;r<=RANK_LABEL;r++) { //閾值數 RANK_LABEL=3

  154. if(sumWX-b[r]<0) {

  155. predictRank = r;

  156. break;

  157. }

  158. }

  159. //獲取真實等級 即資料集中第一個Label數字

  160. realRank = Math.round(x.get(i).get(46)); //四捨五入並轉整數

  161. if(realRank!=predictRank) {

  162. for(int r=1; r < RANK_LABEL; r++){//若136維資料 5個值時

  163. if(realRank <= r) { // y形如 1 1 -1 -1 -1

  164. y[r] = (float)-1;

  165. }

  166. else {

  167. y[r] = (float)1;

  168. }

  169. }

  170. float tao_sum = (float) 0.0; //tau和

  171. for(int r=1; r < RANK_LABEL; r++) { //三個等級

  172. //權重*特徵向量-閾值

  173. if((sumWX - b[r]) * y[r] <= 0) {

  174. tao[r] = y[r];

  175. } else {

  176. tao[r] = (float) 0.0;

  177. }

  178. tao_sum += tao[r];

  179. }

  180. //更新資料

  181. for(int z=0; z<RANK_CATA; z++) { //136維權重

  182. float newWeight = weight.get(z) +tao_sum*x.get(i).get(z);

  183. weight.set(z, newWeight);

  184. }

  185. for(int r=1;r < RANK_LABEL;++r) { //5個閾值

  186. b[r] = b[r] - tao[r];

  187. }

  188. } //End if

  189. else {

  190. continue;

  191. }

  192. } //End 樣本總數

  193. } //End 迭代次數

  194. }

  195. /**

  196. * 函式 預測排序結果

  197. * 主要 通過LearningToRank()函式計算的得分計算分數,再根據閾值劃分等級

  198. */

  199. public void PredictNewLabel() {

  200. float rightCount = 0;

  201. float score = (float) 0.0;

  202. for(int i=0; i < RANK_NUM; i++){

  203. int predict_r = 1;

  204. //權重*特徵向量-閾值 (W*X-B)

  205. float sumWX = (float) 0.0;

  206. for(int z=0; z<46; z++) {

  207. sumWX = sumWX + weight.get(z) * x.get(i).get(z);

  208. }

  209. for(int r=1; r<= RANK_LABEL; r++){ //5

  210. if(sumWX < b[r]){

  211. score = sumWX;

  212. predict_r = r;

  213. break;

  214. }

  215. }

  216. //計算正確概率

  217. if(predict_r == Math.round(x.get(i).get(46))) //46維資料 46-label 47-qid 0-45特徵值

  218. {

  219. rightCount++;

  220. }

  221. System.out.println("predict="+predict_r+" score="+score+" real="+x.get(i).get(46));

  222. }

  223. //輸出結果

  224. System.out.println("正確率:"+rightCount/(float)RANK_NUM);

  225. System.out.println("輸出閾值");

  226. for(int i= 1;i<4;i++){

  227. System.out.println(b[i]+" ");

  228. }

  229. }

  230. /**

  231. * 主函式

  232. */

  233. public static void main(String[] args) {

  234. String fileInput = "train.txt";

  235. String fileOutput = "output.txt";

  236. String fileRank = "rank.txt";

  237. //例項化

  238. Prank prank = new Prank();

  239. try {

  240. //第一步 讀取檔案並解析資料

  241. prank.ReadTxtFile(fileInput);

  242. //第二步 輸出解析的基礎資料

  243. //prank.WriteTxtFile(fileOutput);

  244. //第三步 學習排序訓練模型

  245. prank.LearningToRank();

  246. //第四步 測試打分排序

  247. prank.PredictNewLabel();

  248. } catch (Exception e) {

  249. // TODO Auto-generated catch block

  250. e.printStackTrace();

  251. }

  252. }

  253. /**

  254. * End

  255. */

  256. }

   執行結果如下圖所示,演算法流程分析都很清楚,同時我採用的是下標從0開始取.b[1]和[2]兩個閾值即可劃分為3個不同的類,b[3]=Infinity.但是預測結果總是一個值,不知道為什麼?可能演算法中有些細節錯誤,糾結了我很長時間.如果知道希望告知.下面是採用C++實現.

    2.C++程式碼實現     該部分程式碼參考自新浪播客:     http://blog.sina.com.cn/s/blog_4c98b960010008xn.html     執行結果過程如下圖所示,通過train.txt資料集得到model.txt,裡面儲存的是46個權重.如:     -0.052744 1.886342 1.002179 -6.400005 -1.824795 0.000000 0.000000 ..     然後通過該模型對test.txt進行打分預測,同時計算正確率(已標註Label=預測Label).

  1. #include <iostream>

  2. #include <fstream>

  3. #include <limits>

  4. #include <iomanip>

  5. using namespace std;

  6. #define K 3 //排序的序數,即如排成全相關,部分相關,不相關,序數就是3

  7. #define N 46 //特徵的維數

  8. double *w; //權值

  9. int *b; //偏置項

  10. int *y;

  11. int *t;

  12. //從檔案中獲得特徵值 X 儲存特徵向量 yt 儲存標籤

  13. bool getData(double *x,int &yt,ifstream &fin)

  14. {

  15. if (fin.eof())

  16. return false;

  17. char data[1024];

  18. int index = 1;

  19. fin.getline(data,1024);

  20. char *p = data;

  21. char q[100];

  22. q[0] = p[0];

  23. q[1] = '\0';

  24. yt = atoi(q) + 1; // 標籤

  25. p = p+8;//跳過qid:xx的冒號

  26. for( ; *p != '\0'; ++p)

  27. {

  28. if(*p == ':')

  29. {

  30. ++p;

  31. int i = 0;

  32. for(i=0; *p != ' '; i++, p++)

  33. {

  34. q[i] = *p;

  35. }

  36. q[i] = '\0';

  37. x[index ++] = atof(q);

  38. }

  39. }

  40. return true;

  41. }

  42. //各變數進行初始化

  43. void Initialize()

  44. {

  45. w = new double[N+1];

  46. b = new int[K+1];

  47. y = new int[K+1];

  48. t = new int[K+1];

  49. int i;

  50. int r;

  51. for(i=1; i<=N;i++)

  52. w[i] = 0 ;

  53. for(r=1;r<=K-1;r++)

  54. b[r] = 0;

  55. b[K] = std::numeric_limits<int>::max();//無窮大

  56. }

  57. //利用Prank演算法進行訓練

  58. void PrankTraining(double *x,int yt)

  59. {

  60. int i;

  61. int r;

  62. double wx = 0; //儲存 W*X 的計算結果

  63. for(i =1; i<=N; i++) //計算 W*X

  64. wx += w[i] * x[i];

  65. for(r =1; r<=K; r++) //找到滿足 W*X-b<0 的最小 r

  66. {

  67. if(wx - b[r] <0 )

  68. break;

  69. }

  70. int yy = r ; //預測值

  71. if (yy == yt) //預測正確,直接返回

  72. {

  73. return;

  74. }

  75. else //預測錯誤,權值更新

  76. {

  77. for(r=1; r<K; r++)

  78. {

  79. if(yt <= r)

  80. y[r] = -1;

  81. else

  82. y[r] = 1;

  83. }

  84. for(r=1; r<K; r++)

  85. {

  86. if ((wx-b[r])*y[r] <= 0)

  87. {

  88. t[r] = y[r];

  89. }

  90. else

  91. t[r] = 0;

  92. }

  93. //更新 W 和 b

  94. int sumt = 0;

  95. for(r=1; r<K; r++)

  96. sumt = sumt + t[r];

  97. for(i=1;i<=N;i++) //更新 W

  98. w[i] = w[i] + sumt*x[i];

  99. for(r=1; r<K; r++) //更新 b

  100. b[r] = b[r] - t[r];

  101. }

  102. }

  103. //利用得到的model進行測試

  104. int Pranking(double *x)

  105. {

  106. int i;

  107. int r;

  108. double wx = 0;

  109. for(i=1; i<=N; i++)

  110. wx = wx + w[i] * x[i];

  111. for(r=1; r<=K; r++)

  112. if(wx - b[r] <0 )

  113. {

  114. cout<< " "<<wx;

  115. break;

  116. }

  117. return r;

  118. }

  119. int main(int argc,char **argv)

  120. {

  121. int right=0,wrong=0;//排正確和錯誤的樣本數

  122. //輸入訓練資料檔名

  123. string sin_train = "train.txt";

  124. ifstream fin_train(sin_train.c_str());

  125. if(fin_train.fail())

  126. {

  127. cout << "can't open the traningsetFile!"<<endl;

  128. return -1;

  129. }

  130. //輸入輸出模型檔名

  131. string sout_model = "model.txt";

  132. ofstream fout_model(sout_model.c_str());

  133. if(fout_model.fail())

  134. {

  135. cout << "can't open the ModelFile!"<<endl;

  136. return -1;

  137. }

  138. //輸入測試資料檔名

  139. string sin_test = "test.txt";

  140. ifstream fin_test(sin_test.c_str());

  141. if(fin_test.fail())

  142. {

  143. cout << "can't open the testsetFile!"<<endl;

  144. return -1;

  145. }

  146. // 輸入輸出結果檔名

  147. string sout_result = "result.txt";

  148. ofstream fout_result(sout_result.c_str());

  149. if(fout_result.fail())

  150. {

  151. cout << "open resultFile failed!"<<endl;

  152. return -1;

  153. }

  154. double *tr = new double[N+1]; // 特徵向量

  155. int yt; // 標籤

  156. Initialize(); //初始化權值w和偏置項b

  157. int i = 0;

  158. //讀入訓練資料進行訓練得到model

  159. while(true)

  160. {

  161. if (getData(tr,yt,fin_train))

  162. {

  163. PrankTraining(tr,yt);//訓練

  164. }

  165. else

  166. break;

  167. }

  168. //將得到的w和b寫入檔案

  169. char buff[128];

  170. cout<<"訓練出的w為:\n";

  171. for(i=1; i<=N; i++) //寫 w

  172. {

  173. cout<<setw(8)<<w[i]<<'\t';

  174. memset(buff,0,sizeof(buff));

  175. sprintf(buff,"%f",w[i]);

  176. fout_model << buff << " ";

  177. }

  178. fout_model<<endl;

  179. cout<<"\n\n訓練出的b為:\n";

  180. for(i = 1; i<K;i++) //寫 b

  181. {

  182. cout<<b[i]<<'\t';

  183. memset(buff,0,sizeof(buff));

  184. sprintf(buff,"%d",b[i]);

  185. fout_model << buff << " ";

  186. }

  187. //讀入測試資料進行測試得到正確率

  188. while(true)

  189. {

  190. if (getData(tr,yt,fin_test))

  191. {

  192. int yy = Pranking(tr);

  193. char p[2];

  194. p[0] = yy -1 + 48;

  195. p[1] = '\0';

  196. fout_result << p << endl;

  197. if (yy == yt)

  198. right ++;

  199. else

  200. wrong ++;

  201. }

  202. else

  203. break;

  204. }

  205. cout<<"\n\n排正確的個數為"<<right<<",錯誤的個數為"<<wrong<<",正確率為%"<<right*100*1.0/(right+wrong)<<endl;

  206. cout<<b[0]<<'\t'<<b[1]<<'\t'<<b[2];

  207. //釋放申請的空間並關閉檔案

  208. delete []w;

  209. delete []y;

  210. delete []t;

  211. delete []b;

  212. delete []tr;

  213. fin_train.close();

  214. fin_test.close();

  215. fout_result.close();

  216. fout_model.close();

  217. system("PAUSE");

  218. return 0;

  219. }

五. 總結與問題

    最後講述在該演算法中你可能遇到的問題和我的體會:     1.由於它是讀取檔案,可能檔案很大(幾百兆或上G).最初我設計的陣列是double feature[10000][136],用來儲存每行特徵值,但是如果行數太大時,What can do?此時我們應該設定動態陣列<List<List<Float>>>x解決.     2.最初閱讀了CSDN的Prank程式碼,它迭代了1萬次,最後檢視原文發現它並沒有迭代.所以你可以參考C++那部分程式碼,每次只需要讀取一行資料處理,並記住上一次的46維權重和閾值即可.     3.為什麼我從136維資料轉變成了46維資料?     你開啟136維特徵值資料時,你會發現它的值特別大,不論是Pointwise,還是Pairwise和Listwise都可能出現越界,一次內積求和可能就10的7次方資料了.但是46維資料,每個特徵值都是非常小的,所以如果用136維資料,你需要對資料進行歸一化處理,即資料縮小至-1到1之間.     4.評價Pointwise、Pairwise和Listwise指標通常是MAP和[email protected],後面講述基於對的學習排序和基於列的學習排序會具體介紹.     5.你可能會發現資料集中存在vail驗證集,以及交叉驗證、交叉熵、梯度下降後面都會講述.但由於相對於演算法,我對開發更感興趣,很多東西也是一知半解的.     6.最後要求該演算法到Hadoop或Spark實現並行化處理,但演算法的機制是序列化.有一定的方法,但我沒有實現.我們做的是一種偽並行化處理,即模型得到權重後進行並行化計算分數排序.     最後簡單附上我們的實驗結果,後面的演算法實驗結果是基於MAP和[email protected]

   希望文章對大家有所幫助!主要是現在看到LTR很多都是理論介紹,論文也沒有具體程式碼,而開源的RankLib有點看不懂,所以提出了自己的認識及程式碼執行.我也是才接觸這個一個月,可能過程中存在錯誤或不足之處,歡迎提出建議~同時感謝一起奮鬥的夥伴,尤其是Pu哥.