Applying ListNet Listwise Ranking Model for Car Purchase Intent Prediction
This article introduces the ListNet listwise ranking algorithm, explains its theoretical foundations and loss function, presents a Python implementation with gradient computation, and demonstrates its superior performance over pointwise and pairwise models on public benchmarks and an internal automotive dataset for predicting users' intended car series.
Background : Before buying a car, users generate extensive behavior data on the Home platform (browsing, searching, comparing, posting, inquiry, etc.). Traditional pointwise classification models suffer from severe class imbalance and limited interaction between different car series, resulting in low Top‑1~Top‑3 hit rates.
Model Overview : The ListNet model, proposed in the paper "Learning to Rank: From Pairwise Approach to Listwise Approach" (Cao et al., 2007), treats each list of documents (car series) as a single training instance and adopts a listwise learning‑to‑rank strategy.
Model Principle : For a list of n items, a permutation p defines the ordering. The probability of a permutation under a score vector s is defined as \(P(p|s)=\frac{\exp(s_{p(1)})}{\sum_{j}\exp(s_{p(j)})}\). The loss function is the cross‑entropy between the ground‑truth permutation distribution and the predicted distribution, i.e., \(L = -\sum_{p} P(p|s^{*}) \log P(p|s)\).
Training : Using the cross‑entropy loss, gradients with respect to the model parameters are derived and optimized with stochastic gradient descent. A simple linear scoring function \(f(x)=w\cdot x\) is used for illustration.
Python Implementation : import numpy as np def get_step_direction(pyi, fw, dfw): # compute gradient of loss tmp1 = -np.dot(dfw, pyi[:, np.newaxis]) tmp2 = 1 / np.sum(np.exp(fw)) tmp3 = np.dot(dfw, (np.exp(fw).T[0])[:, np.newaxis]) return tmp1 + tmp2 * tmp3 def get_p(xi, wi): # probability of each item in a list si_e = np.exp(np.dot(xi, wi)) return si_e / np.sum(si_e) # data preparation x = np.array([[1,1,1],[1,0,1],[0,1,1],[0,0,1]]) x1, x2, x3 = x[[0,1]], x[[1,2]], x[[2,3]] x_s = [x1, x2, x3] learning_rate = 0.01 w = np.array([[0.5,0.5,0.5]]).T L = 1000 flag = 0 step = 0 for i in range(10): if flag == 1: break for xi in x_s: L_n = 0 P = get_p(xi, w) L_n -= np.dot(pyi, np.log(P))[0] # update parameters fw = np.dot(xi, w) dfw = xi.T dw = get_step_direction(pyi, fw, dfw) w = w + learning_rate * dw step += 1 if L_n < L: L = L_n else: flag = 1 break
Experimental Results : On the public TREC and OHSUMED datasets, ListNet consistently achieves the highest NDCG@k scores compared with pairwise and pointwise baselines. On the internal Home car‑purchase dataset (≈11,920 users, 81 car series per user), ListNet outperforms Logistic Regression and GBDT in hit‑rate for Top‑1~Top‑3 predictions.
Comparison and Analysis : Pointwise loss treats each car series independently, ignoring relative preferences within a list, which leads to sub‑optimal ranking when the data is highly imbalanced. ListNet re‑defines probabilities based on the whole list, capturing inter‑item comparisons and thus delivering better ranking performance.
Code Development : An open‑source TensorFlow implementation of ListNet is maintained at https://git.corpautohome.com/cmp/ListNet_tensorflow . The earlier Chainer version suffered from performance issues, prompting the TensorFlow rewrite.
Reference : Zhe Cao, Tao Qin (2007). "Learning to Rank: From Pairwise Approach to Listwise Approach".
HomeTech
HomeTech tech sharing
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.