• 为了保证你在浏览本网站时有着更好的体验，建议使用类似Chrome、Firefox之类的浏览器~~
• 如果你喜欢本站的内容何不Ctrl+D收藏一下呢，与大家一起分享各种编程知识~
• 本网站研究机器学习、计算机视觉、模式识别~当然不局限于此，生命在于折腾，何不年轻时多折腾一下

LTR排序之pair-wise-ranknet算法TensorFlow实现

4个月前 (03-27) 840次浏览

$P_{i,j}=\frac{e^{o_{i,j}}}{1+e^{o_{i,j}}}$

$\begin{matrix} P_{i,j}&=&\frac{e^{o_{i,j}}}{1+e^{o_{i,j}}}\\ &=&\frac{e^{o_i-o_j}}{1+e^{o_i-o_j}} \\ &=& \frac{e^{o_i- o_k + o_k + o_j}}{1+e^{o_i-o_k + o_k -o_j}}\\&=& \frac{e^{o_{i,k} + o_{k,j}}}{1+e^{o_{i,k} + o_{k,j}}} \\&=& \frac{P_{i,k}\ P_{k,j}}{1+2P_{i,k}\ P_{k,j}\ -P_{i,k}\ -P_{k,j}}\end{matrix}$

$o_{i,j} = \ln \frac{P_{i,j}}{1-P{i,j}}$

$\begin{matrix}C(o_{ij})&=&-\hat{P}_{i,j} \ln P_{ij} – (1-\hat{P}_{ij})\ln (1-P_{ij})\\&=&-\hat{P}_{ij} o_{ij}+\ln (1+e^{o_{ij}})\end{matrix}$

$\hat{P}_{i,j}=\frac{1}{2}(1+S_{ij})$

TensorFlow 的代码保存在 mac 上面，明天抽个时间贴到这里来。。。。，先去洗澡睡觉觉了

# -*- coding: utf-8 -*-
# @Time    : 2018/3/27 上午 10:55
# @Author  : Tomcj
# @File    : tensor_ranknet.py
# @Software: PyCharm
import  tensorflow as tf
import numpy as np

BATCH_SIZE=100
y_train=[]
X_train=[]
Query=[]
array_train_x1=[]
array_train_x0=[]

feature_num = 46
h1_num = 10
def extractFeatures(split):
'''
获取特征
'''
features = []
for i in range(2, 48):
features.append(float(split[i].split(':')[1]))
return features

def extractQueryData(split):
'''
获取以下数据 quryid documentid 等
Format:

'''
queryFeatures = [split[1].split(':')[1]]
queryFeatures.append(split[50])
queryFeatures.append(split[53])
queryFeatures.append(split[56])

return queryFeatures
def get_microsoft_data():
'''
获取基础样本特征数据
:return:
'''
with open('/Users/leiyang/RankNet/Data/train.txt','r') as fp:
for data in fp:
split = data.split()
y_train.append(int(split[0]))
X_train.append(extractFeatures(split))
Query.append(extractQueryData(split))

def get_pair_feature(y_train,Query):
'''
获取组合样本特征
:return:
'''
pairs = []
tmp_x0=[]
tmp_x1=[]
for i in range(0, len(Query)):
for j in range(i + 1, len(Query)):
# Only look at queries with the same id
if (Query[i][0] != Query[j][0]):
break
# Document pairs found with different rating
if (Query[i][0] == Query[j][0] and y_train[i] != y_train[j]):
# Sort by saving the largest index in position 0
if (y_train[i] > y_train[j]):
pairs.append([i, j])
tmp_x0.append(X_train[i])
tmp_x1.append(X_train[j])
else:
pairs.append([j, i])
tmp_x0.append(X_train[j])
tmp_x1.append(X_train[i])

array_train_x0 = np.array(tmp_x0)
array_train_x1=np.array(tmp_x1)
print('Found %d document pairs' % (len(pairs)))
return pairs,len(pairs),array_train_x0,array_train_x1

with tf.name_scope("input"):
x1 = tf.placeholder(tf.float32,[None, feature_num],name="x1")
x2 = tf.placeholder(tf.float32,[None, feature_num],name="x2")

#添加隐层节点
with tf.name_scope("layer1"):
with tf.name_scope("w1"):
w1 = tf.Variable(tf.random_normal([feature_num, h1_num]), name="w1")
with  tf.name_scope("b1"):
b1 = tf.Variable(tf.random_normal([h1_num]), name="b1")

#此处没有添加激活函数
with tf.name_scope("h1_o1"):
h1_o1 = tf.matmul(x1,w1) + b1
h1_o1=tf.nn.relu(h1_o1)

with tf.name_scope("h2_o1"):
h1_o2 = tf.matmul(x2, w1) + b1
h1_o2 = tf.nn.relu(h1_o2)

#添加输出节点
with tf.name_scope("output"):
with tf.name_scope("w2"):
w2 = tf.Variable(tf.random_normal([h1_num, 1]), name="w2")

with tf.name_scope("b2"):
b2 = tf.Variable(tf.random_normal([1]))

h2_o1 = tf.matmul(h1_o1, w2) + b2
h2_o2 = tf.matmul(h1_o2, w2) + b2
h2_o1=tf.sigmoid(h2_o1)
h2_o2=tf.sigmoid(h2_o2)

#根据输出节点计算概率值
with tf.name_scope("loss"):
# o12 = o1 - o2
h_o12 = h2_o1 - h2_o2
pred = 1/(1 + tf.exp(-h_o12))
#此处的 label_P 就是真实的概率，因为前面组 pair 数据已经人为将相关的样本放在
#前面，所以 Sij 均为 1，所以计算的结果就是 1
lable_p = 1

cross_entropy = -lable_p * tf.log(pred) -(1-lable_p) * tf.log(1-pred)

reduce_sum = tf.reduce_sum(cross_entropy)
loss = tf.reduce_mean(reduce_sum)

with tf.name_scope("train_op"):

with tf.Session() as sess :
# step 1 解析 microsoft 数据集
get_microsoft_data()
#step 2 获取 pair 组合
pairs,datasize,array_train_x0,array_train_x1=get_pair_feature(y_train,Query)
init = tf.global_variables_initializer()
sess.run(init)
for epoch in range(0,10000):
start=(epoch*BATCH_SIZE)%datasize
end=min(start+BATCH_SIZE,datasize)
sess.run(train_op, feed_dict={x1: array_train_x0[start:end,:], x2: array_train_x1[start:end,:]})
if epoch % 1000== 0 :
l_v = sess.run(loss, feed_dict={x1:array_train_x0, x2:array_train_x1})

result_0=sess.run(h2_o1,feed_dict={x1:array_train_x0, x2:array_train_x1})
result_1=sess.run(h2_o2,feed_dict={x1:array_train_x0, x2:array_train_x1})
#使用所有的样本计算模型预测的准确率
print  np.sum(result_0>result_1)*1.0/datasize
# print  sess.run(cross_entropy,feed_dict={x1:array_train_x0, x2:array_train_x1})
# print "------ epoch[%d] loss_v[%f] ------ "%(epoch, l_v)



Deeplearn, 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明LTR 排序之 pair-wise-ranknet 算法 TensorFlow 实现

(12)个小伙伴在吐槽
1. 博主好，/Users/leiyang/RankNet/Data/train.txt 这个文件的数据格式是什么样的？
• 文章中使用的是微软的数据，你可以点击文件下载链接会自动跳转到微软数据链接，格式类似libsvm格式
2. 你好，能请教几个问题吗？1. 代码94行注释说明没有激活函数，但是代码中有加relu、sigmoid？另外我看原理里似乎也没提到要加激活函数？2. 我照您代码运行的结果里，loss居高不下；我把激活函数去掉后，loss则始终是nan，您觉得可能是什么原因呢？非常感谢！
cryyrc2018-05-09 20:07
• nan的问题弄好了，现在loss降得很慢，您有啥好的经验吗？
cryyrc2018-05-10 15:35
• 那么你的loss是否是正常水平？你的迭代次数设置多少？
• 不知道哪里改错了又变nan了，之前loss好像是10左右。accuracy您是用啥的，ndcg吗？我的之前的accuracy（if pred == label_p）只有0.5左右，这是正常水平吗？谢谢
cryyrc2018-05-12 19:12
• 0.5还是比较低的，准确度是使用了相对排序准确率，比如A与B为一个组合，如果A的顺序的确排在B前面，则命中一个有效结果，除以总的组合个数，计算得到准确率；回答你之前的一个问题，代码94行原来是没哟relu函数的，后面是我自己添加的
• 谢谢。还有个问题是：inference的时候应该是以h2_o1和h2_o2作为reference score的预测值吧？但是经过sigmoid之后，h2_o1、h2_o2都在(0,1)上，o1、o2是在[0,4]上的，这个是怎么对上的一直想不通。
cryyrc2018-05-14 11:36
• h2_o1、h2_o2是用来计算i和j商品的得分，也就是用于后续索引i和索引j结果的相对顺序概率Pij，这里用了sigmoid使其处于[0,1]有点误导的含义，我晚上回家重新整理这篇文章
• 不好意思，我又来了，打扰打扰。我试了试下面这俩条路，结果差很多，您觉得原因是啥呢：1. 按您的code（使label_p始终为1）来，有3点区别是： output层去掉了sigmoid； 另外layer1那层relu之前加了个batch_normalization（不加的时候我的loss会nan，遂加之）； cross_entropy改用了tf.nn.sigmoid_cross_entropy_with_logits（也是为了解决nan）。这样的结果是一个epoch之后loss就降到0左右了（accuracy=0.99）。------ epoch[0] loss_v[313.779449] ------ 0.6056278521548547------ epoch[1000] loss_v[0.000299] ------ 0.99999519889766692. 然后我把label_p改成了0或1的形式，其他参数和第一种相同，这次acc始终0.5+，试着把batch_size加到10000，跑了100000个epoch后acc也才到0.6。--- epcoh: 0 loss_v: 3025.997314453125 acc: 0.5300270590833341 ------ epcoh: 1000 loss_v: 0.9345578551292419 acc: 0.5659493897177698 ------ epcoh: 2000 loss_v: 0.7046830654144287 acc: 0.5705210409417381 ---...--- epcoh: 98000 loss_v: 0.6801395416259766 acc: 0.5912141276199131 ------ epcoh: 99000 loss_v: 0.674396276473999 acc: 0.5931556957184217 ---按说这俩应该是一个意思，为什么结果差这么多，百思不得其解。
cryyrc2018-05-14 15:58
• 这个网站的评论模板不太，层数越多越不好看，你可以贴一个github的gist文件？单独在评论一下贴出gist地址或者可以通过service@deeplearn.me联系我
• ok，谢谢
cryyrc2018-05-15 00:12
• 版权声明

本站的文章和资源来自互联网或者站长
的原创，按照 CC BY -NC -SA 3.0 CN
协议发布和共享，转载或引用本站文章
应遵循相同协议。如果有侵犯版权的资
源请尽快联系站长，我们会在24h内删
除有争议的资源。
• 网站驱动

• 友情链接

• 支持主题

邮箱：service@deeplearn.me