前言: 最近在学强化学习,从Sutton的《强化学习(第二版)》开始学起。在GitHub上找到了很不错的资源:Sutton的学生,现在在牛津读强化学习PhD的Shangtong Zhang复现的书中的实例的代码https://github.com/ShangtongZhang/reinforcement-learning-an-introduction。

在得到这份代码前,我自发地、独立地实现过书上k-armed赌博机的实验,自认为效果不错;见到Zhang学长的代码ten_armed_testbed.py后,觉得其代码与自己写的代码简直天壤之别。

阅读源码受益良多,我主要从以下三个方面进行总结:

  • 面向对象的程序设计(封装用于迭代的重复性算法)
  • 数值实验的常用方法与思想:tqdm与更稳健的大数定律
  • 更短的代码收获更好的可读性:lambda、zip()、_、np.zero_like()等技巧的使用

文章目录

  • 面向对象的程序设计
    • 将迭代动作封装在类中
    • 尽量多地将重复动作封装在函数里
    • 对于不同实验,无需声明全局常量,降低了灵活性
  • 数值实验的常用方法与思想
    • tqdm
    • 大数定律
  • 更短的代码收获更好的可读性
    • lambda
    • zip()
    • _
    • [A for a in list]
    • enumerate()
    • np.zero_like()
    • np.ndenumerate()
    • np.random.choice(np.where(np.array == target)[0])
  • 总结:技多不压身,持续学习提效率

面向对象的程序设计

将迭代动作封装在类中

Zhang首先创建了赌博机这个类,这个类不仅仅是字面意义上的“赌博机”(不仅仅拥有返回摇臂结果的功能),还能做到:

  • 输出自己最好的臂(最优动作是什么);
  • 将各种迭代/学习方式封装,根据参数进行选择模式进行执行(如,实例化时UCB_param=1,则该摇臂机自动进行UCB式学习)。
class Bandit:# @k_arm: # of arms# @epsilon: probability for exploration in epsilon-greedy algorithm# @initial: initial estimation for each action# @step_size: constant step size for updating estimations# @sample_averages: if True, use sample averages to update estimations instead of constant step size# @UCB_param: if not None, use UCB algorithm to select action# @gradient: if True, use gradient based bandit algorithm# @gradient_baseline: if True, use average reward as baseline for gradient based bandit algorithmdef __init__(self, k_arm=10, epsilon=0., initial=0., step_size=0.1, sample_averages=False, UCB_param=None,gradient=False, gradient_baseline=False, true_reward=0.):self.k = k_armself.step_size = step_sizeself.sample_averages = sample_averagesself.indices = np.arange(self.k)self.time = 0self.UCB_param = UCB_paramself.gradient = gradientself.gradient_baseline = gradient_baselineself.average_reward = 0self.true_reward = true_rewardself.epsilon = epsilonself.initial = initialdef reset(self):...# get an action for this banditdef act(self):...return np.random.choice(np.where(...))# take an action, update estimation for this actiondef step(self, action):return reward

为什么这么设计?

  • 充分利用共性;
  • 无论是贪心算法、UCB还是梯度下降法,解决摇臂赌博机都遵循“依据某一规则选择动作-动作-获得该动作奖赏,更新奖赏表”这一规则;
  • 让代码更少,并且极大地增强易读性。

尽量多地将重复动作封装在函数里

这个.py文件的调用关系是这样的:

  • __main__调用了figure_2_2()
  • figure_2_2()调用了新建了一个list,保存不同参数的摇臂机(用于对比实验),接着调用simulate(, , list)
  • simulate(, , list)中,list里可能有2个摇臂机对象,而一个simulate就可以输出保存2个摇臂机各自每代结果的解
def simulate(runs, time, bandits):rewards = np.zeros((len(bandits), runs, time))best_action_counts = np.zeros(rewards.shape)for i, bandit in enumerate(bandits):for r in trange(runs):bandit.reset()for t in range(time):action = bandit.act()reward = bandit.step(action)rewards[i, r, t] = rewardif action == bandit.best_action:best_action_counts[i, r, t] = 1mean_best_action_counts = best_action_counts.mean(axis=1)mean_rewards = rewards.mean(axis=1)return mean_best_action_counts, mean_rewards

对于 rewards[i, r, t] 与后来的 best_action_counts.mean(axis=1) 的处理很值得学习。

对于不同实验,无需声明全局常量,降低了灵活性

我实现赌博机时,首先声明了一个全局10臂赌博机,以后所有实验中,都得去使用这个10臂赌博机,因此函数都需要带着个参数( , , 10臂赌博机);其中,10臂赌博机仅用于输入选择的动作,返回奖励值。

这显然是不经济的(总是输入相同参数,则该参数不应作为参数,而应内化到对象中去)。

无需全局的赌博机参数都一样,作对比实验中的赌博机一样就可以了。Zhang的算法中则注意到了这点。

甚至,在示意赌博机分布时,都没有从赌博机内提取数据,而是使用np.random直接生成的。

def figure_2_1():plt.violinplot(dataset=np.random.randn(200, 10) + np.random.randn(10))plt.xlabel("Action")plt.ylabel("Reward distribution")plt.savefig('images/figure_2_1.png')plt.close()

如上图,其实上图中的数据从未被使用过,只是用来画图释义。真正用于实验的数据在每个赌博机实例内。

但是,其同一组实验中,赌博机参数其实也是不一样的,这点我要质疑一下。

数值实验的常用方法与思想

tqdm

from tqdm import trange
def simulate(runs, time, bandits):...for i, bandit in enumerate(bandits):for r in trange(runs):...for t in range(time):......return ...

将 in range(num) 改为 in trange(num) ,可以轻易查看进度条。

大数定律

如何保证实验稳健性?

我在进行我的实验时,每次结果都不一样,但是多次进行是可以看出趋势的。

Zhang如何做到每次输出的图都几乎一样呢?

from tqdm import trange
def simulate(runs, time, bandits):...for i, bandit in enumerate(bandits):for r in trange(runs):...for t in range(time):......return ...

注意到,runs是我写算法时所没有用到的。

其意义为:我的实验每次迭代times步,但是我要做runs次实验,再对每步取平均。一般来讲,times取1000,runs取2000,由大数定律,这么多次实验的取值,每步已经很接近真实的平均值了。

Intel® Core™ i5-8265U CPU @ 1.60GHz 1.80GHz , RAM = 8.00GB

我的电脑大概每秒能做75次实验。

更短的代码收获更好的可读性

lambda

lambda被我理解成很好的委托,不能说自己不会用,但没有Zhang学长用起来这么美观、强健。

def figure_2_6(runs=2000, time=1000):labels = ['epsilon-greedy', 'gradient bandit','UCB', 'optimistic initialization']generators = [lambda epsilon: Bandit(epsilon=epsilon, sample_averages=True),lambda alpha: Bandit(gradient=True, step_size=alpha, gradient_baseline=True),lambda coef: Bandit(epsilon=0, UCB_param=coef, sample_averages=True),lambda initial: Bandit(epsilon=0, initial=initial, step_size=0.1)]parameters = [np.arange(-7, -1, dtype=np.float),np.arange(-5, 2, dtype=np.float),np.arange(-4, 3, dtype=np.float),np.arange(-2, 3, dtype=np.float)]bandits = []for generator, parameter in zip(generators, parameters):for param in parameter:bandits.append(generator(pow(2, param)))...

zip()

zip()将2个同维度对象合并,与for结合方便高效。

for generator, parameter in zip(generators, parameters):for param in parameter:bandits.append(generator(pow(2, param)))...

_

_, average_rewards = simulate(runs, time, bandits)

只想要函数返回值中的一/几个量(严格来说是不想要某一/几个量),使用_补位,类似matlab中的~。

使用_补位后,编辑器也不会提示该常量没有使用过。

[A for a in list]

python这个把 for 简化在 list 声明中的语法确实太友善了。

epsilons = [0, 0.1, 0.01]
bandits = [Bandit(epsilon=eps, sample_averages=True) for eps in epsilons]

enumerate()

枚举器,返回索引与元素。

np.zero_like()

输出一个同尺寸的0矩阵。

np.ndenumerate()

'''
Z=[[0 1 2][3 4 5][6 7 8]]
'''
for index, value in np.ndenumerate(Z):print('\n',index, value)

输出:

(0, 0) 0(0, 1) 1(0, 2) 2

numpy的枚举器,没想到numpy竟然自带枚举器(可返回索引),知道了,效率翻倍。

np.random.choice(np.where(np.array == target)[0])

np.where(condition)[0]返回的是符合condition的全部位置,结合random.choice(),保证选择的随机性。

总结:技多不压身,持续学习提效率

如果不知道 np.ndenumerate() 可以实现类似功能么?可以,但恐怕要写个:

for i in range(array.shape[0]):for j in range(array.shape[1]):...

其实,python早已经把这种常用、重复性强的代码封装好了。了解这些“奇技淫巧”,很有助于我们提升开发效率,且增强代码可读性。

从程序设计、tqdm到lambda:python的“奇技淫巧”,让实现效率翻倍【科学计算类】相关推荐

  1. 了解计算 python零基础_Python零基础入门与科学计算 寒假班

    自从2017年 7 月 20 日,国务院印发<新一代人工智能发展规划>,明确指出在中小学阶段设置人工智能相关课程后,编程教育走进了更多人的视野.Python 作为人工智能时代最合适的语言, ...

  2. python爬虫数据挖掘_Python网页爬虫文本处理科学计算机器学习数据挖掘兵器谱...

    转载自"我爱自然语言处理":http://www.52nlp.cn,已获得授权.更多内容可见公众号:"牛衣古柳"(ID:Deserts-X). 周末时看到这篇不 ...

  3. 做副业月薪10K+,工作效率翻倍!Python是个什么宝藏?

    前几天,和大学几个兄弟聚会.聊到在大家最近的工作怎么样?在北京工作的大学兄弟魏明聊起他的工作,副业赚的钱比工资还要多! 我们几个马上凑过来:副业做的是什么啊? 聊了会才知道,原来他半年前学习了Pyth ...

  4. 做副业月薪12K+,工作效率翻倍,Python是个什么宝藏?

    前几天,和大学几个兄弟聚会.聊到在大家最近的工作怎么样?在北京工作的大学兄弟魏明聊起他的工作,副业赚的钱比工资还要多! 我们几个马上凑过来:副业做的是什么啊? 聊了会才知道,原来他半年前学习了Pyth ...

  5. python与数据处理课后答案_python数据处理与科学计算_课程2020最新章节测试网课课后答案...

    [问答题] 苏联解体后,俄罗斯的外交政策出现了哪些变化? [问答题] 怎样认识世界进入以科技为先导.以经济为中心的综合国力竞争的时期? [问答题] 战后美国全球战略是怎样演变的? [多选] 第三世界发 ...

  6. Python编程及应用--数据分析与科学计算可视化培训班

    附件: 一.[培训对象] 各高等院校大数据相关学科.计算机.软件.数字媒体.信息管理.统计.电子商务等专业教师,在读高年级本科生及研究生.从事计算机.云计算.大数据.互联网等相关领域项目的科研院所的项 ...

  7. 科学计算:Python VS. MATLAB (1)----给我一个理由先

    科学计算:Python VS. MATLAB (1)----给我一个理由先         MATLAB 是一种用于算法开发.数据可视化.数据分析以及数值计算的高级技术计算语言和交互式环境.使用 MA ...

  8. Python科学计算(一)环境简介——Anaconda Python

    Anaconda Python 是 Python 科学技术包的合集,功能和 Python(x,y) 类似.它是新起之秀,已更新多次了.包管理使用 conda,GUI基于 PySide,所有的包基本上都 ...

  9. m1 MBA配置python及Numpy科学计算环境

    文章目录 写在前面 Python的安装(Mini-forge) brew安装优化版本的NumPy及SciPy Sublime的配置 最后总结 2021.1.14更新 主要参考 写在前面 前面说到,m1 ...

最新文章

  1. 使用GoAccess分析Nginx日志
  2. iOS高级音频的设置项
  3. 快速制作你的虚拟头像
  4. css3正方体选中父层 子层解体_CSS3 :nth-child(n)选择器 匹配属于其父元素的第N个子元素...
  5. 4变形物体_Houdini基础(二)曲线变形物体
  6. 蓝昭餐饮管理系统服务器无法连接,服务器安全加固操作指南.docx
  7. 关于投篮的数学建模模型_数学建模 投篮命中率的数学模型
  8. SQL数据库只读问题
  9. Linux下idea 配置Android SDK
  10. 历年全国计算机二级c语言真题,历年全国计算机二级C语言真题
  11. pyecharts世界地图map,geo可视化优化调整
  12. edge浏览器internet选项的设置方法
  13. 在游戏中,爆出神装是真随机还是假随机?
  14. python里的非_python中或与非
  15. ABeam中国2022社招 | ABeam旗下艾宾信息技术开发(大连) 最新招聘信息
  16. Help Hanzo(LightOJ - 1197)(欧拉筛 + 思维)
  17. HTML二级下拉菜单常见样式以及常见问题
  18. 【nlp学习】中文命名实体识别(待补充)
  19. php word权限设置密码,在php中加密和解密word docx文件的问题
  20. c++名字空间指令与名字空间声明

热门文章

  1. java中的linked_为Java实现LinkedArray
  2. 报告一个IE很奇葩的滚动条问题——百分比计算宽度为浮点数时的滚动条显示异常
  3. 利用锁机制解决商品表和库存表并发问题
  4. 解决Python print输出不换行没空格的问题
  5. 解决html5中video标签无法播放mp4问题的办法
  6. Android 9.0/P 开发问题及解决方案汇总
  7. 解决float型数据精度损失问题
  8. Vue-CLI3.x 高版本覆盖低版本Vue-CLI2.x 解决方法
  9. Git子模块头#39;引用不是树#39;错误
  10. HTML标签之间有什么区别 div 和span?/span