This commit is contained in:
louzefeng
2024-07-11 05:50:32 +00:00
parent bf99793fd0
commit d3828a7aee
6071 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,192 @@
<audio id="audio" title="16 | 简单却有效的Bandit算法" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/5b/f0/5b8fb495bbf8b29a650419626595def0.mp3"></audio>
我在之前的文章中表达过,推荐系统的使命就是在建立用户和物品之间的连接。建立连接可以理解成:为用户匹配到最佳的物品;但也有另一个理解就是,在某个时间某个位置为用户选择最好的物品。
## 推荐就是选择
生活中,你我都会遇到很多要做选择的场景。上哪个大学,学什么专业,去哪家公司,中午吃什么等等。这些事情,都让选择困难症的我们头很大。头大在哪呢?主要是不知道每个选择会带来什么后果。
你仔细想一下,生活中为什么会害怕选择,究其原因是把每个选项看成独一无二的个体,一旦错过就不再来。推荐系统中一个一个单独的物品也如此,一旦选择呈现给用户,如果不能得到用户的青睐,就失去了一个展示机会。如果跳出来看这个问题,选择时不再聚焦到具体每个选项,而是去选择类别,这样压力是不是就小了很多?
比如说,把推荐选择具体物品,上升到选择策略。如果后台算法中有三种策略:按照内容相似推荐,按照相似好友推荐,按照热门推荐。每次选择一种策略,确定了策略后,再选择策略中的物品,这样两个步骤。
那么是不是有办法来解决这个问题呢当然有那就是Bandit算法。
## MAB问题
Bandit算法来源于人民群众喜闻乐见的赌博学它要解决的问题是这样的。
一个赌徒,要去摇老虎机,走进赌场一看,一排老虎机,外表一模一样,但是每个老虎机吐钱的概率可不一样,他不知道每个老虎机吐钱的概率分布是什么,那么想最大化收益该怎么整?
这就是多臂赌博机问题(Multi-armed bandit problem, K-armed bandit problem, MAB)简称MAB问题。有很多相似问题都属于MAB问题。
1. 假设一个用户对不同类别的内容感兴趣程度不同,当推荐系统初次见到这个用户时,怎么快速地知道他对每类内容的感兴趣程度?这也是推荐系统常常面对的冷启动问题。
1. 假设系统中有若干广告库存物料,该给每个用户展示哪个广告,才能获得最大的点击收益,是不是每次都挑收益最好那个呢?
1. 算法工程师又设计出了新的策略或者模型,如何既能知道它和旧模型相比谁更靠谱又对风险可控呢?
这些问题全都是关于选择的问题。只要是关于选择的问题都可以简化成一个MAB问题。
我在前面的专栏中提过推荐系统里面有两个顽疾一个是冷启动一个是探索利用问题后者又称为EE问题ExploitExplore问题。针对这两个顽疾Bandit算法可以入药。
冷启动问题好说,探索利用问题什么意思?
利用意思就是:比较确定的兴趣,当然要用啊。好比说我们已经挣到的钱,当然要花啊。
探索的意思就是:不断探索用户新的兴趣才行,不然很快就会出现一模一样的反复推荐。就好比我们虽然有一点钱可以花了,但是还得继续搬砖挣钱啊,要不然,花完了就要喝西北风了。
## Bandit算法
Bandit算法并不是指一个算法而是一类算法。现在就来介绍一下Bandit算法家族怎么解决这类选择问题的。
首先来定义一下如何衡量选择的好坏Bandit算法的思想是看看选择会带来多少遗憾遗憾越少越好。在MAB问题里用来量化选择好坏的指标就是累计遗憾计算公式如图所示。
<img src="https://static001.geekbang.org/resource/image/eb/62/ebae50f93e023a03e54ef057fc4c1062.png" alt="" />
简单描述一下这个公式。公式有两部分构成:一个是遗憾,一个是累积。求和符号内部就表示每次选择的遗憾多少。
Wopt就表示每次都运气好选择了最好的选择该得到多少收益WBi就表示每一次实际选择得到的收益两者之差就是“遗憾”的量化在T次选择后就有了累积遗憾。
在这个公式中为了简化MAB问题每个臂的收益不是0就是1也就是伯努利收益。
这个公式可以用来对比不同Bandit算法的效果对同样的多臂问题用不同的Bandit算法模拟试验相同次数比比看哪个Bandit算法的累积遗憾增长得慢那就是效果较好的算法。
Bandit算法的套路就是小心翼翼地试越确定某个选择好就多选择它越确定某个选择差就越来越少选择它。
如果某个选择实验次数较少,导致不确定好坏,那么就多给一些被选择机会,直到确定了它是金子还是石头。简单说就是,把选择的机会给“确定好的”和“还不确定的”。
Bandit算法中有几个关键元素回报环境。
1. 臂:是每次选择的候选项,好比就是老虎机,有几个选项就有几个臂;
1. 回报:就是选择一个臂之后得到的奖励,好比选择一个老虎机之后吐出来的金币;
1. 环境:就是决定每个臂不同的那些因素,统称为环境。
将这个几个关键元素对应到推荐系统中来。
1. 臂:每次推荐要选择候选池,可能是具体物品,也可能是推荐策略,也可能是物品类别;
1. 回报:用户是否对推荐结果喜欢,喜欢了就是正面的回报,没有买账就是负面回报或者零回报;
1. 环境:推荐系统面临的这个用户就是不可捉摸的环境。
下面直接开始陈列出最常用的几个Bandit算法。
### 1.汤普森采样算法
第一个是汤普森采样算法。这个算法我个人很喜欢它,因为它只要一行代码就可以实现,并且数学的基础最简单。
简单介绍一下它的原理假设每个臂是否产生收益起决定作用的是背后有一个概率分布产生收益的概率为p。
每个臂背后绑定了一个概率分布;每次做选择时,让每个臂的概率分布各自独立产生一个随机数,按照这个随机数排序,输出产生最大随机数那个臂对应的物品。听上去很简单,为什么这个随机数这么神奇?
关键在于每个臂背后的概率分布,是一个贝塔分布,先看看贝塔分布的样子:
<img src="https://static001.geekbang.org/resource/image/8f/c1/8f9e60416f8f32418ef7ba214ce961c1.png" alt="" />
贝塔分布有a和b两个参数。这两个参数决定了分布的形状和位置
1. 当a+b值越大分布曲线就越窄分布就越集中这样的结果就是产生的随机数会容易靠近中心位置
1. 当a/(a+b)的值越大分布的中心位置越靠近1反之就越靠近0这样产生的随机数也相应第更容易靠近1或者0。
贝塔分布的这两个特点,可以把它分成三种情况:
1. 曲线很窄而且靠近1
1. 曲线很窄而且靠近0
1. 曲线很宽。
这和前面所讲的选择有什么关系呢你把贝塔分布的a参数看成是推荐后得到用户点击的次数把分布的b参数看成是没有得到用户点击的次数。按照这个对应再来叙述一下汤普森采样的过程。
1. 取出每一个候选对应的参数a和b
1. 为每个候选用a和b作为参数用贝塔分布产生一个随机数
1. 按照随机数排序,输出最大值对应的候选;
1. 观察用户反馈如果用户点击则将对应候选的a加1否则b加1
注意实际上在推荐系统中要为每一个用户都保存一套参数比如候选有m个用户有n个那么就要保存2 * m * n 个参数。
汤普森采样为什么有效呢?解释一下。
1. 如果一个候选被选中的次数很多也就是a+b很大了它的分布会很窄换句话说这个候选的收益已经非常确定了用它产生随机数基本上就在中心位置附近接近平均收益。
1. 如果一个候选不但a+b很大即分布很窄而且a/(a+b)也很大接近1那就确定这是个好的候选项平均收益很好每次选择很占优势就进入利用阶段反之则几乎再无出头之日。
1. 如果一个候选的a+b很小分布很宽也就是没有被选择太多次说明这个候选是好是坏还不太确定那么用它产生随机数就有可能得到一个较大的随机数在排序时被优先输出这就起到了前面说的探索作用。
用Python实现汤普森采样就一行
>
choice = numpy.argmax(pymc.rbeta(1 + self.wins, 1 + self.trials - self.wins))
### 2.UCB算法
第二个常用的Bandit算法就是UCB算法UCB算法全称是Upper Confidence Bound即置信区间上界。它也为每个臂评分每次选择评分最高的候选臂输出每次输出后观察用户反馈回来更新候选臂的参数。
每个臂的评分公式为.<br />
<img src="https://static001.geekbang.org/resource/image/bc/44/bc3cf903b267ea80309b56a44717b144.png" alt="" />
公式有两部分加号前面是这个候选臂到目前的平均收益反应了它的效果后面的叫做Bonus本质上是均值的标准差反应了候选臂效果的不确定性就是置信区间的上边界。t是目前的总选择次数Tjt是每个臂被选择次数。
观察这个公式如果一个候选的被选择次数很少即Tjt很小那么它的Bonus就会较大在最后排序输出时有优势这个Bonus反映了一个候选的收益置信区间宽度Bonus越大候选的平均收益置信区间越宽越不确定越需要更多的选择机会。
反之如果平均收益很大,就是说加号左边很大,也会在被选择时有优势。
这个评分公式也和汤普森采样是一样的思想:
1. 以每个候选的平均收益为基准线进行选择;
1. 对于被选择次数不足的给予照顾;
1. 选择倾向的是那些确定收益较好的候选。
### 3. Epsilon贪婪算法
这是一个朴素的算法,也很简单有效,思想有点类似模拟退火,做法如下。
<li>
先选一个(0,1)之间较小的数叫做Epsilon也是这个算法名字来历。
</li>
<li>
每次以概率Epsilon做一件事所有候选臂中随机选一个以1-Epsilon的概率去选择平均收益最大的那个臂。
</li>
是不是简单粗暴Epsilon的值可以控制对探索和利用的权衡程度。这个值越接近0在探索上就越保守。
和这种做法相似,还有一个更朴素的做法:先试几次,等每个臂都统计到收益之后,就一直选均值最大那个臂。
### 4.效果对比
以上几个算法,可以简单用模拟试验的方式对比其效果,如图所示。
<img src="https://static001.geekbang.org/resource/image/7a/12/7aa810ba0419fefa12b2291f6f122612.png" alt="" />
横坐标是模拟次数增加,可以看成随着时间推移,纵坐标就是累积遗憾,越高说明搞砸的次数越多。在模拟后期,基本上各种算法优劣一目了然。从上到下分别是下面几种。
1. 完全随机:就是不顾用户反馈的做法。
1. 朴素选择:就是认准一个效果好的,一直推。
1. Epsilon贪婪算法每次以小概率尝试新的大概率选择效果好的。
1. UCB每次都会给予机会较少的候选一些倾向。
1. 汤普森采样:用贝塔分布管理每一个候选的效果。
UCB算法和汤普森采样都显著优秀很多。
## 冷启动
我想你已经想到了推荐系统冷启动问题可以用Bandit算法来解决一部分。
大致思路如下:
<li>
用分类或者Topic来表示每个用户兴趣我们可以通过几次试验来刻画出新用户心目中对每个Topic的感兴趣概率。
</li>
<li>
这里如果用户对某个Topic感兴趣就表示我们得到了收益如果推给了它不感兴趣的Topic推荐系统就表示很遗憾(regret)了。
</li>
<li>
当一个新用户来了针对这个用户我们用汤普森采样为每一个Topic采样一个随机数排序后输出采样值Top N 的推荐Item。注意这里一次选择了Top N个候选臂。
</li>
<li>
等着获取用户的反馈没有反馈则更新对应Topic的b值点击了则更新对应Topic的a值。
</li>
## 总结
今天给你介绍了一种走一步看一步的推荐算法叫做Bandit算法。Bandit算法把每个用户看成一个多变的环境待推荐的物品就如同赌场里老虎机的摇臂如果推荐了符合用户心目中喜欢的就好比是从一台老虎机中摇出了金币一样。
今天重点介绍的Bandit算法有汤普森采样UCB算法Epsilon贪婪并且用模拟的方式对比了它们的效果汤普森采样以实现简单和效果显著而被人民群众爱戴你需要时不妨首先试试它。
同时这里留下一个问题给你今天讲到的Bandit算法有哪些不足欢迎留言和我一起讨论。
<img src="https://static001.geekbang.org/resource/image/87/b0/873b086966136189db14874181823fb0.jpg" alt="" />

View File

@@ -0,0 +1,142 @@
<audio id="audio" title="17 | 结合上下文信息的Bandit算法" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/7e/e8/7ec39180507170be665a6eaa9d59a7e8.mp3"></audio>
上一篇文章我说到Bandit算法用的是一种走一步看一步的思路这一点看上去非常佛系似乎一点都不如机器学习深度学习那样厚德载物但是且慢下结论先看看我在前面介绍的那几个Bandit算法。
## UCB回顾
这些Bandit算法都有一个特点完全没有使用候选臂的特征信息。特征可是机器学习的核心要素也是机器学习泛化推广的依赖要素。
没有使用特征信息的Bandit算法问题就在于只能对当前已有的这些候选臂进行选择对于新加入的候选只能从0开始积累数据而不能借助已有的候选泛化作用。
举个例子假如有一个用户是鹿晗的粉丝通过Bandit算法有两个鹿晗的广告得到展示并得到了较好的收益。
那么对于一个新的广告如果具有鹿晗这个特征直觉上前两个鹿晗广告的收益信息可以泛化到当前新广告上新广告就不是完全从0开始积累数据而是有了一定的基础这样的收敛会更快。
UCB和汤普森采样这两个Bandit算法在实际中表现很好。于是前辈们就决定送UCB去深造一下让它能够从候选臂的特征信息中学到一些知识。
UCB就是置信上边界的简称所以UCB这个名字就反映了它的全部思想。置信区间可以简单直观地理解为不确定性的程度区间越宽越不确定反之就很确定。
1. 每个候选的回报均值都有个置信区间,随着试验次数增加,置信区间会变窄,相当于逐渐确定了到底是回报丰厚还是亏了。
1. 每次选择前,都根据已经试验的结果重新估计每个候选的均值及置信区间。
1. 选择置信区间上界最大的那个候选。
“选择置信区间上界最大的那个候选”,这句话反映了几个意思:
1. 如果候选的收益置信区间很宽,相当于被选次数很少,还不确定,那么它会倾向于被多次选择,这个是算法冒风险的部分;
1. 如果候选的置信区间很窄,相当于被选次数很多,比较确定其好坏了,那么均值大的倾向于被多次选择,这个是算法保守稳妥的部分;
1. UCB是一种乐观冒险的算法它每次选择前根据置信区间上界排序反之如果是悲观保守的做法可以选择置信区间下界排序。
## LinUCB
“Yahoo!”的科学家们在2010年基于UCB提出了LinUCB算法它和传统的UCB算法相比最大的改进就是加入了特征信息每次估算每个候选的置信区间不再仅仅是根据实验而是根据特征信息来估算这一点就非常的“机器学习”了。
在广告推荐领域每一个选择的样本由用户和物品一起构成用户特征物品特征其他上下文特征共同表示出这个选择把这些特征用来估计这个选择的预期收益和预期收益的置信区间就是LinUCB要做的事情。
LinUCB算法做了一个假设一个物品被选择后推送给一个用户其收益和特征之间呈线性关系。在具体原理上LinUCB有一个简单版本以及一个高级版本。简单版本其实就是让每一个候选臂之间完全互相无关参数不共享。高级版本就是候选臂之间共享一部分参数。
先从简单版本讲起。
还是举个例子,假设现在两个用户,用户有一个特征就是性别,性别特征有两个维度,男,女。现在有四个商品要推荐给这两个用户,示意如下。
<img src="https://static001.geekbang.org/resource/image/ea/4b/eae9254fef3cdfc681247f58fa740b4b.png" alt="" />
两个用户就是Bandit算法要面对的上下文表示成特征就是下面的样子。
<img src="https://static001.geekbang.org/resource/image/d4/ae/d47f7f976d7a0fd651f8f4be67f8aeae.png" alt="" />
每一次推荐时,用特征和每一个候选臂的参数去预估它的预期收益和置信区间。
$x_{i}\times\theta_{j}$,这就是给男性用户推荐剃须刀,给女性用户推荐口红,即使是新用户,也可以作出比随机猜测好的推荐,再观察用户是否会点击,用点击信息去更新那个被推荐了的候选臂的参数。
这里的例子还简化了一个地方就是没有计算置信区间这是UCB的精髓。下面来补上。
假如D是候选臂在m次被选择中积累的特征相当于就是m条样本特征维度是d所以D是一个矩阵维度是m x d。
这m次被选择每次得到用户的点击或者没点击把这个反馈信息记录为一个m x 1的向量叫做C。所以这个候选臂对应的参数就是d x 1的向量d就是特征维度数记录为一个戴帽子的西塔$\hat{\theta}$。
按照LinUCB认为参数和特征之间线性相乘就应该得到收益
$$D_{m \times d} \times \hat{\theta_{d \times 1}} = C_{m \times 1}$$<br />
你看D也知道C也知道要求 $\theta$ ,这就很简单了。
$$ \hat{\theta}_{d \times 1} = (D_{m \times d}^{T})^{-1} C_{m \times 1}$$
但是由于数据稀疏,实际上求参数西塔时不会这样简单粗暴,而是采用岭回归的方法,给原始特征矩阵加上一个单位对角矩阵后再参与计算:
$$ \hat{\theta}_{d \times 1} = (D_{m \times d}^{T}D_{m \times d} + I_{d \times d})^{-1}D_{m \times d}^{T}C_{m \times 1}$$
每一个候选臂都像这样去更新它的参数,同时,得到参数后,在真正做选择时,用面对上下文的特征和候选臂的参数一起。
除了估算期望收益还要计算置信区间的上边界如果x是上下文特征则期望收益和置信上边界的计算方法分别是下面的样子。
期望收益:
$$\hat{r} = x^{T}_{d \times 1}\hat{\theta}_ {d \times1 }$$
置信区间上边界:
$$\hat{b} = \alpha \sqrt{x^{T}_{d \times 1}(D_{m \times d}^{T}D_{m \times d} + I_{d \times d})^{-1}x_{d \times 1}}$$
这两个计算结果都是标量数值。置信区间计算公式虽然看起来复杂实际上反应的思想也很直观随着被选择次数的增加也就是m增加这个置信上边界是越来越小的。
每一次选择时给每一个候选臂都计算这两个值相加之后选择最大那个候选臂输出就是LinUCB了。
刚才说到了岭回归ridge regression这里多说一句岭回归主要用于当样本数小于特征数时对回归参数进行修正。对于加了特征的Bandit问题正好符合这个特点试验次数样本少于特征数。
信息量有点大我在这里再一次列出LinUCB的重点。
1. LinUCB不再是上下文无关地像盲人摸象一样从候选臂中去选择了而是要考虑上下文因素比如是用户特征、物品特征和场景特征一起考虑。
1. 每一个候选臂针对这些特征各自维护一个参数向量,各自更新,互不干扰。
1. 每次选择时用各自的参数去计算期望收益和置信区间,然后按照置信区间上边界最大的输出结果。
1. 观察用户的反馈,简单说就是“是否点击”,将观察的结果返回,结合对应的特征,按照刚才给出的公式,去重新计算这个候选臂的参数。
当LinUCB的特征向量始终取1每个候选臂的参数是收益均值的时候LinUCB就是UCB。
说完简单版的LinUCB再看看高级版的LinUCB。与简单版的相比就是认为有一部分特征对应的参数是在所有候选臂之间共享的所谓共享也就是无论是哪个候选臂被选中都会去更新这部分参数。
## 构建特征
LinUCB算法有一个很重要的步骤就是给用户和物品构建特征也就是刻画上下文。
在“Yahoo”的应用中物品是文章。它对特征做了一些工程化的处理这里稍微讲一下可供实际应用时参考借鉴。
首先,原始用户特征有下面几个。
1. 人口统计学性别特征2类年龄特征离散成10个区间
1. 地域信息:遍布全球的大都市,美国各个州。
1. 行为类别代表用户历史行为的1000个类别取值。
其次,原始文章特征有:
1. URL类别根据文章来源分成了几十个类别。
1. 编辑打标签:编辑人工给内容从几十个话题标签中挑选出来的。
原始特征向量先经过归一化,变成单位向量。
再对原始用户特征做第一次降维降维的方法就是利用用户特征和物品特征以及用户的点击行为去拟合一个矩阵W。
$$\phi_{u}^{T}W\phi_{a}^{T}$$
就用逻辑回归拟合用户对文章的点击历史得到的W直觉上理解就是能够把用户特征映射到物品特征上相当于对用户特征降维了映射方法是下面这样。
$$\psi_{u}=\phi_{u}^{T}W$$
这一步可以将原始的1000多维用户特征投射到文章的80多维的特征空间。
然后用投射后的80多维特征对用户聚类得到5个类文章页同样聚类成5个类再加上常数1用户和文章各自被表示成6维向量。
接下来就应用前面的LinUCB算法就是了特征工程依然还是很有效的。
## 总结
今天我和你分享了一种上下文有关的Bandit算法叫做LinUCB它有这么几个优点
1. 由于加入了特征所以收敛比UCB更快也就是比UCB更快见效
1. 各个候选臂之间参数是独立的,可以互相不影响地更新参数;
1. 由于参与计算的是特征,所以可以处理动态的推荐候选池,编辑可以增删文章;
当然LinUCB以及所有的Bandit算法都有个缺点同时处理的候选臂数量不能太多不超过几百个最佳。因为每一次要计算每一个候选臂的期望收益和置信区间一旦候选太多计算代价将不可接受。
LinUCB只是一个推荐框架可以将这个框架应用在很多地方比如投放广告为用户选择兴趣标签你还可以发挥聪明才智看看它还能用来解决什么问题欢迎留言一起交流。
<img src="https://static001.geekbang.org/resource/image/87/b0/873b086966136189db14874181823fb0.jpg" alt="" />

View File

@@ -0,0 +1,134 @@
<audio id="audio" title="18 | 如何将Bandit算法与协同过滤结合使用" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/a4/9a/a41a4d68b12d545f4bacb7ddc8c3709a.mp3"></audio>
推荐系统中最经典的算法是什么?对,是协同过滤,你已经学会抢答了。
是的,协同过滤是推荐系统发展史上浓墨重彩的一笔,其背后的思想简单深刻,在万物互联的今天,协同过滤的威力更加强大。与其说协同过滤是一门技术,不如说是一种方法论,不是机器在为你推荐,而是“集体智慧”在为你推荐。
协同过滤生动地诠释了什么是“物以类聚,人以群分”,你的圈子决定了你能见到的物品,这一点在前面的专栏中已经详细讲过了。但是这背后隐藏了一个重要的问题:是不是会存在信息茧房的问题?
## 信息茧房
其实作为一名对推荐系统略懂一二的普通海淀群众,我个人就会时常担心,是不是还能看到新的东西,是不是有惊喜。时不时乱点一通,是不是叉掉所有的推荐,让物品的推荐系统崩溃一下,这一切就是为了避免进入信息茧房,在眼前的圈子里苟且。
那么作为推荐系统的开发者是不是应该做点什么呢是的在技术上Bandit算法就是一个权衡探索和利用的好方法。如果把它结合传统的协同过滤来做推荐那么在一定程度上就可以延缓信息茧房的到来偶遇诗和远方。
我已经和你聊了两篇关于Bandit算法的内容我介绍过普通的Bandit算法也介绍过加入特征信息的LinUCB算法今天我要介绍的是一个新方法如何结合协同过滤的群体智慧与Bandit的走一步看一步一起让两种思想碰撞也许可以让你的推荐系统与众不同。
这就是2016年有人提出的COFIBA算法下面我就开始与你聊聊这种算法。
## COFIBA算法
### 1 思想
很多的推荐场景中都有两个规律。
<li>
相似的用户对同一个物品的反馈可能是一样的。也就是对一个聚类用户群体推荐同一个item他们可能都会喜欢也可能都不喜欢同样的同一个用户会对相似的物品反馈也会相同。这实际上就是基于用户的协同过滤基本思想。
</li>
<li>
在使用推荐系统过程中用户的决策是动态进行的尤其是新用户。这就导致无法提前为用户准备好推荐候选只能“走一步看一步”是一个动态的推荐过程。这是Bandit的算法基本思想。
</li>
每一个推荐候选物品,都可以根据用户对其偏好的不同,将用户分成不同的群体。
然后下一次由用户所在的群体集体帮他预估可能的收益及置信区间这个集体就有了协同的效果然后再实时观察真实反馈回来更新用户的个人参数用于下次调整收益和置信区间这就有了Bandit的思想在里面。
举个例子,如果你的父母给你安排了很多相亲对象,要不要见面去相一下?那需要提前看看每一个相亲对象的资料,每次大家都分成好几派,有说好的,有说再看看的,也有说不行的。
你自己也会是其中一派的一员每次都是你所属的那一派给你集体打分因为他们是和你“三观一致的人”“诚不欺我”这样从一堆资料中挑出分数最高的那个人你出去见TA回来后把实际感觉说给大家听同时自己心里的标准也有些调整重新再给剩下的其它对象打分打完分再去见
如果要推荐的候选物品较多,需要对物品聚类,就不用按照每一个物品对用户聚类,而是按照每一个物品所属的类簇对用户聚类,如此一来,物品的类簇数目相对于物品数就要大大减少。
# 2.细节
基于上述的思想COFIBA算法要点摘要如下。
1. 在时刻t有一个用户来访问推荐系统推荐系统需要从已有的候选池子中挑一个最佳的物品推荐给他然后观察他的反馈用观察到的反馈来更新挑选策略。
1. 这里的每个物品都有一个特征向量所以这里的Bandit算法是context相关的只不过这里虽然是给每个用户维护一套参数但实际上是由用户所在的聚类类簇一起决定结果的。
1. 这里依然是用岭回归去拟合用户的权重向量用于预测用户对每个物品的可能反馈payoff这一点和我们上一次介绍的LinUCB算法是一样的。
对比上一次介绍的LinUCB算法COFIBA的不同有两个
1. 基于用户聚类挑选最佳的物品,即相似用户集体动态决策;
1. 基于用户的反馈情况调整用户和物品的聚类结果。
整体算法过程如下。
在针对某个用户i在每一次推荐时做以下事情。
1. 首先计算用户i的Bandit参数W做法和LinUCB算法相同但是这个参数并不直接参与到选择决策中注意这和LinUCB不同只是用来更新用户聚类。
1. 遍历候选物品每一个物品已经表示成一个向量x了。
1. 每一个物品都对应一个物品聚类类簇每一个物品类簇对应一个全量用户聚类结果所以遍历到每一个物品时就可以判断出当前用户在当前物品面前自己属于哪个用户聚类类簇然后把对应类簇中每个用户的M矩阵(对应LinUCB里面的A矩阵)b向量表示收益向量对应LinUCB里面的b向量加起来从而针对这个类簇求解一个岭回归参数类似LinUCB里面单独针对每个用户所做同时计算其收益预测值和置信区间上边界。
1. 每个待推荐的物品都得到一个预测值及置信区间上界,挑出那个上边界最大的物品作为推荐结果。
1. 观察用户的真实反馈然后更新用户自己的M矩阵和b向量只更新每个用户对应类簇里其他的不更新。
以上是COFIBA算法的一次决策过程。在收到用户真实反馈之后还有两个计算过程
1. 更新user聚类
1. 更新item聚类。
如何更新user和item的聚类呢我在这里给出了一个示意图。
<img src="https://static001.geekbang.org/resource/image/12/ed/12d5ab9b1c0ce6455ae475d0d8d923ed.jpeg" alt="" />
解释一下这个图。
a 示意图中有6个用户8个物品初始化时用户和物品的类簇个数都是1。
b在某一轮推荐时推荐系统面对的用户是4。推荐过程就是遍历18每个物品然后在面对每个物品时用户4在哪个类簇中把对应类簇中的用户聚合起来为这个物品集体预测收益值置信上边界。这里假设最终物品5胜出被推荐出去了。
在时刻t物品一共有3个聚类类簇需要更新的用户聚类是物品5对应的用户4所在类簇。
更新方式看看该类簇里面除了用户4之外的用户对物品5的预期收益是不是和用户4相近如果是则保持原来的连接边否则删除原来的连接边。删除边之后相当于就重新构建了聚类结果。
这里假设新的聚类结果由原来用户4所在的类簇分裂成了两个类簇4和5成一类6单独自成一类。
c更新完用户类簇后被推荐出去的物品5它对应的类簇也要更新。
更新方式是对于每一个和物品5还存在连接边的物品假如叫做物品j都有一个对这个物品j有相近收益预估值的近邻用户集合然后看看近邻用户集合是不是和刚刚更新后的用户4所在的类簇相同。
是的话保留物品5和物品j之间的连接边否则删除。这里示意图中是物品3和物品5之间的连接边被删除。
物品3变成了孤家寡人一个不再和任何物品有链接独立后就给他初始化了一个全新的用户聚类结果所有用户是一个类簇。
简单来说就是这样:
1. 用协同过滤来少选可以参与决策的用户代表用LinUCB算法来实际执行选择
1. 根据用户的反馈,调整基于用户和基于物品的聚类结果,即对物品和用户的群体代表做换届选举;
1. 基于物品的聚类如果变化,又进一步改变了用户的聚类结果;
1. 不断根据用户实时动态的反馈来调整用户决策参数,从而重新划分聚类结果矩阵。
COFIBA算法也很容易实现GitHub上就有。原始论文也从理论和实验两方面都证明了它的有效性。
## 再谈EE问题
整个专栏的Bandit算法系列主要是解决推荐系统中的冷启动和EE问题。探索和利用这一对矛盾一直客观存在而Bandit算法是公认的一种比较好的解决EE问题的方案。
除了Bandit算法之外还有一些其他的探索兴趣的办法比如在推荐时随机地去掉一些用户历史行为特征
解决兴趣探索势必要冒险势必要面对用户的未知而这显然就是可能会伤害当前用户价值的明知道用户肯定喜欢A你还偏偏以某个小概率给推荐非A。
实际上,很少有公司会采用这些理性的办法做探索,反而更愿意用一些盲目主观的方式。究其原因,可能是因为:
1. 互联网产品生命周期短,而探索又是为了提升长期利益的,所以没有动力做;
1. 用户使用互联网产品时间越来越碎片化,探索的时间长,难以体现出探索的价值;
1. 同质化互联网产品多,用户选择多,稍有不慎,用户用脚投票,分分钟弃你于不顾;
1. 已经成规模的平台,红利杠杠的,其实是没有动力做探索的。
基于这些,我们如果想在自己的推荐系统中引入探索机制,需要注意以下几点:
1. 用于探索兴趣的物品,要保证其本身质量,纵使用户不感兴趣,也不至于引起其反感,损失平台品牌价值;
1. 探索兴趣的地方需要产品精心设计,让用户有耐心陪你玩儿;
1. 深度思考,这样才不会做出脑残的产品,产品不会早早夭折,才有可能让探索机制有用武之地。
## 总结
今天我介绍完成了MAB问题和推荐系统之间的千丝万缕联系。Bandit算法是一种不太常用在推荐系统的算法究其原因是它能同时处理的物品数量不能太多。
但是在冷启动和处理EE问题时Bandit算法简单好用值得一试。当然这个专栏介绍的所有推荐算法都不是单打独斗最好而是与其他算法结合使用才能相映生辉Bandit算法亦是如此。
今天介绍的COFIOBA算法原理很简单就是把协同过滤思想引入到了Bandit算法中不再是用户独立决策而是用户所在的群体共同决策推荐结果。
这样比较问题,也可以加速收敛。不知道对你有没有启发呢?欢迎留言一起讨论。感谢你的收听,我们下次再见。
<img src="https://static001.geekbang.org/resource/image/11/4c/112709b33b1bfe5539eb3f1aa124f54c.jpg" alt="" />