前言:

来万物花开这家创业公司实习,也真是一波三折。先实习了三天,每天下午到公司工作到晚上。工作时间是每天下午到晚上9.30。结果每天上午没法用心干实验室的活了,下午在公司工作的时候,总是提心吊胆,手机震动一下就会立刻拿出来看看是不是老师找我了。这样的日子感觉没法持续下去,想找导师谈谈之前,就从实验室同学那儿知道了老师对我最近的出勤率太低很不高兴。想着还是找找导师谈一谈实习的问题吧,然后还在犹豫的时候,大boss就找我谈话了,退学or干活。于是只能拒绝了实习,安心回实验室吧。

意外的是,创业公司带我的老大陈开江师兄念在是北理工的同门师兄弟上,加上我对于转互联网方向的决心强烈,决定给我一个机会,每周一到周五晚上干活,周末拿一天时间出来交流,就算我实习三天了。只能说太感谢陈开江师兄了,让我在这么恶劣的条件下还给我实习的机会。

我的github:

我实现的代码全部贴在我的github中,欢迎大家去参观。

在作为算法实习生时,所实现的代码产权应该属于公司,所以在github中公布的代码可能会缺少一部分,或者比较简单,不涉及业务。

https://github.com/YinWenAtBIT

算法介绍:

背景介绍:

一、问题模型:

Multi-armed bandit问题,中文译名或叫做“多臂赌博机”问题。

在概率论中,多臂赌博机问题(有时也称为K臂/N臂赌博机问题),是一个赌徒需要在一排***前决定拉动哪一个***的臂,并且决定每个臂需要被拉动多少次的问题。每台***提供的奖励是与它自身的奖励随机分布的函数相关的。赌徒的目标是最大限度地通过杠杆拉动序列,使得获得的奖励最大化。

在实际应用中,多臂模型被用来模拟管理研究项目,比如一家制药公司,要给定一个的预算,问题是在各个项目中分配资源,项目的回报暂时只知道一部分,需要在项目进行的过程中才会知道的越来越清楚。

因此,在解决这个问题的时候,需要在“exploration”(探索新臂以获得跟多关于臂的回报的信息)和“exploitation”(选择已有回报最高的臂来获取最大利益)之中进行权衡。

数学模型:

多个臂的回报可以看做是一组真实的随机分布,K代表臂杆。代表每个臂的平均回报,赌徒每一轮拉下一个臂,并得到一次回报,是剩余可玩的轮数。多臂问题等同于一个状态的马尔科夫问题。轮之后的后悔度为,可用如下公式表示:,其中是最大回报的平均数,是在t时获得的回报。

一个零后悔度的策略会使得在无限次选择臂杆后以概率1趋近与0。那么,零后悔策略将在玩了足够多次之后趋近与最优策略。

UCB1算法:

UCB – The Upper Confidence BoundAlgorithm,上置信算法

小量贪婪算法和SOFTMAX算法缺陷:

前面所提及的算法(《Bandit Algorithms for Website Optimization》一书的前几章所介绍的算法,这里我直接拿它总结的结果使用)有一个弱点,它们只关心回报是多少,并不关心每个臂被拉下了多少次,这就意味着,这些算法不再会选中初始回报特别低臂,即使这个臂的回报只测试了一次。使用UCB1算法,将会不仅仅关注于回报,同样会关注每个臂被探索的次数。

在介绍UCB1算法之前,先总结一下前几章的介绍的epsilon-Greedy and Softmax algorithms(小量贪婪算法和SOFTMAX算法)的特点:

1.默认选择当前已知的回报率最高的臂杆

2.偶尔选择那些没有最高回报的臂杆

——小量贪婪算法随机选择,这样其他每个臂被选中的概率很小;

——SOFTMAX算法根据回报率选择臂杆,回报率比最大值小很多的臂杆很少选中,回报率接近最大值的笔杆被选中的概率接近最大臂被选中的概率

为了让这两种算法表现更好,我们可以在运行过程中动态的修改它们的参数,这种方式叫做退火算法。

我们需要知道每个臂的回报的置信度是因为,我们得到的每一个臂的回报结果是随机的,其中含有噪声。臂A看起来似乎比臂B选择要好,只有我们得到了足够多的数据,这时我们才能确认臂A优于臂B。

其他两种算法是容易被误导的,初始遇上了一次随机坏的结果,会把这个结果当做这个臂的回报。UCB算法不会受到臂回报随机性的影响。

因此,在实现UCB算法时,我们需要记录下对每个臂回报的置信度,做法是记录下每个臂被选择了多少次。

UCB1算法特性:

在实现UCB算法的时候,我们不需要关心其他的假设条件,只要满足一个条件:回报是分布在0-1之间,1代表最大的回报。如果使用的模型最大回报结果超出了这个范围,需要对结果进行归一化。

除了保存每个臂结果的置信度以外,UCB算法还在以下两个点不同与之前的算法:

1. UCB完全不使用随机性,每一种情况下,UCB选择的臂都是可以通过数据计算出来的

2. UCB算法没有任何需要配置的参数,这意味着,在任何情况下你都可以使用UCB算法,没有任何需要的先验条件

UCB算法实现:

每个臂包含的数据:

[cpp] view plain copy
  1. struct UCBNode
  2. {
  3. int counts;
  4. double values;
  5. };

UCB类成员:

[cpp] view plain copy
  1. class UCB1
  2. {
  3. public:
  4. UCB1();
  5. ~UCB1();
  6. bool update(string & key, double res);
  7. bool update(const char *startPos, double res);
  8. string toString();
  9. bool readFromString(const string& JString);
  10. string select_arm();
  11. std::vector<std::string> & select_arm_N(size_t n);
  12. std::vector<std::string> keystrs;
  13. private:
  14. insert(string & key, UCB value);
  15. std::unordered_map<string, UCB> frequencyReward;
  16. int totalcount;
  17. };

选择臂:

UCB算法选择臂算法如下:

1. 如果有counts为0的臂,即从未被选择的臂,那么先选择它(即初始时每个臂都会被选择一次)

2. 如果所有臂都被选择过,则计算每个臂的bonus和value的和,bonus计算公式如下:

[cpp] view plain copy
  1. bonus = sqrt((2 *total_counts) / float(counts);

3.选择其中bonus加value值最大臂

[cpp] view plain copy
  1. string UCB1::select_arm()
  2. {
  3. string maxkey;
  4. double bonus;
  5. double maxvalue = 0.0;
  6. if(frequencyReward.empty())
  7. throw empty_arm("no arm in the map");
  8. for(auto it = frequencyReward.begin(); it != frequencyReward.end(); it++ )
  9. {
  10. if(it->second.counts == 0)
  11. return it->first;
  12. bonus = sqrt(2* log((double)totalcount))/it->second.counts;
  13. if(maxvalue < bonus + it->second.values)
  14. {
  15. maxvalue = bonus + it->second.values;
  16. maxkey = it->first;
  17. }
  18. }
  19. return maxkey;
  20. }

因为每一个臂都需要至少被选择一次,因此,在使用UCB算法时需要注意,如果选择的次数M小于总的臂数N,就要谨慎使用UCB算法了。

bonus的意义在于,如果我们对一个臂的了解过于少,因此它的value值在此时的置信度是很低的,所以我们需要选择这个臂来获取更多的信息。因此,bonus可以当做一个测量对臂了解多少的指标,了解越少,bonus越大。加入了bonus这个指标,我们可以说这个算法是有好奇心的,当对于一个臂的了解少于了下限,它会被选中,即使这个臂的回报率很低。

更新臂:

每一个臂被选中之后,会返回一个value,那么更新的方法如下:

1.该臂的counts成员数加一

2. 该臂的value变为:原有的value值和新返回的结果value值按比例相加

[cpp] view plain copy
  1. values = values*(n-1)/n + res/n;

验证UCB算法:

当我们使用UCB算法,它会以怎样的频率来选中最优的臂?答案在最开始时,所有的臂的置信度都很低,因此每个臂都会被选中几次,此时选中最优臂的概率不高,当已经选择了很多次时,每个臂的置信度都已经相当高了,此时会一直选择最优臂,偶尔选择其他置信度过低的臂。如下图所示

UCB算法与其他算法对比:

这里,我们使用退火版本的小量贪婪算法和Softmax算法与UCB1算法对比,一共有5个臂供选择。

首先是选中最优臂的概率:

可以明显看到,UCB1算法的波动明显高于其他两个算法。不过在模拟末尾处,UCB算法已经追上了其他的两个算法,如果模拟的次数跟多,UCB算法将会优于其他两个算法。

然后是平均的回报率:

UCB算法能很快找到最优的臂,不过优于为了提高每一个臂的置信度,它回去选择那些回报率不高的臂,因此收敛于最优臂的速度慢于softamx算法,平均回报率也低于它。

最后是总回报率:

总回报率的结果已经可以由上一张平均回报率看出来了。softmax最高,UCB第二,小量贪婪最低。

总结:

在实现UCB算法的过程中,首先捡回来了阅读英文文献的能力,UCB算法的内容基本是通过一本英文教材还有英文的维基百科上学习到的。其实以前英文一直挺好的,只是太久不用,忘了挺多。第二点就是对于C++STL库以及模板类的复习,平时stl库还能偶尔用一用,但是map,set等就用的特别少了,更别说模板类了,这次好好的复习了一遍。第三就是学习了一些新的东西,比如Json格式保存数据对,ssh登陆等等一些开发的额外知识。实习的收获确实很大。

UCB算法的更多相关文章

  1. 通俗bandit算法

    [原文链接] 选择是一个技术活 著名鸡汤学家沃.滋基硕德曾说过:选择比努力重要. 我们会遇到很多选择的场景.上哪个大学,学什么专业,去哪家公司,中午吃什么,等等.这些事情,都让选择困难症的我们头很大. ...

  2. 【RL系列】Multi-Armed Bandit笔记——UCB策略与Gradient策略

    本篇主要是为了记录UCB策略与Gradient策略在解决Multi-Armed Bandit问题时的实现方法,涉及理论部分较少,所以请先阅读Reinforcement Learning: An Int ...

  3. 蒙特卡罗树搜索(MCTS)【转】

    简介 最近AlphaGo Zero又火了一把,paper和各种分析文章都有了,有人看到了说不就是普通的Reinforcement learning吗,有人还没理解估值网络.快速下子网络的作用就放弃了. ...

  4. Bandit

    CSE599:online and adaptive machine learning Lecture 3:Stochastic Multi-Armed Bandits, Regret Minimiz ...

  5. Multiarmed Bandit Algorithm在股票中的应用

    股票与Bandit Machine看起来相去甚远,但实际上通过限制买入和卖出的行为,股票可以转换为Bandit Machine,比如:规定股票必须在买入一天以后卖出.为什么要大费周折地把股票变成Ban ...

  6. 基于蒙特卡洛树搜索(MCTS)的多维可加性指标的异常根因定位

    摘要:本文是我在从事AIOps研发工作中做的基于MCTS的多维可加性指标的异常根因定位方案,方案基于清华大学AIOPs实验室提出的Hotspot算法,在此基础上做了适当的修改. 1        概述 ...

  7. Kmeans算法的K值和聚类中心的确定

    0 K-means算法简介 K-means是最为经典的基于划分的聚类方法,是十大经典数据挖掘算法之一. K-means算法的基本思想是:以空间中k个点为中心进行聚类,对最靠近他们的对象归类.通过迭代的 ...

  8. mab算法

    https://zhuanlan.zhihu.com/p/21388070?refer=resyschina 专治选择困难症——bandit算法 改善:https://zhuanlan.zhihu.c ...

  9. Bandit:一种简单而强大的在线学习算法

    假设我有5枚硬币,都是正反面不均匀的.我们玩一个游戏,每次你可以选择其中一枚硬币掷出,如果掷出正面,你将得到一百块奖励.掷硬币的次数有限(比如10000次),显然,如果要拿到最多的利益,你要做的就是尽 ...

随机推荐

  1. Exynos4412 IIC总线驱动开发(一)—— IIC 基础概念及驱动架构分析

    关于Exynos4412 IIC 裸机开发请看 :Exynos4412 裸机开发 —— IIC总线 ,下面回顾下 IIC 基础概念 一.IIC 基础概念 IIC(Inter-Integrated Ci ...

  2. Java搞笑注释(佛-)

    // _ooOoo_ // o8888888o // 88" . "88 // (| -_- |) // O\ = /O // ____/`---'\____ // . ' \\| ...

  3. delegate委托

    https://www.cnblogs.com/leicao/p/5251090.html 委托是一种存储函数引用的类型,在事件和事件的处理时有重要的用途 通俗的说,委托是一个可以引用方法的类型,当创 ...

  4. C#的switch case注意

    参数可以是 switch 语句中的 参数必须是一个整型或枚举类型,或者是一个 class 类型,其中 class 有一个单一的转换函数将其转换为整型或枚举类型. 所以用string也可以,6666

  5. (转)C#自制Web 服务器开发:用C#开发自己的Web服务器

    当输入:127.0.0.1:5050 GET / HTTP/1.1 Accept: text/html, application/xhtml+xml, */* Accept-Language: zh- ...

  6. RedHat7.4最小化安装yum源不可用问题解决

    本次安装的RedHat7.4是安装在Oracle VM VirtualBox5.2.8虚拟机上面的,本文不对安装虚拟机步骤做详细说明. 工具准备: Oracle VM VirtualBox5.2.8 ...

  7. spring mvc 之初体验

    Spring MVC最简单的配置 配置一个Spring MVC只需要三步: 在web.xml中配置Servlet: 创建Spring MVC的xml配置文件: 创建Controller和View &l ...

  8. 谈一谈URL

    作者:ManfredHu 链接:http://www.manfredhu.com/2017/08/16/22-url/index.html 声明:版权所有,转载请保留本段信息,谢谢大家 URL URL ...

  9. sleep(),wait(),yield()和join()方法的区别

    sleep() sleep()方法需要指定等待的时间,它可以让当前正在执行的线程在指定的时间内暂停执行,进入阻塞状态,该方法既可以让其他同优先级或者高优先级 的线程得到执行的机会,也可以让低优先级的线 ...

  10. C3 文件IO:APUE 笔记

    C3:文件IO 1 引言 本章描述的函数被成为不带缓冲的IO,涉及5个函数:open.read.write.lseek.close. 文件控制:dup.sync.fsync.fdatasync.fcn ...