forked from qingyun-wu/CoLinUCB_Revised
-
Notifications
You must be signed in to change notification settings - Fork 6
/
PTS.py
140 lines (114 loc) · 4.29 KB
/
PTS.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import numpy as np
import scipy.stats
class PTSArticleStruct:
def __init__(self, id, dimension, sigma, sigmaV, init="zero"):
self.id = id
self.dimension = dimension
self.sigma = sigma
self.sigmaV = sigmaV
self.A2 = 1.0/(self.sigmaV**2)*np.identity(n = self.dimension)
self.b2 = np.zeros(self.dimension)
self.A2Inv = np.linalg.inv(self.A2)
self.Mu = 1.0/(self.sigma**2)*self.A2Inv.dot(self.b2)
self.count = {}
self.V = np.random.multivariate_normal(self.Mu, self.A2Inv)
def updateParameters(self, user, click):
if user.id in self.count:
self.count[user.id] += 1
else:
self.count[user.id] = 1
self.A2 += 1.0/(self.sigma**2)*np.outer(user.U, user.U)
self.b2 += user.U*click
self.A2Inv = np.linalg.inv(self.A2)
self.Mu = 1.0/(self.sigma**2)*self.A2Inv.dot(self.b2)
#Sample V
self.V = np.random.multivariate_normal(self.Mu, self.A2Inv)
def getCount(self, user_id):
if user_id in self.count:
return self.count[user_id]
else:
return 0
class PTSUserStruct:
def __init__(self, id, dimension, sigma, sigmaU, init="zero"):
self.id = id
self.dimension = dimension
self.sigma = sigma
self.sigmaU = sigmaU
self.A = 1.0/(self.sigmaU**2)*np.identity(n = self.dimension)
self.b = np.zeros(self.dimension)
self.AInv = np.linalg.inv(self.A)
self.Mu = 1.0/(self.sigma**2)*np.dot(self.AInv, self.b)
self.count = {}
self.U = np.random.multivariate_normal(self.Mu, self.AInv)
def updateParameters(self, article, click):
if article.id in self.count:
self.count[article.id] += 1
else:
self.count[article.id] = 1
self.A += 1.0/(self.sigma**2)*np.outer(article.V,article.V)
self.b += article.V*click
self.AInv = np.linalg.inv(self.A)
self.Mu = 1.0/(self.sigma**2)*np.dot(self.AInv, self.b)
#Sample U
self.U = np.random.multivariate_normal(self.Mu, self.AInv)
class PTSParticleStruct:
def __init__(self, dimension, n, itemNum, sigma, sigmaU, sigmaV, weight, ptsb = False):
self.sigma = sigma
self.weight = weight
self.users = []
for i in range(n):
self.users.append(PTSUserStruct(i, dimension, sigma, sigmaU,))
self.articles = []
for i in range(itemNum):
self.articles.append(PTSArticleStruct(i, dimension, sigma, sigmaV,))
class PTSAlgorithm:
def __init__(self, particle_num, dimension, n, itemNum, sigma, sigmaU, sigmaV,): # n is number of users
self.sigma = sigma
self.dimension = dimension
self.particle_num = particle_num
self.particles = [] # Particles
for i in range(particle_num):
self.particles.append(PTSParticleStruct(dimension, n, itemNum, sigma, sigmaU, sigmaV, 1.0/particle_num))
self.time = 0
self.CanEstimateUserPreference = False
self.CanEstimateCoUserPreference = False
self.CanEstimateW = False
self.CanEstimateV = False
def decide(self, pool_articles, userID):
#Sample a Particle
d = np.random.choice(self.particle_num, p = [p.weight for p in self.particles])
p = self.particles[d]
#For PTS-B
maxPTA = float('-inf')
articlePicked = None
for x in pool_articles:
x_pta = p.users[userID].U.dot(p.articles[x.id].V)
# pick article with highest Prob
# print x_pta
if maxPTA < x_pta:
articlePicked = x
maxPTA = x_pta
return articlePicked
def updateParameters(self, articlePicked, click, userID):
self.time += 1
#Reweighting
weights = []
for d in range(self.particle_num):
article = self.particles[d].articles[articlePicked.id]
mean = article.V.dot(self.particles[d].users[userID].Mu)
var = 1.0/(self.particles[d].sigma**2)+article.V.dot(article.A2Inv).dot(article.V)
weights.append(scipy.stats.norm(mean, var).pdf(click))
weights = np.array(weights)/sum(weights) # sum to 1
# print self.time, weights
#Resampling
self.particles = [self.particles[i] for i in np.random.choice(self.particle_num, size = self.particle_num, p = weights)]
for d in range(self.particle_num):
self.particles[d].weight = 1.0/self.particle_num
for d in range(self.particle_num):
self.particles[d].users[userID].updateParameters(self.particles[d].articles[articlePicked.id], click)
self.particles[d].articles[articlePicked.id].updateParameters(self.particles[d].users[userID], click)
def getCoTheta(self, userID):
#Sample a Particle
d = np.random.choice(self.particle_num, p = [p.weight for p in self.particles])
p = self.particles[d]
return p.users[userID].U