学习参数,第 4 部分:调整学习速度的技巧,线搜索
学习参数
在进入高级优化算法之前,让我们回顾一下梯度下降中的学习率问题。
在第 3 部分中,我们看到了优化器的随机性和小批量版本。在这篇文章中,我们将会看到一些关于如何调整学习率等的普遍遵循的启发法。如果你对这些试探法不感兴趣,可以直接跳到学习参数系列的第 5 部分。
引用说明:本博客中的大部分内容和图表直接取自 IIT 马德拉斯大学教授 Mitesh Khapra 提供的深度学习课程第 5 讲。
有人可能会说,我们可以通过设置较高的学习率来解决在缓坡上导航的问题(即,通过将较小的坡度乘以较大的学习率 η )来放大较小的坡度)。这个看似微不足道的想法有时在误差函数平缓的情况下确实有效,但当误差面平坦时就不起作用了。这里有一个例子:
显然,在斜率较大的区域,已经很大的梯度会进一步扩大,较大的学习率在某种程度上有助于这一原因,但一旦误差表面变平,就没有太大帮助了。假设有一个可以适应梯度的学习率总是好的,这是安全的,我们将在学习参数系列的下一篇文章(第 5 部分)中看到一些这样的算法。
一些有用的提示
初学率小贴士
- 调整学习率。在对数标度上尝试不同的值:0.0001、0.001、0.01、0.1、1.0。
- 每一个都运行几个时期,找出一个最有效的学习速率。
- 现在围绕这个值进行更精细的搜索。例如,如果最佳学习率是 0.1,那么现在尝试一些值:0.05,0.2,0.3。
- 声明:这些只是启发,没有明确的赢家策略。
退火学习率提示
阶跃衰减
- 每 5 个周期后将学习率减半
- 如果验证误差大于前一个时期结束时的误差,则在一个时期后将学习率减半
指数衰减
- η = η₀⁻ᵏᵗ ,其中 η₀ 和 k 为超参数,t 为步数
1/t 衰变
- *η = (η₀)/(1+kt),*其中 η₀ 和 k 为超参数,t 为步数。
动量秘诀
以下时间表由 Sutskever 等人于 2013 年提出
其中, γ_max 选自{0.999,0.995,0.99,0.9,0}。
线搜索
在实践中,通常进行线搜索以找到相对更好的 η 值。在线搜索中,我们使用不同的学习率( η )更新 w ,并在每次迭代中检查更新后的模型的误差。最终,我们保留给出最低损失的 w 的更新值。看一下代码:
本质上,在每一步,我们都试图从可用的选项中使用最佳的 η 值。这显然不是最好的主意。我们在每一步都做了更多的计算,但这是寻找最佳学习速率的一种折衷。今天,有更酷的方法可以做到这一点。
在线搜索正在进行
显然,收敛比普通梯度下降更快(见第 1 部分)。我们看到一些振荡,但注意到这些振荡与我们在动量和 NAG 中看到的非常不同(见第 2 部分)。
注: Leslie N. Smith 在他 2015 年的论文中, 循环学习率用于训练神经网络 提出了一种比线搜索更聪明的方式。我建议读者参考这篇由 Pavel Surmenok 撰写的文章来了解更多信息。
结论
在学习参数系列的这一部分中,我们看到了一些启发,可以帮助我们调整学习速度和动力,以便更好地进行培训。我们还研究了线搜索,这是一种曾经流行的方法,用于在梯度更新的每一步找到最佳学习速率。在学习参数系列的下一(最后)部分,我们将仔细研究具有自适应学习速率的梯度下降,特别是以下优化器——AdaGrad、RMSProp 和 Adam。
你可以在这里找到下一部分:
承认
IIT·马德拉斯教授的和 CS7015:深度学习 课程如此丰富的内容和创造性的可视化,这要归功于很多。我只是简单地整理了提供的课堂讲稿和视频。
学习参数,第 5 部分:AdaGrad、RMSProp 和 Adam
学习参数
让我们看看具有自适应学习率的梯度下降。
在第四部分中,我们看了一些启发法,可以帮助我们更好地调整学习速度和动力。在系列的最后一篇文章中,让我们来看看一种更有原则的调整学习速度的方法,并给学习速度一个适应的机会。
引用说明:本博客中的大部分内容和图表直接取自 IIT 马德拉斯大学教授 Mitesh Khapra 提供的深度学习课程第 5 讲。
适应性学习率的动机
考虑以下具有 sigmoid 激活的简单感知器网络。
很容易看出,给定一个点( x , y ),梯度 w 如下:
See Part-1 if this is unclear.
***【f(x)***w . r . t 的梯度到一个特定的权重显然依赖于其相应的输入。如果有 n 个点,我们可以将所有 n 个点的梯度相加得到总梯度。这条新闻既不新鲜也不特别。但是,如果特征 x2 非常稀疏(即,如果它的值对于大多数输入都是 0),会发生什么呢?假设∇ w2 对于大多数输入(见公式)将为 0 是合理的,因此 w2 将不会获得足够的更新。
不过,这有什么好困扰我们的呢?需要注意的是,如果**【x2】**既稀疏又重要,我们会认真对待 w2 的更新。为了确保即使在特定输入稀疏的情况下也能进行更新,我们是否可以为每个参数设定不同的学习速率,以考虑特征的频率?我们当然可以。我的意思是,这是这篇文章的全部要点。
AdaGrad —自适应梯度算法
直觉
衰减参数的学习率与其更新历史成比例(更新越多,衰减越多)。
AdaGrad 的更新规则
从更新规则可以清楚地看出,梯度的历史累积在 v 中。累积的梯度越小,T5【v值就会越小,导致学习率越大(因为v除以 η )。****
阿达格拉德的 Python 代码
阿达格拉德在行动
要查看 AdaGrad 的运行情况,我们需要首先创建一些其中一个要素稀疏的数据。对于我们在学习参数系列的所有部分中使用的玩具网络,我们将如何做呢?嗯,我们的网络正好有两个参数( w 和 b ,参见 part-1 中的动机*)。这其中,b 对应的输入/特征是一直开着的,所以我们不能真的让它稀疏。所以唯一的选择就是让 x 稀疏。这就是为什么我们创建了 100 个随机的 (x,y) 对,然后大约 80%的这些对我们将 x 设置为 0,使得 w 的特征稀疏。*
Vanilla GD(black), momentum (red), NAG (blue)
*在我们实际查看运行中的 AdaGrad 之前,请查看上面的其他 3 个优化器——香草 GD(黑色)、momentum(红色)、NAG(蓝色)。这 3 个优化器为这个数据集做了一些有趣的事情。你能发现它吗?请随意停下来思考。**答:*最初,三种算法都是主要沿垂直( b )轴运动,沿水平( w )轴运动很少。为什么?因为在我们的数据中,对应于 w 的特征是稀疏的,因此 w 经历很少的更新。另一方面, b 非常密集,经历很多更新。这种稀疏性在包含 1000 个输入特征的大型神经网络中非常普遍,因此我们需要解决它。现在让我们来看看阿达格拉德的行动。
瞧啊。通过使用参数特定的学习率,AdaGrad 确保尽管稀疏度w 获得更高的学习率,并因此获得更大的更新。而且,这也保证了如果**b经历大量更新,其有效学习率会因为分母的增长而下降。实际上,如果我们从分母中去掉平方根,效果就不那么好了(这是值得思考的)。另一面是什么?随着时间的推移, b 的有效学习率将衰减到不再对 b 进行更新的程度。我们能避免这种情况吗?RMSProp 可以!****
RMSProp — 均方根传播
直觉
AdaGrad 非常积极地降低学习率(随着分母的增长)。结果,一段时间后,由于学习率的衰减,频繁参数将开始接收非常小的更新。为了避免这种情况,为什么不衰减分母,防止其快速增长。
RMSProp 的更新规则
一切都非常类似于阿达格拉德,除了现在我们也衰减分母。
RMSProp 的 Python 代码
RMSProp 正在运行
Vanilla GD(black), momentum (red), NAG (blue), AdaGrad (magenta)
**你看到了什么?RMSProp 和 AdaGrad 有什么不同?请随意停下来思考。**回答:阿达格拉德在接近收敛的时候卡住了,因为学习率的衰减,已经无法在垂直( b )方向
移动。RMSProp 通过减少衰减来克服这个问题。
Adam — 自适应矩估计
直觉
尽 RMSProp 所能解决 AdaGrad 的分母衰减问题。除此之外,使用梯度的累积历史。
更新 Adam 的规则
以及类似于b***_ t的一组方程组。注意,Adam 的更新规则与 RMSProp 非常相似,除了我们也查看梯度的累积历史(m_ t*)。注意,上面更新规则的第三步是偏差修正。Mitesh M Khapra 教授解释了为什么偏差校正是必要的,可在这里找到。**
亚当的 Python 代码
亚当在行动
很明显,获取梯度的累积历史可以加快速度。对于这个玩具数据集,它似乎是超调(有点),但即使这样,它收敛的方式比其他优化。
百万美元问题:使用哪种算法?
- 亚当现在似乎或多或少是默认的选择( β1 = 0.9, β2 = 0.999,ϵ= 1e 8)。
- 虽然它被认为对初始学习速率是鲁棒的,但是我们已经观察到对于序列生成问题 η = 0.001,0.0001 工作得最好。
- 话虽如此,但许多论文报道,具有简单退火学习速率调度的动量(内斯特罗夫或经典)SGD 在实践中也运行良好(通常,序列生成问题从 η = 0.001 开始,0.0001)。
- 亚当可能是最好的选择。
- 最近的一些工作表明 Adam 存在问题,在某些情况下它不会收敛。
结论
在本系列的最后一篇文章中,我们研究了具有自适应学习速率的梯度下降如何帮助加快神经网络的收敛。本文涵盖了直觉、python 代码和三个广泛使用的优化器的可视化说明——AdaGrad、RMSProp 和 Adam。Adam 结合了 RMSProp 和 AdaGrad 的最佳特性,即使在有噪声或稀疏的数据集上也能很好地工作。
承认
IIT·马德拉斯教授的 和 CS7015:深度学习 课程如此丰富的内容和创造性的可视化,这要归功于 Mitesh M Khapra 教授和助教。我只是简单地整理了提供的课堂讲稿和视频。
Source: Paperspace article on RMSProp by Ayoosh Kathuria.
学习强化学习:用 PyTorch 强化!
策略梯度入门
Photo by Nikita Vantorin on Unsplash
强化算法是强化学习中最早的策略梯度算法之一,也是进入更高级方法的一个很好的起点。策略梯度不同于 Q 值算法,因为 PG 试图学习参数化的策略,而不是估计状态-动作对的 Q 值。因此,策略输出被表示为动作的概率分布,而不是一组 Q 值估计。如果有任何困惑或不清楚的地方,不要担心,我们会一步一步地分解它!
TL;速度三角形定位法(dead reckoning)
在这篇文章中,我们将看看加强算法,并使用 OpenAI 的 CartPole 环境和 PyTorch 对其进行测试。我们假设对强化学习有一个基本的理解,所以如果你不知道状态、动作、环境等是什么意思,在这里查看一些到其他文章的链接或者在这里查看关于这个主题的简单入门。
政策与行动价值观
我们可以将策略梯度算法与 Q 值方法(例如,深度 Q 网络)区分开来,因为策略梯度在不参考动作值的情况下进行动作选择。一些策略梯度学习值的估计以帮助找到更好的策略,但是这个值估计不是选择动作所必需的。DQN 的输出将是价值估计的向量,而政策梯度的输出将是行动的概率分布。
例如,假设我们有两个网络,一个政策网络和一个 DQN 网络,它们已经学会了用两个动作(左和右)做横翻筋斗的任务。如果我们将状态 s 传递给每一个,我们可能会从 DQN 得到以下内容:
这来自政策梯度:
DQN 为我们提供了该州未来奖励折扣的估计值,我们基于这些值进行选择(通常根据一些ϵ-greedy 规则取最大值)。另一方面,政策梯度给了我们行动的概率。在这种情况下,我们做出选择的方式是在 28%的情况下选择行动 0,在 72%的情况下选择行动 1。随着网络获得更多的经验,这些概率将会改变。
概率值
为了得到这些概率,我们在输出层使用了一个简单的函数叫做 softmax 。该函数如下所示:
这将所有值压缩到 0 和 1 之间,并确保所有输出总和为 1(σσ(x)= 1)。因为我们使用 exp(x)函数来缩放我们的值,所以最大的值往往占主导地位,并获得更多分配给它们的概率。
强化算法
现在谈谈算法本身。
如果你已经关注了之前的一些帖子,这看起来应该不会太令人生畏。然而,为了清楚起见,我们还是要走一遍。
要求相当简单,我们需要一个可区分的策略,为此我们可以使用一个神经网络,以及一些超参数,如我们的步长(α)、折扣率(γ)、批量(K)和最大集数(N)。从那里,我们初始化我们的网络,并运行我们的剧集。每集之后,我们会对我们的奖励进行折扣,这是从该奖励开始的所有折扣奖励的总和。我们将在每一批(例如 K 集)完成后更新我们的政策。这有助于为训练提供稳定性。
保单损失(L(θ))起初看起来有点复杂,但如果仔细观察,不难理解。回想一下,策略网络的输出是一个概率分布。我们对π(a | s,θ)所做的,只是得到我们网络在每个状态下的概率估计。然后,我们将其乘以折扣奖励的总和(G ),得到网络的期望值。
例如,假设我们处于状态 s 网络分为两个行动,那么选择 a=0 的概率是 50%,选择 a=1 的概率也是 50%。网络随机选择 a=0,我们得到 1 的奖励,剧集结束(假设贴现因子为 1)。当我们回过头来更新我们的网络时,这个状态-动作对给我们(1)(0.5)=0.5,这转化为在该状态下采取的动作的网络期望值。从这里,我们取概率的对数,并对我们这一批剧集中的所有步骤求和。最后,我们将其平均,并采用该值的梯度进行更新。
快速复习一下,推车杆的目标是尽可能长时间地保持杆在空中。您的代理需要决定是向左还是向右推购物车,以保持平衡,同时不要越过左右两边的边缘。如果你还没有安装 OpenAI 的库,只要运行pip install gym
就应该设置好了。此外,如果你还没有的话,请抓住 pytorch.org 的最新消息。
继续并导入一些包:
import numpy as np
import matplotlib.pyplot as plt
import gym
import sysimport torch
from torch import nn
from torch import optimprint("PyTorch:\t{}".format(torch.__version__))PyTorch: 1.1.0
PyTorch 中的实现
导入我们的包后,我们将建立一个名为policy_estimator
的简单类,它将包含我们的神经网络。它将有两个隐藏层,具有 ReLU 激活功能和 softmax 输出。我们还将为它提供一个名为 predict 的方法,使我们能够在网络中向前传递。
class policy_estimator():
def __init__(self, env):
self.n_inputs = env.observation_space.shape[0]
self.n_outputs = env.action_space.n
# Define network
self.network = nn.Sequential(
nn.Linear(self.n_inputs, 16),
nn.ReLU(),
nn.Linear(16, self.n_outputs),
nn.Softmax(dim=-1))
def predict(self, state):
action_probs = self.network(torch.FloatTensor(state))
return action_probs
请注意,调用predict
方法需要我们将状态转换成一个FloatTensor
,供 PyTorch 使用。实际上,predict
方法本身在 PyTorch 中有些多余,因为张量可以直接传递给我们的network
来得到结果,但是为了清楚起见,我把它包含在这里。
我们需要的另一件事是我们的贴现函数,根据我们使用的贴现因子γ来贴现未来的奖励。
def discount_rewards(rewards, gamma=0.99):
r = np.array([gamma**i * rewards[i]
for i in range(len(rewards))])
# Reverse the array direction for cumsum and then
# revert back to the original order
r = r[::-1].cumsum()[::-1]
return r — r.mean()
我在这里做了一件有点不标准的事情,就是减去最后奖励的平均值。这有助于稳定学习,特别是在这种情况下,所有奖励都是正的,因为与奖励没有像这样标准化相比,负奖励或低于平均水平的奖励的梯度变化更大。
现在谈谈增强算法本身。
def reinforce(env, policy_estimator, num_episodes=2000,
batch_size=10, gamma=0.99): # Set up lists to hold results
total_rewards = []
batch_rewards = []
batch_actions = []
batch_states = []
batch_counter = 1
# Define optimizer
optimizer = optim.Adam(policy_estimator.network.parameters(),
lr=0.01)
action_space = np.arange(env.action_space.n)
ep = 0
while ep < num_episodes:
s_0 = env.reset()
states = []
rewards = []
actions = []
done = False
while done == False:
# Get actions and convert to numpy array
action_probs = policy_estimator.predict(
s_0).detach().numpy()
action = np.random.choice(action_space,
p=action_probs)
s_1, r, done, _ = env.step(action)
states.append(s_0)
rewards.append(r)
actions.append(action)
s_0 = s_1
# If done, batch data
if done:
batch_rewards.extend(discount_rewards(
rewards, gamma))
batch_states.extend(states)
batch_actions.extend(actions)
batch_counter += 1
total_rewards.append(sum(rewards))
# If batch is complete, update network
if batch_counter == batch_size:
optimizer.zero_grad()
state_tensor = torch.FloatTensor(batch_states)
reward_tensor = torch.FloatTensor(
batch_rewards)
# Actions are used as indices, must be
# LongTensor
action_tensor = torch.LongTensor(
batch_actions)
# Calculate loss
logprob = torch.log(
policy_estimator.predict(state_tensor))
selected_logprobs = reward_tensor * \
torch.gather(logprob, 1,
action_tensor).squeeze()
loss = -selected_logprobs.mean()
# Calculate gradients
loss.backward()
# Apply gradients
optimizer.step()
batch_rewards = []
batch_actions = []
batch_states = []
batch_counter = 1
avg_rewards = np.mean(total_rewards[-100:])
# Print running average
print("\rEp: {} Average of last 100:" +
"{:.2f}".format(
ep + 1, avg_rewards), end="")
ep += 1
return total_rewards
对于算法,我们传递我们的policy_estimator
和env
对象,设置一些超参数,然后我们就可以开始了。
关于实现的几点,在将值传递给env.step()
或像np.random.choice()
这样的函数之前,一定要确保将 PyTorch 的输出转换回 NumPy 数组,以避免错误。此外,我们使用torch.gather()
将实际采取的行动与行动概率分开,以确保我们如上所述正确计算损失函数。最后,你可以改变结局,让算法在环境一旦“解出”就停止运行,而不是运行预设的步数(连续 100 集平均得分 195 分以上就解出 CartPole)。
要运行这个程序,我们只需要几行代码就可以完成。
env = gym.make('CartPole-v0')
policy_est = policy_estimator(env)
rewards = reinforce(env, policy_est)
绘制结果,我们可以看到它工作得相当好!
即使是简单的策略梯度算法也可以很好地工作,并且它们比 DQN 算法的负担更少,后者通常采用像记忆回放这样的附加功能来有效地学习。
看看您可以在更具挑战性的环境中使用该算法做些什么!
学习 SQL 201:优化查询,不考虑平台
TL;博士:磁盘太慢了。网络更差。
It’s hard to find a photo that says “optimization” okay? (Photo credit: Randy Au)
警告:在撰写本文时,我已经在生产环境中使用了以下类似数据库的系统:MySQL、PostgreSQL、Hive、Hadoop 上的 MapReduce、AWS Redshift、GCP BigQuery,以及各种本地/混合/云设置。我的优化知识很大程度上来源于这些。在这里,我将坚持策略/思维过程,但是在其他流行的数据库中肯定有我不熟悉的特性和怪癖,尤其是 SQL Server 和 Oracle。
这篇文章是关于速度,让事情进行得更快的通用策略,同时避免具体的实现细节。我是想表达优化的思维过程,而不是具体的机制。在我知道它之前,它已经变成了一篇文章的怪物。有很多要讲的!
简介和背景
优化查询是一个很难写的主题,因为它涉及到细节。关于数据库引擎、软件,有时甚至是硬件和网络架构的细节。我多次被要求写关于这个主题的文章,我总是拒绝,因为我找不到一种方法来写一篇普遍有用的文章,来讨论一些很快就会被遗忘的东西。
有整本 的书 写的关于如何优化不同的数据库系统,其中包括查询,但也包括系统本身调优的细节。他们总是写一个特定的平台,而不是一般的平台。这是有充分理由的——每个平台都是不同的,您需要的调优参数取决于您的工作负载和设置(写密集型与读密集型、固态硬盘与旋转磁盘等)。
但是在一次令人讨厌的热浪中,在回家的路上,我突然有了一个关于线程将优化联系在一起的想法。所以我试着写这篇疯狂的文章。我将避免过多的细节,而将重点放在核心的思考过程上,这一过程将使您的查询进行得更快。出于说明的目的,将会涉及一些细节,没有真正的代码示例。同样为了简洁起见,我不能说得太详细,但是我会边走边链接例子和进一步的阅读。
注意:在整篇文章中,我将使用“数据库/DB”这一术语来表示各种各样的数据存储,这些数据存储具有完全不同的后端架构,但呈现类似 SQL 的接口。它们可能是传统的 SQL 数据库,如 MySQL/PostgreSQL,或者基于 Hadoop 的 NoSQL 商店,或者是 NewSQL 数据库,如 CockaroachDB。
查询优化是指计算机在查询时做更少的工作
让您的查询更快归结为让您的查询做更少的工作来获得相同的结果。有许多不同的策略来实现这个目标,并且需要技术知识来知道采用哪种策略。
做更少的工作意味着理解两件事:
- 知道你的数据库在做什么
- 知道如何调整你命令数据库做的事情,减少工作量
实现这一目标的途径在很大程度上取决于您工作的具体技术环境。优化 Hive 查询在某些方面与优化 PostgreSQL 查询截然相反。事实上,一些加快一个查询策略实际上会减慢另一个查询策略。
当“优化查询”时,您实际上正在做的是找到使您的查询工作负载与您的系统的硬件和算法限制很好地配合的方法。这意味着你对硬件、软件和系统特性越熟悉,你就会做得越好。
这也是为什么这个话题如此难教,你不能把它当作一种 SQL 风格的问题。“规则”只是指导方针,经常有反例。你也不能忽视系统的基本实现——尽管供应商会告诉你不必担心实现,但它很重要。
这是我们今天要讲的要点列表,从数据库开始,向外扩展。为了您的方便,它有链接跳转。
我可能漏掉了一些案例(你可以随时评论并告诉我我错过的事情),但这些都是我能想到的大案例。
让我们开始吧。
查看查询计划
当谈到互联网上关于优化 SQL 查询的博客帖子时,使用 EXPLAIN 通常是你听到的第一件事(有时是唯一的事……)。EXPLAIN 是从数据库中调用查询执行计划的 SQL 方式。它是数据库给你一个关于它如何计划执行你的查询的报告——它将描述它正在使用的任何索引、表连接的顺序、它是否将扫描整个表,有时甚至它在执行操作时期望查看多少行(基数)。
这个想法是,如果您可以访问这个计划,您应该能够找出(以某种方式)如何使您的查询更快。令人沮丧的是,人们通常不会告诉你该怎么做。
你在寻找工作的迹象
学习如何阅读乍一看像是胡言乱语的查询执行计划需要练习和耐心。更糟糕的是,执行计划看起来没有标准。每个数据库都有很大的不同。
An example MySQL EXPLAIN (src: https://www.eversql.com/mysql-explain-example-explaining-mysql-explain-using-stackoverflow-data/)
An example PostgreSQL EXPLAIN (src: https://thoughtbot.com/blog/reading-an-explain-analyze-query-plan)
对于您使用的每个数据库,您都必须重新学习这项技能。您还必须了解怪癖在哪里(就像有时 MySQL 解释说基数错误,它被误导了,因为它对索引做了奇怪的假设)。
不管您使用的是什么数据库,您都在寻找数据库做大量工作和花费大量时间的地方。然后你看看能不能想办法减少。请查阅特定数据库的文档,以了解每个语句的确切含义。
工作的迹象
- 表扫描—数据库读取表中的每一个条目,产生大量磁盘使用(这很慢)
- 高基数操作——无论是否使用了索引,当数据库必须处理大量的行时,这又意味着使用更多的磁盘和内存,并且需要花费时间来完成所有这些操作
- 对大量记录执行类似循环的操作——比如相关子查询,其中数据库必须为一组行执行单独的子查询,这很像一般编程中的 for 循环。
- 由于内存限制而写入磁盘——在 RDBMS 和 Hadoop 系统中都会发现这种情况,当某个东西变得太大而无法在内存中处理时,它必须将其工作写入磁盘才能继续运行而不会崩溃
- 使用网络——尤其是对于几乎需要网络流量的分布式系统,相比之下,它使磁盘看起来更快。
“磁盘”确实经常出现
希望你注意到“磁盘”这个词的出现。因为一般来说,磁盘访问是非常慢的,并且是你想要最小化的。即使固态硬盘比旋转硬盘快,它们仍然比 RAM 慢几个数量级。这就是为什么优化的第一部分通常包括寻找尽可能避免使用磁盘的方法,以及在别无选择的情况下有效使用磁盘的方法。
侧栏:各种组件的速度
Comparison of access latency, data and scaling from https://www.prowesscorp.com/computer-latency-at-a-human-scale/
如果你看一下 CPU 和 RAM 速度与 SSD/HDD/网络操作的比较,你就会明白为什么。RAM 访问比 CPU/L1/L2 慢 3 个数量级。永久内存模块,本质上是位于 DRAM 总线而不是 SATA 或 SAS 总线上的固态硬盘,仍然比标准 RAM 慢大约 3 倍。
一个更典型的 SSD,我们会认为是一个比 RAM 慢 2-3 个数量级的磁盘,旋转磁盘以毫秒为单位:比 RAM 慢 4 个数量级。根据条件和光速的不同,网络在 5-6 个数量级时甚至更差。
减少工作量:索引
在“减少工作”的背景下,希望你能明白为什么索引出现得如此频繁。索引本质上是一个预先计算的数据映射,更重要的是,它告诉数据库在磁盘上的什么位置可以找到某些条目。这意味着您不必扫描整个表来搜索所有内容。
全表扫描会变得非常昂贵,因为:
- 你正在阅读大量可能与你无关的数据(白费力气)
- 您可能正在使用旋转磁盘,这种磁盘具有非常慢的随机存取特性(固态硬盘可以大大改善这一点),并且
- 数据库文件可能会在整个驱动器上被分割,这使得寻道惩罚更加严重(固态硬盘肯定有助于这一点)。
但是,深入研究索引,您仍然需要知道您的特定数据库如何使用索引。例如,从版本 8 开始,PostgreSQL 已经能够通过位图扫描同时使用多个索引。与此同时,MySQL,即使在他们的版本 8 中,在默认的 InnoDB 存储引擎中只有 HASH 和 B-tree 索引,不能将索引组合在一起。
了解这些繁琐的细节对于优化来说是非常重要的。在 MySQL 中,如果您想在一个索引查询中使用两列或更多列,您必须使用这些列构建一个特定的多列索引,并且顺序很重要。索引(A,B)!= index(B,A) 。如果您有 index(A,B) ,但是需要快速访问 B,您至少需要一个单独的 index(B) 。
与此同时,在 PostgreSQL 中,更常见和更受欢迎的做法是只使用单独的索引:index(A)和 index(B ),因为它可以通过位图扫描自动使用 AND/OR 逻辑动态地组合 A 和 B。您可以构建一个多列的索引(A,B) ,这样会更有效,但是您已经通过两个单独的索引获得了很多好处。此外,如果您稍后需要您想要编写的索引© 和索引(D) ,它的伸缩性会更好。
针对您的用例使用正确的索引类型
大多数数据库引擎在定义索引时会提供多种索引算法供您使用。缺省值通常是 B 树的某种变体,它适用于大量典型的数据库用例。但是您经常会发现像用于空间数据库的 R/radix 树,或者散列和全文索引这样的东西。有时,对于更具体的用例,甚至有更深奥的东西可用。
不管它是什么,使用最适合您的特定用例的,使用错误的会适得其反。
索引提示
有时您有一个索引,但是由于某种原因查询计划没有使用它。MySQL 经常因其相当愚蠢的查询优化器而被取笑,因此许多高级用户使用了大量的索引提示,特别是**force index(idx)**和 **straight_join,**来强制优化器做正确的事情。经常发生的情况是,优化器认为忽略并索引和扫描一个表会更有效,因为它对正在操作的一个表的基数估计不正确。
同时,在 PostgreSQL 中,索引提示实际上并不是一件事。由于索引提示本质上是对某些表的基数的攻击,随着数据库的不断变化,它们很容易过时。相反,开发人员强迫您依赖他们的查询优化器。谢天谢地,优化器犯的错误比 MySQL 少,所以它不像听起来那么疯狂,尽管您偶尔会遇到难以克服的边缘情况。
不是每个数据库都有索引,许多运行 Hadoop 的东西,比如 Hive 和 Spark,没有这个概念,因为底层的 HDFS 系统只是一个大型文件的分布式存储系统,工具只是数据处理器。您可以使用文件结构(最常见的是使用年/月/日)人工创建类似于索引的东西,但是您必须自己维护它。
希望上面的例子也能向你说明,你擅长优化意味着非常熟悉你正在使用的特定工具。
减少工作量:磁盘写入
索引在很大程度上是从磁盘上读取内容。当数据进入时,您需要支付预先计算的成本来维护索引,但是读取索引变得更快。那么,如何管理磁盘写入呢?
—等一下。你会问,当我们在做选择操作时,为什么还要讨论写入磁盘呢?选择不主要是一个阅读操作吗?
遗憾的是,选择不仅仅是读取数据,因为有时需要写入磁盘来完成操作。最简单的例子是 Hadoop,MapReduce 过程中的每一步都涉及到将数据写到磁盘,以便它可以传输到下一步(参见这里的执行概述)。
另一个相当常见的场景是当您处理大型数据集时,结果集不适合进程的主内存。然后,数据库必须将内容转储到磁盘(无论是通过操作系统交换还是显式转储到文件)才能继续工作。当一个在小数据集上运行耗时 5 秒的查询在稍大的数据集上却要花 5000 秒时,您会经常注意到这一点。
您需要参考特定的数据库文档,以确定如何判断您的数据库是否正在写入磁盘,但是当您发现它正在发生时,它通常是优化的候选对象。
一次使用较少的数据
在任何系统中,最小化磁盘写入的唯一通用方法是减少您在任何给定时间处理的数据量。这将减少你被迫离开高速内存并掉进磁盘的机会。即使在必须放到磁盘上的情况下,您至少可以写得更少。
您还应该警惕增加行数的操作,也就是将一行数据连接到多行数据的连接。如果您给数据库一些条件,您的数据库可能会进行优化以过滤掉一个巨大的交叉连接,但是如果您一开始就不给它一些条件,您可能会创建一些巨大的数据集。
这在分布式数据系统(如 Hadoop)上变得更加重要,集群中的单个工作节点在某些时候需要将状态和中间结果传输到其他工作节点以继续工作,并且**网络流量甚至比旋转磁盘还要慢。**您希望尽可能地减少这种情况,同样是通过限制您在任一时间点处理的数据量。
兼职工作:子查询
子查询可以有效地在数据库中创建一个临时结果表,该表只在查询期间有效。我在这里汇总的一个相关特性是通用表表达式(CTE ),它本质上是一个具有更好语法的命名子查询,可以在一个查询中多次使用,而无需重复执行。
子查询是有趣的生物。它们可能会使事情变得更快或更慢,你必须努力思考和实验,看看它们是否适合用作优化。
关于子查询要知道的是,它们通常是驻留在内存中的未索引的临时表状结构。 PostgreSQL 不会,但是 MySQL“可能”尝试用散列索引具体化子查询。从技术上来说,Hive 和 Spark 中的子查询也是无索引的,但是索引在这些系统中并不是一件真正的事情,所以这一点有点争议。它只是内存中的一堆行或一个慢慢溢出到磁盘的表。(又是那个词,明白我的意思了吗?)
CTE/ 物化视图还可以阻止优化器利用子查询和主查询之间的索引关系,就像数据库无法跨越的优化栅栏。这可能是你想要的,也可能不是。
使子查询成为潜在优化的部分是当使用它们过滤数据源的成本低于使用原始数据源的成本时。1K 行的表扫描可能比扫描 1M 行索引更便宜。您在 Hadoop 生态系统中尤其会看到这一点,因为没有索引,而且 MapReduce 总是会溢出到磁盘。一个写得好的子查询可以过滤掉一个数据集,并减少整个查询过程中的后续操作。这也是为什么您更喜欢在 MapReduce 作业的早期使用这些操作,因为您需要为每一步付出磁盘和网络成本。
同时,在关系数据库中,使用现有索引通常比使用无索引的临时结构更有效。如果子查询太大以至于必须转到磁盘,情况就更糟了。所以你会更小心地使用它们,并且在使用时尽量保持它们的尺寸较小。
减少低效/重复性工作:临时表格
继续关于子查询的讨论,如果子查询是不可避免的,但是非常慢,该怎么办?您还可以尝试进一步优化—使用带索引的临时表。
请记住,子查询的主要缺陷是它们(大部分)没有被索引。如果我们能给它们加上一个指数,我们就成了黄金。解决这个问题的方法是制作一个临时表,把你需要的索引放在上面。然后我们将有一个合适的索引(通常是 B 树或类似的),它支持我们想要的许多比较操作符。
构建临时表的另一个潜在优势是,它离成为永久表只差一个关键字。创建表与创建临时表。如果您打算跨事务多次重用该结构,那么这是值得的。
但是有什么问题呢?使用临时表的成本是多少?
一个是简单的权限,您需要能够运行创建命令,但可能没有该级别的访问权限。这可能是一个服务器安全性/可靠性问题,尤其是在生产环境中。
第二个缺点是,一旦系统中有了这个中间表,就必须维护它,使它不会过时。随着人们开始依赖该表,您可能需要维护它,或者更频繁地更新它。这都是开销和开发时间。
最后,因为您正在创建一个表,所以您是显式地写入磁盘。我已经反复强调这是一个缓慢的操作,你不想重复做。您的 DB 可以选择在内存中而不是磁盘中创建临时表,但是如果它变得太大,您就要返回到磁盘。因此,您只希望在已经准备写入磁盘的情况下使用这个选项,这样写入磁盘和创建索引的成本基本上可以忽略不计。如果您打算多次重复使用该表,也可以这样做,这样做一次比重复使用花费更少。
减少在网络上的工作
如果有什么东西比旋转硬盘还慢,那就是网络连接。如果您有一个分布式系统,需要与位于大陆或地球另一端的服务器进行协调,那么您确实会受到光速和网络拥塞的限制。
最重要的是,网络链接的带宽通常小于硬盘驱动器到 CPU 的带宽,这使得运行速度更慢。此外,网络 TCP 开销是一个非常重要的事情。
您可能会认为这是复杂分布式系统人员的问题。的确,对于大型 Hadoop 集群来说,它们比单个托管服务器更重要,但即使是单个服务器安装也会偶尔出现这种情况。当您向外扩展时,您的数据库将独立于您的应用程序或您的分析服务器。
我曾经有一个简单的即席查询变成了常规仪表板,从一个大表中选择一些字段,简单的从表中选择 foo。将数据拉到另一台服务器上运行摄取到 pandas,并在那里进行分组/透视以进行演示,这非常容易。有索引和一切,但它的反应仍然相对较慢。
当我更深入地研究时,我发现很多延迟是在数据库准备和通过网络发送许多兆字节的输出到进行分析的地方。加快速度的最简单方法是将一些分析逻辑转移到 SQL 查询本身,这样就可以减少通过网络发送的原始数据。
最小化算法工作:使用更少的 CPU
对于本文的大部分内容,我一直在谈论 I/O,因为 I/O 通常是最大的缓慢来源,也是您可以找到最容易摘到的果实的地方。但我们还没完!计算机还有其他可能很慢的东西,比如实际的计算部分本身!
数据库不是神奇的计算系统,它们和你自己编写的任何程序一样,受到相同的算法约束。它们的排序速度不会比最著名的排序算法更快(因为它们是通用的,所以它们可能也不会使用最适合你的特定情况的算法)。这意味着您调用的特定操作和函数会对性能产生重大影响。
请记住,就像所有复杂的现实世界一样,没有灵丹妙药。我不能给你一个做什么/不做什么的硬性规定。您必须努力思考并实际测试性能,以找出适合您的特定设置的方法。
文本匹配功能
处理大量文本总是计算密集型工作。无论你做什么,如果你必须一个字符一个字符地扫描整个字符串,这是对字符串长度的 O(n)运算。
所以像这样带有%通配符和不区分大小写的操作符是相当密集的操作。他们还可以与索引进行有趣的互动。其他字符串函数,split、substr、concat 等。所有这些都使用相似的算法,因此在许多情况下并没有好到哪里去。正则表达式使用起来更加昂贵,因为它们更加复杂。所以你越是使用这些功能,一切都变得越慢。
除了纯粹的计算复杂性,字符串可能是数据库中实际存储的最大的东西。这是假设您使用最佳实践,并且不在数据库中存储大型 BLOBs。如果使用 unicode 编码,每个字符可能是 1、2、3 甚至 4 个字节的数据,这取决于具体情况。文本字段可能包含数兆字节的文本。所有这些意味着我们将很快耗尽内存。
分析和窗口功能
窗口函数非常棒,从过去遗留的 MySQL 5.4 代码库(没有窗口函数)移植到 Postgres 9.4 令人大开眼界。窗口函数可以做像计算百分位数或滞后这样的事情,这在过去是非常困难的和/或非常繁重的连接/子查询。在这方面,窗口函数往往比没有它们的性能有很大的提升。
也就是说,它们不是魔法。所有数组值的额外存储和排序都是计算工作。除非你注意排序、索引和窗口大小等细节,否则它们可能会造成浪费。优化它们往往需要实验和深入阅读查询计划。但这是可行的,用举例 像 这些。
降低交易成本:避免开销
开销很少对新手,甚至是系统的中级用户讲。不管出于什么原因,它被认为是一种深度的书呆子式的巫术,因为它被束缚在“系统的内脏”,在系统如何工作的元层中。但是当你开始寻找它时,你开始意识到开销无处不在。
实际上,我们用计算机做的每件事都涉及一定数量的开销,基本上是准备和维护我们“真正在做”的事情。我们已经讨论了很多可以被认为是开销的东西,这取决于你的观点。
正如我之前提到的,网络开销可能很大,我们希望通过减少网络传输来避免这种情况。它会消耗您的最大传输带宽,并且不是特定的数据库问题。谷歌提出并发布了一种网络协议 QUIC ,这种协议通过使用 UDP 避免了一些与 TCP(以及网络堆栈的邻近部分)相关的开销,从而提高了网络性能,这一点非常重要。
还有旋转磁盘并寻找正确的扇区来读取数据的“开销”,我们认为这是硬盘寻道时间慢的原因,也是大多数系统转向固态硬盘的原因。这也是我们在整篇文章中竭力避免磁盘操作的部分原因。
类似地,数据库本身也有大量的开销要处理。当它接受一个连接、打开一个事务、提交一个事务时,所有这些都需要时间。
你会问,这种开销可能是多少?一个非常常见的优化是将大量数据上传到数据库。几乎总是这样,您希望避免进行多次小的插入,因为数据库在负载下会陷入困境。即使您在一个包含许多插入的事务中处理所有的事情,它仍然需要不断旋转查询解释器来处理每个查询调用。
相反,最佳实践是创建一个巨大的文件(CSV 或其他文件)并一次性批量加载数据,即使您通过查询在网络上发送文件。这是快了多个数量级的。当你第一次遇到它的时候,它是完全疯狂和令人兴奋的。但是这完全有意义,因为对于一个小的单行插入,连接/事务处理时间比处理该插入所花费的时间要多。
同样的开销问题也适用于运行一组 SELECT 语句。这里仍然涉及到开销。如果你一直用大量的小请求敲打数据库,最终会使整个服务器陷入瘫痪。您可能希望获取更多数据并在本地处理,或者考虑使用某种缓存。
到处都有缓存
应用程序实际使用的一种提高应用程序响应性和减少数据库负载的技术是简单地缓存一段时间的查询结果,以便重复的调用完全跳过数据库,只给出缓存的结果。这是应用程序级别的东西,并不是专门针对您的 SQL 查询的优化,但这是一种足够常见的响应模式,您可能会在您使用的一些分析工具上看到它。
然而,在 SQL 查询级别,缓存仍然是一件需要注意的事情。快速连续多次运行完全相同的查询实际上会产生不同的运行时间,即使数据库没有缓存结果。这是因为第一个查询用数据填充了磁盘缓存和操作系统缓存。这不太可能是你的全部数据集,但在少数情况下,这是一个显著的(暂时的)速度提升。如果你是标杆管理的新手,这会影响你做的任何标杆管理。
事实上,虽然您通常没有意识到这一点,但数据库和它们运行的操作系统会使用缓存技术来挤出额外的性能。这里有一个关于 Postgresql 的缓存行为的帖子,广义来说其他数据库也会在不同程度上做类似的事情。
与他人相处融洽:竞争
作为一个刚刚开始查询的最终用户,很容易忘记数据库是多用户系统。他们通常处理事情非常迅速和透明,你永远不会真正知道通常有其他人在同一时间攻击服务器。但是,当您将服务器推向其操作极限时,您可能会遇到这样的情况:来自其他用户的查询,或者来自您自己的并行作业的查询,将开始对您产生影响。
一般负荷计划
机器的使用遵循人类的活动模式,因此一天中的某些时段会比其他时段更忙。随着报告和数据库查询开始自动化,大批量请求的时间安排变得越来越重要。夜间作业通常被设置为在晚上非常相似的时间运行(通常在午夜左右),因为每个人都认为那时服务器将是“空的”。只不过它们不是,现在系统必须在同时运行的 10 个批处理查询之间进行任务切换。
这仅仅是一个资源调度问题,但是我见过在空载服务器上运行只需 5-10 分钟的批处理作业延长到 30 分钟以上,因为它们与一堆其他作业争夺资源。
锁
一般负载有点烦人,因为它使事情变得不必要的慢,但是通过锁定会出现更严重的争用问题。数据库希望保持一致,这有时意味着当某些操作运行时,例如大的更新/写操作,整个表可能会被锁定。无论是行级锁还是表级锁,都取决于具体的细节,但无论是哪种情况,如果您有另一个查询需要访问那些被锁定的行,那么在解除锁之前,它们都不允许运行。
锁有时会变成被称为死锁的病态情况,但这种情况很少见。更常见的情况是,它会造成查询堵塞,因为等待而似乎要花很长时间。另一个不太常见的影响是,锁会影响服务器复制,有时甚至会停止数据复制,这意味着没有新的数据更新进出服务器,并导致各种意想不到的副作用。(我向 devops 的人们深表歉意,这些年来我用超长的分析查询破坏了复制)。
最后,答案是否定的:向问题扔钱
整篇文章都致力于让查询变得更快。因此,忽视速度的最终答案是错误的——更多的硬件和/或更好的软件,也就是投入资金解决问题。这是我们的最后一招,不是第一招,但也不能忽视。
有时,尽管您尽了最大努力来优化您的查询以使事情进行得更快,您还是会遇到 I/O 的限制,有时还会遇到 CPU 的限制,这些限制是您所拥有的系统无法克服的。在这种情况下,只有一件事可以让事情进展得更快:砸钱。重新设计您的系统,获得更多内存、更大的 RAID 阵列、更快的磁盘、更快的 CPU、更快的网卡和互连,当所有这些还不够时,可以通过集群进行扩展。我不是在开玩笑,脸书一度将他们的 MySQL 设置扩展到了完全疯狂的水平。如果集群还不够,请根据您需要的速度重新设计您的整个应用系统架构,而不仅仅是 DB 架构。
我们优化查询和数据库的目标是尽可能提高设备的性能,让花在系统上的每一分钱都获得最佳性能。非常容易顾全大局,嘲笑“如果不能做得更快,就多买些设备”的想法。但我们需要承认,优化不可避免地会有一个硬限制,即使这是一个非常高的限制,但确实存在一个收益递减点。经过完美调校和改装的 Miata 永远无法达到 F1 赛车的速度。
查询优化的最后一个技巧是了解何时已经完成了所有可行的优化,何时速度的代价比升级的成本更高。这很难,因为这是一个排除的过程,一个否定的“我们在这里做不了更多”的证明。
但是如果你真的发现自己处于这种情况,那就出去升级系统,这样你就可以重新优化了。
在写这篇文章的时候,我发现了一些有趣的链接
排名不分先后:
- 使用索引,Luke—DB performance for developers,许多关于数据库不同部分的性能和行为的小细节
- 关于硬件和服务器性能的神话——有很多关于用硬件解决问题并不是解决方案的故事
- PostgreSQL 中的缓存 —详细介绍了 PostgreSQL 用来更快交付结果的缓存策略,以及缓存如何与其他函数交互
- 查询优化的故事 —有人记录了他们优化查询的旅程,经历了许多曲折,最终追溯到数组比较& &操作符
艰难地学习 SQL
通过写它
一个不懂 SQL 的数据科学家不值他的盐 。
在我看来,这在任何意义上都是正确的。虽然我们觉得创建模型和提出不同的假设更有成就,但数据管理的作用不能低估。
当谈到 ETL 和数据准备任务时,SQL 无处不在,每个人都应该知道一点,至少是有用的。
我仍然记得我第一次接触 SQL 的时候。这是我学会的第一种语言(如果你可以这么说的话)。这对我产生了影响。我能够将事情自动化,这是我以前从未想过的。
在使用 SQL 之前,我曾经使用 Excel——VLOOKUPs 和 pivots。我在创建报告系统,一次又一次地做着同样的工作。SQL 让这一切烟消云散。现在我可以写一个大的脚本,一切都将自动化——所有的交叉表和分析都是动态生成的。
这就是 SQL 的强大之处。尽管你可以使用 Pandas 做任何你用 SQL 做的事情,你仍然需要学习 SQL 来处理像 HIVE,Teradata,有时还有 Spark 这样的系统。
这个帖子是关于安装 SQL,讲解 SQL,运行 SQL 的。
设置 SQL 环境
现在,学习 SQL 的最好方法是用它来弄脏你的手(对于你想学的任何其他东西,我也可以这么说)
我建议不要使用像 w3schools/tutorialspoint for SQL 这样的基于网络的菜谱,因为你不能用这些菜谱来使用你的数据。
此外,我会建议您学习 MySQL 风格的 SQL,因为它是开源的,易于在您的笔记本电脑上安装,并且有一个名为 MySQL Workbench 的优秀客户端,可以让您的生活更加轻松。
我们已经解决了这些问题,下面是一步一步地设置 MySQL:
- 您可以从下载 MySQL 社区服务器为您的特定系统(MACOSX、Linux、Windows)下载 MySQL。就我而言,我下载了 DMG 档案。之后,双击并安装文件。你可能需要设置一个密码。记住这个密码,因为以后连接到 MySQL 实例时会用到它。
- 创建一个名为
my.cnf
的文件,并将以下内容放入其中。这是向 SQL 数据库授予本地文件读取权限所必需的。
[client]
port= 3306
[mysqld]
port= 3306
secure_file_priv=''
local-infile=1
- 打开
System Preferences>MySQL
。转到Configuration
并使用选择按钮浏览到my.cnf
文件。
- 通过点击停止和启动,从
Instances
选项卡重启服务器。
- 一旦服务器开始运行,下载并安装 MySQL 工作台:下载 MySQL 工作台。工作台为您提供了一个编辑器来编写 SQL 查询并以结构化的方式获得结果。
- 现在打开 MySQL 工作台,通过它连接到 SQL。您将看到如下所示的内容。
- 您可以看到本地实例连接已经预先为您设置好了。现在,您只需点击该连接,并开始使用我们之前为 MySQL 服务器设置的密码(如果您有地址、端口号、用户名和密码,您还可以创建一个到现有 SQL 服务器的连接,该服务器可能不在您的计算机上)。
- 你会得到一个编辑器来编写你对特定数据库的查询。
- 检查左上方的
Schemas
选项卡,查看显示的表格。表sys_config
中只有一个sys
模式。不是学习 SQL 的有趣数据源。所以还是装一些数据来练习吧。 - 如果你有自己的数据要处理。那就很好。您可以使用以下命令创建一个新的模式(数据库)并将其上传到表中。(您可以使用
Cmd+Enter
或点击⚡️lightning 按钮来运行命令)
然而,在本教程中,我将使用 Sakila 电影数据库,您可以通过以下步骤安装该数据库:
- 转到 MySQL 文档并下载 Sakila ZIP 文件。
- 解压文件。
- 现在转到 MySQL 工作台,选择文件>运行 SQL 脚本>选择位置
sakila-db/sakila-schema.sql
- 转到 MySQL 工作台,选择文件>运行 SQL 脚本>选择位置
sakila-db/sakila-data.sql
完成后,您将看到模式列表中添加了一个新的数据库。
玩弄数据
现在我们有了一些数据。最后。
让我们从编写一些查询开始。
您可以尝试使用 Sakila Sample Database 文档详细了解 Sakila 数据库的模式。
Schema Diagram
任何 SQL 查询的基本语法都是:
SELECT col1, SUM(col2) as col2sum, AVG(col3) as col3avg
FROM table_name
WHERE col4 = 'some_value'
GROUP BY col1
ORDER BY col2sum DESC;
该查询中有四个元素:
- 选择:选择哪些列?在这里,我们选择
col1
,在col2
上进行总和聚合,在col3
上进行 AVG 聚合。我们还通过使用as
关键字给SUM(col2)
起了一个新名字。这就是所谓的混叠。 - FROM :我们应该从哪个表中选择?
- WHERE :我们可以使用 WHERE 语句过滤数据。
- 分组依据:不在聚合中的所有选定列都应该在分组依据中。
- 排序依据:排序依据
col2sum
上面的查询将帮助您在数据库中找到大多数简单的东西。
例如,我们可以使用以下方法找出不同审查级别的电影在不同时间播放的差异:
SELECT rating, avg(length) as length_avg
FROM sakila.film
GROUP BY rating
ORDER BY length_avg desc;
练习:提出一个问题
你现在应该提出一些你自己的问题。
例如,你可以试着找出 2006 年发行的所有电影。或者试图找到分级为 PG 且长度大于 50 分钟的所有电影。
您可以通过在 MySQL Workbench 上运行以下命令来实现这一点:
SELECT * FROM sakila.film WHERE release_year = 2006;
SELECT * FROM sakila.film WHERE length>50 and rating="PG";
SQL 中的联接
到目前为止,我们已经学习了如何使用单个表。但实际上,我们需要处理多个表。
接下来我们要学习的是如何连接。
现在连接是 MySQL 数据库不可或缺的一部分,理解它们是必要的。下图讲述了 SQL 中存在的大多数连接。我通常最后只使用左连接和内连接,所以我将从左连接开始。
当您希望保留左表(A)中的所有记录并在匹配记录上合并 B 时,可以使用左连接。在 B 没有被合并的地方,A 的记录在结果表中保持为 NULL。MySQL 的语法是:
SELECT A.col1, A.col2, B.col3, B.col4
FROM A
LEFT JOIN B
ON A.col2=B.col3
这里,我们从表 A 中选择 col1 和 col2,从表 b 中选择 col3 和 col4。
当您想要合并 A 和 B,并且只保留 A 和 B 中的公共记录时,可以使用内部联接。
示例:
为了给你一个用例,让我们回到我们的 Sakila 数据库。假设我们想知道我们的库存中每部电影有多少拷贝。您可以通过使用以下命令来获得:
SELECT film_id,count(film_id) as num_copies
FROM sakila.inventory
GROUP BY film_id
ORDER BY num_copies DESC;
这个结果看起来有趣吗?不完全是。id 对我们人类来说没有意义,如果我们能得到电影的名字,我们就能更好地处理信息。所以我们四处窥探,发现表film
和电影title
都有film_id
。
所以我们有所有的数据,但是我们如何在一个视图中得到它呢?
来加入救援。我们需要将title
添加到我们的库存表信息中。我们可以用——
SELECT A.*, B.title
FROM sakila.inventory A
LEFT JOIN sakila.film B
ON A.film_id = B.film_id
这将向您的库存表信息中添加另一列。正如你可能注意到的,有些电影在film
表中,而我们在inventory
表中没有。我们使用了一个左连接,因为我们希望保留库存表中的所有内容,并将其与film
表中对应的内容连接,而不是与film
表中的所有内容连接。
因此,现在我们将标题作为数据中的另一个字段。这正是我们想要的,但我们还没有解决整个难题。我们想要库存中标题的title
和num_copies
。
但是在我们继续深入之前,我们应该首先理解内部查询的概念。
内部查询:
现在您有了一个可以给出上述结果的查询。您可以做的一件事是使用
CREATE TABLE sakila.temp_table as
SELECT A.*, B.title FROM sakila.inventory A
LEFT JOIN sakila.film B
ON A.film_id = B.film_id;
然后使用简单的 group by 操作:
SELECT title, count(title) as num_copies
FROM sakila.temp_table
GROUP BY title
ORDER BY num_copies desc;
但这是多走了一步。我们必须创建一个临时表,它最终会占用系统空间。
SQL 为我们提供了针对这类问题的内部查询的概念。相反,您可以在一个查询中编写所有这些内容,使用:
SELECT temp.title, count(temp.title) as num_copies
FROM (
SELECT A.*, B.title
FROM sakila.inventory A
LEFT JOIN sakila.film B
ON A.film_id = B.film_id) temp
GROUP BY title
ORDER BY num_copies DESC;
我们在这里做的是将我们的第一个查询放在括号中,并给这个表一个别名temp
。然后我们按照操作进行分组,考虑temp
,就像我们考虑任何表一样。正是因为有了内部查询的概念,我们才能编写有时跨越多个页面的 SQL 查询。
从句
HAVING 是另一个有助于理解的 SQL 结构。所以我们已经得到了结果,现在我们想得到拷贝数小于或等于 2 的影片。
我们可以通过使用内部查询概念和 WHERE 子句来做到这一点。这里我们将一个内部查询嵌套在另一个内部查询中。相当整洁。
或者,我们可以使用 HAVING 子句。
HAVING 子句用于过滤最终的聚合结果。它不同于 where,因为 WHERE 用于筛选 from 语句中使用的表。在分组发生后,对最终结果进行过滤。
正如您在上面的例子中已经看到的,有很多方法可以用 SQL 做同样的事情。我们需要尽量想出最不冗长的,因此在许多情况下有意义的。
如果你能做到这一步,你已经比大多数人知道更多的 SQL。
接下来要做的事情: 练习 。
尝试在数据集上提出您的问题,并尝试使用 SQL 找到答案。
首先,我可以提供一些问题:
- 在我们的盘点中,哪位演员的电影最鲜明?
- 在我们的盘点中,哪些类型片的租借率最高?
继续学习
这只是一个关于如何使用 SQL 的简单教程。如果你想了解更多关于 SQL 的知识,我想向你推荐一门来自加州大学的关于数据科学的优秀课程。请务必阅读它,因为它讨论了其他 SQL 概念,如联合、字符串操作、函数、日期处理等。
将来我也会写更多初学者友好的帖子。在 中 关注我,或者订阅我的 博客 了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter @mlwhiz 联系到我。
此外,一个小小的免责声明——这篇文章中可能会有一些相关资源的附属链接,因为分享知识从来都不是一个坏主意。
学习理论:(不可知论者)大概近似正确的学习
在我以前的文章中,我讨论了什么是经验风险最小化,以及它在某些假设下产生令人满意的假设的证明。现在我想讨论一下大概正确的学习(这有点拗口,但是有点酷),这是 ERM 的一个概括。对于那些不熟悉 ERM 的人来说,我建议阅读我在该主题上的前一篇文章,因为这是理解 PAC learning 的先决条件。
请记住,在分析 ERM 时,我们得出的结论是,对于有限的假设空间 H 我们可以得出一个假设,当假设假设空间中存在这样一个假设时,它的误差以一定的概率低于ε。基于这些参数,我们可以计算出需要多少样本才能达到这样的精度,我们得出了样本的下限值:
这可以放入通用的 PAC 学习框架,下面的正式定义来自《理解机器学习》一书:
至少对我来说,这个定义一开始有点混乱。这是什么意思?该定义规定,如果存在函数 m_H 和算法,则假设类是 PAC 可学习的,该算法对于输入域 X、δ和ε上的任何标记函数 **f、**分布 D ,其中 m ≥ m_H 产生假设 h ,使得概率为 1-δ一个标注函数无非就是说我们有一个确定的函数 f 标注域内的数据。
这里,假设类可以是任何类型的二元分类器,因为将标签分配给来自域的例子的标签函数分配标签 0 或 1。****m _ H函数为我们提供了一个最小样本数的界限,我们需要该界限来实现低于ε的误差,并具有置信度δ。精确度ε逻辑上控制必要的样本大小,因为我们的精确度越高,逻辑上我们需要我们的训练集是来自领域的更忠实的样本,因此,增加实现这种精确度所需的样本数量。
使模型不可知
Photo by Evan Dennis on Unsplash
上述模型有一定的缺点,由于可实现性假设(在经验风险最小化中解释),它不够通用——没有人保证存在一个假设,由于模型的失败,该假设将导致我们当前假设空间中的真实误差为 0。另一种看待它的方式是,也许标签没有被数据很好地定义,因为缺少特征。
我们绕过可实现性假设的方法是用数据标签分布代替标签函数。您可以将此视为在标注函数中引入了不确定性,因为一个数据点可以共享不同的标注。那么为什么叫不可知 PAC 学习?不可知论一词来源于这样一个事实,即学习对于数据标签分布是不可知论的——这意味着它将通过不对数据标签分布做任何假设来学习最佳标签函数 f 。这种情况下有什么变化?嗯,真误差定义改变了,因为一个标签到一个数据点是在多个标签上的分布。我们不能保证学习者将达到最小可能的真实错误,因为我们没有数据标签分布来说明标签的不确定性。
经过这些考虑,我们从一书中得出了以下正式定义:
注意定义中关于 PAC 可学习性的变化。通过引入数据标签分布 D ,我们考虑到学习假设的真实误差将小于或等于最优假设的误差加上因子ε的事实。这也包含了 PAC 在假设空间中存在最优假设的情况下的自我学习,该假设产生的真误差为 0,但我们也考虑到可能不存在这样的假设。这些定义将在稍后解释 VC 维度和证明没有免费的午餐定理时有用。
如果术语对你来说有点陌生,我建议你看一下学习理论:经验风险最小化或者更详细地看一下文章中提到的 Ben-David 的精彩著作。除此之外,继续机器学习!
学习理论:经验风险最小化
经验风险最小化是机器学习中的一个基本概念,然而令人惊讶的是许多从业者并不熟悉它。理解 ERM 对于理解机器学习算法的限制以及形成实用的问题解决技能的良好基础是至关重要的。企业风险管理背后的理论是解释风险资本维度、大概正确(PAC)学习和其他基本概念的理论。在我看来,任何认真对待机器学习的人都应该乐于谈论 ERM。我将尽可能简单、简短和有理论依据地解释基本概念。这篇文章在很大程度上基于 Schwartz 和 Ben-David 的书理解机器学习,我强烈推荐给任何对学习理论基础感兴趣的人。
先说一个简单的监督学习分类问题。假设我们要对垃圾邮件进行分类,这可能是机器学习中最常用的例子(注意,这不是关于朴素贝叶斯的帖子)。每封邮件都有一个标签 0 或 1,要么是垃圾邮件,要么不是垃圾邮件。我们用 X 表示领域空间,用 Y 表示标签空间,我们还需要一个将领域集合空间映射到标签集合空间的函数,f:X->Y,这只是一个学习任务的形式化定义。
现在我们有了正式的问题定义,我们需要一个模型来做出我们的预测:垃圾邮件或不是垃圾邮件。巧合的是,模型的同义词是假设 h ,可能有点混乱。在这种情况下,假设只不过是一个从我们的域 X 获取输入并产生标签 0 或 1 的函数,即函数h:X->Y**。**
最后,我们实际上想找到最小化我们误差的假设,对吗?由此,我们得出了经验风险最小化这一术语。术语“经验的”意味着我们基于来自领域集合 X 的样本集合 S 来最小化我们的误差。从概率的角度来看,我们说我们从域集合 X 中采样 S ,其中 D 是在 X 上的分布。因此,当我们从域中采样时,我们表示从域 X 中通过D*(S)采样的域子集的可能性有多大。*
在下面的等式中,我们可以定义真误差,它基于整个域 X :
The error for hypothesis h. Starting from left to right, we calculate the error L based on a domain distribution D and a label mapping f. The error is equal to the probability of sampling x from d such that the label produced by the hypothesis is different from the actual label mapping.
由于我们只能访问输入域的子集 S ,因此我们是基于训练示例的样本进行学习的。我们无法访问真实误差,但可以访问经验误差:
m denotes the number of training examples. You can see from the equation that we effectively define the empirical error as the fraction of misclassified examples in the set S.
经验误差有时也被称为概括误差。原因是,实际上,在大多数问题中,我们无法访问输入的整个域,而只能访问我们的训练子集**。W* e 想在 S 的基础上进行归纳学习,也叫归纳学习。这个误差也被称为*风险,因此在经验风险最小化中称为风险。如果这让你想起了小批量梯度下降,那你就对了。这个概念在现代机器学习中基本是无处不在的。
现在可以谈谈过拟合的问题了。也就是说,由于我们只有数据的子样本,可能会发生这样的情况:我们最小化了经验误差**,但实际上增加了**真实误差。这个结果可以在简单的曲线拟合问题中观察到。让我们想象一下,我们有一些想要控制的机器人,我们想要将一些传感器数据 X 映射到扭矩。传感器数据有某种噪声,因为传感器从来都不是完美的,在这种情况下,我们将对传感器数据使用简单的高斯噪声。我们拟合了一个神经网络来做这件事,并且我们获得了以下结果:
The green points are the points that were used in fitting the model, our sample S. The model is actually quite good considering the small number of training points, but deviations from the optimal curve can be seen.
我们可以从另一个图中看到这种泛化误差,请注意,在某个点上,真实误差开始增加,而经验误差进一步减少。这是模型过度拟合训练数据的结果。
既然我们已经定义了我们的经验风险和实际风险**,那么问题就来了,我们是否真的能用这些做些有用的事情?事实证明,我们可以有把握地保证 ERM 将会发挥作用。换句话说,我们希望以一定的信心得出模型误差的上界。上界的意思很简单,我们可以保证误差不会超过这个界限,因此有了界这个词。**
This gives us the probability that a sampled set S (or training data in other words) is going to overfit the data, i.e. that the true error(L) is going to be larger than a certain number epsilon.
在当前情况下,我们将在可实现性假设下操作。我不打算写正式的定义,但简而言之,假设表明在所有可能的假设的空间中存在一个假设 h ,该假设在真实风险为 0 的意义上是最优的,这也意味着在子集 S 上找到的假设实现了 0 经验误差。当然,这在现实世界的用例中大多是不正确的,有一些学习范例放松了这种假设,但这一点我可能会在另一篇文章中讨论。
让我们定义真实误差高于ε的一组假设:
对于这组假设,很明显,要么他们接受了一组非代表性的学习,这导致了低的经验误差(风险)和高的真实误差(风险),要么他们没有好到足以学到任何东西。
我们想要分离导致假设的误导性训练集,这些假设导致低经验误差和高真实误差(过拟合的情况),这将在稍后推导上限时有用:
抽样一个非代表性样本的特定子集 S 的概率在逻辑上等于或低于抽样 M 的概率,因为 S 是M 的子集,因此我们可以写成:
我们将并集界引理应用于等式的右侧,该引理表示对两个集合的并集进行采样的概率低于对它们分别进行采样的概率。这就是为什么我们可以把总数写在右边:
此外,我们假设这些例子是独立同分布的(iid ),因此我们可以将经验误差为零的概率写成各个预测正确的概率的乘积:
The constant m denotes the number of training examples in the set S.
假设在某一数据点正确的概率可以写成 1 减去真实风险*。这是因为我们将风险定义为错误分类样本的分数。这个不等式来自于我们假设误差低于或等于上界的事实。***
如果我们把前面的两个等式结合起来,我们会得到下面的结果:
The exponential on the right comes from a simply provable inequality.
如果我们将上面的等式与应用联合边界的前一个等式结合起来,我们会得到以下有见地的结果:
Here |H| is the cardinality of the hypothesis space, obviously, this formulation doesn’t make sense in the case when we have an infinite number of hypotheses.
我们可以用某个常数 1δ来代替左侧,其中δ是我们希望误差不高于ε的置信度。我们可以简单地重新排列等式,以便表述如下:
最终结果告诉我们,我们需要多少个示例(m ),以便 ERM 不会导致高于ε且具有某个置信度δ的误差,即,当我们为 ERM 选择足够多的示例时,它可能不会具有高于ε的误差。我在这里使用可能这个词,因为它取决于我们的置信常数δ,它在 0 和 1 之间。这是一件很直观的事情,但我认为有时候看看方程,发现它在数学上是有意义的,这很好。
在企业风险管理中,做出了许多假设。我提到了可实现性假设,即在我们的假设池中有一个最优假设。此外,假设空间可能不是有限的,因为它是在这里。在未来,我计划通过范例来放松这些假设。然而,ERM 是学习理论中的一个基本概念,对于任何认真的机器学习实践者都是必不可少的。
为数据科学家和学者学习编码
作为一名高年级研究生,我经常指导低年级实验室成员(例如,本科生、学士后研究助理、低年级研究生)。我强烈鼓励我的同事学习如何编码。即使你不打算成为一名“数据科学家”,知道如何编码也是非常有用和有市场的(至少现在是这样!)技能要有。如果你是一名“学者”,我几乎可以向你保证,编码至少会节省你处理/分析数据的时间,甚至可能提高你的研究质量。你甚至会发现你喜欢编码——我知道我喜欢!
我认为最适合数据科学家和学者学习的两种语言是 Python 和 r。虽然我认为这两种语言都很好学,但我个人鼓励我的同事使用 Python。Python 是一种相对容易学习的语言,而且非常灵活。此外,我是一名深度学习实践者,深度学习库主要基于 Python。下面我有一些关于如何开始学习编码的建议。
1.学习基础知识
在深入所有的机器学习和统计软件包之前,我认为学习 Python 的基础知识是很重要的。一个非常好的免费在线资源是为每个人准备的 T2 Python。该资源包括讲座和附带练习的教科书。当我刚开始学习 Python 时,我浏览了这些讲座/章节。学完这门课后,你将会有一个相当扎实的 Python 基础。
2.Jupyter 笔记本中的代码
我强烈建议您在 Jupyter 笔记本中编写代码。Jupyter Notebook 是一个基于 web 应用程序的编码环境,允许您交互式编码。这些笔记本允许您轻松地记录代码和创建可视化效果。对于学者,我通常会有一个 Jupyter 笔记本,里面包含我在出版物中使用的代码。这让我可以轻松地查阅我的代码,并与想要复制我的分析的其他人分享。我甚至在非正式的演讲中使用过 Jupyter 笔记本。最近有一篇关于 Jupyter 笔记本不同特性的非常好的博文(点击此处查看博文),我推荐你去看看。你可以在 Anaconda 发行版中随 Python 一起安装 Jupyter Notebook。我推荐用这种方式安装 Python因为它也会安装很多有用的包。
3.用于数据辩论、统计、机器学习和数据可视化的学习包
学习对数据争论、统计、机器学习和数据可视化有用的 Python 包。大的有 NumPy,Pandas,SciPy,scikit-learn,Matplotlib 和 seaborn。我通过 Python 数据科学手册学习了这些包,在作者的网站上可以免费获得。这本书的一个缺点是它没有任何练习*。*我也听说过关于用于数据分析的 Python的很棒的东西,最近更新了。这本书是熊猫的创造者写的。我想在某个时候抽出时间来浏览这本书。对于不一定使用机器学习的学者,你可能也想看看 StatsModels 。当处理较小的数据集并且不使用机器学习时,我经常使用这个包。对于那些没有使用机器学习方法的人,我最近看到了这个关于 Python 中统计的教程,它快速介绍了这些包的一些有用特性。
结论
在经历了这些步骤之后,你将在数据科学家和学者经常使用的编码技巧方面有一个相当坚实的基础(并不是说不可能有学术数据科学家!).当你开始学习编码时要记住的一点是,任何人刚开始时都不容易,需要耐心。坚持下去,尽可能多地编码,最终你会达到像专业人士一样编码的地步!祝你的编码之旅好运,并请在评论中发布你认为对初学编码者有益的其他资源!
几分钟内学会平稳驾驶
小型赛车上的强化学习
Donkey Car in action with teleoperation control panel in the unity simulator
在这篇文章中,我们将看到如何在几分钟内训练一辆自动驾驶赛车,以及如何平稳地控制它。这种基于强化学习(RL)的方法,在这里的模拟(驴车模拟器)中提出,被设计成适用于现实世界。它建立在一家名为 Wayve.ai 的专注于自动驾驶的初创公司的工作基础上。
本文中使用的代码和模拟器是开放源代码和公共的。更多信息请查看关联的 GitHub 库;)(预先训练的控制器也可以下载)
重要提示:如需最新版本(使用Stable-baselines 3和 PyTorch),请查看https://github . com/araffin/aae-train-donkey car/releases/tag/live-twitch-2
2022 年 4 月更新:我用强化学习做了一系列学习赛车的视频:https://www.youtube.com/watch?v=ngK33h00iBE&list = pl 42 JK f1 t1 f 7 dfxe 7 f 0 vteflhw 0 ze Q4 XV
录像
GitHub 库:重现结果
实施强化学习方法,使汽车在几分钟内学会平稳驾驶——5 分钟内学会驾驶
github.com](https://github.com/araffin/learning-to-drive-in-5-minutes)
赛车比赛
自从几年前 DIY Robocars 诞生以来,现在已经有了无数的自主赛车比赛(例如图卢兹机器人大赛、铁车、……)。在这些项目中,目标很简单:你有一辆赛车,它必须在赛道上尽可能快地行驶,只给它车载摄像头的图像作为输入。
The Warehouse level, inspired by DIY Robocars
自驾挑战是进入机器人领域的好方法。为了方便学习,开发了开源的自动驾驶平台驴车。在其生态系统中,现在有一个以那个小机器人为特色的 unity 模拟器。我们将在这辆驴车上测试提议的方法。
概述
在简要回顾了小型自动驾驶汽车比赛中使用的不同方法后,我们将介绍什么是强化学习,然后详细介绍我们的方法。
Autonomous Racing Robot With an Arduino, a Raspberry Pi and a Pi Camera
自驾车比赛中使用的方法:路线跟踪和行为克隆
在介绍 RL 之前,我们将首先快速回顾一下目前在 RC 赛车比赛中使用的不同解决方案。
在之前的博客文章中,我描述了第一种自动驾驶的方法,它结合了计算机视觉和 PID 控制器。虽然这个想法很简单,适用于许多设置,但它需要手动标记数据(以告诉汽车赛道的中心在哪里),这既费钱又费力(相信我,手动标记并不好玩!).
Predicting where is the center of the track
作为另一种方法,许多竞争对手使用监督学习来再现人类驾驶员行为。为此,人类需要在几圈期间手动驾驶汽车,记录相机图像和来自操纵杆的相关控制输入。然后,训练一个模型来再现人类驾驶。然而,这种技术并不是真正健壮的,需要每条赛道的同质驾驶和再训练,因为它的泛化能力相当差。
什么是强化学习(RL),我们为什么要使用它?
鉴于上述问题,强化学习(RL)似乎是一个有趣的选择。
在强化学习环境中,一个代理(或机器人)作用于它的环境,并接收一个奖励作为反馈。它可以是积极的奖励(机器人做了好事)或消极的奖励(机器人应该受到惩罚)。
*机器人的目标是累积奖励最大化。*为了做到这一点,它通过与世界的交互来学习所谓的策略(或行为/控制器),将它的感官输入映射到行动。
在我们的例子中,输入是摄像机图像,动作是油门和转向角度。因此,如果我们以这样一种方式模拟奖励,即赛车保持在赛道上并使其速度最大化,我们就完成了!
Stable-Baselines: an easy to use reinforcement learning library
这就是强化学习的美妙之处,你只需要很少的假设(这里只设计一个奖励函数),它会直接优化你想要的(在赛道上快速前进,赢得比赛!).
注意:这不是第一篇关于小型无人驾驶汽车强化学习的博客文章,但与以前的方法、相比,本文介绍的技术只需要几分钟、、(而不是几个小时)就可以学会一个良好而平滑的控制策略(对于一个平滑的控制器来说大约需要 5 到 10 分钟,对于一个非常平滑的控制器来说大约需要 20 分钟)。
现在我们已经简要介绍了什么是 RL,我们将进入细节,从剖析 Wayve.ai 方法开始,这是我们方法的基础。
一天学会驾驶——way ve . ai 方法的关键要素
Wayve.ai 描述了一种在简单道路上训练现实世界中自动驾驶汽车的方法。这种方法由几个关键要素组成。
Wayve.ai approach: learning to drive in a day
首先,他们训练一个特征提取器(这里是一个可变自动编码器或 VAE )将图像压缩到一个更低维度的空间。该模型被训练来重建输入图像,但是包含迫使其压缩信息的瓶颈。
这个从原始数据中提取相关信息的步骤叫做 状态表征学习(SRL) ,是我主要的研究课题。这显著地允许减少搜索空间,并因此加速训练。下图显示了 SRL 和端到端强化学习之间的联系,也就是说,直接从像素学习控制策略。
注意:训练自动编码器并不是提取有用特征的唯一解决方案,你也可以训练例如逆动力学模型。
Decoupling Feature Extraction from Policy Learning
第二个关键要素是使用名为深度确定性策略梯度(DDPG) 的 RL 算法,该算法使用 VAE 特性作为输入来学习控制策略。这个政策每集之后都会更新。该算法的一个重要方面是它有一个内存,称为重放缓冲区**,在这里它与环境的交互被记录下来,并可以在以后“重放”。因此,即使汽车不与世界互动,它也可以从这个缓冲区中采样经验来更新它的策略。**
在人类干预之前,汽车被训练成最大化行驶的米数。这是最后一个关键因素:一旦汽车开始偏离道路,人类操作员就结束这一集。这个提前终止真的很重要(如深度模仿所示),防止汽车探索不感兴趣的区域来解决任务。
到目前为止,没有什么新的东西被提出来,我们只是总结了 Wayve.ai 的方法。以下是我对基本技术的所有修改。
几分钟内学会驾驶——最新方法
虽然 Wayve.ai 技术在原理上可能行得通,但要将其应用于自动驾驶的遥控汽车,还需要解决一些问题。
首先,因为特征提取器(VAE)在每集之后被训练,所以特征的分布不是固定的。也就是说,特征随着时间的推移而变化,并可能导致策略训练的不稳定性。此外,在笔记本电脑上训练 VAE(没有 GPU)相当慢,所以我们希望避免在每一集后重新训练 VAE。
为了解决这两个问题,我决定事先训练一只 VAE并使用谷歌 Colab 笔记本来保存我的电脑。通过这种方式,使用固定的特征提取器来训练策略。
在下面的图片中,我们探索 VAE 学到了什么。我们在它的潜在空间中导航(使用滑块)并观察重建的图像。
Exploring the latent space learned by the VAE
*然后,众所周知,DDPG 是不稳定的(在某种意义上,它的表现会在训练中灾难性地下降),并且很难调整。幸运的是,最近一个名为 的软演员评论家 (SAC)的算法具有相当的性能,并且更容易调整 。
*在我的实验中,我尝试了 PPO、SAC 和 DDPG。DDPG 和 SAC 在几集内给出了最好的结果,但 SAC 更容易调整。
对于这个项目,我使用了我为stable-baselines编写的软 Actor-Critic (SAC)实现(如果你正在使用 RL,我肯定推荐你看一看;) ),里面有算法的最新改进。
OpenAI 基线的一个分支,强化学习算法的实现- hill-a/stable-baselines
github.com](https://github.com/hill-a/stable-baselines)
最后,我更新了奖励函数和动作空间,以平滑控制和最大化速度。
奖励功能:走得快但留在赛道上!
机器人汽车没有任何里程计(也没有速度传感器),因此行驶的米数(也没有速度)不能作为奖励。
因此,我决定在每个时间步给予“生命奖励”(即停留在赛道上的+1 奖励),并对机器人进行惩罚,对离开赛道的使用碰撞惩罚(-10 奖励)。此外,我发现惩罚开得太快的车也是有益的:与油门成比例的额外负奖励被加到撞车惩罚上。****
最后,因为我们想跑得快,因为它是一辆赛车,我添加了一个与当前油门成比例的“油门加成”。这样,机器人会尽量待在轨道上,同时最大化速度。
总结一下:
其中 w1 和 w2 是常数,允许平衡目标(w1 << 10,w2 << 1,因为它们是次要目标)
避免摇晃控制:学习平稳驾驶
世界并不是随机的。如果你注意到了——机器人不会自发地开始颤抖。除非你给它接上一个 RL 算法。—埃莫·托多洛夫
Left: Shaky Control — Right: Smooth Control using the proposed technique
****如果到目前为止你应用了所介绍的方法,它将会起作用:赛车将会停留在赛道上,并且会试着跑得更快。然而,你很可能会以一个不稳定的控制结束:汽车会如上图所示振荡,因为它没有动力不这样做,它只是试图最大化它的回报。
平滑控制的解决方案是约束转向角的变化,同时用先前命令的历史增加输入(转向和油门)。这样,你就能在转向中保持连贯性。
举例来说,如果当前汽车转向角度为 0,并且它突然尝试以 90°转向,则连续性约束将仅允许它以 40°转向。因此,两个连续转向命令之间的差异保持在给定的范围内。这种额外的约束是以多一点培训为代价的。
在找到令人满意的解决方案之前,我花了几天时间试图解决这个问题,以下是我尝试过但没有成功的方法😗***
- 输出相对转向而不是绝对转向:产生较低频率的振荡
- 添加一个连续性惩罚(惩罚机器人转向的高变化):机器人没有优化正确的事情,它有时会工作,但后来不会留在轨道上。如果惩罚的成本太低,它就忽略它。
- 限制最大转向:在急转弯时,汽车不能再停留在赛道上
- 堆叠几帧以给出一些速度信息:产生较低频率的振荡
注:最近,苏黎世联邦理工学院的研究人员建议利用课程学习进行持续的节能控制。这可能是第二个解决方案(尽管有点难以调整)。
概述该方法
在我们的方法中,我们将策略学习从特征提取中分离出来,并添加一个额外的约束来平滑控制。
首先,人类通过手动驾驶汽车来收集数据(手动驾驶约 5 分钟可获得 10k 张图像)。这些图像被用来训练 VAE。
然后,我们在探索阶段(使用随机策略)和策略培训(当人类将汽车放回赛道以优化花费的时间时完成)之间交替。
为了训练该策略,首先使用 VAE(这里具有 64 维的潜在空间)对图像进行编码,并将其与最近十次采取的动作(油门和转向)的历史连接,从而创建 84D 特征向量。
控制策略由神经网络(32 和 16 个单元的两个全连接层,具有 ReLU 或 eLU 激活功能)表示。
该控制器输出转向角和油门。我们将油门限制在给定的范围内,并且还限制了当前转向角和先前转向角之间的差异。
结论
在这篇文章中,我们提出了一种方法,只使用一个摄像头,在几分钟内学习驴车的平稳控制策略。
由于该方法旨在应用于现实世界,这肯定是我在这个项目中的下一步:在一辆真实的 RC 汽车上测试该方法(见下文)。这将需要缩小 VAE 模型(政策网络已经很小了),以便让它在树莓派上运行。*
今天到此为止,不要犹豫测试代码,评论或提问,记住,分享是关怀;)!
***** Roma Sokolkov 在一辆真实的遥控汽车上复制了 wayve.ai 方法,但是这不包括平滑控制的最新改进****
感谢
如果没有 Roma Sokolkov 对 Wayve.ai 方法的重新实现、 Tawn Kramer 的驴车模拟器、 Felix Yu 的博客文章获得灵感、 David Ha 在 VAE 的实现、 Stable-Baselines 及其 model zoo 用于 SAC 实现和训练脚本、用于遥控操作的赛车机器人项目和就不可能完成这项工作
我也想感谢罗马,塞巴斯蒂安,塔恩,佛罗伦萨,约翰内斯,乔纳斯,加布里埃尔,阿尔瓦罗,阿瑟和塞尔吉奥的反馈。
附录:学习状态表示
潜在空间维度和样本数的影响
VAE 的潜在空间维度只需要足够大,以便 VAE 能够重建输入图像的重要部分。例如,64D 和 512D VAE 之间的最终控制策略没有巨大差异。
重要的不是样本的数量,而是样本的多样性和代表性。如果你的训练图像没有覆盖所有的环境多样性,那么你需要更多的样本。
我们能从随机特征中学习控制策略吗?
我试图在初始化后立即修正 VAE 的权重,然后学习关于那些随机特征的策略。然而,这并没有奏效。
与像素学习的比较
我没有时间(因为我的笔记本电脑没有 GPU)来比较直接从像素学习策略的方法。然而,如果有人能使用我的代码库做到这一点,我会对结果感兴趣。
有效的最低限度政策是什么?
单层 mlp 有效。我也尝试了线性策略,但是,我没有成功地获得一个好的控制器。****
学习用价值迭代网络进行计划
可以学习模仿规划算法的神经网络
被动策略
深度强化学习的第一个主要成就是著名的 DQN 算法在各种雅达利视频游戏中的人类水平表现,其中一个神经网络使用原始屏幕像素作为输入来学习玩游戏。在强化学习中,我们希望学习一种将状态映射到行动的策略,这样可以最大化累积的回报。例如,在 DQN 的论文中,神经网络是一种卷积神经网络,它将屏幕图像作为输入,并输出可能行为的得分。
虽然强化学习算法被设计成使得该策略应该学习挑选具有长期益处的动作,但是我们从我们的策略获得的信息仅适用于当前状态。这被称为反应策略,这是一种将当前状态映射到应该立即采取的行动的策略,或者映射到行动的概率分布**。**
[source](http://: http://karpathy.github.io/assets/rl/policy.png)
虽然反应性政策主导了大多数强化学习文献,并导致了一些惊人的和广为人知的成就,但有时我们需要从我们的政策中获得更丰富的答案,而当前推荐的行动是不够的。有时,我们的应用程序要求我们能够比单一步骤看得更远,并验证我们的策略将带我们走上一条“安全”的轨道,这要受到我们系统的其他组件的审查。在这种情况下,与其采取当前最好的行动,我们更想要的是一个完整的计划,或者至少是一个特定的未来计划。
但是,如果我们伟大的 RL 算法可以学习非常好的反应策略,我们为什么要为一个完整的计划费心呢?一个可能的原因是,RL 策略在成功学习任务之前通常需要大量的尝试,因此通常在模拟环境中训练,在模拟环境中,策略可以尽可能多地撞车,或者射击友军,如果失败就再试一次。模拟并不是真实世界的完美呈现,尽管存在这些差异,我们还是希望我们的系统能够正常工作,这是 RL 代理人所知的与斗争的事情。
拥有一个完整的计划可以让我们使用外部知识来评估它,并防止采取危险的行动。例如,如果一辆自动驾驶汽车希望改变车道,但突然有一辆汽车非常快地接近,比训练期间模拟汽车的速度还快,则外部程序可以预测当前计划的轨迹将会发生碰撞,并中止操纵。对于被动的政策来说,这要困难得多,因为在这种情况下,可能很难预测该情景在结束之前会如何结束。
渴望一个完整计划的另一个原因是,它可能使我们的政策执行得更好。也许通过迫使它提前计划,我们可以约束我们的政策更加一致,并能够更好地适应看不见的情况,这就是我们想要的。
马尔可夫决策过程
规划问题的一个非常常见的模型是马尔可夫决策过程,或 MDP。在 MDPs 中,我们将世界定义为一组状态 S,一组可能的动作采取 A,一个奖励函数 R 和一个转换模型 P。它们一起构成元组:
它定义了 MDP。让我们看一个具体的例子:
在这个例子中,一个代理人必须在 2D 地图上导航并到达绿色方块,因为绿色方块它会得到+1 奖励,并且避免红色方块,如果踩到红色方块它会受到-1 奖励的惩罚。其余状态不返回奖励(或等价地返回 0)。按照我们对 MDP 的定义,州是地图上除了黑色瓦片以外的瓦片集合,黑色瓦片是一个障碍。这组动作是四个方向,转移概率由右图给出。假设采取了特定的动作,转换模型指定了从当前状态转换到另一个状态的概率。在我们的例子中,如果我们的代理选择向上的动作,它将有 80%的机会向上移动,10%的机会向左或向右移动。如果代理选择正确的动作,它有 80%的机会向右移动,10%的机会向上或向下移动。
假设我们的代理位于地图的左下角,并且必须安全地导航到绿色方块。必须区分规划轨迹还是寻找政策。如果我们计划一个轨迹,我们会得到一个序列,指定应该采取行动的顺序,例如:(上,上,右,右,右)。如果我们的问题是确定性的,并且选择一个方向将有 100%的机会将我们的代理移动到那个方向,那么这将对应于轨迹:
edited from: source
然而,由于我们的问题是不确定的,我们可能会偏离我们想要走的道路,这一系列的行动对我们来说是不够的。我们真正想要的是一个完整的计划,将每个状态映射到期望的动作,就像这样:
这实际上正是我们的 RL 策略所代表的,从状态到行动的映射。不同的是,在这个简单的问题中,我们可以把整个状态空间展开,然后观察这个策略。在复杂的现实世界问题中,我们可能知道我们当前的状态,但是计算出未来行动和状态的可能结果可能是不现实的。,尤其是当我们的输入是图像或视频等高维传感器观测数据时。
但是回到我们的例子,我们怎样才能找到如上图所示的最优策略呢?这类问题有一个经典的算法叫做值迭代。这个算法所做的是计算当前处于某种状态所能获得的长期收益,通过问这个问题“如果我从这种状态开始,我能获得的最佳利润是多少?”。在 MDP 行话中,这个量被称为状态值,直观地看,如果我们知道每个状态的值,我们可以尝试总是移动到具有更高值的状态,并获得它们承诺的好处。
假设我们知道问题中所有状态的最优值;V*(s),我们现在可以定义从我们的状态采取一个具体行动的价值,并从此采取最优行动;Q*(s,a)。
状态-行动值是除了下一个状态的最优值之外,我们在这个状态采取行动得到的回报。但是,由于我们的问题是随机的,我们必须对它有一个预期,以便考虑到达未来状态的不同概率。但是由于 V*(s)是我们可以从当前状态获得的最佳值,这意味着这个等式必须成立:
这给出了最优值函数的递归定义:
如果我们为每个状态猜测一个值,并且证明这个递归方程对所有状态都是满足的,我们知道我们有一个最优值函数。但除此之外,我们可以猜测我们状态的任何初始值函数,并使用我们的方程来更新它,如下所示:
通过使用一些数学方法,我们甚至可以证明,如果在每次迭代中,我们对问题中的所有状态都这样做,最终我们的价值函数将收敛到最优价值函数。一旦我们有了最优价值函数,我们就可以很容易地提取最优策略:
这整个过程被称为值迭代。
但是这显然不能用于复杂的问题,在这些问题中,我们甚至无法获得转换模型的描述,甚至可能无法获得回报函数,那么我们如何将它用于更有趣的问题呢?
价值迭代网络
伯克利的研究人员在 NIPS 2016 上发表了一篇非常有趣的论文(他们因此获得了最佳论文奖),试图以一种非常优雅的方式解决这个问题,即赋予神经网络在其内部执行类似过程的能力。想法是这样的:因为我们并不真正知道我们真正问题的潜在 MDP,我们可以让我们的神经网络学习一些其他的 MDP,这与真正的不同,但为真正的问题提供了有用的计划。他们在一个 2D 导航问题上演示了他们的方法,其中输入是包含目标位置和障碍的地图图像。
输入图像被馈送到卷积神经网络,该网络输出相同大小的图像,该图像表示地图中不同位置的“回报”,并被馈送到另一个 CNN,该 CNN 将其映射到初始值地图。然后,对于 M 次迭代,卷积层被应用于连接的值和回报图像,以产生 Q 值图像,其具有 K 个通道,每个通道表示学习的 MDP 中的“动作”。最大池是沿着通道的维度执行的,本质上执行的操作与我们在上一节中看到的值迭代算法非常相似。
最终的价值图(或者它的特定部分)被提供给一个反应策略,该策略选择在我们的实际问题中执行的动作。值得注意的是,在任何阶段,模型都不会被赋予真实回报图或价值图的基础事实标签,我们只是希望通过学习使用这个价值迭代网络(VIN)来行动,我们的神经网络将学习 成为有用的 MDP 。
这整个模型要么通过使用给定轨迹的模仿学习来训练,要么通过从头开始的强化学习来训练,结果证明,结果确实非常棒:
在没有给它们的情况下,神经网络学习了奖励和价值图,它们看起来正是我们认为它们应该是的。奖励地图在目标位置及其附近具有高值,在障碍物处具有负值。值图似乎有一个梯度,将点引向目标位置并远离障碍物,这非常有意义。
虽然这个 2D 导航任务看似简单,但实际上看似困难。在一组训练地图上训练一个标准的 CNN 策略是可行的,但是对于看不见的地图的泛化能力相对较差。使用 VIN,作者显示了对不可见地图的更好的概括能力。除了简单的网格世界导航任务,他们还展示了使用月球表面高程图像的导航问题的算法,其中月球车必须在不可穿越的特征中安全导航,以及使用自然语言输入的网络导航问题。
我发现非常令人惊讶的是,模型可以完全从图像输入中学习这些,我们可以将这一成功归因于架构产生的固有归纳偏差,这迫使模型以类似于规划算法的方式执行计算。从那时起,这项工作已经得到了改进,并扩展到了更一般的领域,如图形,但仍然是一个令人印象深刻的成就。
学习以每秒 100 万帧的速度玩蛇
用优势耍蛇演员兼评论家
在这篇博文中,我将向你介绍我最近的项目,它结合了我觉得很有趣的两件事——电脑游戏和机器学习。很长一段时间以来,我一直想掌握深度强化学习,我认为没有比做自己的项目更好的方法了。为此,我用 PyTorch 实现了经典的手机游戏“Snake ”,并训练了一个强化学习算法来玩这个游戏。这篇文章分为三个部分。
- 贪吃蛇游戏的大规模并行矢量化实现
- 《优势演员——批评家》解读(A2C)
- 结果:分析不同药剂的行为
本项目的代码可在 https://github.com/oscarknagg/wurm/tree/medium-article-1获得
Snake —矢量化
尽管取得了令人印象深刻的成就,深度强化学习仍然非常缓慢。像 DotA 2 和星际争霸这样的复杂游戏已经取得了令人难以置信的成绩,但这些都需要数千年的游戏时间。事实上,即使是引发当前对深度强化学习(即学习玩 Atari 游戏)兴趣的工作,也需要数周的游戏时间和每场游戏数亿帧的画面。
从那以后,在通过并行化和改进的实现来加速深度强化学习方面,以及在样本效率方面,已经进行了大量的研究。许多先进的强化学习算法同时在一个环境的多个副本中被训练,通常每个 CPU 核心一个。这产生了一个大致线性的速度,你可以用更多的 CPU 内核产生游戏体验的速度。
因此,为了试验深度强化学习,你最好有大量的计算资源。一个非常快速/并行的环境或愿意等待很长时间。由于我没有访问大型集群的权限,并且希望快速看到结果,所以我决定创建一个矢量化的 Snake 实现,它可以实现比 CPU 内核数量更高级别的并行化。
什么是矢量化?
矢量化是单指令多数据(SIMD)并行的一种形式。对于那些 Python 程序员来说,这就是 numpy 运算通常比执行相同计算的显式 For 循环快几个数量级的原因。
基本上,矢量化是可能的,因为如果处理器可以在一个时钟周期内对 256 位数据执行操作,而您的程序的实数是 32 位单精度数,那么您可以将这些数中的 8 个打包到 256 位中,并在每个时钟周期内执行 8 次操作。因此,如果一个程序足够聪明,能够在正确的时间调度指令,使得总是有 8 个操作数在适当的位置,那么理论上,它应该能够比一次对一条数据执行指令实现 8 倍的加速。
实现:表示环境
关键思想是将环境的完整状态表示为单个张量。事实上,多个环境被表示为单个 4d 张量,其方式与表示一批图像的方式相同。在蛇的情况下,每个图像/环境有三个通道:一个用于食物颗粒,一个用于蛇头,一个用于蛇身,如上所示。这样做可以让我们利用 PyTorch 已经拥有的许多特性和优化来处理这类数据。
The example above rendered with a black border. This is a smaller version of the input given to the agent.
实现:移动蛇
所以现在我们有了一种表示游戏环境的方法,我们需要只使用矢量化张量运算来实现游戏。第一个技巧是,我们可以在每个环境中移动所有蛇头的位置,方法是用手工制作的过滤器对环境张量的头部通道应用 2D 卷积。
然而 PyTorch 只允许我们对整批应用相同的卷积滤波器,但我们需要能够在每个环境中采取不同的行动。因此,我应用了第二个技巧来解决这个限制。首先将 4 个输出通道(每个运动方向一个)的卷积应用到每个环境,然后使用非常有用的 torch.einsum 使用一个动作的热键向量来“选择”正确的动作。我强烈推荐阅读这篇文章,它很好地介绍了爱因斯坦求和及其在 NumPy/PyTorch/Tensorflow 中的应用。
The full head movement procedure. See this gist for a minimal implementation.
现在我们移动了头部,我们需要移动身体。首先,我们通过从所有身体位置减去 1 来向前移动尾部(即身体上的 1 位置),然后应用 ReLu 函数来保持其他元素大于 0。其次,我们复制头部通道,乘以最大身体值加 1。然后,我们将这个副本添加到头部通道,为身体创建一个新的前位置(图中的位置 8)。
Body movement procedure. The input action is left. See this gist for a minimal implementation.
需要更多的逻辑,例如检查碰撞和食物收集,在死后重置环境,以及检查蛇是否试图向后移动——以跨多个环境矢量化的方式实现这一切的方法非常有趣。所有这些优化的最终结果如下图所示。使用单个 1080Ti 和大小为 9 的环境,我可以使用随机代理每秒运行超过一百万步的最大值,而使用 A2C 训练卷积代理每秒运行超过 70,000 步(使用与后面结果相同的超参数)。
Clickbait title — justified [✓] not justified [ ]
最佳演员兼评论家(A2C)
在本节中,我假设您熟悉强化学习的基本术语,例如价值函数、政策、奖励、回报等……
演员-评论家算法背后的想法很简单。你训练一个行动者网络,将观察到的状态映射到行动(即策略)的概率分布。这个网络的目的是学习在特定的状态下应该采取的最佳行动。
同时,你也训练了一个从状态映射到期望值的批判网络,即处于这种状态后预期的未来回报的总和。这个网络的目的是学习处于某一特定状态是多么令人向往。这个网络还通过为状态提供基线值来稳定策略网络的学习,我们将在后面看到。
Table of symbols to make reading the following part easier.
价值函数学习
价值函数的损失是特定状态的预测值和从该状态产生的实际回报之间的平方差。因此,价值函数学习是一个回归问题。
在实践中,计算特定状态的真实回报需要从该状态开始直到时间结束的轨迹,或者至少直到代理人死亡。由于有限的计算资源,我们计算来自固定有限长度 T 的轨迹的回报,并使用自举来估计最终轨迹状态之后的遥远回报。Bootstrapping 是指我们用轨迹中最终状态的预测值替换总和的最后一部分——模型的预测被用作其目标。
政策学习
在强化学习领域有一个重要的结果被称为策略梯度定理。这表明,对于参数化策略(例如,从状态到动作的神经网络映射),其参数存在梯度,平均而言,这导致策略性能的提高。策略的性能被定义为该策略所访问的状态分布的期望值,如下所示。
The loss function maximised by the policy gradient.
这实际上是一个比乍看起来更令人惊讶结果。给定一个特定的状态,计算改变策略参数对行为的影响以及相应的回报是相对简单的。然而,我们感兴趣的是策略所访问的州的分布中的期望回报,这也将随着策略参数而改变。改变策略对状态分布的影响通常是未知的。因此,当性能取决于政策变化对状态分布的未知影响时,我们可以计算性能相对于政策参数的梯度,这是非常值得注意的。
下面显示的是策略梯度算法的一般形式。这个定理的证明和完整的讨论超出了范围,但是如果你有兴趣深入研究,我推荐萨顿和巴尔托的《强化学习:导论》的第 13 章。
利用一点数学知识,我们可以把它写成对我们的策略所访问的状态和动作的期望。
这个表达式更容易得到一个直觉。期望中的第一部分是政策所看到的状态-动作对的 Q 值,即性能测量。第二部分是增加特定动作的对数概率的梯度。直觉上,总体政策梯度是增加高回报行为概率的个体梯度的期望。相反,如果 Q(s,a)为负(即不良行为),那么各个梯度必须降低这些行为的概率。
如前所述,对政策梯度定理的一个重要认识是,如果将任意的独立于动作的基线 b(s) 添加到 Q 函数中,它不会改变。
添加这个基线函数给策略梯度引入了一个新的项,假设它是独立于动作的,则总是等于 0。
尽管添加基线不会影响政策梯度的预期,但它会影响其方差,因此一个精心选择的基线可以加快学习一个好政策的速度。
A2C 选择的基线是优势函数(因此是优势行动者-批评家)。这量化了特定状态-动作对与该状态的平均动作相比有多好。
The advantage function.
基于这个表达式,看起来我们需要学习另一个网络来逼近 Q(s,a)。然而,我们可以用更简单的方式重写。
我们可以这样做,因为一个特定政策的 Q 函数的定义是在状态 s 中行动 a 的回报加上在这一集的剩余时间里遵循该政策的预期回报。因此,最终的优势演员-评论家梯度如下。
整体算法
既然我已经勾勒出了 A2C 的理论基础,是时候看看完整的算法了。我们需要获得我们的策略的增量改进的全部是获得样本的方法,使得那些样本的梯度的期望与上面的表达式成比例。这可以非常简单地通过记录我们的政策所产生的经验轨迹来实现。为了加速这一过程,我们可以在同一环境中并行运行我们策略的多个副本,以更快地获得经验。
这是著名的异步优势行动者-批评家算法(A3C)的同步版本。不同之处在于,在 A3C 中,参数更新是在许多工作线程中分别计算的,并用于更新其他线程定期与之同步的主网络。在分批 A2C 中,来自所有工人的经验被周期性地组合以更新主网络。
A3C 异步的原因是不同线程中环境速度的差异不会降低彼此的速度。然而,在我的 snake 环境中,环境速度总是完全相等的,因此同步版本的算法更有意义。
结果
现在我已经描述了实现和理论,是时候学习玩蛇了!在我的实验中,我比较了三种不同架构的性能,如下图所示。
第一个代理(左)是 Deepmind 的“具有关系归纳偏差的深度强化学习”论文中的代理的较小版本。这个代理包含一个“关系模块”,它本质上是来自应用于 2D 图像的转换器模型的自我关注机制。此后,我们在值和策略输出之前应用通道式最大池和单个全连接层。为了比较,我还实现了一个卷积代理(中间层),除了关系模块被更多的卷积层取代之外,其他方面都是一样的。
作为一个黑马竞争者,我介绍了一个更简单的纯前馈代理(右),它只接受蛇头周围的局部区域作为输入,使环境部分可观察。输入观测值被展平并通过两个前馈层。与其他两个代理一样,值和策略输出是最终激活的线性投影。
所有 3 个代理都在 9 号环境中接受了 5 次 5000 万步的训练。具有 1 个标准偏差的训练曲线如下所示。
有趣的是,仅具有部分环境可观测性的前馈代理表现非常好,实现了所有代理中最高的平均规模。它也学得最快,可能是因为拥有最小的观察空间。正如你从下面的 GIF 中看到的,它已经学会了通过环绕环境来避免能见度有限的缺点,这样所有的东西最终都会进入视野。这使它能够找到分散在环境中的回报,但不如卷积和关系代理有效。
The feedforward agent in action
其他代理或多或少地直接走向奖励,但也有犯一些愚蠢错误的倾向,这反映在与前馈代理相比相当高的边缘冲突率上。有趣的是,在这个任务中,关系代理的表现并不比卷积代理好。我假设 snake 环境太简单了,关系模块中的归纳偏差没有用。Deepmind 对关系代理进行基准测试的任务涉及收集多个盒子的多个密钥,以便收集最终的大笔奖励——比 snake 复杂得多!
The convolutional agent in action.
好处:转移到更大的环境中
作为一个额外的实验,我决定看看如果我将代理从较小的 9 号环境转移到较大的 15 号环境会发生什么。我评估了每种类型的代理的 5 个实例在更大的环境中一百万步的性能,没有进一步的培训。
Agent performance transferring from size 9 to size 15 environment.
此表显示了不同类型的座席代表之间,甚至在特定的训练运行中,转移性能的巨大差异。卷积代理泛化得很好,实现了大的平均规模和高报酬率。事实上,卷积代理比在更大的环境中从头开始训练的相同代理表现得更好。我相信这是因为在更大的环境中奖励更少,所以在更大的环境中学习会明显变慢。
可以观察到一些相当聪明的行为。在大型蛇形飞机上,卷积智能体有时会执行一个“盘旋”动作,同时等待它的尾巴移开。
Convolutional agent waiting for its tail to move out of the way.
有限可见性代理继续围绕环境边缘的相同策略。这可以很好地执行,直到一个食物团在代理人无法观察到的环境中心产生,从这一点上代理人不再收集奖励。这与上表中的大尺寸小奖励率是一致的。
The limited visibility agent fails to transfer.
关系代理的性能至少可以说是不稳定的。在某些运行中,它会执行得相当好,而在其他运行中,它几乎会立即死亡或陷入循环。
Transfer behaviour of the relational agent.
在这个项目中,我亲眼目睹了深度强化学习的缓慢,并想出了一个简洁的、尽管是蛮力的方法来绕过它。我还通过观察非常相似的环境之间失败的转移,体验了 deep RL 的脆弱本质。
在未来的工作中,我想实现更多的 gridworld 游戏,以研究游戏之间的迁移学习。或者,我可能会实现一个多代理 snake 环境,以便回答 OpenAI 的“slither in”研究请求。
Multi-agent slither.io style environment.
本项目的代码位于:
学习模拟
学习如何模拟更好的合成数据可以改善深度学习
在 ICLR 2019 发表的论文可以在 这里 。我还有一张 幻灯片 以及一张 海报 详细解释了这项工作。
Photo by David Clode on Unsplash
深度神经网络是一项令人惊叹的技术。有了足够多的标记数据,他们可以学习为图像和声音等高维输入产生非常准确的分类器。近年来,机器学习社区已经能够成功解决诸如分类对象、检测图像中的对象和分割图像等问题。
上述声明中的警告是带有足够的标记数据。对真实现象和真实世界的模拟有时会有所帮助。在计算机视觉或机器人控制应用中,有合成数据提高深度学习系统性能的案例。
模拟可以给我们免费标签的精准场景。但是我们就拿侠盗猎车手 V (GTA)来说吧。研究人员利用了通过自由漫游 GTA V 世界收集的数据集,并一直使用该数据集来引导深度学习系统等。许多游戏设计师和地图创作者都致力于创造 GTA V 的错综复杂的世界。他们煞费苦心地设计它,一条街一条街,然后精细地梳理街道,添加行人,汽车,物体等。
An example image from GTA V (Grand Theft Auto V)
这个很贵。无论是时间还是金钱。使用随机模拟场景我们可能不会做得更好。这意味着重要的边缘情况可能严重欠采样,我们的分类器可能不知道如何正确地检测它们。让我们想象我们正试图训练一个检测危险场景的分类器。在现实世界中,我们会很少遇到像下面这样的危险场景,但它们非常重要。如果我们生成大量的随机场景,我们将很少有像下面这样的危险场景。对这些重要案例进行欠采样的数据集可能会产生一个对这些案例失败的分类器。
Example of a dangerous traffic scene. These important cases can be undersampled when randomly sampling synthetic data. Can we do better?
学习模拟的想法是,我们可以潜在地学习如何最佳地生成场景,以便深度网络可以学习非常好的表示,或者可以在下游任务中表现良好。
为了测试我们的工作,我们使用虚幻引擎 4 和 Carla 插件创建了一个参数化程序交通场景模拟器。我们的模拟器创建了一条具有不同类型十字路口(X、T 或 L)的可变长度的道路。我们可以在道路上放置建筑物,并在道路上放置 5 种不同类型的汽车。建筑物和汽车的数量由可调参数以及汽车的类型控制。我们还可以在 4 种不同的天气类型之间改变天气,这 4 种天气类型控制照明和雨的效果。主要思想是学习控制不同任务(例如语义分割或对象检测)的这些场景特征的最佳参数。
A demo of our procedural scene simulator. We vary the length of the road, the intersections, the amount of cars, the type of cars and the amount of houses. All of these are controlled by a set of parameters.
为了获得传感器数据,我们在我们生成的场景的道路上放置一辆汽车,它可以从生成的场景中捕获 RGB 图像,这些图像自动具有语义分割标签和深度注释(免费!).
An inside view of the generated scenes from our simulator with a fixed set of parameters
但是,模拟算法的学习比这更一般。我们不必将它专门用于交通场景,它可以应用于任何类型的参数化模拟器。我们的意思是,对于任何将参数作为输入的模拟器,我们提供了一种搜索最佳参数的方法,使得生成的数据对于深度网络学习下游任务是最佳的。据我们所知,我们的工作是首先进行模拟优化,以最大限度地提高主要任务的性能,以及将其应用于交通场景。
继续我们算法的关键点。传统的机器学习设置如下,其中数据从分布 P(x,y)中采样(x 是数据,y 是标签)。这通常是通过在现实世界中收集数据并手动标记样本来实现的。这个数据集是固定的,我们用它来训练我们的模型。
Traditional machine learning setup
通过使用模拟器来训练主任务网络,我们可以从模拟器定义的新分布 Q 中生成数据。这个数据集不是固定的,只要计算和时间允许,我们可以生成尽可能多的数据。尽管如此,在该域随机化设置中生成的数据是从 Q 中随机采样的**。获得一个好的模型所需的数据可能很大,并且性能可能不是最佳的。我们能做得更好吗?**
我们引入了学习模拟,它优化了我们在主要任务上选择的度量——通过定义与该度量直接相关的奖励函数 R(通常与度量本身相同)来训练流水线。我们从参数化模拟器 Q(x,y |θ)中采样数据,用它在算法的每次迭代中训练主任务模型。然后,我们定义的奖励 R 用于通知控制参数θ的策略的更新。通过在验证集上测试训练好的网络来获得奖励 R。在我们的例子中,我们使用普通的策略梯度来优化我们的策略。
非正式地说,我们试图找到最佳参数θ,它给出了分布 Q(x,y |θ),使主要任务的精度(或任何度量)最大化。
Learning to simulate setup
学习模拟问题的数学公式是一个双层优化问题。试图用基于梯度的方法来解决它对较低层次的问题提出了光滑性和可微性约束。在这种情况下,模拟器也应该是可微的,这通常是不正确的!这就是为什么像香草政策梯度这样的无导数优化方法是有意义的。
Mathematical formulation of the bi-level learning to simulate optimization problem
我们在实例计数和语义分割上演示我们的方法。
我们研究的汽车计数任务很简单。我们要求网络计算场景中每种特定类型的汽车数量。下面是一个右侧带有正确标签的示例场景。
Car counting task example
我们使用学习来模拟来解决这个问题,并与仅使用随机模拟的情况进行比较。在下图中,关注红色和灰色曲线,它们显示了学习模拟(LTS)如何在 250 个时期后获得更高的回报(计数车辆的平均绝对误差更低)。随机抽样的情况会有短暂的改善,但是一旦抽样的随机批次不足以完成任务,性能就会下降。灰色曲线在几次迭代中缓慢上升,但是学习模拟收敛于蓝色曲线所示的最佳可能精度(这里我们使用地面真实模拟参数)。
Reward for the car counting task. Note how learning to simulate converges to the best possible reward (on a simulated dataset) shown by the blue curve.
发生了什么事?一个很好的方法是通过可视化我们场景中不同场景和物体的概率。我们绘制了一段时间内的天气概率。我们生成的地面实况验证数据集对某些天气(晴朗的正午和晴朗的日落)进行了过采样,而对其余天气进行了欠采样。这意味着与其他类型的天气相比,有更多清晰的中午和清晰的日落天气的图像。我们可以看到,我们的算法恢复了粗略的比例!
Weather probabilities (logits) over time
让我们对汽车产卵概率做同样的事情。我们的地面实况数据集对某些类型的汽车(银色日产和绿色甲壳虫)进行了过度采样。学习模拟也反映了训练后的这些比例。本质上,该算法推动模拟器参数生成类似于地面真实数据集的数据集。
Car probabilities (logits) over time
现在,我们展示一个示例,说明学习模拟如何提高在 KITTI 交通分割数据集上进行随机模拟的准确性,该数据集是在现实世界中捕获的数据集**。**
An example image from the KITTI dataset.
An example of ground-truth semantic segmentation labels on our simulator. In a simulator, you can get object labels for free — no need for a human annotator
作为我们的基线,我们分别训练主任务模型 600 次,模拟器使用不同的随机参数集为每个模型生成数据。我们监控每个网络的验证 Car IoU 指标,并选择验证奖励最高的网络。然后我们在看不见的 KITTI 测试设备上测试它。我们训练学习以模拟 600 次迭代,并获得汽车 IoU(广泛分割度量)的 **0.579,**远高于使用随机参数基线(random params)获得的 0.480 。我们还使用另一种无导数优化技术(随机搜索)展示了我们的结果,这种技术在本实验中没有获得好的结果(尽管它在汽车计数中工作得相当好)。最后,我们还通过在 982 个带注释的真实 KITTI 训练图像(KITTI 训练集)上进行训练来展示我们用于分割的 ResNet-50 网络的实际性能,以展示上限。
Results for semantic segmenation on the unseen KITTI test set for car semantic segmentation
学习模拟可以被视为元学习算法,其调整模拟器的参数以生成合成数据,使得基于该数据训练的机器学习模型分别在验证和测试集上实现高精度。我们证明它在实际问题中胜过领域随机化,并且相信它是一个非常有前途的研究领域。在不久的将来,看到这方面的扩展和应用会发生什么将是令人兴奋的,我鼓励每个人都来看看模拟和学习模拟如何帮助你的应用或研究。
欢迎所有问题!我的网站在下面。
研究我探索了计算机视觉的几个课题,包括面部和手势分析,模拟和…
natanielruiz.github.io](https://natanielruiz.github.io)
使用强化学习从头开始学习解魔方
Photo by Olav Ahrens Røtne on Unsplash
动机:
魔方是一种有 6 个面的 3D 拼图,每个面通常有 9 个 3×3 布局的贴纸,拼图的目标是达到每个面只有一种独特颜色的已解状态。一个 3x3x3 魔方的可能状态有万亿次的数量级,其中只有一个被认为是“已解”状态。这意味着任何试图解立方体的强化学习代理的输入空间都是 huuuuuge。
数据集:
我们使用 python 库 pycuber 来表示一个立方体,并且只考虑四分之一旋转(90°)移动。这里没有使用注释数据,所有样本都是作为一个状态序列生成的,该序列从已解状态开始,然后被反转(以便它进入已解状态),然后这些序列就是用于训练的序列。
问题设置:
Flattened Cube
从一个像上面例子一样的随机洗牌的立方体中,我们想要学习一个模型,它能够从集合{‘F’:0,‘B’:1,‘U’:2,‘D’:3,‘L’:4,‘R’:5,【F’:6, “L’” : 10, “R’” : 11} (定义见【https://ruwix.com/the-rubiks-cube/notation/】*)*进入如下所示的求解状态:
Flattened solved Cube
型号:
这里实现的想法大多来自论文在没有人类知识的情况下解魔方
Figure from “Solving the Rubik’s Cube Without Human Knowledge” by McAleer et al.
所实施的 RL 方法被称为自动教学迭代或 ADI。如上图所示(来自论文)。我们通过从已解决状态往回走来构建到已解决状态的路径,然后我们使用完全连接的网络来学习路径中每个中间状态的“策略”和“值”。学习目标被定义为:
其中“A”是引言中定义的所有 12 个可能动作的变量,R(A(x_i,A))是通过从状态 x_i 采取动作 A 获得的回报。如果我们到达已解决状态,我们定义 R = 0.4,否则为-0.4。奖励是网络在训练期间得到的唯一监督信号,该信号可能被多个“动作”决策延迟,因为具有正奖励的唯一状态是最终解决的状态。
在推理时,我们使用价值策略网络来指导我们对已解决状态的搜索,以便我们可以通过减少“值得”采取的行动的数量来在尽可能短的时间内解决难题。
结果:
我用这个实现得到的结果并不像论文中呈现的那样壮观。据我所知,这个实现可以很容易地解决离已解决状态 6、7 或 8 步的立方体,但在此之后就困难多了。
网络运行示例:
结论:
这是强化学习的一个非常酷的应用,用来解决一个组合问题,其中有大量的状态,但没有使用任何先验知识或特征工程。
重现结果的代码可在:https://github.com/CVxTz/rubiks_cube获得
如何在 Python 中使用进度条
4 个不同库的介绍(命令行和用户界面)
Types of bars from ProgressBar (gif from the library’s page)
进度条太棒了
进度条是一个可视化的表示,表示一个过程还剩下多长时间才能完成。它们让您不必担心进程是否已经挂起,或者试图预测您的代码进展如何。您可以实时直观地看到脚本的进展情况!
如果你以前从未想过或使用过进度条,很容易认为它们会给你的代码增加不必要的复杂性,并且很难维护。这与事实相去甚远。在短短几行代码中,我们将看到如何在命令行脚本和 PySimpleGUI UIs 中添加进度条。
使用进度
第一个要回顾的 python 库是 Progress。
你需要做的就是定义你期望做的迭代次数,bar 的类型,并在每次迭代时让 bar 知道。
import time
from progress.bar import IncrementalBarmylist = [1,2,3,4,5,6,7,8]bar = IncrementalBar('Countdown', max = len(mylist))for item in mylist:
bar.next()
time.sleep(1)bar.finish()
返回:
Incremental Bar of Progressbar
如果你不喜欢进度条的格式,有很多供你选择:
Types of bars from ProgressBar (gif from the library’s page)
别忘了查看他们的文档。
使用 tqdm
在我们的评论中,下一个是 tqdm 库。
Python 和 CLI 的快速、可扩展进度条
就像前面的库一样,我们已经看到,通过几行代码我们可以引入一个进度条。在设置方面只有一点不同:
import time
from tqdm import tqdmmylist = [1,2,3,4,5,6,7,8]for i in tqdm(mylist):
time.sleep(1)
给了我们:
和以前一样,酒吧也有一些选择。务必检查文档。
使用实时进度
顾名思义,这个库试图让进度条活起来。它比我们之前看到的进度条多了一些动画。然而,就代码而言,它们非常相似:
from alive_progress import alive_bar
import timemylist = [1,2,3,4,5,6,7,8]with alive_bar(len(mylist)) as bar:
for i in mylist:
bar()
time.sleep(1)
酒吧看起来像预期的那样:
再一次,不要忘记探索图书馆的不同特性。
使用 PySimpleGUI 的图形进度条
本着我们到目前为止所看到的同样的精神,我们可以添加一行代码来获得命令行脚本的图形化进度条。
为了实现上述目标,我们需要的是:
import PySimpleGUI as sg
import timemylist = [1,2,3,4,5,6,7,8]for i, item in enumerate(mylist):
sg.one_line_progress_meter('This is my progress meter!', i+1, len(mylist), '-key-')
time.sleep(1)
感谢 麦克 指出这一点!
PySimpleGUI 应用程序中的进度条
如果你一直关注我最近的博客,我们探索了如何快速启动 Python UI,然后我们构建了一个带有 UI 的比较工具。为了继续我们的学习之旅,今天我们将探讨如何集成一个进度条。
用户界面:
代码:
import PySimpleGUI as sg
import timemylist = [1,2,3,4,5,6,7,8]progressbar = [
[sg.ProgressBar(len(mylist), orientation='h', size=(51, 10), key='progressbar')]
]
outputwin = [
[sg.Output(size=(78,20))]
]layout = [
[sg.Frame('Progress',layout= progressbar)],
[sg.Frame('Output', layout = outputwin)],
[sg.Submit('Start'),sg.Cancel()]
]window = sg.Window('Custom Progress Meter', layout)
progress_bar = window['progressbar']while True:
event, values = window.read(timeout=10)
if event == 'Cancel' or event is None:
break
elif event == 'Start':
for i,item in enumerate(mylist):
print(item)
time.sleep(1)
progress_bar.UpdateBar(i + 1)window.close()
结论
伙计们,这就是了!只需几行代码,您就可以在 python 脚本中实现进度条!这没什么太复杂的,你不必再猜测你的剧本进展如何了!
希望你觉得这有用!
学习矢量量化
基于原型的学习导论
Image from Pixabay from Pexels
如今,机器学习和人工神经网络这两个术语似乎可以互换使用。然而,当谈到最大似然算法时,世界上不仅仅只有神经网络。事实上,在大学期间,我所做的大多数 ML 项目根本没有使用神经网络。但是除了神经网络,为什么还要使用其他东西呢?的确,神经网络可以对非常复杂的问题取得惊人的结果。不过,还有一些关键问题。它们需要大数据集和大量的计算能力,尤其是后者并不总是在一个人的能力范围内。通常,对于相当小而简单的问题,使用像神经网络这样复杂的东西是没有意义的,因为其他算法更快,可能更好!
学习矢量量化(LVQ)就是我经常使用的一种算法。虽然与其他一些算法相比,该算法本身并不是特别强大,但它非常简单和直观。此外,它有一些扩展,可以使算法在各种 ML 相关任务中成为一个强大的工具。
LVQ 是一种所谓的原型学习方法。一个或多个原型用于表示数据集中的每个类,每个原型被描述为特征空间中的一个点。然后,新的(未知的)数据点被分配最接近它们的原型的类别。为了使“最近”有意义,必须定义距离度量。您可以自由选择任何距离度量,通常,欧几里得距离是距离度量的选择。每个类可以使用多少原型没有限制,唯一的要求是每个类至少有一个。下图显示了一个简单的 LVQ 系统,其中每个类(红色和蓝色)由一个原型(较大的点)表示。
2D LVQ system with corresponding prototypes representing each class
那么我们如何让原型适合每一个类,使它们能够很好地代表那个类呢?我们从选择距离度量开始,在本例中,我们将应用欧几里德距离度量。
回想一下,N 维中 2 个向量之间的欧几里德距离由下式给出:
请注意,我们可以使用平方欧几里得距离,这不需要我们计算平方根。
那么我们该怎么做呢?
第一步是初始化每个原型,通常原型是通过使用类方法初始化的。
下一步是遍历数据集,一次一个特征向量。对于每个这样的特征向量,我们需要使用选择的距离度量来计算到每个原型的距离。然后我们只对最接近的原型进行更新。如果原型具有与特征向量相同的标签,则原型被推向特征向量,否则原型被从相反方向推离特征向量。推动的幅度由学习速率决定,学习速率是算法的参数之一。
一旦我们对数据集中的每一个样本都这样做了,我们就可以多次重复这个过程,直到算法收敛。对数据集的一次扫描也称为训练时期。该算法通常需要几个历元来达到收敛,这取决于问题的复杂程度。
下面的代码是 LVQ 算法在 Python 中的实现。
给我看看代码
请随意试验您选择的数据集。你也可以改进算法,例如允许每个类使用多个原型。
现在我们已经有了算法,让我们在一些样本数据上测试它。在这种情况下,我使用了来自 UCI 机器学习库的钞票认证数据集。
在对样本数据集运行算法之前,我对特征向量进行了洗牌,并留出 10 %的集合作为验证集。现在你应该如何处理学习率参数呢?理想情况下,您应该使用交叉验证来找出最佳值。在这种情况下,我简单地选择 0.01 作为学习率,因为它给出了不错的结果。在 25 个时期之后,该算法在验证集上收敛到 0.28 的误差。对于一个如此直观和简单的算法来说,这并不是一件坏事。
为了对新的未知特征向量进行分类,只需将最接近的原型的类别分配给它。
谢谢,还有别的吗?
LVQ 有一些明显的优势:它简单、直观、易于实现,同时还能产生不错的性能。然而,在更复杂的问题中,如果数据有很多维度或有噪声,欧几里德距离会引起问题。特征空间的适当归一化和预处理是必要的。但是,即使这样,如果你的数据有很多维度,你可能会遭受维度的诅咒,你可能会考虑某种形式的降维。
伙计们,现在就到这里吧!下一次,我们将研究处理 LVQ 问题的算法的更复杂的变体。
Python 中的最小二乘线性回归
Photo by Jeswin Thomas on Unsplash
顾名思义,****最小二乘法的方法是将数据集中观察到的目标与线性近似预测的目标之间的残差的平方之和最小化。在这篇后续文章中,我们将看到如何使用线性代数找到最佳拟合线,而不是像梯度下降这样的东西。
算法
与我最初的想法相反,线性回归的scikit-learn
实现最小化了以下形式的成本函数:
利用 x 的奇异值分解。
如果您已经熟悉线性回归,您可能会发现它与前面的方程和均方差(MSE)有一些相似之处。
作为快速复习,假设我们有下面的散点图和回归线。
我们通过从一个数据点减去另一个数据点来计算从直线到给定数据点的距离。我们取差值的平方,因为我们不希望低于实际值的预测值被高于实际值的预测值抵消。用数学术语来说,后者可以表示如下:
****
scikit-learn
库中使用的成本函数是相似的,只是我们同时使用矩阵运算来计算它。
对于那些上过微积分课程的人来说,你们可能以前遇到过这种符号。
在这种情况下, x 是一个矢量,我们正在计算它的大小。
在同样的意义上,当我们用竖线包围矩阵的变量(即 A)时,我们是说我们想从一个由行和列组成的矩阵变成一个标量。从矩阵中导出标量有多种方法。取决于使用哪一个,你会在变量的右边看到一个不同的符号(等式中多出来的 2 不是偶然放在那里的)。
额外的 2 意味着我们取矩阵的欧几里德范数。
假设我们有一个矩阵 A,A 的欧氏范数等于 A 点转置的最大特征值的平方根,为了清楚起见,我们来走一个例子。
****
这就是我们量化误差的方法。然而,这引起了一个新的问题。具体来说,我们实际上如何着手最小化它呢?事实证明,最小范数最小二乘解(系数)可以通过计算输入矩阵 X 的伪逆并乘以输出向量 y 来找到。
其中 X 的伪逆定义为:
上述矩阵均可从 X 的奇异值分解(SVD)中获得。回想一下,X 的 SVD 可描述如下:
如果你对如何确定 U,sigma 和 V 的转置很好奇,看看我不久前写的这篇文章,这篇文章讲述了如何使用 SVD 进行降维。****
我们通过对sigma
矩阵中的值求逆来构造对角矩阵 D^+。+
指的是所有元素必须大于 0,因为我们不能被 0 整除。
********
Python 代码
让我们看看如何使用基本的numpy
函数从头开始实现线性回归。首先,我们导入以下库。
**from sklearn.datasets import make_regression
from matplotlib import pyplot as plt
import numpy as np
from sklearn.linear_model import LinearRegression**
接下来,我们使用scikit-learn
库生成数据。
**X, y, coefficients = make_regression(
n_samples=50,
n_features=1,
n_informative=1,
n_targets=1,
noise=5,
coef=True,
random_state=1
)**
我们将矩阵的秩和列数存储为变量。
**n = X.shape[1]
r = np.linalg.matrix_rank(X)**
我们使用奇异值分解找到我们的特征矩阵的等价物。
**U, sigma, VT = np.linalg.svd(X, full_matrices=False)**
然后,D^+可以从西格玛得到。
**D_plus = np.diag(np.hstack([1/sigma[:r], np.zeros(n-r)]))**
V 当然等于其转置的转置,如以下恒等式所述。
**V = VT.T**
最后,我们确定 x 的 Moore-Penrose 伪逆。
**X_plus = V.dot(D_plus).dot(U.T)**
正如我们在上一节中看到的,系数向量可以通过矩阵 X 的伪逆乘以 y 来计算。
**w = X_plus.dot(y)**
为了获得实际误差,我们使用我们看到的第一个等式来计算残差平方和。
**error = np.linalg.norm(X.dot(w) - y, ord=2) ** 2**
为了验证我们得到了正确的答案,我们可以使用一个numpy
函数来计算并返回线性矩阵方程的最小二乘解。具体来说,该函数返回 4 个值。
- 最小二乘解
- 残差和(误差)
- 矩阵的秩(X)
- 矩阵的奇异值(X)
**np.linalg.lstsq(X, y)**
通过绘制回归线,我们可以直观地确定系数实际上是否导致最佳拟合。
**plt.scatter(X, y)
plt.plot(X, w*X, c='red')**
让我们使用线性回归的scikit-learn
实现做同样的事情。
**lr = LinearRegression()lr.fit(X, y)w = lr.coef_[0]**
最后,我们使用新发现的系数绘制回归线。
**plt.scatter(X, y)
plt.plot(X, w*X, c='red')**
Python 中熊猫数据框的左连接
关于如何在左连接的结果中正确标记空值来源的教程。
StackOverflow 文章 Pandas Merging 101 详细介绍了合并 Pandas 数据帧。然而,我给数据科学课后测试评分的经验让我相信左连接对许多人来说仍然是一个挑战。在本文中,我将展示如何正确处理 Pandas 左连接中的右表(数据框)包含空值的情况。
让我们考虑一个场景,其中我们有一个表transactions
包含一些用户执行的事务,还有一个表users
包含一些用户属性,例如他们喜欢的颜色。我们希望用用户的属性来注释事务。以下是数据框:
import numpy as np
import pandas as pdnp.random.seed(0)
# transactions
left_df = pd.DataFrame({'transaction_id': ['A', 'B', 'C', 'D'],
'user_id': ['Peter', 'John', 'John', 'Anna'],
'value': np.random.randn(4),
})# users
right_df = pd.DataFrame({'user_id': ['Paul', 'Mary', 'John',
'Anna'],
'favorite_color': ['blue', 'blue', 'red',
np.NaN],
})
请注意,彼得不在users
表中,安娜也没有最喜欢的颜色。
>>> left_df
transaction_id user_id value
0 A Peter 1.867558
1 B John -0.977278
2 C John 0.950088
3 D Anna -0.151357>>> right_df
user_id favorite_color
0 Paul blue
1 Mary blue
2 John red
3 Anna NaN
使用用户 id 上的左连接将用户喜欢的颜色添加到事务表中似乎很简单:
>>> left_df.merge(right_df, on='user_id', how='left')
transaction_id user_id value favorite_color
0 A Peter 1.867558 NaN
1 B John -0.977278 red
2 C John 0.950088 red
3 D Anna -0.151357 NaN
我们看到彼得和安娜在favorite_color
列中有NaN
。然而,丢失的值有两个不同的原因:Peter 的记录在users
表中没有匹配,而 Anna 没有最喜欢的颜色的值。在某些情况下,这种细微的差别很重要。例如,它对于在初始勘探期间理解数据和提高数据质量至关重要。
这里有两个简单的方法来跟踪左连接结果中缺少值的原因。第一个由merge
函数通过indicator
参数直接提供。当设置为True
时,结果数据帧有一个附加列_merge
:
>>> left_df.merge(right_df, on='user_id', how='left', indicator=True)
transaction_id user_id value favorite_color _merge
0 A Peter 1.867558 NaN left_only
1 B John -0.977278 red both
2 C John 0.950088 red both
3 D Anna -0.151357 NaN both
第二种方法与它在 SQL 世界中的实现方式有关,它在右边的表中显式地添加了一个表示user_id
的列。我们注意到,如果两个表中的连接列具有不同的名称,那么这两个列都会出现在结果数据框中,因此我们在合并之前重命名了users
表中的user_id
列。
>>> left_df.merge(right_df.rename({'user_id': 'user_id_r'}, axis=1),
left_on='user_id', right_on='user_id_r', how='left')
transaction_id user_id value user_id_r favorite_color
0 A Peter 1.867558 NaN NaN
1 B John -0.977278 John red
2 C John 0.950088 John red
3 D Anna -0.151357 Anna NaN
一个等效的 SQL 查询是
**select**
t.transaction_id
, t.user_id
, t.value
, u.user_id as user_id_r
, u.favorite_color
**from**
transactions t
**left join**
users u
**on** t.user_id = u.user_id
;
总之,添加一个额外的列来指示 Pandas left join 中是否有匹配,这允许我们随后根据用户是否已知但没有最喜欢的颜色或者用户是否从users
表中缺失来不同地处理最喜欢的颜色的缺失值。
这篇文章最初出现在 Life Around Data 博客上。照片由 Unsplash 上的 伊洛娜·弗罗利希 拍摄。
法律确定性和法庭上计算机决策的可能性
薇薇安·林登伯格(2018)撰写,作为阿姆斯特丹自由大学的法学学士论文。
电脑会接管我们的工作吗?在工业革命期间,许多人担心自动化会导致依赖体力劳动的行业出现大规模失业。更复杂的机器人和人工智能的发展带来了类似的讨论,只是现在智力工作也处于危险之中。这包括法律行业的许多职业,如律师和法官(维穆伦&芬威克,2017)。然而,荷兰司法系统似乎很有信心,他们不会很快被计算机取代。2017 年,司法委员会与 LexIQ 一起做了一项实验。两起案件由一名法官和一个计算机程序进行了评估。在这两种情况下,计算机得出的结论都不如法官满意(De Rechtspraak,2017b)。尽管如此,人工智能在未来变得越来越重要似乎是不可避免的。荷兰公诉机关使用软件 BOS-Schade 来帮助评估不太严重的刑事案件中的损失金额。这是一种已经在使用的计算机辅助的基本形式(Openbaar Ministerie,2018 年 6 月 11 日访问)。另一方面,人们仍然担心自动化系统的使用会侵犯人们的权利。例如,见最近生效的《一般数据保护条例》第 22 条:个人有权"不受制于仅基于自动处理的决定(《一般数据保护条例》,第 22 条)。
荷兰法律体系中一个重要的基本价值观是法律确定性。它认为政府的行为应该是可预测的。然而,这并不意味着法官总是必须严格遵守法律条文。有时,立法者故意留下解释的空间,甚至允许法官无视法律条文,以确保合理和公平的结果。
计算机的一个重要优势是它们似乎能够做出中立的决定。这意味着,从法律确定性的角度来看,计算机的裁决将优于人类法官的裁决。本论文的主要问题是:“从法律确定性的角度来看,在荷兰法律体系中,计算机应该在多大程度上做出法律决定?”。要回答这个问题,一些关键问题将是:计算机真的使司法判决对公民来说更确定吗?法律的确定性总是可取的吗?
这篇论文的结构如下。我将首先介绍计算机已经在司法系统中使用的一些方式。接下来,我将探讨法律确定性的一个重要特征:一致的决策。然后,我将着眼于一个更实际的方面:偏见会破坏法律的确定性,即使这些偏见是一贯适用的。之后,我将着眼于法律确定性的一个更为主观的方面:公民眼中的决策透明度。对于每个主题,我将试图确定计算机对法律确定性的影响。最后,我会给出一个结论,并参考未来可能的研究。
1.当前状态
1.1 决策和计算机系统
法官的目标取决于他们处理的案件类型。在刑法中,目标是发现真相,然后找到适当的应对措施。另一方面,在民法中,目标是解决争端。这通常涉及查明事实真相,但当事方也有可能构建自己的法律现实。法官的任务是根据双方提出的事实来解决冲突,而不是查明无争议的主张是否属实。“真相”不是可以确定的东西。一般来说,法官会假定某些事实是真实的,并根据这些事实做出判决。
计算机可以以许多不同的方式帮助做出法律决定。最重要的区别在于计算机在多大程度上接管了决策过程。Noortwijk & De Mulder 描述了法律系统中计算机系统的三个等级。法律技术 1.0 是旨在仅仅帮助人类做决定的系统。这方面的一个例子是用于法律研究的链接数据系统。法律技术 2.0 接管了很大一部分决策过程,而人类仍然拥有最终发言权。自动起草合同就是一个例子。最后,法律技术 3.0 完全接管了这一过程,因为系统可以在没有人类帮助的情况下自主做出决定(Noortwijk & De Mulder,2018,第 10 段)。5).
这种区别并不表明具体使用什么方法来实现目标。一个显而易见的方法是大数据分析,比如数据挖掘。通过使用数据挖掘算法,可以在大量数据中寻找模式(Calders & Custers,2013 年,第 28、32 页)。
1.2 示例
荷兰司法系统已经在尝试各种数字技术。数字诉讼正逐渐成为强制性的,最近甚至有一项实验将计算机的司法判决与人类法官的判决进行了比较(Heemskerk & Teuben,2017 年,第 10 章;2017 年 b)。在一份报告中,司法委员会陈述了试验“智能技术”的重要性,尽管是小心翼翼的小规模试验(De Rechtspraak,2017a,第 44-45 页)。
在荷兰之外,也使用法律技术。例如,在美国,风险评估工具 COMPAS 除了其他用途之外,还被用作刑事案件判决的辅助工具。诸如药物滥用、犯罪态度和先前的犯罪参与等变量被用来确定一个人是高风险还是低风险。然后法官可以在量刑时使用这个分数(Equivant,2017 年,第 4 章)。在量刑中使用这一工具并非完全没有争议,因为它可能对黑人被告有偏见(Angwin,Larson,Mattu & Kirchner,2016)。
在荷兰,法官目前尚未将大数据用于决策,但越来越多的数据正在数字系统中收集,这为未来使用大数据分析奠定了基础(De Rechtspraak,2017a,第 44–45 页)。除了数字系统的实际应用,预测法律判决的研究也在进行中。来自英国的研究人员试图通过使用自然语言处理和机器学习来预测欧洲人权法院的决定。在预测是否会出现侵犯人权的情况时,这些预测达到了 79%的准确率。分析文本以做出预测的方法似乎是有效的,尽管研究人员并不打算设计一个可以完全接管法官工作的系统(阿勒特拉斯,Tsarapatsanis,Preoţiuc-Pietro 和兰波斯,2016 年)。
除了司法系统中的技术,许多律师事务所也在探索计算机可以提供的可能性。律师事务所正在开发软件,以便与客户进行更多的互动,并使律师的日常工作变得更容易。使用计算机系统可以提高效率,通常受到客户的重视(Rietbroek,2017)。
2.一致的决策
2.1 界定法律确定性
法律确定性的早期描述可以追溯到公元前 350 年左右,在亚里士多德的著作中。他描述说,由法律裁决比根据个案做出决定更好,因为“法律是经过长时间考虑后做出的,而法院的决定是在短时间内做出的,这使得那些审理案件的人很难满足正义的要求”(斯坦福哲学百科全书,2016 年,第 101 段)。3.1).洛克在他的社会契约理论的背景下描述说,无论是谁掌权“都必然要按照既定的、颁布的、为人民所知的法律来治理,而不是通过临时的法令”(洛克,1689 年,第 111 段)。131).拉德布鲁赫描述了法律的三个核心价值:正义、权宜和法律确定性。法官适用法律最重要的价值是法律确定性,因为法律秩序的存在比法律秩序中的正义和权宜更重要。然而,这三个值都很重要,它们之间应该有一个平衡(Radbruch,(1932) 1950,第 108-111 页,第 118-119 页)。
法律确定性原则包含许多不同的要求,如:政府只应在法律范围内行事,法官应独立,法律应公开。此外,法官应当适用法律,而不应当根据个人意见作出判断(斯坦福哲学百科全书,2016 年,第 10 段)。5.1–5.2;Van Ommeren,2003 年,第 7-22 页;Scheltema,1989 年,第 10 段。2.1).然而,不遵守法律可能有重要的原因。保护基本权利有时意味着绕过国家法律。这可以被视为对法律确定性原则的明显侵犯,因为法律没有得到遵守。另一方面,公民最有可能期望法律的解释能够保证他们的基本权利。无论法律如何适用,始终以某种程度上一致的方式来实现这一点是很重要的(Soeteman,2010 年,第 10 段)。7.6).德沃金将法律中的这种稳定性称为“完整性”。以一致的方式应用法律使社区具有凝聚力,因为它在整个社区中创造了连续性,并且随着时间的推移也是如此(德沃金,1986 年,第 225-275 页;马莫,2012 年,第 9 章)。
总而言之,法律确定性是一种针对国家公民的价值观:它提供了针对政府权力的保护,并诱导对政府的信任,从而使制度更加稳定。这种价值的客观方面是,法律制度应该永远得到遵守;决定不应该随意做出。还有一个主观成分:公民应该有一种感觉,他们的政府是值得信任的(斯坦福哲学百科全书,2016 年,第 10 段)。5.1).
2.2 实现法律确定性
尽管荷兰没有具有法律约束力的先例制度,但由于上诉和翻案制度,法律通常会得到统一解释。当最高法院以某种方式解释规则时,下级法院通常会遵循这种方式(Soeteman,2010,第 46–47 页)。这意味着,一般来说,每个法官在每个案件中都会以同样的方式适用法律。然而,并非在法律的每一个领域都有对案件进行上诉的惯例,即使在司法系统的较高层次,法官在类似案件中适用法律的方式也可能略有不同。在某些情况下,法律是非常明确的,法官将很可能在每个案件中得出相同的结论。在这些案件中有很高的确定性:如果你只是严格遵守法律,结果是显而易见的。对于这些类型的情况,决定是由人还是由计算机做出并不重要,尽管后者会更有效(如果广泛使用的话)。同样,在有些情况下,法律模糊不清,但结果却非常清楚。以刑事案件中的证据标准为例。根据《荷兰刑事诉讼法》第 338 条,只有在法官根据现有证据确信嫌疑人有罪的情况下,才能认定嫌疑人有罪。除此之外,认定嫌疑人有罪的门槛相当低:一般来说,两件证据就足够了。尽管第 338 条的标准是模糊的,但在个别情况下,结果可能非常清楚。例如:当有许多证人时,嫌疑人被当场抓住,手里拿着一把带血的刀,并且承认了罪行,每个人都可能得出相同的结论,即嫌疑人就是犯下谋杀罪的人。
当证据不明确时,事情就变得更复杂了。那么,类似的案件完全可以区别对待,因为两种选择(有罪或无罪)都同样有利。当在一个问题上意见分歧时,一个可能的妥协是随机决定某人是否有罪。实际上,这种情况下的结果或多或少确实是随机的。德沃金谈到了“棋盘式”解决方案:由于意见分歧,法律对类似的案件采取不同的处理方式。在大多数情况下,人们会本能地认为这些类型的解决方案是不公平的(德沃金,1986 年,第 179-183 页)。判决也是如此。从法律确定性的角度来看,最好是每一项决定都是一样的,不管这项决定是比另一项选择稍微好一点还是差一点。
当法官在相同的案件中可以得出两种不同的结论时,当输入相同时,计算机总是给出相同的结论。每个人类法官都以独特的方式做出决定,计算机系统可以很容易地被复制。这意味着通过使用后者,可以在全国范围内采用相同类型的决策,并且可以在整个时间内保持稳定。
2.3 法律确定性的限制
然而,法律的确定性总是可取的吗?一些哲学家公开反对严格遵守规则。例如,柏拉图曾说,这种做法“就像一个顽固、愚蠢的人,他拒绝允许对自己的规则有丝毫的偏离或质疑,即使事实上情况已经改变,事实证明违反这些规则对某人来说更好”(斯坦福哲学百科全书,2016 年,第 10 段)。7).甚至拉德布鲁赫也认为,虽然原则上必须遵循实在法,但当法律极不公正时,它就不能被视为有效(Bix,2013 年,第 10 段)。5.2).在个别案件中,严格执行法律可能会导致不公正的结果。尽管法律确定性是荷兰法律体系最重要的价值观之一,但灵活性也很重要。正如导言中已经提到的,立法者有时故意在法律中留有解释的余地,基本权利保护可以影响法律在个案中的适用,即使法律非常具体。后者可被视为法律确定性的一种形式,即无论如何,特定的一般原则总是受到保护。另一方面,它可以被视为是不确定的,因为针对这种情况的具体法律并不适用。基本权利原则上在某种程度上是不变的,因此从一致性的角度来看,遵循前一种解释是有意义的。然而,在现实中,即使是基本权利也会随着社会的变化而变化,其适用也并非总是没有争议。此外,在这些权利通常表述模糊的情况下,下级立法往往更加具体,从而使公民更加确定如何在他们的具体情况下适用法律。
尽管偏离特定法律对法律确定性没有好处,但它可能会导致更公正的结果。刑法中的一个例子说明了在个别案件中背离法律的重要性,这就是" Huizense Veearts "案的判决。在这种情况下,一名兽医将健康的奶牛与感染口蹄疫的奶牛接触,根据当时适用的法律,这是刑事犯罪。然而,兽医故意应用这种(科学上可接受的)方法,以保护动物在未来不会得重病。这项法律制定时显然没有预料到这种可能性。最高法院最终判决惩罚兽医是不公正的,因为他的行为符合法律的根本目标,尽管法律明确禁止这些行为(最高法院,1933 年 2 月 20 日)。
计算机很可能会“盲目”应用法律,而不考虑法律所基于的原则。通常,这将导致正确的结果。然而,在这样的例子中,它会导致不可接受的结果。这是因为立法者很难预测未来会发生什么情况。此外,基于统计分析得出结果的计算机系统可能无法处理异常值:非典型的情况(Custers,2016 年,第 15-16 页)。法官的一个重要职能是在不同的个案中适用法律,甚至在一定程度上创造新的法律(Bovend’Eert,2011 年,第 32-40 页)。对计算机的严重依赖可能意味着离群值将被不公正地视为平均案件,法律发展将会停止。
3.不必要的偏见
3.1 消除不必要的偏见
仅仅一贯地适用法律并不意味着歧视是不可能的。一个法律中嵌入了某种形式歧视的系统仍然是确定的,只要它不是随机适用的(斯坦福哲学百科全书,2016 年,第 102 段)。5.3).在荷兰,根据《宪法》第 1 条,法律禁止歧视,尤其是当歧视涉及性别、种族和宗教等因素时。因此,这些因素不应在法律决策中发挥作用,除非有具体的法律允许这样做。
计算机自动化肯定会导致更一致的判断,即使它们包含不必要的偏见。然而,从法律确定性的角度来看,判决中的差异应基于案件之间司法相关的差异。没有一个好的理由来区分群体是武断的,因此是不确定的(斯坦福哲学百科全书,2015 年,第 10 段)。4.1).使用计算机代替人类法官的一个重要原因是为了消除不必要的偏见。例如,根据美国的多项研究,某些种族的人在刑事案件中被判的刑罚更高,更有可能被判死刑(C. O’Neil,2016,第 29 页)。在荷兰,关于种族偏见的存在一直存在争议。研究表明,不同种族群体之间的量刑差异在很大程度上可归因于司法相关情况(De Rechtspraak,2016 年)。尽管如此,任何司法系统中一个迫在眉睫的危险是,法官让本应无关紧要的因素在他们的决策过程中发挥作用(尽管是无意识的)。人类容易受到各种偏见的影响,而计算机原则上是中立的。他们根据预先编程的算法做出可预测的决策。因此,如果一个变量,如种族,不应该在决策过程中发挥作用,它可以简单地不输入系统。然后,该系统将对这一因素视而不见(这假设也没有以间接方式指示人的种族的变量,例如区分某些街区或家庭背景)。然而,Harcourt 提到,在美国,大约从 20 世纪 70 年代开始,将种族作为风险分析的一个因素在政治上不再正确,然而该系统仍然具有歧视性(Harcourt,2010 年,第 4 页)。在使用大数据时,会有系统用来进行计算的预定因素。只要清楚系统使用的是什么因素,这将是保护司法系统免受不必要的偏见的有效方式。
3.2 隐藏的偏见
尽管计算机可能是对抗不必要的偏见的解决方案,但它在很大程度上取决于系统是如何设计的。计算机系统将由人类设计,并使用现有数据,这可能意味着它只是复制现有的偏见,而系统似乎是中立的。
即使算法本身没有偏差,插入的数据也可能导致不想要的结果。如果现状中的法官有歧视,系统使用的数据就会有偏差。奥尼尔描述说,在美国,风险模型在全国范围内被用来(除其他外)提高一致性和打击个别法官的偏见。这些模型用于确定罪犯的预期再犯率。这些特定模型使用与所犯罪行相关的信息,以及与犯罪者相关的因素。利用一个人的特征可能是一种歧视性的做法。即使该系统看起来没有偏见,因为它根据早期的信息做出一致的决定,但它实际上是歧视性的(O’Neil,2016,第 29-30 页)。
calders & lio bait 指出,仅仅消除一个特定的歧视性变量并不一定能解决问题,因为可能还有其他变量与之相关,因此结果是一样的。然而,有办法通过纠正数据来消除歧视。另一种可能出现问题的方式是使用不正确或不完整的数据。如果数据的选择性部分缺失,将会导致不良后果(Calders & lio bait,2013 年,第 10 段)。3.4.1–3.4.2).
3.3 检测偏差
尽管计算机化的决策不会自动减少偏差,但大数据可以用来使原本隐藏的偏差变得可见(Pedreschi,Ruggieri & Turini,2013 年,第 100-101 页)。然而,通过研究现有的判例法来发现偏见是极其困难的。因为没有一个案例是完全相同的,很多事情总是可以归因于案例中的具体事实。即使计算机程序和普通法官一样有偏见,计算机似乎也比人更容易测试。一个可能的选择是让系统判断一个假案件,特别是设计来检查它是否对某一群人有不合理的偏见。例如,可以进行一项测试,对两个相同的刑事案件进行判决,只是一个案件中的嫌疑人是女性,另一个是男性。如果系统偏向于某一性别,其中一名嫌疑人更有可能被判有罪,或者比另一名嫌疑人获得更高的刑期。要发现人类法官的这一点要困难得多。首先,人类将很可能知道自己正在接受测试,并将(有意识或无意识地)相应调整自己的行为。第二,如前一段所述,每个法官都可能做出略微不同的决定,因此需要大量的参与者来对总体做出一些结论。与法官不同,如果结果不理想,计算机系统可以直接调整。所使用的模型可以简单地改变。
4.透明性
4.1 预编程透明度
法律确定性的一个重要因素是公民对法律的了解(因此也是法庭案件最有可能的结果)。尽管司法判决确实包含了对为什么选择某一结果的解释,但这些解释可能有些模糊。当不同的利益被权衡时,这是显而易见的。虽然会有不同利益的概述,但在大多数情况下,不清楚他们在最终决策中究竟扮演什么角色。
法律现实主义理论认为,在困难的案件中,法官将首先决定结果,随后将构建一个合适的推理。现实主义者杰罗姆·弗兰克等人认为,案件的结果在很大程度上取决于法官的个性及其与诉讼当事人的特征和案件事实的相互作用(Marmor,2012 年,第 9 章)。如果事实确实如此,法官的推理在某种程度上是无关紧要的。如果用计算机来做决策,很明显,在做最后决定之前会进行“推理”。结果总是基于案件的某些客观情况。
根据所使用的计算机程序的类型,这可能是一种非常透明的决策方式。理论上,所有的输入和系统如何处理都可以公开。因此,决策过程将比现状更加透明。
4.2 算法的保密性
尽管理论上使用计算机系统是一种透明的决策方式,但实际上它可能一点也不透明。使用计算机系统可能就像人类法官一样不透明。不同之处在于,目前是由一名法官负责裁决。当使用先进的计算机(支持)系统时,可能不清楚出错时谁负责。此外,如果人们不知道系统是如何做出决定的,他们可能不会信任系统。一个解决办法是告诉人们算法是如何工作的。国务委员会在 2017 年的一项判决中表示,(在行政法中)政府有义务公布决策方法以及决策所依据的数据和假设,以保护公民的法律地位(国务委员会,2017 年 5 月 17 日,第 10 段)。14.4).
所使用的算法通常由第三方创建。算法的确切工作方式通常是公司的秘密,被它评判的人是不知道的。保守秘密的好处是,人们将无法“玩弄系统”(奥尼尔,2016 年,第 17、29、32 页)。这将是一个危险,因为系统有时依赖于受试者直接生成的数据,如问卷(例如:[ link )。围绕透明度的另一个问题是,计算机系统可能非常复杂,可能只有直接参与创建它们的人才能理解(O’Neil,2016,第 14 页)。
一个表明缺乏透明度会造成问题的例子是电子法院。E-Court 是一家为解决消费者和某些大公司之间的纠纷提供司法程序私人替代方案的公司。原则上,所有程序都是在线的,似乎该服务使用算法来做出决定(Aanhangsel handelingen II 2017/18,1803,答案 9,on:https://zoek . officielebekendmaking en . nl/ah-tk-2017 2018-1803 . html)。这项服务因缺乏透明度而受到抨击。不清楚“法官”是谁,以前做出了什么决定,以及这些决定是如何做出的(由于受到批评,电子法院最近开始公布决定和他们的“法官”的姓名)。此外,缺乏向消费者提供的关于其权利和程序的信息。正因为如此,有人担心消费者在这类争端解决中的法律地位(Kuijpers,2018)。
4.3 激励的重要性
判断的一个重要部分,如果不是最重要的部分,是最终决定的动机。《宪法》第 121 条要求做出决定。这可以是仅仅引用裁决所依据的适用法律,也可以是进一步解释如何适用规则的广泛推理。这要看具体情况。如果一个规则是明确的,并且应该毫无例外地被执行,这就不需要太多的动机。计算机可以在没有任何人工干预的情况下做出决定,因为它只需参考适用的法律。如果一个定律更模糊,它很可能需要一个更广泛的动机,这对计算机程序来说是困难的。即使人类法官仍有最终决定权,动机也可能是一个问题。计算机可能会对案件的某一特定方面做出判断,然后法官会对所有其他因素进行权衡。如果法官(部分)遵循计算机的决定,那么就很难解释确切的原因,除非计算机说这是基于所用算法的正确反应。尽管决策可能非常确定,但如果人们不知道在他们的个人情况下是如何做出决策的,那么这些决策就会显得很随意。
5.结论
本论文的主要问题是:“从法律确定性的角度来看,在荷兰法律体系中,计算机应该在多大程度上做出法律决定?”。
法律确定性是荷兰法律制度的重要价值。总有一定程度的不确定性,这在个别情况下可能是件好事。然而,一般来说,法官在作出裁决时最好不要偏离法律。法律确定性的第一个方面是一致性。尽管上诉和翻案制度确实有助于法律的适用更加统一,但下级法院仍然可能存在差异,因为并非所有案件都进入上诉程序,最高法院可能以不同方式判决两起类似案件。使用计算机辅助决策肯定会使法律的应用更加一致。另一方面,它也可能减缓法律发展,并可能在具体案件中导致不公正的结果。
偏见的存在是一个更实际的问题,会损害法律的确定性。根据所使用的计算机系统的类型,它可以对抗歧视,因为单个法官的偏见将不再是一个问题。尽管在设计系统时必须小心谨慎:如果数据有偏差,可能会导致不希望的结果。分析现有数据可能有助于发现偏见,从而消除偏见。
法律确定性的另一个重要方面是透明度。公民必须了解法律以及如何适用法律。当人类法官作出决定时,有不止一种方法来得出这个决定。尽管判断包含动机,但动机之外的其他因素也很可能发挥了作用。在这方面,人类的决策过程是非常不透明的。一个设计良好的计算机系统可以提供高度的透明度,因为决策过程是由算法规定的,理论上可以向公众公开。计算机的一个困难是激发判断。即使人类法官继续参与,他们也很难解释(甚至理解)算法是如何做出决定的,因为这些算法可能非常复杂。
从法律确定性的角度来看,使用计算机做出法律决定肯定有一些有前途的可能性。然而,需要注意的是,数据可能会有偏差,判断动机的重要性不应被低估。此外,法律的某些领域对计算机来说比其他领域更难掌握。对于不需要太多解释的基本定律,使用计算机可能是一个结果。有待解释的开放规范和需要快速适应时间的法律将需要更先进的计算机程序来应用它们。目前,在这些复杂的法律领域中,人的参与是必不可少的,尽管计算机在任何类型的案件中都是有用的助手,即使它们只是用来寻找适用的判例法。
6.讨论
值得注意的是,尽管从法律确定性的角度来看,计算机的参与肯定是有益的,但这个问题还有许多其他方面。例如,在决策过程中使用计算机可能有经济上的原因。然而,至关重要的是,不能仅仅为了效率而损害法律制度最重要的价值观。
为了在司法系统中实施技术,法律专业和算法开发人员之间需要进行强有力的合作。当司法系统开始使用计算机程序作为量刑的辅助手段时,法官必须知道结果意味着什么,从而知道如何在他们的判决中使用它。此外,系统的设计必须符合法律制度的要求,例如,不应有不合理的歧视(即使这可能是有效的)。
此外,研究立法过程能在多大程度上适应技术进步也是很有意思的。如果法律能够以一种更容易在计算机系统中实施的方式来制定,那么在计算机的帮助下根据这些法律做出决定也就不会那么复杂了。
尽管计算机在短期内完全接管司法系统的可能性极小,但看看先进技术在法律方面的机遇还是很有趣的,即使它们只是用作辅助手段。为了有效地实施这种类型的决策支持,不仅有必要探索法官在其案件中使用该技术的最佳方式,而且有必要为这些技术进步培养未来的法律专业人员。
来源
**Aletras,Tsarapatsanis,preoţiuc-pietro&Lampos 2016—**n . aletras,D. Tsarapatsanis,D. Preoţiuc-Pietro &诉 lampos,“预测欧洲人权法院的司法判决:自然语言处理视角”,PeerJ Comput。Sci。2:e93 2016。
安格温,拉森,马特图&基什内尔 2016 — J .安格温,j .拉森,s .马特图& L .基什内尔,《机器偏见:全国各地都有用来预测未来罪犯的软件。而且对黑人有偏见。,ProPublica 2016 年 5 月,https://www . ProPublica . org/article/machine-bias-risk-assessments-in-criminal-pending
Bix 2013 — B.H. Bix,“拉德布鲁赫公式、概念分析和法治”,载于:Flores I .、Himma K. (eds) 法律、自由和法治,Ius Gentium:法律和司法的比较观点,第 18 卷,多德雷赫特:Springer,2013 年。
**bo vend ’ eert 2011—**p . bo vend ’ eert,Dient de rechtsvormende taak van de Hoge Raad versterkt te worden bij de herziening van het cassatiestelsel?,in: A. Nieuwenhuis e.a .(红色。), Rechterlijk activisme。J.A. Peters 先生教授的意见,奈梅亨:Ars Aequi Libri 2011,第 31–41 页。
**Calders&Custers 2013—**t . Calders&b . Custers,“什么是数据挖掘,它是如何工作的?”B. Custers,T. Calders,B. Schermer,T. Zarsky(编辑),信息社会中的歧视和隐私:大型数据库中的数据挖掘和分析,第 2 章,柏林:Springer,2013 年。
**Calders&lio bait 2013—**T. Calders&I . lio bait,“为什么不带偏见的计算过程会导致歧视性决策程序”,载于:B. Custers,t . Calders,B. Schermer,T. Zarsky(编辑),《信息社会中的歧视和隐私:大型数据库中的数据挖掘和分析》,第 2 章,柏林:Springer,2013 年。
**Custers 2016—**b . h . m . Custers,“wetenschappelijk onderzoek 中的大数据”, JV 2016/01,第 8–21 页。
德沃金 1986 — R .德沃金,劳氏帝国,麻省剑桥。:哈佛大学出版社 1986 年。
Equivant 2017 — Equivant,“COMPAS Core 从业者指南”,2017 年 12 月。
哈科特 2010 — B. E .哈科特,“风险作为种族的代理”,法律&经济学工作论文第 535 号,2010。
希姆斯克尔克&条顿 2017 — W .希姆斯克尔克& K .条顿,科特·贝格里普·范·KEI,多德雷赫特:2017 年护航队。
**Kuijpers 2018—**k . Kuijpers,“电子法院 zwicht voor kritiek”,Investico 2018,https://www . platform-Investico . nl/artikel/E-Court-zwicht-voor-krit iek/
洛克 1689 — J .洛克,1689,两部政府论,p .拉斯莱特(编。),剑桥:剑桥大学出版社,1988 年。
Marmor,2012 — A. Marmor,The Routledge companies to The Law Philosophy,第 9 章,阿宾登:Routledge 在线手册,2012 年。
**Noortwijk&De Mulder 2018—**k . Noortwijk&r . De Mulder,’ Computerrecht,Het rijpingsprocess van juridische technologie ', Computerrecht 2018/50。
范·奥米伦 2003 — 范·奥米伦,《法律与实践》,载于:范·奥米伦& S.E. Zijlstra(红色。),De rechtstat als toetsingskader,海牙:Boom Juridische uitgevers 2003,第 7-22 页。
Openbaar Ministerie,2018 年 6 月 11 日访问— Openbaar Ministerie,’ BOS-Schade ',https://www . om . nl/onderwerpen/slacht offers/BOS-Schade/BOS-Schade-0/
奥尼尔 2016 — C .奥尼尔,数学毁灭的武器:大数据如何增加不平等并威胁民主,纽约:皇冠出版集团 2016。
**Pedreschi,Ruggieri&Turini 2013—**d . Pedreschi,S. Ruggieri & F. Turini,《歧视的发现》,载于:B. Custers,T. Calders,B. Schermer,T. Zarsky (eds),《信息社会中的歧视和隐私:大型数据库中的数据挖掘和分析》,第 2 章,柏林:Springer,2013 年。
拉德布鲁赫(1932 年)1950 年法律哲学,收录于:《拉斯克、拉德布鲁赫和达宾的法律哲学》,第 43–224 页。剑桥:哈佛大学出版社 1950。
〔t0〕〔2016 年第一次审判〕〔不同但同样受到惩罚〕〔2016 年第二次〕〔https://www . justice . com/组织/联系/组织/法律顾问/新闻/网页/其他
**2017 a—**法庭,“在法治之家建设:2030 年情景规划法庭”,2017 年法庭建议。
**法庭 2017 b)**法庭,“审判日:计算机与法官”,2017,https://www . court . com/组织/联系/组织/咨询/诉讼前/新闻/页面/日
2017 年直条裤子— 。“大律师与新技术创新”,2017 年律师杂志,T12 版" http://advocationmagazine . sdu . nl/2017 年 9 月-2011 年版"!《律师与法律技术》T13
原理图 1989 — 。"法治"的模式,在英语 e.a. (red)中。法治回顾,肿胀:w . j .检查威尔林克 1989。
第一季第 10 集。苏特曼、关于权利的介绍,,第 4 和第 10 章,奈梅亨:埃奎利 2010 年。
2015 年《斯坦福哲学百科全书》【2015 年 8 月《斯坦福哲学百科全书》、《歧视》】【t26 https://Plato .斯坦福. edu/entries/歧视】
**2016 年《斯坦福哲学百科全书》**2016 年《斯坦福哲学百科全书》、《法律规则》,t30《https://Plato .斯坦福. edu/entry/法律规则》
**&芬威克 2017】**p . m . vermeel&的缩写。芬威克,《技术革命与法律的未来》,T34 年第 35 期《最高法院》2017 年(5 期)378 期。
案例法:
国务委员会 2017 年 5 月 17 日**——**国务委员会,2017,ECLI:NL:RVS:2017:1259。
最高法院,1933 年 2 月 20 日— 最高法院,1933 年 2 月 20 日, NJ 1933/918 (Veearts)。
2018 年 6 月 11 日访问了所有在线来源。
顶部的横幅图片是由 FlitsArt。所有其他图片来自 pixabay.com。