当前位置:   article > 正文

【学习排序】 Learning to Rank中Pointwise关于PRank算法源码实现_pranking with ranking

pranking with ranking

    最近终于忙完了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和NDCG@k,后面讲述基于对的学习排序和基于列的学习排序会具体介绍.
    5.你可能会发现数据集中存在vail验证集,以及交叉验证、交叉熵、梯度下降后面都会讲述.但由于相对于算法,我对开发更感兴趣,很多东西也是一知半解的.
    6.最后要求该算法到Hadoop或Spark实现并行化处理,但算法的机制是串行化.有一定的方法,但我没有实现.我们做的是一种伪并行化处理,即模型得到权重后进行并行化计算分数排序.
    最后简单附上我们的实验结果,后面的算法实验结果是基于MAP和NDCG@k



   希望文章对大家有所帮助!主要是现在看到LTR很多都是理论介绍,论文也没有具体代码,而开源的RankLib有点看不懂,所以提出了自己的认识及代码执行.我也是才接触这个一个月,可能过程中存在错误或不足之处,欢迎提出建议~同时感谢一起奋斗的伙伴,尤其是Pu哥.
       (By:Eastmount 2015-01-28 夜5点半    http://blog.csdn.net/eastmount/


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/509302
推荐阅读
相关标签
  

闽ICP备14008679号