├── PE-LTR.py └── README.md /PE-LTR.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import numpy as np 3 | from scipy.optimize import minimize 4 | from scipy.optimize import nnls 5 | 6 | seed = 3456 7 | tf.set_random_seed(seed) 8 | np.random.seed(seed) 9 | 10 | x_data = np.float32(np.random.rand(100, 4)) 11 | y_data = np.dot(x_data, [[0.100], [0.200], [0.3], [0.4]]) + 0.300 12 | 13 | weight_a = tf.placeholder(tf.float32) 14 | weight_b = tf.placeholder(tf.float32) 15 | 16 | b = tf.Variable(tf.zeros([1])) 17 | W = tf.Variable(tf.random_uniform([4, 1], -1.0, 1.0)) 18 | y = tf.matmul(x_data, W) + b 19 | 20 | loss_a = tf.reduce_mean(tf.square(y - y_data)) 21 | loss_b = tf.reduce_mean(tf.square(W) + tf.square(b)) 22 | loss = weight_a * loss_a + weight_b * loss_b 23 | 24 | optimizer = tf.train.GradientDescentOptimizer(0.1) 25 | 26 | a_gradients = tf.gradients(loss_a, W) 27 | b_gradients = tf.gradients(loss_b, W) 28 | 29 | train = optimizer.minimize(loss) 30 | 31 | init = tf.initialize_all_variables() 32 | 33 | sess = tf.Session() 34 | sess.run(init) 35 | 36 | 37 | def pareto_step(w, c, G): 38 | """ 39 | ref:http://ofey.me/papers/Pareto.pdf 40 | K : the number of task 41 | M : the dim of NN's params 42 | :param W: # (K,1) 43 | :param C: # (K,1) 44 | :param G: # (K,M) 45 | :return: 46 | """ 47 | GGT = np.matmul(G, np.transpose(G)) # (K, K) 48 | e = np.mat(np.ones(np.shape(w))) # (K, 1) 49 | m_up = np.hstack((GGT, e)) # (K, K+1) 50 | m_down = np.hstack((np.transpose(e), np.mat(np.zeros((1, 1))))) # (1, K+1) 51 | M = np.vstack((m_up, m_down)) # (K+1, K+1) 52 | z = np.vstack((-np.matmul(GGT, c), 1 - np.sum(c))) # (K+1, 1) 53 | hat_w = np.matmul(np.matmul(np.linalg.inv(np.matmul(np.transpose(M), M)), M), z) # (K+1, 1) 54 | hat_w = hat_w[:-1] # (K, 1) 55 | hat_w = np.reshape(np.array(hat_w), (hat_w.shape[0],)) # (K,) 56 | c = np.reshape(np.array(c), (c.shape[0],)) # (K,) 57 | new_w = ASM(hat_w, c) 58 | return new_w 59 | 60 | 61 | def ASM(hat_w, c): 62 | """ 63 | ref: 64 | http://ofey.me/papers/Pareto.pdf, 65 | https://stackoverflow.com/questions/33385898/how-to-include-constraint-to-scipy-nnls-function-solution-so-that-it-sums-to-1 66 | 67 | :param hat_w: # (K,) 68 | :param c: # (K,) 69 | :return: 70 | """ 71 | A = np.array([[0 if i != j else 1 for i in range(len(c))] for j in range(len(c))]) 72 | b = hat_w 73 | x0, _ = nnls(A, b) 74 | 75 | def _fn(x, A, b): 76 | return np.linalg.norm(A.dot(x) - b) 77 | 78 | cons = {'type': 'eq', 'fun': lambda x: np.sum(x) + np.sum(c) - 1} 79 | bounds = [[0., None] for _ in range(len(hat_w))] 80 | min_out = minimize(_fn, x0, args=(A, b), method='SLSQP', bounds=bounds, constraints=cons) 81 | new_w = min_out.x + c 82 | return new_w 83 | 84 | 85 | w_a, w_b = 0.5, 0.5 86 | c_a, c_b = 0.2, 0.2 87 | for step in range(0, 10): 88 | res = sess.run([a_gradients, b_gradients, train], feed_dict={weight_a: w_a, weight_b: w_b}) 89 | weights = np.mat([[w_a], [w_b]]) 90 | paras = np.hstack((res[0][0], res[1][0])) 91 | paras = np.transpose(paras) 92 | w_a, w_b = pareto_step(weights, np.mat([[c_a], [c_b]]), paras) 93 | la = sess.run(loss_a) 94 | lb = sess.run(loss_b) 95 | print("{:0>2d} {:4f} {:4f} {:4f} {:4f} {:4f}".format(step, w_a, w_b, la, lb, la / lb)) 96 | # print(np.reshape(sess.run(W), (4,)), sess.run(b)) 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PE-LTR 2 | Demo implementation for PE-LTR. 3 | 4 | paper: "A Pareto-Efficient Algorithm for Multiple Objective Optimization in E-Commerce Recommendation". RecSys, 2019, Alibaba 5 | 6 | link: http://www.yongfeng.me/attach/lin-recsys2019.pdf 7 | 8 | ref: https://github.com/jackielinxiao/PE-LTR 9 | --------------------------------------------------------------------------------