Sutton and Barto 教材中多臂老虎机(k-armed bandit testbed)模拟

  • 简介
  • 问题描述
    • kkk-armed bandit 问题
  • ϵ\epsilonϵ-greedy 和 greedy 算法
    • q∗(a)q_{*}(a)q∗​(a) 和 Qt(a)Q_{t}(a)Qt​(a)
    • Exploration 和 exploitation
    • Sample-average 方法
    • kkk-armed testbed greedy 算法
    • ϵ\epsilonϵ-greedy 算法
    • greedy 算法和 ϵ\epsilonϵ-greedy 算法的模拟结果
  • 理想的初值情况(optimistic initial values)
  • 其他算法
  • 参考文献

简介

在 Sutton 和 Barto 的经典教材 《Reinforcement learning - an introduction》的第二章中,有一个模拟10臂老虎机 (10-armed bandit testbed) 的例子。这个例子显示了 ϵ\epsilonϵ-greedy 算法优于 greedy(贪心)算法。由于课本中并没有介绍这两个算法的模拟细节,本文将介绍如何通过模拟得到课本中的结论。

问题描述

kkk-armed bandit 问题

kkk-armed bandit 问题是这样一种问题,假如我们面临 kkk 种选择,或者我们可以做 kkk 种动作 (action)。在某个时间 ttt 或者某一步 (step),我们可以从这 kkk 种选择之间做出一个选择,之后我们将会根据我们的选择得到一个回报(reward),这里回报是服从某个概率分布的随机变量,具体的概率分布取决于我们所做的选择。注意,这里的回报所服从的概率分布取决于不同的选择,即不同的选择对应不同的概率分布。我们的目的是经过很多步数之后,最大化这个回报。

我们记在时间 ttt 做的选择为 AtA_tAt​,得到的回报记为 RtR_tRt​。

注意,这里在时间 ttt 我们做出一个选择 At=aA_t = aAt​=a,我们只能得到对应选择 aaa 的一个回报,并不能得到关于其他选择的任何信息。我们把这种情况称为 bandit feedback [1]。

ϵ\epsilonϵ-greedy 和 greedy 算法

q∗(a)q_{*}(a)q∗​(a) 和 Qt(a)Q_{t}(a)Qt​(a)

在介绍 ϵ\epsilonϵ-greedy 和 greedy 算法之前,我们先来看一下 q∗(a)q_{*}(a)q∗​(a) 和 Qt(a)Q_{t}(a)Qt​(a) 的定义。我们把 q∗(a)q_{*}(a)q∗​(a) 称为动作 aaa 的价值 (action value)。它是这么定义的:对于 kkk 种选择中的任一个,这个选择都对应一个回报的期望值,我们把对应于选择 aaa 的回报的期望值记为 q∗(a)q_{*}(a)q∗​(a)。也就是说,

q∗(a)=E[Rt∣At=a]\displaystyle q_{*}(a) = \mathbb{E}[ R_t \vert A_t = a]q∗​(a)=E[Rt​∣At​=a] …(1)。

如果我们知道 q∗(a)q_{*}(a)q∗​(a) 的准确值,那么这个问题就解决了。我们只须要每次都选择 q∗(a)q_{*}(a)q∗​(a) 最大的那个动作即可。

在实际情况中,我们往往不知道 q∗(a)q_{*}(a)q∗​(a) 的准确值,从而我们需要去估计这个期望值。我们记在时间 ttt 对 q∗(a)q_{*}(a)q∗​(a) 的估计记为 Qt(a)Q_{t}(a)Qt​(a)。我们希望 Qt(a)Q_{t}(a)Qt​(a) 能尽量与 q∗(a)q_{*}(a)q∗​(a) 接近。

Exploration 和 exploitation

接下来我们介绍 exploration 和 exploitation。因为我们不知道 q∗(a)q_{*}(a)q∗​(a) 的准确值,我们只能通过 Qt(a)Q_{t}(a)Qt​(a) 去估计动作 aaa 的价值。对于某一个时刻 ttt,我们在所有动作中可以贪心地选择当前 Qt(a)Q_{t}(a)Qt​(a) 最大的那个动作。这样做的代价就是我们失去了尝试其他动作的机会 (在一个时刻 ttt,我们只能选择一个动作),也就是失去了更加准确估计其他动作价值的机会。我们把这种选择叫做 exploitation。也就是说,exploitation 只会选择目前来看价值估计最大的动作,不会去尝试其余的动作。

相反的,如果在某一个时刻 ttt,我们不去选择Qt(a)Q_{t}(a)Qt​(a) 最大的那个动作,而是选择其余的动作,于是我们就有机会对其余的动作的价值做出更好的估计。我们把这种选择叫做 exploration。exploration,顾名思义,就是对所有动作的一个探索。因为如果当前 Qt(a)Q_{t}(a)Qt​(a) 最大的那个动作其真实的价值不是最大的,而我们选择 exploitation 的话,我们就会始终无法知道价值真正最大的动作。

为了通俗起见,我们再举一个例子。

假设我们在玩一个足球电竞游戏,系统随机分给我们一个队。我们可以控制一个队的 11 名球员。除了守门员,其余场上的 10 个球员都有射门的能力。但是我们不知道这 10 个球员每个人射门能力的准确值。这里每个球员射门能力的准确值就是选择这个球员射门的价值,即我们上面定义的 q∗(a)q_{*}(a)q∗​(a),在这里 aaa 是选择某一个球员射门。为了估计每个球员真实的射门水平,我们就要不断去尝试用不同的球员去射门。我们用不同球员射门,从而估计不同球员射门水平的过程,就是 exploration。如果我们认为我们已经对这 10 个球员的射门水平有了较好的估计,那么每次射门我们都可以用目前射门能力估计值最高的那个球员,这就是 exploitation。

所以我们看到,为了准确估计所有球员的射门价值(即射门能力),我们需要做 exploration。但是一场比赛的时间是固定的,我们不能一直在做 exploration,我们需要在尽量少 exploration 的情况下,找出射门能力最强 (或者大概率最强) 的球员,进行射门得分。也就是说,exploitation 和 exploration 存在一种 trade-off 的关系。

Sample-average 方法

如何去估计一个动作的价值呢,我们根据 (1)知道,一个动作的价值是选取这个动作获得的回报的期望,从而我们用常见的取样本平均的方法去估计 q∗(a)q_{*}(a)q∗​(a)。这就是课本中的公式 2.1,即

Qt(a)=sum of rewards when ataken prior to tnumber of times ataken prior to t=∑i=1t−1Ri⋅IAi=a∑i=1t−1IAi=a\displaystyle Q_t(a) = \frac{\text{sum of rewards when \textit{a} taken prior to }t}{\text{number of times \textit{a} taken prior to }t} = \frac{\sum_{i = 1}^{t - 1}R_i \cdot \mathbb{I}_{A_i = a}}{\sum_{i = 1}^{t - 1} \mathbb{I}_{A_i = a}}Qt​(a)=number of times a taken prior to tsum of rewards when a taken prior to t​=∑i=1t−1​IAi​=a​∑i=1t−1​Ri​⋅IAi​=a​​

根据大数定理,如果我们选取 aaa 的次数足够多,我们计算得到的 Qt(a)Q_t(a)Qt​(a) 就足够接近 q∗(a)q_{*}(a)q∗​(a)。我们称这种求样本平均的方法为 sample-average 方法。

了解了 exploration,exploitation 和 sample-average 方法之后,我们来看 greedy 算法和 ϵ\epsilonϵ-greedy 算法。

kkk-armed testbed greedy 算法

我们先来看 greedy 算法。顾名思义,greedy 算法是指在时间 ttt,我们总是选取目前为止价值估计最大的那个动作,即 At=˙arg max⁡aQt(a)\displaystyle A_t \dot{=} \argmax_{a} Q_{t}(a)At​=˙aargmax​Qt​(a) (这里等于号上面加一点表示定义)。也就是在时间 ttt,我们选择 exploitation。值得注意的是,当前价值估计最大的这个动作,并不一定真的是 q∗(a)q_{*}(a)q∗​(a) 最大的值。如果当前估值最大的这个动作不是使得 q∗(a)q_{*}(a)q∗​(a) 最大的动作,那么我们就失去了更进一步寻找 At=˙arg max⁡aQt(a)\displaystyle A_t \dot{=} \argmax_{a} Q_{t}(a)At​=˙aargmax​Qt​(a) 的机会。

ϵ\epsilonϵ-greedy 算法

不同于 greedy 算法,ϵ\epsilonϵ-greedy 算法是在大部分情况下进行 exploitation,也就是选择当前价值估计最大的那个动作,而在极少数情况下随机得选择 kkk 个动作中的一个(即 exploration)。这里 ϵ\epsilonϵ 是一个很小的数,比如 0.1。在做选择之前,我们随机生成一个 [0,1][0, 1][0,1] 之间的随机数,如果这个数小于 ϵ\epsilonϵ,我们就在 kkk 个动作中随机得选择一个;如果这个数大于 ϵ\epsilonϵ,我们就进行 exploitation,选取目前价值估计最大的动作。

greedy 算法和 ϵ\epsilonϵ-greedy 算法的模拟结果

在 Sutton 和 Barto 的教材中 [2] 的 2.3 这一节中,作者给出了分别用 greedy 算法和ϵ\epsilonϵ-greedy 算法 (ϵ=0.1,ϵ=0.01\epsilon = 0.1, \, \epsilon = 0.01ϵ=0.1,ϵ=0.01)的模拟结果。但是课本没有对模拟的细节做详细的解释,这里我们来介绍下具体的模拟是如何完成的。

这是一个 10-armed testbed 问题,我们面临 10 种不同的选择。每一个选择的价值 q∗(a),a=1,2,⋯,10q_{*}(a), \, a = 1, \, 2, \, \cdots, 10q∗​(a),a=1,2,⋯,10 是从标准正态分布 N(0,1)N(0, \, 1)N(0,1)生成的一个随机数。

在每一个时间 ttt,我们从 10 个选择中选出一个动作 AtA_tAt​,这个选择会给我们回报 RtR_tRt​,RtR_tRt​ 是从一个期望为 q∗(a)q_{*}(a)q∗​(a),方差为 1 的正态分布中生成。在得到了 RtR_tRt​ 之后,我们用 sample-average 方法更新对于动作 AtA_tAt​ 的价值的估计。

这里课本中模拟的步数是 1000,即 ttt 从1 到 1000。而为了减少随机噪音,课本对问题做了 2000 次模拟,然后取 2000 次的平均值作为每一个时刻的值。对于课本中图 2.2 的两张图,上面一张的 yyy 轴表示的是在时间 ttt 所获的的平均回报 (即取 2000 次实验的平均值),下面一张的 yyy 轴表示的是在时间 ttt 所做出的选择,是最佳选择(即 q∗(a)q_{*}(a)q∗​(a) 最大的那个动作)的比例。

于是我们可以模拟这个问题。

class bandit_simulation:def __init__(self, n_testbed: int, epsilon: float, n_iterations: int, n_plays: int, mean: float,std: float, initial_Q1: float, sample_average: bool, step_size: float):"""n_testbed: value of k for the k-armed problemepsilon: the parameter for epsilon-greedy algorithmn_iterations: number of repeated experiments (or plays), for the example in the book,its value is 2000n_plays: number of steps for per iteration, for the example in the book, its value is 1000mean, std: mean and standard deviation for the normal distribution that is used to generated the valuesfor q*(a), a = 1, 2, ..., n_testbedinitial_Q1: initial estimate for the action valuesample_average: if True, we use sample average method for estimating q*(a), otherwise we use the constant step-size parameter method, i.e., the equation of 2.5 in the book. step_size: step_size only makes sense when sample_average is False, it is the alpha parameterin equation 2.5. """self.n_testbed = n_testbedself.epsilon = epsilonself.n_iterations = n_iterationsself.n_plays = n_playsself.mean = meanself.std = stdself.optimal_action = -1self.action_val = Noneself.sum_rewards = np.zeros((n_testbed, ))self.cnt_actions = np.zeros((n_testbed, ))self.action_val_esti = np.ones((n_testbed, )) * initial_Q1self.score_arr = np.zeros((n_plays, ))self.optimal_arr = np.zeros((n_plays, ))self.sample_average = sample_averageself.step_size = step_sizedef generate_testbed(self):"""Generate n_testbed number of normal random variables with mean self.mean and standard deviation of self.std"""self.action_val = np.random.normal(self.mean, self.std, self.n_testbed)self.optimal_action = np.argmax(self.action_val)returndef simulate_one_run(self):"""For one run, we run self.n_plays steps to simulate.Note that for the initial stage, that is when i == 0, we just pick one action randomly as the initialself.action_val_esti is all zero. """self.generate_testbed()for i in range(self.n_plays):rnd = np.random.random()# get the greedy actionmax_action_val_idx = np.argmax(self.action_val_esti)# if there are multiple of max value for self.action_val_esti, possible_idxes has length # larger than 1. possible_idxes = np.where(self.action_val_esti == self.action_val_esti[max_action_val_idx])[0]if rnd < self.epsilon:cur_action = np.random.choice(self.n_testbed)else:if len(possible_idxes) > 1:cur_action = np.random.choice(possible_idxes)else:cur_action = max_action_val_idxcur_reward = np.random.normal(self.action_val[cur_action], 1)self.sum_rewards[cur_action] += cur_rewardself.cnt_actions[cur_action] += 1if self.sample_average:self.action_val_esti[cur_action] = self.sum_rewards[cur_action] / self.cnt_actions[cur_action]else:self.action_val_esti[cur_action] += self.step_size * (cur_reward - self.action_val_esti[cur_action])self.score_arr[i] += cur_rewardif cur_action == self.optimal_action:self.optimal_arr[i] += 1returndef reset(self):self.optimal_action = -1self.action_val = Noneself.sum_rewards = np.zeros((n_testbed, ))self.cnt_actions = np.zeros((n_testbed, ))self.action_val_esti = np.zeros((n_testbed, ))def simulate_total_iterations(self):"""Run self.n_iterations. For each iteration, we call simulate_one_run once(), and then we call reset()."""for i in range(self.n_iterations):if i % 200 == 0:print("=============" + 'iteration' + str(i) + "=============")self.generate_testbed()self.simulate_one_run()self.reset()return self.score_arr, self.optimal_arr
n_testbed = 10
epsilon = 0
n_iterations = 2000
n_plays = 1000
mean = 0
std = 1
initial_Q1 = 0
sample_average = True
step_size = 0
a = bandit_simulation(n_testbed=n_testbed, epsilon=epsilon, n_iterations=n_iterations, n_plays=n_plays,mean=mean, std=std, initial_Q1=initial_Q1, sample_average=sample_average, step_size=step_size)
res_greedy = a.simulate_total_iterations()
n_testbed = 10
epsilon = 0.1
n_iterations = 2000
n_plays = 1000
mean = 0
std = 1
initial_Q1 = 0
sample_average = True
step_size = 0
a = bandit_simulation(n_testbed=n_testbed, epsilon=epsilon, n_iterations=n_iterations, n_plays=n_plays,mean=mean, std=std, initial_Q1=initial_Q1, sample_average=sample_average, step_size=step_size)
res_0p1 = a.simulate_total_iterations()
n_testbed = 10
epsilon = 0.01
n_iterations = 2000
n_plays = 1000
mean = 0
std = 1
initial_Q1 = 0
sample_average = True
step_size = 0
a = bandit_simulation(n_testbed=n_testbed, epsilon=epsilon, n_iterations=n_iterations, n_plays=n_plays,mean=mean, std=std, initial_Q1=initial_Q1, sample_average=sample_average, step_size=step_size)
res_0p01 = a.simulate_total_iterations()
# plot average reward vs. steps
plt.figure(figsize=(10, 8))
plt.plot(range(a.n_plays), res_greedy[0] / a.n_iterations, range(a.n_plays), res_0p1[0] / a.n_iterations,range(a.n_plays), res_0p01[0] / a.n_iterations, linewidth=2)
plt.xticks(fontsize=24)
plt.yticks(fontsize=24)
plt.xlabel('steps', fontsize=48)
plt.ylabel('average reward', fontsize=48)
plt.legend(['greedy', 'epsilon0.1', 'epsilon0,01'], fontsize=24, bbox_to_anchor=(0.95, 0.4))# plot percentage of optimal actions vs. steps
plt.figure(figsize=(10, 8))
plt.plot(range(a.n_plays), res_greedy[1] / a.n_iterations, range(a.n_plays), res_0p1[1] / a.n_iterations,range(a.n_plays), res_0p01[1] / a.n_iterations, linewidth=2)
plt.xticks(fontsize=24)
plt.yticks(fontsize=24)
plt.xlabel('steps', fontsize=48)
plt.ylabel('% optimal action', fontsize=48)
plt.legend(['greedy', 'epsilon0.1', 'epsilon0.01'], fontsize=24, bbox_to_anchor=(0.85, 0.35))

可以看出,ϵ\epsilonϵ-greedy 算法比 greedy 算法在平均回报和选择最佳动作的比例两方面都要好。

理想的初值情况(optimistic initial values)

我们在上面的例子中,greedy 算法和 ϵ\epsilonϵ-greedy 算法所选择的初始对每个动作价值的估计都是 0,并且所用到的估计动作价值的方法是 sample-average 方法。如果我们用课本中提到的 constant step-size 方法,即

Qn+1=˙Qn+α[Rn−Qn]\displaystyle Q_{n + 1} \dot{=} \, Q_n + \alpha [R_n - Q_n]Qn+1​=˙Qn​+α[Rn​−Qn​] … (2)

我们可以选择一个较大的初始值 Q1Q_1Q1​,从而鼓励我们的 action-value 方法做 exploration。

如果我们选 Q1=5Q_1 = 5Q1​=5, α=0.1\alpha = 0.1α=0.1,我们就得到了课本中的 Figure 2.3。

注意,代码中的 step_size 就是公式 (2) 中的 α\alphaα。

n_testbed = 10
epsilon = 0
n_iterations = 2000
n_plays = 1000
mean = 0
std = 1
initial_Q1 = 5
sample_average = False
step_size = 0.1
a = bandit_simulation(n_testbed=n_testbed, epsilon=epsilon, n_iterations=n_iterations, n_plays=n_plays,mean=mean, std=std, initial_Q1=initial_Q1, sample_average=sample_average, step_size=step_size)
res_greedy_init5 = a.simulate_total_iterations()
# plot percentage of optimal actions vs. steps
plt.figure(figsize=(10, 8))
plt.plot(range(a.n_plays), res_greedy_init5[1] / a.n_iterations, range(a.n_plays), res_0p1[1] / a.n_iterations, linewidth=2)
plt.xticks(fontsize=24)
plt.yticks(fontsize=24)
plt.xlabel('steps', fontsize=48)
plt.ylabel('% optimal action', fontsize=48)
plt.legend(['greedy initial 5', 'epsilon 0.1'], fontsize=24, bbox_to_anchor=(0.85, 0.35))

其他算法

除了上面介绍的 greedy 和 ϵ\epsilonϵ-greedy 算法,解决 multi-bandit 问题还有 explore-first,successive elimination,UCB-based arm selection (upper confidence bound) 等。具体的算法细节可以参考 [1]。

参考文献

[1] Introduction to multi-armed bandits, Aleksandrs Slivkins, Foundations and Trends in Machine Learning: Vol. 12, No. 1-2, pp 1–286 (2019)
[2] Reinforcement learning, an introduction 2nd edition, Richard S Sutton, Andrew G Barto (2018)
[3] Github 相关资料

Sutton and Barto 教材中多臂老虎机(k-armed bandit testbed)模拟相关推荐

  1. 【科普】强化学习之多臂老虎机问题(bandit算法:softmax,random,UCB)

    本博客上的文章分为两类:一类是科普性文章,以通俗易懂的语言风格介绍专业性的概念及其应用场景(公式极少或没有),适合入门阶段.另一类是专业性文章,在科普阶段上做出详细的专业性数学推导,深刻理解其概念的内 ...

  2. 从多臂老虎机开始学习强化学习中的探索与利用

    从多臂老虎机开始学习强化学习中的探索与利用 \quad 目录 从多臂老虎机开始学习强化学习中的探索与利用 多臂老虎机问题 形式化描述 估计期望奖励 代码实现 策略中的探索与利用 ϵ\epsilonϵ- ...

  3. 强化学习的学习之路(十)_2021-01-10:K臂老虎机介绍及其Python实现

    作为一个新手,写这个强化学习-基础知识专栏是想和大家分享一下自己学习强化学习的学习历程,希望对大家能有所帮助.这个系列后面会不断更新,希望自己在2021年能保证平均每日一更的更新速度,主要是介绍强化学 ...

  4. 多臂老虎机(Multi-armed bandit problem)

    我们会遇到很多选择的场景,如:上哪所大学,学什么专业,去哪家公司,等等.这些选择问题都会让选择困难症患者头大.那么,有什么科学的办法来解决这些问题呢?答案是:有!而且是非常科学的办法,那就是多臂老虎机 ...

  5. 强化学习——day12 多臂老虎机问题MAB

    在多臂老虎机(multi-armed bandit,MAB)问题(见图 2-1)中,有一个拥有 根拉杆的老虎机,拉动每一根拉杆都对应一个关于奖励的概率分布 .我们每次拉动其中一根拉杆,就可以从该拉杆对 ...

  6. 深度学习核心技术精讲100篇(三)-层次自适应的多臂老虎机决策算法 ( HATCH )在滴滴中的应用

    前言 需要源码的小伙伴参见: Contextual Bandits 算法在推荐场景中的应用源码 https://download.csdn.net/download/wenyusuran/155784 ...

  7. 冷启动中的多臂老虎机问题(Multi-Armed Bandit,MAB)

    转载请注明出处:https://thinkgamer.blog.csdn.net/article/details/102560272 博主微博:http://weibo.com/234654758 G ...

  8. 强化学习の学习笔记(一)——多臂老虎机、ε-greedy策略、乐观初始值、增量式实现、梯度赌博机

    文章目录 前言 符号约定 多臂老虎机 基于平均学习Q函数 ε-greedy策略 乐观初始值 增量式实现 梯度赌博机 前言 因为毕设的关系,要学习点强化学习的内容.我采用的教材是Richard S. S ...

  9. Multi-Armed Bandit--基于时变多臂老虎机的在线上下文感知推荐

    Online Context-Aware Recommendation with Time Varying Multi-Armed Bandit 基于时变多臂老虎机的在线上下文感知推荐 摘要 为了预测 ...

  10. 强化学习——day31 多臂老虎机MAB的代码实现(Python)

    多臂老虎机MAB的代码实现 2.3 算法基本框架搭建 2.4 epsilon贪心算法 2.4.1 参数为0.01的绘图 2.4.2 不同的参数 2.4.3 值随时间衰减的 epsilon-贪婪算法 2 ...

最新文章

  1. Akka的好用例[关闭]
  2. SD--订单最小量限制的增强
  3. QT QNetworkInterface::allAddresses();获取了很多无效的地址_Qt编写地图综合应用16-省市轮廓图下载...
  4. python爬虫实训日志_Python学习学习日志——爬虫《第一篇》(BeautifulSoup)
  5. #if defined 和 #if ! defined 的用法
  6. Linux下MySQL忘记root密码及解决办法
  7. C# 读取照片的EXIF信息
  8. 拍雪景得诗一首,记之,以表心绪[有能和者,不妨凑个热闹给大家解闷]
  9. 力压腾讯!《原神》连续5个月成中国手游海外收入冠军
  10. mysql如何管理innodb元数据_1.1.20 可动态关闭InnoDB更新元数据的统计功能
  11. VR 、AR 谁让你眼前一亮
  12. 自动生成员工号c语言,C语言课程设计级.doc
  13. 会长大人的《从小麦到馒头的过程》
  14. 新兴基金经理凯瑟琳·伍德ARKK基金在 2022 年初遭受新的打击
  15. 制作用于图像语义分割训练的标签数据【图像分割】【labelme】
  16. 关于“发现在互联网状态下的生活时间流逝很快的分析”
  17. 有关 ovirt 的分析
  18. 使用ARCHPR明文攻击爆破压缩包
  19. 联想miix2-11 linux,联想Miix2 11电脑硬盘分区详细教程
  20. 魔域私服服务器连接中断,魔域私服服务器中装备武器道具爆率相关算法及设置详细攻略分享...

热门文章

  1. 【寻找最佳小程序】11期:车来了——时时公交就在你身边,到站准确率可控制在90%以上...
  2. SHELL 内置变量
  3. setcpu_cpuset子系统
  4. 2022大厂真题盘点!190道大数据高频面试题+答案详解
  5. c# 使用Microsoft.Office.Interop.Excel导出文件时提示 兼容性检查
  6. 微信 及支付宝 支付接口 功能
  7. Xbox360自制系统GOD包安装教程
  8. 嵌套在iframe页面打印去掉页眉页尾
  9. 声音采样率对声音事件分类的简单探究
  10. 2021-07-16思考-资本源于贪婪(与人性抗争)