深度神经网络:如何定义?
正确定义什么使神经网络变得更深很重要。这篇文章提出了深度神经网络的另一种定义。
Jonathan Borba 在 Unsplash 上拍摄的照片。
现在人工智能(AI)的成功,基本上是得益于深度学习(DL)及其相关模型。DL 是机器学习(ML)的一个子领域,其中一组算法试图利用几个处理层对高级数据抽象进行建模,其中每种类型的层都有特定的用途。
然而,深度神经网络(DNNs),如深度卷积神经网络(CNN),是基于多层感知器(MLP),这是一类已经使用了相当长时间的前馈人工神经网络,甚至在 1989 年第一个 CNN 出现之前。因此,问题来了:什么时候一个模型/网络被认为是“深”而不是“浅”?
浅层 x 深层神经网络
传统上,浅层神经网络(SNN)是具有一个或两个隐藏层的网络。因此,深度神经网络(DNN)是具有两个以上隐藏层的网络。这是最被接受的定义。下面,我们展示了一个 SNN 和 DNN 的例子(隐藏层是红色的)。
图片作者。
但是,只考虑隐藏层的数量是一个好方法吗?正确定义什么使一个模型变得深刻,对于明确理解这个概念是很重要的,特别是从行业采用 DL 的角度来看。在这篇文章中,我们提出了 DNNs 的另一种定义。我们建议,被视为“深度”的模型不仅应考虑隐藏层的数量,还应考虑:
a.)考虑训练和/或评估它所需的时间;
b.)考虑到计算平台(例如 GPU)方面的需求。
经典基准数据集(如 CIFAR-10、ImageNet)和标准环境/平台(Google Colab、kaggle)可用于获得这些度量。为了支持对另一个定义的需求,我们将使用两个传统数据库:修改后的国家标准与技术研究所( MNIST )和 CIFAR-10 。
MNIST 数据库
关于 MNIST 数据库,我们考虑了三个神经网络:Aviv Shamsian 的 SNN500、T2 的 CNN3L、T4 的 Nutan、T5 和 Bolla Karthikeya 的 LeNet-5。SNN500 是一个包含 500 个神经元的单隐层 SNN。CNN3L 也被认为是 SNN,因为它有两个隐藏(卷积)层和输出层。第三个网络是经典的 LeNet-5,有五层,其中四层是隐藏层。让我们看看代码(访问这里)。
图片作者。
**import** **torch**
**import** **torch.nn** **as** **nn**
**import** **torchvision.datasets** **as** **dsets**
**import** **torchvision.transforms** **as** **transforms**
**from** **torch.autograd** **import** Variable
**from** **prettytable** **import** PrettyTable
**import** **matplotlib.pyplot** **as** **plt**
**import** **time***# This function obtains the number of trainable parameters of the*
*# model/network.*
**def** count_parameters(model):
table = PrettyTable(["Modules", "Parameters"])
total_params = 0
**for** name, parameter **in** model.named_parameters():
**if** **not** parameter.requires_grad: **continue**
param = parameter.numel()
table.add_row([name, param])
total_params+=param
print(table)
print(f"Total trainable params: **{**total_params**}**")
**return** total_params*# Just visualising some images*
**def** visualise_images(img, lab, t):
fig = plt.figure()
**for** i **in** range(6):
plt.subplot(2,3,i+1)
plt.tight_layout()
plt.imshow(img[i][0], cmap='gray', interpolation='none')
plt.title("**{}** - class: **{}**".format(t,lab[i]))
plt.xticks([])
plt.yticks([])
现在,我们定义类的数量和一些超参数。请注意,批量大小为 100。
num_classes = 10 *# Number of output classes, discrete range [0,9]*
*# Hyper-parameters*
num_epochs = 20 *# Number of epochs*
batch_size = 100 *# The size of input data took for one iteration*
lr = 1e-3 *# Learning rate*
我们下载并处理 MNIST 数据集。
*# Downloading MNIST dataset*
train_data = dsets.MNIST(root = './data', train = **True**,
transform = transforms.ToTensor(), download = **True**)
test_data = dsets.MNIST(root = './data', train = **False**,
transform = transforms.ToTensor())
print('#'*20)
print('Training dataset: ', train_data)
print('Test dataset: ', test_data)*# Wrap an iterable around the dataset to enable easy access to the samples.*
train_gen = torch.utils.data.DataLoader(dataset = train_data,
batch_size = batch_size,
shuffle = **True**)
test_gen = torch.utils.data.DataLoader(dataset = test_data,
batch_size = batch_size,
shuffle = **False**)
device = torch.device("cuda:0" **if** torch.cuda.is_available() **else** "cpu")
print('Device is: ', device)
只是快速浏览一下训练数据集。
batch_train = enumerate(train_gen)
batch_idx, (batch_train_data, batch_train_classes) = next(batch_train)
print('One batch - training dataset:', batch_train_data.shape)
print('**\n**Each image of the batch:')
**for** i **in** range(batch_train_classes.shape[0]):
print('Image: **{}** - Input shape: **{}** - Class: **{}**'.format(i, batch_train_data[i].shape, batch_train_classes[i]))
**if** i == (batch_train_classes.shape[0]-1):
print('The "image" itself: ', batch_train_data[i])
visualise_images(batch_train_data, batch_train_classes, 'Training')
注意一批的形状是:[100,1,28,28]。这意味着一批 100 个图像,一个通道(MNIST 的图像是灰度的),每个图像的尺寸为 28 x 28 像素。现在,我们定义三个神经网络。基于测试数据集的准确性来测量它们的性能。
**class** **SNN500**(nn.Module):
**def** __init__(self, input_sz, hidden_sz, num_clas):
super(SNN500,self).__init__()
self.fc1 = nn.Linear(input_sz, hidden_sz)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(hidden_sz, num_clas)
**def** forward(self,x):
out = self.fc1(x)
out = self.relu(out)
out = self.fc2(out)
**return** out**class** **CNN3L**(nn.Module):
**def** __init__(self, num_clas):
super(CNN3L, self).__init__()
self.conv1 = nn.Sequential(
nn.Conv2d(
in_channels=1,
out_channels=16,
kernel_size=5,
stride=1,
padding=2,
),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2),
)
self.conv2 = nn.Sequential(
nn.Conv2d(16, 32, 5, 1, 2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2),
)
*# Fully-connected layer*
self.out = nn.Linear(32 * 7 * 7, num_clas)
**def** forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
*# Flatten the output of conv2 to (batch_size, 32 * 7 * 7)*
x = x.view(x.size(0), -1)
output = self.out(x)
**return** output**class** **LeNet5**(nn.Module):
**def** __init__(self, num_clas):
super(LeNet5, self).__init__()
*# Convolution*
self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, stride=1, padding=2, bias=**True**)
*# Max-pooling*
self.max_pool_1 = nn.MaxPool2d(kernel_size=2)
*# Convolution*
self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5, stride=1, padding=0, bias=**True**)
*# Max-pooling*
self.max_pool_2 = nn.MaxPool2d(kernel_size=2)
*# Fully-connected layers*
self.fc1 = nn.Linear(16*5*5, 120) *# convert matrix with 16*5*5 (= 400) features to a matrix of 120 features (columns)*
self.fc2 = nn.Linear(120, 84) *# convert matrix with 120 features to a matrix of 84 features (columns)*
self.fc3 = nn.Linear(84, num_clas) *# convert matrix with 84 features to a matrix of 10 features (columns)*
**def** forward(self, x):
*# Convolve, then perform ReLU non-linearity*
x = nn.functional.relu(self.conv1(x))
*# Max-pooling with 2x2 grid*
x = self.max_pool_1(x)
*# Convolve, then perform ReLU non-linearity*
x = nn.functional.relu(self.conv2(x))
*# Max-pooling with 2x2 grid*
x = self.max_pool_2(x)
*# First flatten 'max_pool_2_out' to contain 16*5*5 columns*
x = x.view(-1, 16*5*5)
*# FC-1, then perform ReLU non-linearity*
x = nn.functional.relu(self.fc1(x))
*# FC-2, then perform ReLU non-linearity*
x = nn.functional.relu(self.fc2(x))
*# FC-3*
x = self.fc3(x)
**return** x
现在,我们可以选择其中一个模型/网络进行训练和评估。我们还定义了损失函数、优化器,并显示了所选模型的可训练参数的数量。
loss_function = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(net.parameters(), lr=lr)
print('Checking trainable parameters: **{}**'.format(count_parameters(net)))
现在,我们可以训练模型了。
train_losses = []
train_acc = []
train_time_init = time.time()
**for** epoch **in** range(num_epochs):
net.train()
running_loss = 0.0
running_corrects = 0
**for** images,labels **in** train_gen: *# Iterate over data: begin*
**if** opt == '1':
images = Variable(images.view(-1,28*28)).to(device) *# Send to GPU*
**elif** (opt == '2') **or** (opt == '3'):
images = Variable(images).to(device) *# Send to GPU*
labels = Variable(labels).to(device) *# Send to GPU*
optimizer.zero_grad()
**with** torch.set_grad_enabled(**True**):
outputs = net(images)
_, preds = torch.max(outputs, 1)
loss = loss_function(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item() * images.size(0)
running_corrects += torch.sum(preds == labels.data)
*# Iterate over data: end*
epoch_loss = running_loss / len(train_data)
epoch_acc = running_corrects.double() / len(train_data)
print('Epoch [**%d**/**%d**], Loss: **%.4f**, Accuracy: **%.4f**'
%(epoch+1, num_epochs, epoch_loss, epoch_acc))
train_losses.append(epoch_loss)
train_acc.append(epoch_acc)
train_time_end = time.time() - train_time_init
在推理阶段,我们测量所选模型的性能。
am_training = net.training
print('Am I training? ', am_training)
net.eval()
am_training = net.training
print('Am I training? ', am_training)
inference_loss = 0.0
inference_corrects = 0
infer_time_init = time.time()
**with** torch.no_grad():
**for** images,labels **in** test_gen: *# Iterate over data: begin*
**if** opt == '1':
images = Variable(images.view(-1,28*28)).to(device) *# Send to GPU*
**elif** opt == '2' **or** opt == '3':
images = Variable(images).to(device) *# Send to GPU*
labels = labels.to(device) *# Send to GPU*
outputs_infer = net(images)
_, preds_infer = torch.max(outputs_infer,1)
loss_infer = loss_function(outputs_infer, labels)
inference_loss += loss_infer.item() * images.size(0)
inference_corrects += torch.sum(preds_infer == labels.data)
*# Iterate over data: end*
final_inference_loss = inference_loss / len(test_data)
final_inference_acc = inference_corrects.double() / len(test_data)
infer_time_end = time.time() - infer_time_init
print('**\n**Training and inference in **{:.0f}**m **{:.0f}**s OR **{:.0f}**s'.format(
(train_time_end + infer_time_end) // 60,
(train_time_end + infer_time_end) % 60,
train_time_end + infer_time_end))
print('**\n**Loss of **{}**: **{:.4f}**'.format(opt_name, final_inference_loss))
print()
print('Accuracy of **{}**: **{:.4f}**'.format(opt_name, final_inference_acc))
因此,我们将三个模型中的每一个运行 20 个时期,并测量所需的总时间(train_time_end + infer_time_end)。注意总时间只包括这两个阶段)以秒为单位,如下所示。时间和准确度是三次运行的平均值。
MNIST:结果。图片作者。
请注意,即使 CNN3L 具有最少数量的可训练参数(#Train Par),它也需要更多的时间来运行(训练和评估)。CNN3L 也是所有型号中最好的(即使精度非常接近)。下面,我们展示了通过带有 PyTorch Profiler 的 TensorBoard 插件获得的 GPU 概要(此处访问)。
MNIST: GPU 总结。图片作者。
在所有模型中,GPU 利用率都非常低。不过注意,CNN3L 的 GPU 利用率最高(4.57%)。底线是,根据经典定义,SNN (CNN3L)比 DNN (LeNet-5)需要更多的运行时间,获得更高的准确性,并利用更多的 GPU。
值得一提的是,GPU 利用率越高越好。但是,由于我们在这里比较的模型具有相同的配置(相同的批量大小,相同数量的 DataLoader 构建工人等等),GPU 利用率较高的模型可能会被解释为比其他模型需要更多的计算资源。我们预计较深的模型比较浅的模型需要更高的 GPU 利用率。
CIFAR-10 数据库
CIFAR-10 数据库包含 32 x 32 幅彩色图像(三个波段)。这里,我们使用了前面三个模型中的两个:由 Nutan 开发的 CNN3L 和在训练分类器 PyTorch 教程中介绍的 LeNet-5。为了正确处理 CIFAR-10 图像,我们对这两个模型做了一些小的修改。优化器,学习率是相同的,但现在批量大小是 4,我们运行每个模型三次,10 个时期。在这里访问代码。
图片作者。
在下图(表格)中,时间和准确度是三次运行的平均值。
CIFAR-10:结果。图片作者。
现在,我们有了一个更“自然”的情况。换句话说,LeNet-5 有更多的可训练参数,需要更多的时间来运行。但是,同样,CNN3L 取得了更好的性能。通过带有 PyTorch Profiler 的 TensorBoard 插件获得的 GPU 摘要如下所示(访问此处)。
图片作者。
同样,CNN3L(浅层)比 LeNet-5(深层)显示了更多的 GPU 利用率。
结论
在这篇文章中,我们建议用另一种定义来看待 DNN 模型。我们认为,一个不仅包含隐藏层数量,而且包含训练和/或评估模型所需时间以及计算平台要求的定义可能更合适。
深度神经网络和高斯过程:相似性、差异和权衡
思想和理论
动机:比较最先进的
深度神经网络****(DNNs)****和 ***高斯过程(GPs)**是两类极具表现力的监督学习算法。当考虑这些方法的应用时,一个自然的问题出现了:“什么时候以及为什么使用一种算法比使用另一种有意义?”
希望这篇文章能为你提供一些指导,告诉你什么时候一个模型比另一个模型更好。照片由 Kristin Snippe 在 Unsplash 上拍摄
在本文中,我们将致力于制定决定使用哪种方法的指南。然而,要开始开发这些指南,我们首先需要了解这些方法之间的关系。在本文中,我们将涵盖:
- GPs 和 DNNs 的理论异同
- GPs 与 DNNs 的优缺点
- 使用 GPs 与 DNNs 的示例案例研究
我们开始吧!
*** 有关高斯过程(GPs)的详细介绍/入门知识,请参见*
(几个)理论差异
A.参数与非参数
从参数的角度来看,dnn 通常比 GPs 有更多通过学习来调整的“旋钮”。黛安·皮凯蒂诺在 Unsplash 上拍摄的照片
量化这两个模型之间差异的一个方向是考虑每个框架中参数的数量和类型。一般来说,由于高斯过程被认为是非参数机器学习技术,高斯过程(GPs) 学习明显更少的参数,并且预测很大程度上由定义它们的训练数据集驱动。他们的参数选择完全由以下选项表示:
- 核/协方差函数( k(x,x’))
- 平均函数( m(x) )
- 似然协方差噪声( σ )。
这些选择/变量通常被称为“超参数”。
在高斯过程(GPs)** 中发现的参数缺乏与许多现代深度神经网络(DNNs) 形成鲜明对比,后者旨在利用尽可能多的参数,即所谓的权重,来解决机器学习问题。在经典的统计学习文献中,使用大量的参数一直不被认可,因为这种想法会导致显著的过度拟合和对非分布数据的较差概括。然而,这种经典的统计学习理论未能解释过度参数化神经网络的经验成功,因此“过度参数化或“插值”机制的新理论开始占据上风[1,6]。**
___________________________________________________________________
TL/DR #1: GPs 为(近) 非参数,而 DNNs 为过参数。***
___________________________________________________________________
*GPs 仅通过其超参数进行参数化,例如上面找到的那些参数。
B .直接与逆方法
与参数/非参数属性相关, GPs 和 DNNs 的另一个不同之处在于 DNNs 是 逆方法 , GPs 是 直接方法 。
逆方法 涉及从训练数据中优化参数,因此也被称为监督学习方法。通常,这些方法涉及参数化,带有一些关于参数的初始信念(称为“先验”)。这些方法通常采用自上而下的方法,由此数据被用来更新参数中捕获的信念【7】。
直接方法 依赖于直接使用训练数据来进行新的预测/获得洞察力,正如大多数内核机器所完成的那样。特别是核机器有一个简单的现象,即缺少核函数本身的细节,新的预测完全由现有的可用数据驱动。由于这些方法能够进行实质性的数据探索和洞察,而不需要对底层模型产生强烈的初始信念,因此这些方法通常被称为自底向上【7】。
___________________________________________________________________
TL/DR #2: GPs 是(近) 正方法,而 DNNs 是逆方法。***
___________________________________________________________________
GPs 的超参数优化是一个间接的例程,通常使用基于梯度和 Hessian 的学习方法来完成。
C.学习差异
GPs 和 dnn 的部分不同之处在于它们如何遍历用于优化性能的损耗表面。丹尼尔·科波尼亚斯在 Unsplash 上拍摄的照片
在没有深度高斯过程的情况下,在 GPs 和 DNNs 之间学习到的内容也不同。然而,完成学习的方法并没有太大的不同:两者都使用第一(在某些情况下,第二)阶方法。这两种方法也优化了不同的函数:对于神经网络,这是一个损失/风险函数**,对于高斯过程,这是边际似然函数。**
对于高斯过程,边际似然目标往往更加非凸,因此,经常使用二阶梯度下降算法,如L-BFGS【5】,进行优化以避免局部极小值。
___________________________________________________________________
TL/DR #3: GPs 一般用二阶方法优化,如 L-BFGS【5】,使用目标函数的海森,而 DNNs 一般用一阶方法优化,如 SGD【8】,使用目标函数的梯度。**
___________________________________________________________________
D .可解释性
在应用机器学习任务中,能够解释你的结果可能与结果本身一样重要。
人工智能:神经网络的可解释性
虽然一些较新的 DNN 结构允许对不确定性进行更彻底的近似,例如通过任意的和认知的不确定性【9】,但是这些网络中的许多只是提供了估计的预测值,并且可能提供了多类分类的逻辑值(预测概率)。然而,由于 DNNs 普遍缺乏可解释性已经成为一个热门的研究课题[10],我相信未来更多的网络架构将会包含一些预测不确定性的元素。
其他进展,如 GradCam [12]等梯度可视化工具,也提高了 dnn 的可解释性,并有助于减少它们的“黑色性”。
D . II:GPs 的可解释性
相反, GPs 固有的高斯结构使其非常适合可解释的不确定性估计。对于某些需要直观风险评估的应用,这可能使该方法更具优势。
此外,GPs 具有良好的直观特性,即所有插值均值预测都是作为训练集中现有均值点的加权线性组合生成的,并按测试点到给定数据点的距离(在核函数空间中测量)进行缩放[11]。GPs 以线性方式重新组合他们以前看到的点,以产生新的预测。
___________________________________________________________________
****TL/DR # 4:GPs**的线性和高斯特性很好地帮助他们增加这些模型的可解释性。尽管 DNNs 长期以来被批评为“黑箱”,但今天的重大研究工作正在进行中,以帮助使这些模型更容易解释。
___________________________________________________________________
(一些)理论上的相似之处
A.“插值机制”中的核心机器
最近的研究表明,当具有线性激活函数的神经网络在其隐藏层中接近无限宽度时,它们渐近收敛于核机器[1,2]。这是一个神经切线核(NTK)【2】的想法。这种现象发生在所谓的“插值区域”,也称为“双下降曲线”的后半部分[1]。
高斯过程也是核机器,因为确定测试点的预测均值和方差的训练点的线性组合是由高斯过程的核函数确定的。
___________________________________________________________________
TL/DR #5: 在一定条件下[1,2], DNNs 可以作为所谓“插值”中的核机器进行分析,配备了一个核函数,从观察点的核加权组合中形成对观察点的预测。 GPs 本质上是内核机器【11】。
___________________________________________________________________
B .目标函数的优化
虽然有各种二阶方法,如 BFGS 和 BFGS,用于优化全球定位系统,一阶方法也可以用来优化这些模型。像 DNNs 一样,GPs 仍然努力最小化泛函(通常是具有核正则项的负对数似然),就像神经网络努力最小化损失函数一样。
___________________________________________________________________
TL/DR #6: 两个 DNNs 和 GPs 都是通过一阶和二阶优化方法来改进他们的模型。
___________________________________________________________________
每种方法的优势
此列表绝非详尽无遗,但在决定使用神经网络还是高斯过程时,这里仅列出了一些优势(相对于其他框架):
GP 优点 / DNN 缺点:
- 通常比 DNNs 需要更少的数据,因为它们需要调整的参数更少。然而,拥有更多数据,特别是在固定域上密度不断增加的情况下(称为固定域渐近[1]),有助于显著提高性能。
- 只需要优化少量(超)参数。
- 对爆炸和消失梯度等现象具有鲁棒性(因为,除非您使用深度 GPs ,否则在此框架内没有“层结构”)。
****GP缺点/DNN优点:
- 运行时间与样本数量的比例很小。运行时复杂度为 O(n ) ,其中 n 为样本数。这是必须执行大协方差矩阵的矩阵求逆(或伪求逆)的结果。
- 相对于神经网络,自动学习更少,并且对于核/协方差函数、均值函数和超参数先验分布的选择,需要进行更多的设计考虑。这些参数会对 GP 能够学习的内容产生实质性的影响。
每种技术的示例用例
请注意,以下建议不是绝对的,也就是说,这些建议的目的更多的是为了应用我们在上面学到的原则。
****注意:虽然我对这两个模型类都有丰富的实践和理论经验,但请不要将下面的这些建议视为绝对——在某些情况下,使用另一个模型类可能会更有利。
- 数据集很大 → 使用 DNNs: 推荐使用 DNNs,因为 GPs 运行时间与示例数据集数量的比例很小,还因为 DNNs 已被证明在给定足够大的数据集的情况下,可以在各种机器学习任务上实现最先进的性能。
- 执行连续插值→ 使用 GPs: 推荐使用连续 GPs,因为连续 GPs 使用连续核函数测量距离,如 RBF 核和 Matern 核[11],从而允许以考虑数据集中所有点的方式对来自现有点的新点进行线性加权。通过要求现有点的线性组合,仍然可以观察到精致的插值细节。**
- 执行离散插值→ 使用 GPs: 推荐使用,因为离散/网格 GPs 使用稀疏、离散核函数测量距离,如网格插值核。稀疏结构仍然允许通过考虑所有现有的点来预测新点,但是是以计算效率更高的方式进行的。
- 在动态数据集上学习和预测→ 使用 DNNs: 由于 GPs(几乎)是直接方法,它们的预测机制很大程度上由创建它们的数据集定义。因此,如果定义 GPs 的数据集是动态的,这将需要重新调整/添加新的数据点,这将涉及重新计算协方差矩阵的逆,这是一项成本高昂的操作。相反,DNNs 可以很容易地适应新的数据点,因为它们是逆模型,并且预测仅由这些模型所基于的数据间接确定。
- 其他情况→您决定: 当然还有其他情况没有被上述建议解决。为了解决这些情况,考虑分析上面讨论的相似性/差异/权衡,以确定两个模型类中哪一个性能更好。
总结
我们已经讨论了高斯过程(GPs) 和深度神经网络(DNNs) 的理论相似性/差异、优缺点和应用。我们发现了以下情况:
___________________________________________________________________
- GPs 为【几乎】非参数化,而 DNNs 为过参数化。**
- GPs 为(近)正方法,而 DNNs 为逆方法。**
- GPs 一般用二阶方法优化,而 DNNs 一般用一阶方法优化。
- GPs 的结构使得这些模型具有很强的可解释性。尽管 DNNs 长期以来被批评为“黑箱”,但今天的研究正在帮助这些模型变得更容易解释。
- GPs 本质上是内核机器。在一定条件下, DNNs 也可以分析为内核机。
- DNNs 和 GPs 都通过一阶和二阶优化方法改进了他们的模型。
- GPs 一般比 DNNs 需要更少的数据,只需要优化少量(超)参数,对爆炸和消失梯度等现象具有鲁棒性。
- GP 运行时相对于 DNNs 的样本数伸缩性较差,相对于神经网络的自动学习较少。
- 如果:(i) 数据集很小,或(ii) 执行插值→ 使用 GPs。
- 如果:(i) 数据集很大,或者(ii) 数据集是动态的→ 使用 DNNs。
****感谢您的阅读!想看更多关于计算机视觉、强化学习、机器人技术的内容,请 关注我 。考虑加盟 Medium?请考虑通过这里 报名 。感谢您的阅读!
参考
[1]贝尔金,米哈伊尔。“无所畏惧地适应:通过插值棱镜进行深度学习的显著数学现象.” arXiv 预印本 arXiv:2105.14368 (2021)。
[2] Jacot,Arthur,Franck Gabriel 和 Clément Hongler。“神经正切核:神经网络中的收敛和泛化.” arXiv 预印本 arXiv:1806.07572 (2018)。
[3]达米亚诺、安德烈亚斯和尼尔·劳伦斯。"深度高斯过程。"人工智能与统计。PMLR,2013 年。
[4] Blomqvist、Kenneth、Samuel Kaski 和 Markus Heinonen。“深度卷积高斯过程.” arXiv 预印本 arXiv:1810.03052 (2018)。
****[5]Liu d . c .,Nocedal,j .关于用于大规模优化的有限内存 BFGS 方法。数学规划 45、503–528(1989)。https://doi.org/10.1007/BF01589116
[6]罗伯茨、丹尼尔·a、绍·亚伊达和鲍里斯·哈宁。“深度学习理论的原理。” arXiv 预印本 arXiv:2106.10165 (2021)。
[7]自顶向下与自底向上的数据科学方法。
[8] 赫伯特·罗宾斯和萨顿·门罗一种随机逼近方法《数理统计年鉴》,第 22 卷,№3。(1951 年 9 月),第 400–407 页,DOI: 10.1214/aoms/1177729586。**
[9]阿米尼、亚力山大、威尔科·施瓦廷、艾娃·索莱马尼和丹妮拉·鲁斯。“深度证据回归” arXiv 预印本 arXiv:1910.02600 (2019)。
[10] Park,Sangdon 等人,“深度神经网络分类器的 PAC 置信度预测” arXiv 预印本 arXiv:2011.00716 (2020)。
11 卡尔·爱德华·拉斯姆森。"机器学习中的高斯过程."关于机器学习的暑期学校。施普林格,柏林,海德堡,2003。
[12] Selvaraju,Ramprasaath R .等人,“Grad-cam:通过基于梯度的定位从深度网络进行视觉解释。”IEEE 计算机视觉国际会议论文集。2017.
悬崖漫步的深度政策梯度
提示和技巧
用 Python 实现 TensorFlow 2.0。在此解决方案中,参与者由神经网络表示,该网络使用深度策略梯度算法进行训练。
在这个关于悬崖行走问题的强化学习系列中,最后一篇文章关注的是普通策略梯度算法(REINFORCE)。尽管具有很强的相似性,但深度学习变体(使用神经网络表示)中有一些重要的细微差别需要注意。本文展示了使用 Tensorflow 2.0 的完整 Python 实现。
悬崖行走问题
悬崖行走问题需要一个代理人(从左下角开始),他需要到达右下角才能获得奖励并赢得游戏[1]。每走一步都会产生一点小成本,以鼓励你走最短的路,然而底部的悬崖应该被避开。
悬崖漫步世界的再现[图片由作者提供]
深度离散政策梯度
在这个问题的策略梯度解决方案中,随机策略 π_θ 为可以在任何瓷砖上采取的四个动作(左、右、上、下)中的每一个分配一个概率。形式上,策略(使用 softmax 函数)可以表示如下:
softmax 函数的 DPG 策略。神经网络 f(⋅)由θ参数化,并对特征向量 ϕ (s,a)执行非线性变换
敏锐的观察者可能会注意到与普通实现相比的微小变化——取代教科书中遇到的矢量点积ϕ(s,a)^⊤⋅θ=∑_ I ϕ(s,a)_i ⋅θ_i,我们现在采用一个通用参数化函数f(ϕ(s,a);θ) 。
前一篇文章中使用的特征向量 ϕ(s,a) 直接基于状态(每个状态四个θ参数,代表每个动作)。因此,我们有效地训练了各个州的模型。用神经网络表示,这样的隔离是不可能的。将输入层连接到第一个隐藏层的权重可以单独训练,而所有剩余的权重表示对所有状态都有效的紧凑表示。
实际上,这是演员网络的优势和劣势。对于大的状态空间,我们根本无法观察到每一个状态,我们需要一个一般的表示,这正是网络所能提供的。然而,设计一个能够捕捉所有状态的单一表示是一个重大挑战。
具体来说,我们用一个通用的参数化函数 f(ϕ(s,a 来代替ϕ(s,a)^⊤θ*;θ)* ,其中 f 为神经网络, θ 为网络权重。除此之外,还是以前那个畜生。
网络架构有两种主要的变体。我们可以输入基于状态和动作的特征向量,并输出单个值(后决策方法,需要运行相同的模型四次),或者使用仅基于状态的特征向量,并输出所有四个动作的值。我们在这里实现后者。
离散行动者网络示例。网络将状态作为输入(对于这个问题是一个 48 维的一维热点向量),并输出每个动作的概率(左、右、上、下)。第一层中的权重对于每个图块是唯一的,但是隐藏层是所有状态的一般表示。[图片由作者提供]
使用 TensorFlow(或任何其他深度学习库,就此而言)的一个便利之处是,训练在很大程度上是自动化的;我们只需要给网络输入一个损失函数,训练几乎是自动进行的。对于离散策略梯度,我们采用对数损失(或交叉熵损失)函数:
Tensorflow 实现
首先,我们需要一个演员网络。下面的代码片段构建了它。最终层中的 softmax 激活确保输出是概率总和为 1 的向量,而权重初始化确保初始动作概率相等。
接下来,我们需要损失函数。在这种情况下,我们使用交叉熵损失函数:
我们不必为手动计算梯度而烦恼;所需要的只是损失函数。GradientTape
功能跟踪我们的数学运算,并使用该内存为我们计算梯度。更新程序——在每集之后执行——概述如下。注意,针对轨迹中每个奖励的所有损失立即计算梯度;这是因为中间更新会改变参与者网络,从而改变梯度所需的计算概率(尽管这种方法肯定不是唯一的解决方案)。
完整的实现可以在我的 GitHub 上找到:
https://github.com/woutervanheeswijk/cliff_walking_public
还没完全明白吗?没问题:这个最小的工作示例一步一步地引导您完成离散策略梯度算法:
实验
像往常一样,如果不进行测试,实现就不完整。让我们试一试,对比香草策略梯度(基本强化实现)【SARSA】(基于价值的基于策略比较)深度 Q 学习(基于价值的神经网络对应物)。
普通策略梯度很难获得稳定的解决方案,遭受高方差和探索的不利影响。以前,深度 Q 学习也比普通 Q 学习困难得多。深度政策梯度似乎结合了两个世界最糟糕的部分,它的表现如何?下注吧。
Rien ne va plus…照片由 Adi Coco 在 Unsplash 上拍摄
它进行得不顺利。事实上,这是我第一次决定调整奖励结构,提高达成目标的相对奖励。最初,每次移动的概率是 0.25;因此,达到目标的几率相当低。行动者网络还对状态进行概括,例如,如果在开始时“向右”移动是不好的,则减少的概率倾向于延续到其他图块。
实际上,如果在前一千次左右的迭代中没有找到目标,找到满意的策略的机会相当小。增加熵奖励也没有起到任何作用。在高学习率的情况下,该算法可能会收敛到一个局部最优(例如,直接跳进悬崖或呆在角落里),而无需观察目标。由于学习率低,偶尔达到目标并不能为重复提供足够强的奖励信号。
所以,我作弊了一点,把奖励提高了 10 倍,大大提高了可靠性。姑且称之为奖励塑造。这样一来,是时候比较表现了,强调单次运行显示出大的波动。
普通政策梯度与深度政策梯度的比较。两者都在努力应对巨大的差异,但最终通常会趋同于可比的政策。不过,深度变体似乎不太稳定。[图片由作者提供]
vanilla SARSA 和 Deep policy gradient 的比较。SARSA 收敛得更快,并且达到更稳定的策略。[图片由作者提供]
深度 Q 学习和深度策略梯度的比较。虽然两者都在努力学习,但是深度 Q 学习通常收敛得更早,并且收敛到更好的策略。[图片由作者提供]
尽管 SARSA 并不以其强大的探索机制而闻名——在这种情况下,这是一种简单的ϵ-贪婪方法,其中ϵ= 0.05——q 值的初始化对该问题有重大影响。当所有 Q 值都设置为 0 时,有一种尝试新状态的强烈倾向;代理可能相当快地发现目标(对于深度 Q 学习,这个逻辑不成立,因为 Q 值是一般化的,而不是从查找表中获得的)。策略梯度算法——尽管有一个非常的显式探索机制——实际上找到目标的机会相当小,并且它经常在达到目标之前就收敛到局部最优。
使用深度策略梯度获得的示例路径。由于策略中固有的随机性,代理经常绕道或重新访问图块。[图片由作者提供]
最后,一切都解决了。旅程并不总是像事先希望的那样顺利,但我们又一次安全地避开了悬崖。
外卖食品
- 普通政策梯度和深度梯度的关键区别在于,深度变量概括了跨州的行动概率。将线性表达式 ϕ(s,a)^⊤θ 替换为神经网络f(ϕ(s,a);θ).
- 学习策略具有挑战性,因为(I)概率策略使发现目标变得困难,以及(ii)概率通常在各州之间通用。为此,增加了目标奖励。
- 由于策略梯度算法的高方差,奖励整形可能会有所帮助。在这个问题中,好的回报轨迹是罕见的;更新信号需要足够强以产生影响。
深度策略梯度算法的完整代码可以在我的 GitHub 资源库 上找到。
表格 Q-learning 和 SARSA 对于悬崖行走问题的实现如下:
深度 Q 学习算法 也已经实现:
*
最后, 离散政策梯度 变体如本文所示:
参考
[1]萨顿和巴尔托(2018 年)。强化学习:简介。麻省理工出版社。*
悬崖行走问题的深度 Q 学习
思想和理论
一个完整的 Python 实现,用 TensorFlow 2.0 导航悬崖。
乍一看,从普通 Q-学习到深度 Q-学习似乎是一小步。只要把查找表换成神经网络就大功告成了。然而,事情远不止如此——即使对于最简单的问题,深度 Q 学习也可能难以取得结果。
为了展示如何避免一些常见的陷阱,本文讨论了深度 Q 学习的 TensorFlow 2.0 实现,以解决众所周知的悬崖行走问题。首先,我们展示了标准化和一键编码如何将输入和输出与神经网络对齐。然后,我们部署了三种据说可以显著提高深度 Q 学习常见技术:经验重放、目标网络和小批量。
悬崖行走问题
悬崖行走问题(关于普通 Q-learning 和 SARSA 实现的文章这里)相当简单[1]。代理从左下角开始,必须到达右下角。走进分隔这些瓦片的悬崖会产生巨大的负面奖励,并结束这一集。否则,每一步都要付出很小的代价,这意味着最短的路径是最优策略。
悬崖行走词示例。目标瓦片产生正奖励,每走一步产生小负奖励,掉下悬崖产生大负奖励[图片作者]
这个问题可以通过 Q-learning 解决。这是一种强化学习方法,它存储代表每个状态-动作对(总 48⋅4)的预期未来回报的 q 值。值得一提的是,Q-learning 是一种非策略方法——对于下游动作,我们假设最佳可能动作,而不是实际样本轨迹的动作。因此,Q-learning 鼓励探索,因为失败的影响相对有限。
Q-learning 是一种非策略强化学习方法。基于在 t+1 时采取的最佳动作而不是实际动作来更新时间 t 时的 Q 值。因此,勘探的负面影响是有限的。
从普通 Q 学习到深度 Q 学习
在之前的一篇文章中,我提供了一个在 TensorFlow 2.0 中实现深度 Q 学习的最小工作问题。这里,我假设你熟悉普通 Q 学习,至少熟悉深度 Q 学习的基础。简单回顾一下:在深度 Q 学习中,我们训练一个神经网络,它将状态作为输入,并输出每个动作的 Q 值。虽然查找表对于大的状态空间来说是爆炸式的,但是深度 Q 网络提供了一种对所有状态都适用的紧凑表示。然而,仅仅替换查找表通常是不够的,我们通常需要以下技术(在这里用实现细节描述)来获得更好的结果[2]:
- 标准化和一次性编码
- 体验回放
- 目标网络
- 小批量
标准化和一次性编码
重要的事情先来。当处理相同大小的输入和输出时,神经网络往往工作得更好。因此,我们将所有奖励除以 100,所以它们(大约)在-1 和 1 之间。累积奖励信号是网络的目标输出。
我们还必须注意定义作为神经网络输入的状态。在普通的 Q 学习中,我们可能会将目标瓦片称为“状态 48”,将相邻的悬崖瓦片称为“状态 47”,但自然地,这样的数值对神经网络来说意义不大。它只是简单地乘以某个权重向量,无法区分 47 和 48 具有完全不同的含义。为了解决这个问题,我们应用了 one-hot 编码,将输入定义为长度为 48 的数组。对应于当前图块的元素具有值 1,所有其他元素具有值 0。因此,我们可以学习对应于每个瓦片的唯一的一组权重
输入和输出现在都被规范化了,所以我们可以安全地用类似于 He-或 Glorot 初始化的东西初始化权重,不再担心标度差异。
体验回放
Q-learning 依赖于时间差异,使用“预期”值Q_t
和“观察”值r_t+Q_t+1
之间的差异作为误差。不幸的是,Q_t+1
和Q_t
一样都是猜测——它们是由同一个神经网络决定的。
更糟糕的是,随后的观察结果往往高度相似,使用几乎相同的状态作为输入。这在相邻的观测值之间产生了很强的相关性。特别是非线性近似(如神经网络)往往很难处理这种相关性。
用不那么抽象的术语来说:考虑一个被困在悬崖世界角落里的代理人。这个代理人可能会不断地收到相同的奖励信号,并在网络上对这个特定角落的行动进行过度拟合。你会经常看到这种情况发生:代理人了解到向右走不好(由于悬崖)并且倾向于停留在世界的左边,不断地调整网络以适应那个区域。
为了打破这种关联,我们部署了体验回放。不是总是选择最近的观察来更新我们的网络,而是每当我们更新时,我们存储我们过去的所有观察和来自这个重放缓冲器的样本。每个观察由一个(s,a,r,s’)
元组表示。为了计算当前的 Q 值(从我们获得观测值的时间起而不是,我们将s
和s’
馈送到主要网络,使用a
获得Q_t
,使用 argmax 动作获得Q_t+1
。
小批量
理论上,我们可以收集许多观察结果,并将神经网络拟合到单个批次,通过单次更新来确定最终策略。在许多监督学习问题中,在大型数据集上一次性训练网络是非常常见的。然而,在强化学习的情况下,我们将总是基于我们的初始策略进行观察(这可能非常糟糕)。我们希望探索足够多的东西,以避免陷入局部极小,同时也主要从好的动作中学习。
因此,大批量不是很有用。我们希望将观察和更新结合起来,逐步改进我们的政策。然而,这并不意味着我们必须更新每一个观察结果——悬崖行走问题中的一个步骤往往教会我们很少东西,因为我们没有观察到任何有意义的东西。显而易见的妥协是小批量,这意味着我们经常用大量的观察数据更新我们的网络。特别是与经验重放相结合,这是一种强大的技术,可以基于大量以前的观察获得稳定的更新。
目标网络
到目前为止,我们已经从同一个网络中得出期望值Q_t
和“真实”值r_t+Q_t+1
。因此,观察和目标相互关联,再次使用一个猜测来更新另一个猜测。
为了缓解这个问题,我们使用了一个目标网络。目标网络只不过是 Q 网络的周期性副本,以较低的频率更新(比如每 100 集一次)。因此,我们将期望值和目标值分离,减少了两者之间的相关性。目标保持相当稳定,而政策本身则在逐步改进。
深度 Q-将学习付诸行动
在概述了实现深度 Q 学习所需的理论之后,让我们看看它在实践中的实际表现。在我的 GitHub 库中可以找到关于悬崖行走问题的学习方法的完整实现。
让我们给我们的 Q 网络——3 个隐藏层,每个层有 25 个神经元——一个旋转。与表格变量相同的 0.1 学习率,没有重大调整。
所有算法学习率α=0.1 的强化学习。深度 Q 学习在这里实际上什么也学不到,而 Q 学习和 SARSA 很快收敛。[图片由作者提供]
呀!Q-learning 和 SARSA 迅速收敛到稳定的政策,而深度 Q-learning 似乎没有学到任何值得注意的东西。最重要的是,后者需要更多的计算工作。在我的笔记本电脑上,10,000 次迭代需要大约 20 分钟,而 Q-learning 和 SARSA 只需要几秒钟。
幸运的是,经过一些调整——特别是,将学习率降低到 0.001——事情看起来好多了。收敛仍然需要比表格变量长得多的时间,但是整体深度 Q-学习达到了与常规 Q-学习相同的策略。
用于深度 Q 学习的学习速率α=0.001 的强化学习在这里有效地不学习任何东西,而 Q 学习和 SARSA 快速收敛。[图片由作者提供]
让我们拿出我们剩下的武器库:体验重播,一批 5 个(更新频率也减少了 5 倍,以保留相同数量的数据点)和每 10 集更新一次的目标网络。最终结果:
深度 Q 学习,包括经验回放、小批量和目标网络。部署这些稳定技术并没有导致更快的收敛。[图片由作者提供]
那…实际上很令人失望。尽管我们做了所有的努力,一个简单的 Q 表仍然打败了我们花哨的神经网络。发生了什么事?哪里出了问题?
什么都没发生。没出什么差错。深度学习很有挑战性。我们试图学习一个适用于所有状态的通用函数。通过多层反向传播错误也需要时间。我们的未过滤重放缓冲区保存了许多糟糕的观察结果。为了适应目标网络和批量学习,降低更新频率也会降低收敛速度。神经网络不是神奇的黑匣子。机器学习并不等同于超自然智能。
当然,我们本可以在调优上花更多的时间来提高性能。在一个小批量中应该有多少个观察值?什么是理想的网络架构?我们应该给予体验什么样的优先权?我们必须多久更新一次目标网络?所有重要的问题,也是需要时间来回答的问题。参数调整成本高。
有些发人深省的结论是:尽管潜在的难以置信的强大,深度学习也很难成功实施。根据你的问题和你的目标,这可能并不总是最好的方法。
外卖食品
- 深度 Q 学习不仅仅是用神经网络代替查找表。它的性能通常不太稳定,需要更多的建模和调优工作。
- 使用适当的归一化和一键编码使状态和动作适合神经网络。
- 经验回放——从过去
(s,a,r,s)
元组的缓冲区中随机取样——打破了后续观察之间的相关性。 - 目标网络——Q 网络的周期性副本——可用于计算 Q_t+1。这降低了期望值
Q_t
和目标值r_t+Q_t+1
之间的相关性。 - 小批量稳定更新,一次利用多个观察更新网络。
深度 Q 学习算法的完整代码可以在我的 GitHub 资源库 上找到。
我实现的表格 Q-learning 和 SARSA 对于悬崖行走的问题在这里有详细介绍:
离散策略梯度 变体如本文所示:
*
为 深策渐变 ,勾选:
下面的文章给出了 深度 Q 学习 的一个最小工作示例:
最后,我在这里讨论经验回放、批量学习和目标学习的实现示例:
参考
[1]萨顿和巴尔托(2018 年)。强化学习:简介。麻省理工出版社。
[2]t .马蒂森(2015 年 12 月 19 日)。《揭秘深度强化学习》。神经外科。计算神经科学实验室。检索时间 2021 年 9 月。*
深度 Q 学习不是火箭科学
用 pytorch 解释和编码的深度 Q 和双 Q 学习
(作者 GIF)
情境中的深度 Q 学习
q 学习是强化学习社区中早已存在的一种方法。然而,最近通过使用神经网络与 Q 学习相结合,在该领域取得了巨大进展。这就是所谓的深度 Q 学习的诞生。这种方法的全部潜力在 2013 年被看到,当时谷歌向世界展示了他们的 DQN 代理商在玩雅达利突破。对我来说,这是我第一次接触这个领域,我立刻对它产生了兴趣。
其他强化学习方法有策略梯度法和行动者批评法。行动者批评方法是 Q 学习和政策梯度方法的混合。
一般强化学习
在强化学习中,我们有一个环境,在这个环境中,一个代理正在做动作。环境然后返回一个奖励和一个新的状态。代理人得到的报酬也取决于他实际所处的状态。所以他/她应该学会根据实际情况调整行动。
作者图片
我们要用的环境是 CartPole-v0 环境,作者是 OpenAI。我们可以在 python 中轻松使用它。这是一个非常简单的环境,对于大多数深度强化学习算法来说,求解起来也非常简单。因此,每当我写一个新的强化学习脚本时,我都会用它来测试我是否正确地实现了所有的东西。
南极环境
我们从导入和测试 openai gym 开始。如果您还没有安装它,您可以通过键入:
pip3 install gym
然后,我们编写一个脚本来测试我们的健身房环境。我们让代理执行随机的动作,只是为了看看是否一切正常。
(作者代码)
代理的目标是尽可能长时间地平衡操纵杆。如果棍子在两个方向偏离垂直方向 15 度,它就失败了,这一集就结束了。
(图片由作者提供)
代理为实现其目标可以采取的行动有:
- 向左移动(0)
- 向右移动(1)
奖励以如下方式给出:
- 每走一步+1,棍子是直立的
状态/观察值是 4 个值的列表:
(图片由作者提供)
深度 Q 学习
蜕变能
考虑下面的场景。我们的鸡饿了。它可以执行两个操作。它可以吃也可以写。因为它饿了,更好的选择是吃东西,这确实会给它更多的奖励。但是奖励是在行动完成后给予的,所以它怎么会知道。在这种情况下,动作值(或 Q 值)可以帮助我们。这个 Q 值取决于状态和可能的动作。给定状态,Q(s,a)然后返回执行动作的值。所以鸡应该执行 q 值最高的动作!
(图片由作者提供)
Q 值由下式定义:
(图片由作者提供)
这个等式简单地意味着,对于一个给定的状态,一个行为的价值是这个行为执行后的直接回报加上下一个状态的(贴现的)最高 Q 值。伽马(𝛾)实际上是贴现因子,它解释了未来回报的不确定性。𝛾通常大于 0.9,小于 1。低 gamma 导致代理更关注眼前的回报,而高 gamma 导致代理更关注未来的高回报。
时间差异学习
我们已经看到了 Q 值的精确定义,我也告诉过你,我们将使用神经网络来逼近它。
(图片由作者提供)
因此,我们希望使网络的近似 Q 值尽可能接近数学定义。为了实现这个目标,我们使用神经网络的能力来最小化损失函数。
(图片由作者提供)
我们把实际时间步长的 Q 值称为“预测”,把包括下一个状态目标的 Q 值的项称为“预测”。
我们使用的损失函数就是均方误差损失。
(图片由作者提供)
利用时间差学习,我们可以在每个时间步训练我们的代理。
勘探开发的困境
如果我们让我们的代理贪婪地利用它认为具有最高 Q 值的策略,很可能存在更好的策略。代理人将永远无法探索这一策略,因此永远看不到与之相关的高回报。所以在开始的时候,我们让自己做随机的行为,随着时间的推移,减少它采取随机行为的可能性。这个过程被称为𝜖贪婪行动选择。
体验回放
深度 Q 学习不是特别好,除非我们通过添加经验回放来增强它。为此,我们建立了一个记忆,把所有的状态、行为和奖励都储存在里面。训练时,我们从记忆中检索随机的一批,并在其上进行训练。这意味着代理很可能没有接受过上一个时间步骤的培训。
下面的代码实现了一个 ReplayBuffer 类,它负责存储和分配随机批次的内存。对于我们存储的每个时间步长:(state,action,reward,next_state,done)。done 是一个结束标志,在一集期间为 0,在一集结束时为 1。
(作者代码)
整个算法和代码
我决定向您展示主文件的 python 代码,而不是伪代码,因为如果您了解 python,这更容易理解。
(作者代码)
我们可以调整许多超参数,我们将它们传递给代理的构造函数:
agent = DQAgent(learning_rate=0.00025, gamma=0.90, batch_size=32,
state_len=len(env.reset()),
n_actions = env.action_space.n,
mem_size = 1000000,
min_memory_for_training=1000,
epsilon=1,epsilon_dec=0.99,
epsilon_min = 0.02)
**学习率:**神经网络的学习率。高值会使代理学习得更快,但也会导致在复杂环境中的次优行为。
伽马: 𝛾通常在 0.9 以上,小于 1。低 gamma 导致代理更关注眼前的回报,而高 gamma 导致代理更关注未来的高回报。
**批次大小:**给神经网络执行小批次梯度下降的时间步数。高批次数量稳定了学习,但也可能导致停滞。
**训练的最小记忆:**学习过程开始前的步数。一个合适的数字可以防止网络过度适应前几个训练样本。
**ε:**ε的起始值。1 表示代理在开始时只执行随机操作(探索), 0 表示它不会开始执行随机操作(利用)。
**Epsilon dec:**Epsilon 在每个时间步长后乘以的系数,以减少它。应该大于 0.99 小于 1。
**ε最小值:**ε的最小值。如果ε低于该值,它将不会减小。任何时候都要有一点点的探索,让代理不断学习,这一点很重要。
现在我们代理的全部代码是:
(作者代码)
让我们来讨论一下这段代码:
target = rewards_batch + torch.mul(self.gamma* self.q(new_states_batch).max(axis = 1).values, (1 - dones_batch))
它计算 TD 目标。假设我们的批量是 5。
那么计算可能如下所示:
(图片由作者提供)
我们做 max 运算,因为我们想看 Q 值最高的动作。
让我们看看下面的代码行:
prediction = self.q.forward(states_batch).gather(1,actions_batch.unsqueeze(1)).squeeze(1)
它计算预测。批量大小为 5 时,可能如下所示:
(图片由作者提供)
我们从代理实际采取的行动中选择 q 值。
测试
现在是时候实际测试我们的算法了。我们用我们的 CartPole-v0 环境测试它。
在 100 集之后,代理人设法击败了环境,并且几乎总是获得最高分。
(图片由作者提供)
(图片由作者提供)
双 Q 学习
为了进一步改进我们的深度 Q 学习算法,我们可以选择具有单独的预测值和目标值。这也叫双 Q 学习。
(图片由作者提供)
这些动作将通过预测网络进行预测。反向传播只发生在预测网络中。预测网络的参数每隔几次迭代就被复制到目标网络。目标网络的参数保持“冻结”多久也是一个超参数。
(图片由作者提供)
我们冻结了目标网络,因为这样预测和目标的相关性就降低了。这有助于学习过程
测试
现在我们有一点乐趣,用 openai 环境“LunarLander-v2”测试我们的算法。你可以在下面找到我用过的超参数列表。
学习率: 0.001
伽玛: 0.99
批量: 64
训练最小内存: 1000000
**ε:**1
**εdec:**0.995
ε最小值: 0.02
冻结 _ 迭代: 6
以下 GIF 拍摄于 729 集。代理人在着陆方面做得很好。
(作者 GIF)
学习曲线是这样的。
(图片由作者提供)
请注意,它被 20 集移动平均线平滑。
密码
主文件:
(作者代码)
代理的代码:
(作者代码)
作者相关文章
想联系支持我?
领英
https://www.linkedin.com/in/vincent-m%C3%BCller-6b3542214/
脸书
https://www.facebook.com/profile.php?id=100072095823739
推特
https://twitter.com/Vincent02770108
中等
https://medium.com/@Vincent.Mueller
成为中等会员并支持我(你的部分会费直接归我)
https://medium.com/@Vincent.Mueller/membership
深度 Q 网络:结合深度和强化学习
实践教程
揭示深度强化学习的力量
R 强化学习(RL)是数据科学中最令人兴奋的研究领域之一。长期以来,它一直是许多数学家工作的中心。
而今天,随着深度学习的改善和计算资源的可用性,RL 引起了更大的兴趣:随着大量数据不再代表障碍,新的建模方法出现了。
在这种背景下,强化学习方法和深度学习模型的结合,通常被称为深度 RL,已经被证明是强大的。
它是最近人工智能领域令人印象深刻的进步的基础。它甚至使算法在雅达利、围棋或扑克等领域超过了人类的表现。
**本文的目的是阐明深度学习在 RL 领域的贡献。**为此,它重点介绍了 Q-Learning(一种常见的 RL 模型)的示例,并解释了包含神经网络的附加价值。
阅读本文后,您将了解到:
- 什么是强化学习,它基于什么原则和技术?
- 什么是深度 Q 学习,它与“通常的”Q 学习有什么不同?
- 强化学习的潜在贡献是什么,还有哪些挑战?
1。什么是强化学习?
让我们首先快速概述一下 RL 原则。
从一般的角度来看,RL 可以用非常简单的话来解释。这就像教你的狗一个新把戏。当你的狗表演魔术时,你给它一点狗粮作为奖励,如果它不表演,你就惩罚它,不给它任何东西。
同样,RL 包括训练机器学习模型来做出决策。代理通过与他的环境交互以自主的方式学习采取什么行动,即通过根据它采取的行动接收奖励或惩罚。通过其经验,代理人寻求找到最优决策策略,这将使他能够最大化随着时间积累的回报。
术语
在这种情况下,有一个特定的术语用于描述 RL 环境的组件:
- 代理人:要培训的决策者。
- 环境:代理学习和决定采取什么行动的一般环境。
- 动作 𝑎:代理可以执行的一组可能动作中的一个
- 状态代理所处的𝑠:状态
- 奖励 𝑟:代理人因其自身行为而从环境中获得的收益或损失
- 策略 𝜋:代理人选择采取的策略。它表示一组情况和一组可能的动作之间的映射。
为了了解每个元素具体对应什么,让我们以吃豆人游戏为例。如下图所示,代理人是 Pacman。他的目标是避开鬼魂,尽可能多地吃掉食物和受惊的鬼魂。
强化学习环境组件,图片由作者提供
韵律学
在这种情况下,有两个指标:
- 价值函数 𝑉𝜋(𝑠):期望贴现回报从𝑠开始,接着是𝜋政策
其中𝑅代表回归。
- Q 值在给定的状态 s 和给定的动作 a 下,𝑄𝜋(𝑠,𝑎):期望贴现收益,同时遵循此后的政策𝜋∗。
2。深度学习给强化学习带来了什么?
为了了解深度学习对 RL 的附加值,我将重点讨论 Q-Learning 的例子,并将‘通常的’Q-Learning 与深度 Q-Learning (也称为深度 Q 网络)进行比较。
Q-Learning
直观地说,Q 学习算法包括学习一个 Q 表(‘Q’代表质量),它包含每个状态和行为的预期未来回报。
由于有了这个**‘备忘单’**,代理人能够通过选择使预期未来回报最大化的行动来为每个状态做出最佳选择。
该图说明了 PacMan 案例中的过程:
q-学习管道,图片由作者提供
代理如何学习 Q 表?
- 首先, Q 表被任意初始化。列数是可能动作的数量,行数是状态的数量。
- 那么,只要这一集还没有结束:
- 2.1.处于状态 s 的代理人根据 Q 表选择 a *,*使的期望未来报酬最大化的行动。
- 偶尔,代理被允许随机选择一个动作。这就是所谓的ε贪婪策略。它使代理能够探索他的环境(并不总是以狭隘的方式遵循他的“备忘单”)。更多信息,可以阅读这篇文章。
- 2.2.代理观察结果状态 s’和奖励 r,并且更新函数 Q(s,a)** 如下:**
深度 Q 学习
正如您所料,当处理具有各种可能性和结果的复杂环境时,以前的 Q 学习算法很快变得低效。
****幸运的是,通过将 Q-Learning 方法与深度学习模型相结合,Deep RL 克服了这个问题。它主要包括建立和训练一个神经网络,能够估计给定状态下每个动作的不同 Q 值。
深度 Q-学习管道,受这篇文章的启发
下图比较了 Q 学习和深度 Q 学习方法:
Q-Learning vs . Deep Q-Learning,受这篇文章的启发
注意,网络的损失函数是损失函数:
这还不是全部:深度 Q 学习引入了两个额外的机制,可以实现更好的性能。
1。记忆回放:
- 神经网络不是在每一步后立即更新。取而代之的是,它将每次经历(通常作为一个元组<动作、状态、奖励、下一个状态>)存储在存储器中。
- 然后对从重放存储器中随机选择的称为小批量的一组元组进行更新。
- 这种机制使算法能够记住并跟踪他过去的经历。
2。单独目标网络:
- 使用相同的网络计算预测值和目标值(在损失函数中使用)会导致严重的不稳定性**。**
- 为了克服这个问题,通常适合于训练两个具有相同架构的独立网络:预测网络和目标网络。****
- 预测网络作为“在线”网络工作,而目标网络是预测网络的副本,具有定期更新的冻结参数。****
3。深度强化学习能带来什么?
应用
如果说 RL 机型主要以在雅达利或围棋等高精尖游戏中的骄人表现著称,那么适用范围就要广得多。它们可以应用于各种商业案例。
从金融交易、推荐系统到机器人,RL 能够通过其独特的试错学习方法捕捉复杂的现实生活问题。
**这当然是这种学习范例的关键优势: **RL 模型在没有任何关于环境的先验知识的情况下被训练。它们不需要对金融市场风险、客户行为或系统的物理特征进行任何建模。因此,它们可以避免一些非常重要的步骤。
挑战
尽管如此,使用 RL 来解决特定的问题带来了 3 个主要挑战 s:
- ****模拟环境:构建环境是一个关键点,因为 RL 模型从与环境的交互中学习。如果模拟一个游戏看起来相对容易,那么当涉及到自动驾驶汽车、能源优化等其他应用领域时,它就会变得很快变得更加复杂。
- 达到全局最优:如果一个 RL 代理没有充分探索他的环境,他可能会陷入局部最优。这就是所谓的勘探开发困境。当代理了解他的环境时,他在探索更多的环境以获得更好的可能性视野和利用他的经验来追求他认为最有利可图的策略之间左右为难。
- ****选择合适的算法并调整其参数:RL 模型种类繁多。他们依赖于完全不同的方法,并使用几个超参数。因此,有必要仔细选择算法和正确的度量来评估其性能。
深度 Q 网,带 PyTorch
解释无模型 RL 算法的基础:深度 Q-网络模型(带代码!)
Mathias P.R. Reding 在 Unsplash 上拍摄的照片
在 Q 学习中,我们将 Q 值表示为一个表格。然而,在许多现实世界的问题中,存在巨大的状态和/或动作空间,并且表格表示是不够的。例如,电脑围棋有 10 个⁷⁰状态,像马里奥兄弟这样的游戏有连续的状态空间。当不可能在二维数组或 Q 表中存储状态和动作对值的所有可能组合时,我们需要使用深度 Q 网络(DQN)来代替 Q 学习算法。[1]
DQN 也是一种无模型的 RL 算法,其中使用了现代深度学习技术。DQN 算法使用 Q 学习来学习在给定状态下采取的最佳动作,并使用深度神经网络或卷积神经网络来估计 Q 值函数。
DQN 建筑的插图
神经网络的输入包括一个 84 x 84 x 4 的图像,随后是 3 个卷积层和 2 个完全连接的层,它们为每个有效动作输出一个输出。[1]
DQN 建筑的插图[1]
DQN 算法
DQN 算法[1]
DQN 的主要成分— 1。q 值函数
在 DQN,我们用权重 w 来表示价值函数,
q 值函数。作者图片来源于[1]。
- 在选择行动时,Q 网络的工作方式类似于 Q 学习中的 Q 表。Q-learning 中的状态是可数的和有限的,而 DQN 中的状态可以是有限的或无限的/连续的或离散的。
- Q 网络中的更新是通过更新权重来完成的。
DQN 的主要成分— 2。损失函数
让我们用 Q 值的均方误差来定义目标函数。
损失函数。作者图片来源于[1]。
这是一个损失函数,用于最小化在 Q 网络中更新权重的误差。
DQN 的主要成分— 3。优化算法
让我们用随机梯度来优化上面的目标函数,用 δL(w)/δw 。Tensorflow 或 PyTorch 中有许多可用的优化算法。比如亚当,RMSProp,阿达格拉德等。
DQN 的主要成分— 4。体验回放
朴素 Q 学习用神经网络振荡或发散。
数据是连续的,这意味着连续的样本是相关的,而不是独立的和同分布的。
政策会随着 Q 值的轻微变化而快速变化或振荡,因此,数据的分布会从一个极端转移到另一个极端。
奖励和 Q 值的比例是未知的。当反向传播时,朴素 Q 学习的梯度可能很不稳定。[2]
为了解决上述问题,我们可以将转换存储在重放缓冲器中,并从重放缓冲器中采样一小批经验来更新 Q 网络。从中。通过使用经验重放,它将打破样本之间的连续相关性,并且还允许网络更好地利用经验。[1]
下面的教程将使用 DQN 来解决来自健身房环境的雅达利游戏。对于所有 Atari 游戏,它接受 84x84x4 图像帧作为输入。因此,首先,我们需要进行数据预处理,即跳过连续样本之间的一些帧,因为这些连续样本几乎是相同的,重新缩放图像,灰度化图像,归一化图像等。
DQN 解算器将使用 3 层卷积神经网络来构建 Q 网络。然后,它将使用优化器(下面代码中的 Adam)和经验重放来最小化误差以更新 Q 网络中的权重。
最后,我们可以用run(training_mode=True, pretrained=False)
进行训练,用run(training_mode=False, pretrained=True, num_episodes=1, exploration_max=0.05)
进行测试。
- 第一次训练网络时,可以使用
pretrained=False
,否则可以使用pretrained=True
从上次停止的地方继续训练。 - 然而,对于测试,你只能使用
pretrained=True
,这意味着你使用训练好的 Q-网络来测试代理的性能。 exploration_max=1
在训练过程中,因为我们希望 agent 以ε概率随机进行探索,这样就不会陷入局部极小值。然而,在测试过程中,我们希望代理从经过训练的 Q 网络中采取优化的行动,因此,我们应该使用 0 或非常少的探索exploration_max=0.05.
推荐阅读
参考
[1] V. Mnih et al. ,通过深度强化学习的人级控制。
[2]华盛顿大学 CSE571 讲座—“概述深度学习强化学习深度价值功能深度政策深度模型。”
深度 Q 网配合 Pytorch 和 Gym 解决 Acrobot 游戏
一种深度强化学习算法的介绍与实现
作者插图
强化学习是机器学习的一个分支,受人类和动物行为主义心理学的启发。在监督学习 (SL)中,学习受到限制,因为它总是需要外部教学信号来帮助解决任务,如分类和回归。
与第二语言不同,强化学习基于不同的原则。主要目标之一是生产一个自主代理,它与环境交互,学习和选择最优行动,有助于实现其目标,如最大化回报。代理观察环境中的状态,并在给定状态的情况下执行动作。每次代理通过采取行动进行交互,他可能会收到一个正或负奖励。
通过反复试验,最佳行为会随着时间的推移而改进。这种学习方式让我想起了我们从经验中学习的方式。我们讨厌犯错,但当我们从中吸取教训并在未来做得更好时,错误会变得很有价值。同样,强化学习探索不同的动作和状态,以找到最优的动作。此外,当在学习过程的早期出现错误时,对代理的训练更加健壮。
由于这些特点,强化学习经常被应用于玩游戏、机器人和许多其他决策问题。强化学习与深度学习技术的结合,使得在高维状态空间的问题上取得巨大进步成为可能。例如,当代理需要从屏幕像素中学习时,它就很有用。这些方法被称为深度强化学习。
在本教程中,我将训练一个执行 Acrobot 任务的代理。Pytorch 将用于实现 Deep Q 网络,而 Google Colab 笔记本将被使用,因为它提供免费的 GPU 来加速训练。
目录:
1.奥鹏健身馆
Gym 是一个开源库,提供强化学习算法的实现【1】。有许多教学代理可供培训,如 Cart-Pole 和 Pong。在本教程中,我将重点介绍 Acrobot 环境。在之前的教程中,我很好地解释了这个游戏如何如果你想更深入地理解它。
2.安装和导入库
我们需要安装和导入 python 包。最重要的套餐是健身房,它提供强化学习环境。还有其他有用的库,如:
torch.nn
实现深度 Q 网络- 需要
IPython.display
和pyvirtualdisplay.display
来创建一个虚拟显示器,在上面绘制游戏图像 collections
是 Python 基本对象的替代品,如列表、元组、字典和集合。它用来存储代理的经验。
3.行动和观察空间
我们可以使用 make 函数实例化体育馆环境。我们也可以设置一个随机的环境种子,每次都产生相同的结果。你大概可以想象,每个环境都有不同数量的动作和观察。通过打印,我们可以很容易地看到操作和观察空间:
动作空间是离散的,因此动作可以是以下非负数之一:[0,1,2]。与动作空间不同的是,观察空间是盒子,代表一个 n 维的盒子。然后,每个观察值将是一个范围在-28 和 28 之间的 6 个数字的数组。
4.随机代理
这是一个运行代理的例子,它只是随机选择一个动作。Acrobot 环境将运行 10 集,显示游戏每一步的视频。在运行代理之前,我们定义了可视化环境视频的函数。在 Google Colab 中启用健身房环境渲染需要这些函数。
最后,我们运行随机代理 10 个时间步:
这段代码总结了有限马尔可夫决策过程(MDP)的形式问题,这是一个强化学习问题的一般设置。我们知道有五个组件协同工作:
- 代理是由两个关节和两个连杆组成的机械臂,两个连杆之间的关节被驱动
- 环境对应于 Acrobot 环境
- 状态由一组 6 个数字组成
- 动作:有 3 种可能的动作
- 每一步奖励 : -1
作者插图
根据萨顿和巴尔托在的《强化学习:导论一书中对 MDP 的主体-环境交互的定义,我们知道:
主体和环境在一系列离散的时间步长 t=0,1,2,3,…中的每一步都相互作用。在每个时间步骤 t,代理接收环境的状态的一些表示,并在此基础上选择动作。一个时间步骤之后,部分地作为其动作的结果,代理收到一个数字奖励,并发现自己处于一个新状态【3】。
因此,实现了代理-环境循环:在每一集里,我们得到:
- 初始状态调用
env.reset
- 代理选择一个随机动作,它取值[0,1,2]中的一个
- 通过 step 函数,应用随机动作并返回四个值:新状态**、上一步获得的奖励**、游戏结束时为真的标志完成以及用于调试的诊断信息**。******
- 为下一次迭代设置当前状态
现在,我们调用函数 show_videos 来显示视频,每集一个:
show_videos()
5.实施深度 Q 网络
在这一节中,我将展示如何在 Acrobot 游戏上用 Pytorch 实现深度 Q 网络。该模型是一个神经网络,它将状态空间的维度作为输入,并返回对应于每个可能动作的最佳 q 值。因为有三种可能的动作来移动机械臂,所以返回的输出数是 3。
6.体验回放
一旦我们定义了网络,我们就可以创建一个名为 ReplayMemory 的类。当观察序列中存在相关性时,神经网络可能不稳定或发散。出于这个原因,我们还需要体验回放,它使代理能够记住过去的体验并从中学习。此外,它使数据随机化,从而提供不相关的数据。
为了执行体验重放,我们将代理在每个时间步长 t 的体验 e_t=(状态,动作,下一个状态,奖励)存储在数据集 D_t={e_1,…,e_t}中。在学习过程中,我们应用 Q 学习更新,对经验(s,a,r,s’)~U(D)的样本(或小批量)进行更新,这些样本是从存储的样本池中均匀随机抽取的[4]。
在实践中,我们需要一个具有预定义容量的队列。当我们达到最大容量时,队列中最旧的元素将被新的元素替换。使用 python 集合库中的 deque 对象可以实现这种行为。
最大容量是ReplayMemory
对象请求的唯一输入。这样,我们用 maxlen capacity 定义了一个等同于 deque 对象的内存属性。
我们还定义了 push 函数,将新的体验添加到重放存储器中。
sample
方法被定义为从记忆中采样经验。体验次数等于 batch_size。如果请求的批量大于内存中当前的样本数,我们将获取所有样本。
7.ε贪婪策略
在执行体验重放之后,下一步是根据ε-greedy 策略选择并执行一个动作。该策略选择一个概率为ε的随机行动**,否则,选择对应于最高 Q 值的最佳行动。主要思想是代理在开始时探索环境而不是利用它。代理从环境中学习得越多,它就越会选择基于开发的最优行动。**
8.Softmax 策略
在给定温度参数的情况下,Softmax 策略用于基于通过将 softmax 应用于估计的 Q 值而获得的分布来选择最佳动作。有两种可能的情况:
- 温度越高,分布就越趋于随机均匀分布。
- 在零度时,策略将总是选择具有最高 Q 值的动作。
9.勘探剖面
我们使用 softmax 策略定义了一个指数递减的勘探配置文件:
soft max _ temperature = initial _ temperature * exponential_decay^(i)
10.培养
我们可以初始化超参数,如 SGD 优化器和 Huber 损失函数、ReplayMemory 对象、策略网络和目标网络。
函数 update_step 被定义为执行单个优化步骤。首先,它从重放存储器中抽取一批样本。之后,它为批中的每个元素创建张量。它还计算非最终状态的掩码,并连接批处理元素。
随后,它计算给定实际状态的策略网络的所有 Q 值。与策略网络不同,价值函数是基于使用目标网络的下一个状态来计算的。
最后,我们可以基于策略网络的 Q 值和使用目标网络计算的 Q 值的最大值来获得预期的 Q 值。
现在,是时候训练深度 Q 学习代理了,运行 800 集。在 for 循环中,我们只需要调用前面定义的所有函数。我们还会每 100 集放一次视频。
最后几集的结果
第 100 集视频
最后一集的视频
从上一集的视频中,我们可以注意到代理学会了解决任务。很明显,如果你也看了 100 集之后的视频,代理人还在探索最佳行动。训练结束后,我们还可以显示每集获得的累积分值:
开始时,累积奖励保持在-500。在 200 集之后,分数呈指数增长,保持在-100 左右。最后一步是测试代理是否真的学会了解决它的任务。类似于之前实现的随机代理。我们选择温度参数设置为 0 的最佳操作,而不是采取随机操作。这样,softmax 策略将总是选择具有最高 Q 值的动作。
我们可以观察到,在最终的测试结果中,累积奖励分数仍然很小。
最终想法:
在本教程中,我提供了使用 Pytorch 构建深度 Q 网络的概述。为了更好地理解它,更好的方法是将代码分成构建块,每次只关注一个块。起初,如果你以前只尝试过监督和非监督技术,这似乎很难。但经过一些努力,你将能够理解它,甚至将深度 q 网络应用到其他环境中。这里的 GitHub 代码是。
参考资料:
[1]https://gym.openai.com/docs/
https://www.nature.com/articles/nature14236
你喜欢我的文章吗? 成为会员 每天无限获取数据科学新帖!这是一种间接的支持我的方式,不会给你带来任何额外的费用。如果您已经是会员, 订阅 每当我发布新的数据科学和 python 指南时,您都会收到电子邮件!
AGV 路径的深度强化学习
通过对自动导引车路线安排使用强化学习来提高仓库生产率
带有 8 个工作站的 AGV 设置示例—(图片由作者提供)
在一个**配送中心(DC),**在拣货路线上从一个地点走到另一个地点可以占操作员工作时间的 60%到 70% 。减少步行时间是提高整体生产力的最有效方法。
在以前的一系列文章中,我已经分享了几种使用优化策略来减少仓库中操作员行走距离的方法。( 链接 )
当你有一个大的提货区时,这些方法有局限性。因此,使用自动导向车将货架直接送到操作人员手中的自动化解决方案现在非常流行。
本文将解释如何使用强化学习来组织这些机器人的路线,以确保最佳生产率。
💌新文章直接免费放入你的收件箱:时事通讯
一.从人对货物到货物对人
电子商务公司最早采用了这种从人工操作——人对商品——到商品对人的转变。
因为他们的业务量波动很大(促销、节日),参考范围很广,而且劳动力资源短缺:自动化对他们来说是必须的。
使用自动引导车进行货物到人的拣选
“货物到人”提货解决方案可在提货站将货物直接交付给您的操作员。您消除了操作员搜索商品所需的所有非增值时间。
车辆上的货架示例—(图片由作者提供)
货物储存在货架上,可以由这些车辆直接移动到提货站,在那里操作员将获得所需的数量来准备订单。
Pickstation 示例—(图片由作者提供)
AGV 安装布局
AGV 设置示例—(图片由作者提供)
在这个布局中,你有
- 8 个拾取站两个一组,每个站 1 名操作员
- 16(8×2)条货架大巷
- 1 个车辆充电站
二。构建您的优化模型
创建 AGV 布局的拓扑图
Djisktra 图表示例—(图片由作者提供)
我们的布局由图 G(N;e)
- n 是节点的集合(上面的圆圈)
- e 是边的集合(实线和箭头)
- s 代表货架(实心灰色节点表示存放货架的位置)
- r 代表 AGV 旋转货架的点
- w 代表等待点,带货架的 AGV 在此等待之前到达提货站的 AGV 完成提货活动
- p 代表提货点,提货人将在此提货
这种映射将包含在一个 AGV 拣选模拟模型中,该模型将用于测试我们的路径选择策略。
使用 Djisktra 算法的路径查找
Dijkstra 算法是一种优化算法,解决带权边(非负权)的有向图的单源最短路径问题。
Djisktra 图表示例—(图片由作者提供)
该长度可以是路径的绝对长度,也可以考虑位于边或节点上的其他约束来计算。
我们可以使用三种类型的权重,从节点 u 到节点 v 标注为 w(u,v)
- 最短距离路线权重: w(u,v) = d(u,v) (1)
用 d(u,v)表示 u 和 v 之间的距离
- **目标:**取距离最短的路线 - 最短行驶时间: w(u,v) = d(u,v)/s(u,v) + r(u,v) (2)
用 s(u,v)表示 AGV 平移速度和 r(u,v)表示所有旋转所需的时间 - **目标:**选择行驶时间最短的路线 - 拥塞避免: w(u,v) = d(u,v)/s(u,v) + r(u,v) + Co(u,v) (3)
with o(u,v)*AGV 计划通过边缘的数量,C 为调整权重的定值- **目标:*选择与其他 AGV 避免拥塞的路线
强化学习方法
在时间 t,我们通过下式定义仓库的状态:
- 所有活动车辆的空间位置(分配了路线的 AGV)**
- 所有活动货架的空间位置(有待拣选商品的货架)**
- 工作站订单行分配(需要转移物料的工作站)
这些参数会随时间变化,因此让我们使用强化学习方法根据这种状态从这些候选中选择最佳路线。
代理回报策略
使用三种不同的奖励值方法来奖励您的学习代理到达目的节点
- ***生产率:*从 AGV 从起点出发到到达目的地这段时间内,每个工时拣选的物品数量。
- ***空闲时间:*在用 AGV 从货架上拣货后,拣货员等待下一辆 AGV 的时间。
- ***速度:*AGV 从起点到终点的平均速度
流程—(图片由作者提供)
三。模拟
关注我的 medium,了解更多与供应链数据科学相关的见解。
场景
第一次模拟是基于三天的挑选:第一天用于训练;测试的第 2 天和第 3 天。
模拟的第一个场景—(图片由作者提供)
RL 模型的结果将与两种简单的路线规划
策略进行比较
- ***随机:*在最短距离路线、最短行驶时间路线和拥堵避免路线中随机选择一条路线
- ***拥塞:*始终选择拥塞避免路线
结果
每种策略的结果—(图片由作者提供)
令人惊讶的是,生产率奖励的效果不如速度奖励。试图最大化每个 AGV 的生产率可能不是车辆之间协作工作以确保高全球生产率的最佳方法。
当拥塞是主要瓶颈时(即,当您有高密度的车辆同时运行时),拥塞策略表现良好,同时与 RL 方法相比需要较少的计算资源。
后续步骤
这些结果基于只有两天提货活动的特定布局。为了更好地理解这种方法,我将在下一篇文章中解释如何构建 AGV 拣选模拟器并实现路由策略。
应该在各种订单配置文件上测试该模型,以测试通过调整对生产率的影响
- 每份订单的行数(每份订单的移动次数)
- 每行领料的单位数量
- 活动 SKU 的范围
如果你在 SKU 的某个特定群体、购物节(黑色星期五,11.11)或淡季有促销活动,策略的选择可能会有所不同。
超出
对于更传统的拣货流程,您可以找到使用高级分析工具和流程分析概念进行流程优化的示例。
*https://www.samirsaci.com/supply-chain-process-design-using-the-queueing-theory/ https://www.samirsaci.com/improve-warehouse-productivity-using-order-batching-with-python/ https://www.samirsaci.com/optimize-workforce-planning-using-linear-programming-with-python/
关于我
让我们连接上 Linkedin 和 Twitter ,我是一名供应链工程师,正在使用数据分析来改善物流运营和降低成本。
如果你对数据分析和供应链感兴趣,可以看看我的网站
参考
[1]使用深度强化学习的自动导引车辆路线规划策略的获取,IEEE 高级物流与运输国际会议(ICALT 2017)
[2]一种面向制造业多 AGV 调度的强化学习方法,,薛,彭增,
实验室。沈阳自动化研究所网络控制系统
[3]使用深度强化学习在线优化 AGV 运输系统,网络、计算、系统和软件通报,Kei Takahashi,Sogabe Tomah*
深度强化学习:从萨莎到 DDPG 及其他
思想和理论
抓住使 RL 成功的基本要素
让机器学习的能力是过去几十年的一项令人着迷的成就。许多新的商业机会已经开放,公司每天都在使用机器学习。
就在几年前,DeepMind 的 AlphaGo 算法打败了围棋世界冠军 Lee Sedol。这一惊人的壮举是在强化学习的帮助下实现的,但无疑不是一夜之间实现的。这个雄心勃勃的项目的研究始于 21 世纪初。从那以后,这个领域本身已经有了很大的发展。本文旨在涵盖向现代方法转变的一部分。
基础
在我们开始之前,我们必须缩小强化学习的含义:强化学习的目标是尽可能好地行动。这个定义是的简化版本
目标是找到一个使预期收益最大化的政策。
为了实现这一点,我们有几个组件协同工作,如图所示:
强化学习的循环。图片由作者提供,来自一个封闭的大学讲座的例子。
代理与环境交互,环境改变它的状态,并为动作产生奖励。然后,又一轮开始了。在数学上,这个周期是基于马尔可夫决策过程(MDP)。这种市场发展计划包括五个组成部分:南非、阿尔及利亚、菲律宾和 p₀.
s 是状态空间,它包含环境可能处于的所有可能状态 s 。例如,如果我们要为一辆汽车建模,它可能有以下状态:前进、后退、转弯、刹车等等。
第二个组件 A 是动作空间,它定义了代理可以执行的所有动作 a 。对于汽车设定,这可能是转动方向盘,加速,刹车。这样的行动导致了新的国家。
第三个分量 R 是奖励函数。该功能负责根据代理的动作和结果状态对其进行奖励。例如,如果汽车成功地在交通灯前停下来,代理人会得到一个积极的奖励。然而,如果它没有停止,代理人会得到一个负的奖励。
第四个分量 P 是转移概率函数。通常情况下,一个动作并不能保证会达到期望的状态。因此,该属性被建模为概率函数。例如,它可能会说:“有 90%的可能性,我们会到达理想的状态,但有 10%,可能会发生不同的事情。”
最后,我们有 p₀,它是一组初始状态。每当一个代理被训练,它开始在这些状态之一。
现在我们已经介绍了 RL 设置的要素,我们如何教代理学习期望的行为呢?这是在π策略的帮助下完成的,我之前简单地提到过。形式上,策略从状态空间 S 映射到所有可能动作的概率。听起来很复杂?考虑一下:你站在路边,想要过马路。这种情况就是你的起始状态。一个策略现在将列出所有可能的动作:向左看、向右看、直线向前跑、等待等等。所有这些选择都有被选中的概率。例如,你可能有 20%的机会选择向左看,有 50%的机会,你可能选择跑。
随着代理被训练,它优化策略。一开始,π可能会经常选择跑过街道。然而,由于代理人多次被车撞,他获得了许多负面奖励,这是他不想要的。随着时间的推移,代理人学会做得更好,导致越来越多的积极回报。最终,这将导致找到一个最大化总回报的政策。
但是,我们怎么知道我们现在的状态是好的呢?对于我们可以采取的行动,我们可能会问:我们如何知道它们是好的?这种评估借助于价值函数来实现。我们有两个,一个是国家的,一个是行动的。
第一个函数称为状态值函数,缩写为 v。对于每个状态,它“知道”这个状态有多好。状态的好与坏是由我们在这种状态下开始时期望得到的回报决定的。比如赛车中的杆位更容易赢得比赛;这位司机获得了积极的奖励。这一事实影响了杆位的价值:随着越来越多的人从这里开始并赢得比赛,从这种状态中获得的积极奖励的数量也在增加。反过来,极点位置的值增加。
第二个函数是动作值函数,缩写为 q。对于我们在给定状态下可以做的所有动作,它“知道”它们有多好。这个知识的获得与上面类似:导致好的回报的行为比导致负面回报的行为有更高的价值。
萨尔萨
现在我们已经讨论了基础知识,我们可以检查一个经典的 RL 算法,称为 SARSA [1]。这个名字是州行动奖励州行动的缩写,它巧妙地抓住了功能。
首先,我们从一个状态(S)开始,采取行动(A),得到回报®。现在我们处于后继状态,并选择另一个动作(A)。我们这样做是为了更新我们的 Q 函数。如前所述,行动的价值是当我们从一个状态开始并选择这个行动时,我们期望的总回报。同样的属性适用于下一个状态、下一个状态和下一个下一个状态。我们可以用动作值函数的更新规则来表达这一点:
Q(s,a) ← Q(s,a) + α(r + γ Q(s ‘,a’) — Q(s,a))
上面写了什么?我们的状态-动作对的新值是旧值加上,这是括号中的部分,与我们从这里得到的不同:r + γ Q(s ',a ')。所以随着时间的推移,我们的 Q 函数越来越接近我们得到的回报,这是由这个更新规则模拟的。
现在我们有了 Q 函数(或者有时称为 Q 值),我们能用它做什么呢?首先,这些值存储在一个表中。每当我们的策略必须选择一个操作时,它会检查它处于哪个状态,然后查看所有可能的操作:
从所有这些动作中,选择具有最高值的动作。这个动作导致了一个新的状态,在这个状态中,再次选择了最有价值的动作。在训练期间,Q 值按照先前引入的更新规则进行更新。一旦完成,我们就找到了一个合适的好策略,并准备在我们的环境中使用代理。
但是至少有一点我们可以改进算法。这就是 Q-Learning 发挥作用的地方。
q 学习
Q 学习算法[2,3]可以概括为非策略 SARSA。为了解释这一点,让我们再来看看 SARSA 的更新规则:
Q(s,a) ← Q(s,a) + α(r + γ Q(s ‘,a’) — Q(s,a))
我们的策略π选择一个初始动作,这是 Q(s,a)部分。然后,我们使用相同的策略来选择后续动作,也就是说 Q(s ',a ')的部分。使用相同策略来确定两个动作的过程由关键字 on-policy 表示。代理基于当前使用的策略所选择的动作来学习 Q 值。
相比之下,在非策略算法中,我们有不止一个策略。这可以从 Q-learning 的更新规则中看出:
Q(s,a) ← Q(s,a) + α(r + γ maxₐ’ (s ‘,a’) — Q(s,a))
关键的区别是选择最佳后续行动,这是而不是遵循政策完成的。相反,我们总是选择具有最高 Q 值的动作。这个选择过程可以理解为另一个用于确定后续行动的策略。好处是我们可以遵循不同的策略来选择后续操作。这看起来没什么大不了的,但是在 Q-learning 中,我们总是利用这个事实来选择一个最优的后续行动。
此外,它允许我们很容易地加入额外的影响。例如,考虑我们可能拥有的第二个代理。这个代理有一个独特的功能,我们希望我们的主要代理也能学习它。我们采用二级代理的策略,并在我们选择新的后续动作时使用它。
到目前为止,我们不需要任何神经网络。但是表格方法不适合大的状态和动作空间:首先,它们的内存消耗很大。其次,我们甚至可能不会在训练过程中访问所有的状态或动作,所以有些条目可能没有初始化。第三,查找时间过长。然而,作为第一个措施,我们可以使用神经网络来代替 Q 表。
深度 Q 学习
神经网络已经被证明具有良好的泛化能力。我们也可以利用它们从数据中学习特征的能力来进行强化学习。在深度强化学习中,我们用神经网络来逼近我们的函数,比如 Q[4]。为了突出这种修改,Q 或 V 函数通常标有θ: Q_θ和 V_θ。除了神经网络之外,还有其他方法,但这些不是本文的范围。
在默认的 Q 学习中,更新规则帮助我们找到最优的 Q 值。最优意味着它们会带来最高的回报;我们可以用它们来选择最佳行动(在给定状态下具有最高 Q 值的行动)。并且,通过使用神经网络,我们直接逼近这个最佳 Q 或 V 函数。我们从少量的训练数据中学习,然后可以推广到新的情况。
在深度 Q 学习算法中,我们使用两种技术,称为经验重放和目标网络。
第一个修改将旧的转换(通过选择一个动作从一个状态转移到下一个状态)存储在重放缓冲区中。在训练期间,我们从这个缓冲区取样。这项技术提高了效率,并使数据分布更加稳定。这是怎么回事?现在,想想如果我们有几个动作,并且在每个训练步骤中,我们选择不同的一个,会发生什么。我们总是在各种可能性之间跳跃。有了重放缓冲区,我们可以检测分布中的趋势,使动作的选择更加稳定。
第二个修改,目标网络,用于计算我们的目标。一般来说,我们的目标是最大化预期报酬。问题是,我们如何知道我们的奖励是否已经是最好的了?目标网络帮助我们解决这个问题。我们使用旧更新步骤中的网络,并将其 Q 值作为目标。这听起来可能很难,因此我们来看看目标网络的更新规则:
Q_θ(s,a) ← Q_θ(s,a) + α((r + γ maxₐ’ Qₜₐᵣ(s’,a′))—q _θ(s,a))
不同之处在于更新目标的计算。通过最小化我们的当前值 Q_θ(s,a)和 Qₜₐᵣ(s’,a’)之间的差,我们接近目标,这里缩写为“tar”。而这个目标就是目标网络的价值。
现在,如果我们不使用这样的目标网络,而只使用当前网络 Q_θ,我们的目标将不断变化:随着网络参数的每次更新,我们将得到不同的 Q 值。这就是为什么我们使用旧网络(意味着在我们当前网络之前的一些更新步骤)作为固定目标。它的参数更新得更慢,使目标更稳定。最终,我们降低了我们的主网络可能会被自己的尾巴所困扰的风险。
换句话说,想想跑步比赛。每当你接近终点线时,它就会后退。这样,你永远不知道你离目标有多近。
这两个提出的修改,使用一个重放缓冲器和目标网络,被用来帮助 RL 代理玩 Atari 游戏,有时甚至在超人的水平[5]。
深度确定性政策梯度
到目前为止,我们只考虑了离散动作空间。在离散的动作空间中,我们有固定数量的动作,只有一个单一的粒度级别。相比之下,想想门和墙之间的角度。如果我们只有,比方说,两个位置,我们有一个离散的空间。但是我们可以把门放在任意的位置:全开、全关、45 度、30 度、30.1 度、30.01 度等等。这个空间是连续的,用表格方法和深度 Q 学习方法都难以覆盖。
连续动作的问题是到目前为止我们对 Q 函数的更新步骤:
Q_θ(s,a) ← Q_θ(s,a) + α((r + γ maxₐ’ Qₜₐᵣ(s’,a′))—q _θ(s,a))
上面写着 maxₐ’ Qₜₐᵣ(s’的那一部分才是问题所在。在离散的行动空间中,很容易找到导致最高回报的行动。然而,在具有任意细粒度动作的连续动作空间中,这是非常昂贵的,如果不是不可能的话。
一个简单的解决方案是将空间离散化。但是,挑战在于如何离散化。然而,我们可以完全跳过这个问题,修改深度 Q 学习算法以支持连续动作。这导致了深度确定性策略梯度(DDPG)算法[6,7],它本质上是针对连续动作的深度 Q 学习。它也使用重放缓冲区和目标网络,但采用最大运算。
我们引入另一个神经网络,而不是“手动”执行对最佳状态-动作对(最佳 Q 值)的搜索。这个神经网络学习逼近最大化器。然后,每次我们用一个状态查询它,它都返回最佳的对应动作——这正是我们需要的。
取代最大化器的新网络然后被用于目标的计算。因此,前面的等式可以改写为
Q_θ(s,a) ← Q_θ(s,a) + α((r + γ Qₜₐᵣ(s’,μₜₐᵣ(s’))—q _θ(s,a))
正如我提到的,我们使用网络,μₜₐᵣ,来决定最佳行动。和以前一样,我们使用稍微旧一点的网络版本,因此有“tar”部分。即使μₜₐᵣ和 Qₜₐᵣ每一步都被更新,它们也永远不会被更新到最新的参数。相反,他们通常保留 90%的参数值,剩下的 10%来自当前网络。这样,目标网络既不会太旧,也不会太新。
双延迟 DDPG 算法[8]引入了三种改进,以提高默认版本的性能。首先,TD3,也是缩写,学习两个 Q 函数并使用较小的值来构建目标。此外,策略(负责选择初始动作)更新不太频繁,并且添加噪声以平滑 Q 函数。
熵正则化强化学习
所有算法面临的挑战是过早收敛。这种不受欢迎的行为是探索不足的结果。例如,如果代理已经确定了一个好的动作序列,他可能会关注这些特定的动作。在这个过程中,他没有探索其他州,而这些州可能会给他带来更好的回报。这种现象也被称为探索与开发的权衡:探索旨在探索动作和状态空间,而开发则寻求开发已经探索过的区域。
熵正则化强化学习方法是应对这一挑战的一种方式:我们训练策略来最大化如上所述的权衡。为了做到这一点,我们使用一个叫做熵的概念。简而言之,熵给出了关于分布“混沌”的信息。混沌程度越高(“分布越不均匀”),熵值越低。因此,所有值都有相同的机会被抽取的均匀概率产生最大熵值。
我们利用这个事实,在我们的价值函数中加入一个熵项。详细解释它们超出了本文的范围,但要点如下:附加术语强制执行策略,以在高回报(探索)和从各种行动中选择(探索)之间保持平衡。
在软演员-评论家算法[9,10]中,这个概念尤其用于训练机器人行走。虽然机器人只在平坦的地形上训练,但由此产生的策略足以应对训练期间看不到的环境。
强化学习库
上面所有的算法都已经在各种 python 包中实现了。OpenAI Gym 和 Safety Gym 框架提供了构建代理培训环境的代码。来自 DeepMind 的研究人员提供了控制套件包,用于基于物理的 RL 模拟。
要构建算法,可以从各种选项中进行选择。稳定基线 3 库是在 PyTorch 中实现的,它提供了比较算法和创建新算法的工具。类似地,ACME 库提供了 RL 代理和构建块,并且足够灵活,可以进行自己的研究。作为第三种选择,你可以考虑谷歌的多巴胺框架,它专注于投机性研究的快速原型制作。它支持 JAX 和张量流。
摘要
在过去的几十年里,强化学习领域经历了许多批判性的观点。该基金会是基于学习功能的理念建立的,这种学习功能可以量化处于特定情况下的优势。研究人员随后提议使用不同的政策来有效地探索环境。当在神经网络的帮助下学习函数时,向前迈出了重要的一步,这引入了算法设计的许多新的可能性。随着时间的推移,其他想法被纳入其中,如逐步更新作为训练目标的其他网络。然而,开发并没有完成。仍然有许多挑战需要克服,例如处理约束,弥合训练和现实生活环境之间的差距,或者可解释性。
文学
[1] Gavin Rummery 和 Mahesan Niranjan,使用连接主义系统的在线 Q-learning,1994 年,Citeseer
[2] C.J.C.H .沃特金斯,从延迟回报中学习,博士论文,1989 年,剑桥大学国王学院
[3] C.J.C.H .沃特金斯和彼得·达扬, Q-learning ,1992,机器学习 8
[4] Kurt Hornik,多层前馈网络的逼近能力,1991,神经网络 4
[5] Mnih 等人,用深度强化学习玩雅达利,2013,arXiv
[6] Silver 等人,确定性策略梯度算法,2014,机器学习国际会议
[7] Lillicrap 等,深度强化学习的连续控制,2015,arXiv
[8] Stephen Dankwa 和 Wenfeng Zheng,双延迟:一种深度强化学习技术,用于模拟智能机器人智能体的连续运动,2019,ACM
[9]哈尔诺贾等,软行动者-批评家:带随机行动者的离策最大熵深度强化学习,2018,ICML
[10]哈尔诺贾等,软演员-评论家算法与应用,2018,arXiv
[11]哈尔诺贾等,通过深度强化学习学习走路,2018,arXiv
优化广告投放的深度强化学习实践
本文使用深度强化技术来优化网站上的广告投放,以最大化用户点击概率并增加数字营销收入。提供了一个详细的案例研究和代码,以帮助用户在任何现实世界的例子中实现该解决方案。
联盟营销和点击付费是数字营销的两个重要方面。这些技术的优化实施可以极大地增加公司的产品/服务销售额,也可以为营销人员带来巨大的收入。随着深度强化学习的进展,数字营销是受益最大的领域之一。
传统的微调数字营销活动的方法需要大量的历史数据。这既耗费时间又耗费资源。通过强化学习,可以节省时间和资源,因为它们不需要任何历史数据或活动的先验信息。在这篇文章中,我们可以看到一个简单的深度 RL 技术如何优化一个相当复杂的数字营销活动,并取得几乎完美的结果。
在本文中,通过一个接近真实的案例研究,让我们看看强化学习如何帮助我们管理广告投放,以获得最大的利益。
问题陈述
我们管理着 10 个电子商务网站,每个网站都专注于销售不同类别的商品,如电脑、珠宝、巧克力等。我们的目标是通过将在我们的一个网站购物的顾客推荐到他们可能感兴趣的另一个网站来增加产品的销售。当客户查看我们的一个网站时,我们会显示另一个网站的广告,希望他们也会购买其他产品。我们的问题是,我们不知道客户应该被推荐到哪个网站,或者我们没有客户偏好的任何信息。
让我们用强化学习来解决问题吧!!
图 1:RL 背后的基本概念示意图
一般来说,强化学习是一种技术,在这种技术中,我们训练一个代理在一个环境中操作。代理人在状态“s”采取行动“a ”,并从环境收到行动的奖励“r”。所以(s,a,r)成为一个状态-动作-回报元组。我们培训的目标是使代理获得的总回报最大化。因此,我们找到了(s,a,r)元组,它对于给定的状态和动作具有最大的回报。为了找到优化的元组,我们运行了许多集,每次都重新计算奖励。
在这个广告投放问题中,我们需要测试不同的行为,并自动学习给定情况、状态或背景下最有益的结果。所以我们称之为语境强盗框架,其中状态成为语境信息,代理为当前语境找到最佳行动。
比方说,我们有 10 个网站要管理,它们构成了 10 个不同的州,客户在其中一个网站上。因为我们有 10 种不同的产品类别,所以我们可以向客户展示这 10 种产品中的任何一种。所以每个州有 10 个不同的动作。这将导致 100 个不同的状态-动作-回报元组。我们需要存储 100 个不同的数据点,并在每次有新的奖励时重新计算。在这个例子中,这似乎是合理的。但是,如果我们有 1000 个网站要管理,这将产生 1000000 个数据点。存储和重新计算这将花费大量的时间和资源。
这意味着当状态和动作空间很大时(状态和动作的总数很大),强化学习会失败???
这就是深度强化学习的由来。我们使用神经网络来提取每个状态和动作的奖励值,而不是存储每个状态、动作和奖励元组。神经网络非常擅长学习抽象概念。他们学习数据中的模式和规律,并可以将大量信息作为权重压缩到他们的内存中。因此,神经网络可以学习状态——行动和奖励之间的复杂关系。
神经网络充当从环境中学习以最大化回报的代理。在本文中,我们将使用 PyTorch 构建一个神经网络,并训练它来优化广告投放,以获得最大回报。
让我们从编码开始吧!!
让我们首先为上下文强盗创建一个模拟环境。这个环境应该包括代表 10 个网站(0 到 9)的 10 个状态和产生广告点击奖励的方法,以及选择一个动作(显示 10 个广告中的哪一个)的方法
class ContextBandit:
def __init__(self, arms=10):
self.arms = arms
self.init_distribution(arms)
self.update_state()
def init_distribution(self, arms): #**1**
self.bandit_matrix = np.random.rand(arms,arms)
def reward(self, prob):
reward = 0
for i in range(self.arms):
if random.random() < prob:
reward += 1
return reward
def get_state(self):
return self.state
def update_state(self):
self.state = np.random.randint(0,self.arms)
def get_reward(self,arm):
return self.reward(self.bandit_matrix[self.get_state()][arm])
def choose_arm(self, arm): #**2**
reward = self.get_reward(arm)
self.update_state()
return reward
这是一个代表每个州的矩阵。行代表状态,列代表臂(动作)
选择一只手臂(动作)会返回奖励并更新状态
下面的代码显示了如何使用环境
env = ContextBandit(arms=10)
state = env.get_state()
reward = env.choose_arm(1)
print(state)
>>> 1
print(reward)
>>> 7
该环境由一个名为 ContextBandit 的类组成,该类可以通过 arm(动作)的数量进行初始化。在这个例子中,我们采取的状态数等于动作数。但这在现实生活中可能会有所不同。该类有一个函数 get_state(),调用它时会从均匀分布中返回一个随机状态。在现实生活的例子中,状态可以来自更复杂的或与业务相关的分布。用任何动作(arm)作为输入调用 choose_arm()将模拟投放广告。该方法返回对该动作的奖励,并使用新状态更新当前状态。我们需要一直调用 get_state()然后选择 _arm()不断获取新数据。
ContextualBandit 也有一些辅助功能,如一键编码器和 *softmax。*一键编码器函数返回一个 1 和全 0 的向量,其中 1 代表当前状态。Softmax 函数用于设置每个状态下各种动作的奖励分配。对于 n 个状态中的每一个,我们将有 n 个不同的 softmax 奖励分配。因此,我们需要了解状态和它们的动作分布之间的关系,并为给定的状态选择概率最高的动作。下面提到了这两个函数的代码
def one_hot(N, pos, val=1): #N- number of actions , pos-state
one_hot_vec = np.zeros(N)
one_hot_vec[pos] = val
return one_hot_vecdef softmax(av, tau=1.12):
softm = np.exp(av / tau) / np.sum( np.exp(av / tau) )
return softm
现在让我们创建一个具有 ReLU 激活的两层前馈神经网络作为代理。第一层将接受 10 个元素的一次性编码向量(状态向量),最后一层将输出 10 个元素向量,表示每个动作的奖励。
图 2:计算图表
从图 2 中,我们可以看到 get_state()函数返回一个随机状态值,该值使用一位热码编码器转换为 10 个元素的向量。该向量作为输入被馈送到神经网络。神经网络的输出是 10 个元素的向量,表示给定输入状态下每个动作的预测回报。输出是一个密集向量,使用 softmax 函数进一步转换为概率。基于概率,选择样本动作。一旦选择了动作,choose_arm()就会获得奖励,并使用环境中的新状态进行更新。
最初,对于状态 0,神经网络将产生类似于[1.4,50,4.3,0.31,0.43,11,121,90,8.9,1.1]的输出向量。在运行 softmax 并对动作进行采样后,最有可能的动作 6 将被选中(最高预测奖励)。选择动作 6 运行 choose_arm()后会产生奖励比如说 8。我们训练神经网络用[1.4,50,4.3,0.31,0.43,11,8,90,8.9,1.1]更新向量,因为 8 是实际的奖励。现在,下一次神经网络将预测,每当看到状态 0 时,行动 6 的奖励接近 8。当我们在许多状态和动作上不断训练我们模型时,神经网络将学习为各种状态-动作对预测更准确的回报
下面是创建神经网络和初始化环境的代码
arms = 10
N, D_in, H, D_out = 1, arms, 100, armsmodel = torch.nn.Sequential(
torch.nn.Linear(D_in, H),
torch.nn.ReLU(),
torch.nn.Linear(H, D_out),
torch.nn.ReLU(),
)
loss_fn = torch.nn.MSELoss()
env = ContextBandit(arms)
现在,让我们看看如何训练代理,并按照图 2 中说明的所有步骤进行操作
def train(env, epochs=5000, learning_rate=1e-2):
cur_state = torch.Tensor(one_hot(arms,env.get_state())) #1
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
rewards = []
for i in range(epochs):
y_pred = model(cur_state) #2
av_softmax = softmax(y_pred.data.numpy(), tau=2.0) #3
av_softmax /= av_softmax.sum() #4
choice = np.random.choice(arms, p=av_softmax) #5
cur_reward = env.choose_arm(choice) #6
one_hot_reward = y_pred.data.numpy().copy() #7
one_hot_reward[choice] = cur_reward #8
reward = torch.Tensor(one_hot_reward)
rewards.append(cur_reward)
loss = loss_fn(y_pred, reward)
optimizer.zero_grad()
loss.backward()
optimizer.step()
cur_state = torch.Tensor(one_hot(arms,env.get_state())) #9
return np.array(rewards)
- 1 获取环境的当前状态;转换为 PyTorch 变量
- 2 向前运行神经网络以获得奖励预测
- 3 用 softmax 将奖励预测转换成概率分布
- 4 对分布进行规范化,以确保其总和为 1
- 5 概率性地选择新动作
- 6 采取行动,获得奖励
- 7 将 PyTorch 张量数据转换为 Numpy 数组
- 8 更新 one_hot_reward 数组作为标注的训练数据
- 9更新当前环境状态
在对网络进行了大约 5000 个纪元的训练后,我们可以看到平均回报有所提高,如下所示
图 3:培训后的平均回报
我们可以看到平均奖励达到 8 或以上。
整个项目可以在这个 GIT 链接中找到。
本文基于布兰登·布朗和亚历山大·扎伊的《深度强化学习行动》一书。这本书的链接是这里
请在下面找到我关于各种数据科学主题的其他文章的链接
https://nandakishorej8.medium.com/multivariate-timeseries-forecast-with-lead-and-lag-timesteps-using-lstm-1a34915f08a https://nandakishorej8.medium.com/realtime-2d-yoga-pose-estimation-with-code-walk-through-cfd69262d356 https://nandakishorej8.medium.com/auto-tuning-multiple-timeseries-sarimax-model-with-a-case-study-and-detailed-code-explanation-c136293b8457 https://nandakishorej8.medium.com/introduction-to-pytorch-lightning-framework-for-enlightning-research-fe953bbea03b
非常欢迎反馈。你可以在 LinkedIn 上联系我
深度分裂 Q-learning 和 Pacman 女士
思想和理论
通过修改更新规则在深度 RL 环境中重新创建分裂 Q 学习的尝试
2013 年,谷歌 DeepMind 的一篇论文引发了深度强化学习(RL),特别是深度 Q 学习网络(DQN)的爆炸。2015 年,这是在基础上改进的,具有双重深度 Q 学习网络。这个网站和其他网站上有许多帖子详细介绍了如何构建一个在那篇论文中非常成功的网络版本;这篇文章是关于扩展这个框架到最近的一个想法:分裂 Q 学习。分裂 Q 学习已经在一些不同的环境中被提出,但是林等人的论文。2020 年自治主体和多主体系统国际会议(AAMAS)接受的人工智能是本文的重点。
在这里,我们将看看这种范式是什么,并尝试在《吃豆人 MDP》上实现它的深度 Q 学习网络,这是范·哈瑟尔特等人在 2015 年的论文中唯一表现不佳的游戏之一。艾尔。代码可以在这个链接上公开获得,它源于我在研究生院做的一个项目。
OpenAI Gym 的 MsPacman 环境的开始状态
背景
DeepMind 架构已经在很多地方解释过了,在健身房的突破环境的模型和教程可以在成为人类找到。我们的版本将使用 PyTorch vice TensorFlow,所以如果你没有看过他们的基本深度 Q-learning 教程你可能也想看看,我假设读者至少对 Q-learning 有点熟悉。
作为概述,2015 年的论文以 RGB Atari 图像作为游戏状态,以分数作为主要的奖励系统。最高分范·哈塞尔特等人。al 调优的双深度 Q-Learning 网络在 50M 训练迭代后,在特定的评测实验中能够达到 was 3210。
林等人提出的分裂 Q 学习。人工智能是一种将奖励分为积极和消极两种的想法。在正常的 Q 学习中,对模型或表的更新是基于预期奖励(Q 值)和实际奖励(奖励加上下一个状态中的最大 Q 值)之间的差异。如何应用这种差异可以有所不同,我们可以使用 L1,L2 类型的更新,但我们通常也会将学习率α应用于我们决定使用的任何更新。数学上,对于 Q 表、状态 s、行动 a、奖励 r 和新状态 s’,这看起来像:
这里,γ是预计未来回报的折现率。等式 1 是来自贝尔曼等式的 Q 值的定义,而等式 2 来自时间差分(TD)型训练。然后基于学习率α: Q(s,a) ← Q(s,a) + α*diff 更新 Q 表。在林等人提出的分裂 Q 学习中。al,有一个单独的奖励和惩罚表(或模型),它遵循的思想是,生物制剂倾向于用单独的而不一定是排他的机制来处理奖励和惩罚。除了分流,林等人。al 还建议改变如何通过两个超参数来更新 Q 值。其中一个,W,在每个状态转换时缩放奖励。另一个λ缩放更新前的旧 Q 值:
注意:方程式编号可能有问题
这两个表是奖励(+)和惩罚(-)的。深度 Q-learning 的这次更新特别困难的是,目标不仅仅是基于贝尔曼方程(方程 1),它是λ的目标的折扣版本,好像深度 Q-learning 在移动目标和收敛方面没有足够的问题!值得注意的是,αt(学习率)和γ(折扣)在正向和负向流之间共享。对于 Q 表的更新规则,林等。所有人都能够在代理人身上表现出与某些奖赏处理障碍的临床行为相匹配的紧急行为。最值得注意的是,他们的“慢性疼痛”模型将两个奖励超参数都设置为 0.5,显示出与实际慢性疼痛患者的临床行为相似的紧急行为。我最初的目标是用深度分裂 Q-learning 框架复制这种行为,并可能尝试改进 Hasselt et。al 基线,虽然这两个都还在工作。
预备:预处理、重放缓冲、深度 Q 学习
让深度 Q 学习网络(DQN)和双重深度 Q 学习网络(DDQN)收敛有几个重要的技巧,因此我们将详细介绍它们,以及它们是如何在这个项目的 PyTorch 代码库中实现的。第一个是我们的状态表示和重放缓冲区。MsPacmanDeterministic-v4(我们将使用的健身房环境)中的状态表示为大小为(3x210x160)的 RGB 图像,并且是 0–255 范围内的整数元素。
为了节省内存,我们将这些帧存储为向下采样的灰度图像,并将它们转换为 uint8 (8 位与 64 位相比,存储的 100000 多个帧有很大的不同)。根据 DeepMind 的论文,我们还希望包含最后几帧,以给当前图像一些上下文,并防止代理在每次帧更新时计算动作。这个预处理步骤被封装在下面的代码片段中:
.unsqueeze(0)
是因为我们将把 4 个帧连接成一个状态供网络处理,所以一个图像的输出大小是(1x105x80),但是一个状态最终将是(1x4x105x80)。暂时忽略 self 参数,它最终将成为 DQN 包装类的一部分。
重放缓冲区只是一个存储大量 MDP 转换的对象。您可能还记得,MDP 转换采用一个状态-动作对,并返回新状态 s’和采取该动作的即时回报 r。在我们的实现中,我们将在更高的级别收集这些不同的元素,并将我们需要的所有内容的元组传递到缓冲区。这个缓冲区被实现为一个环形缓冲区,这意味着一旦我们超过了允许的最大元素数,它就开始覆盖它的元素。重放缓冲区的目的是能够从缓冲区中对成批的随机元素进行采样:这打破了状态之间的相关性,并允许我们使用深度神经网络来估计 Q 值。
实际的代理类需要用超参数(现在是 gamma,和某种探索/开发方案)初始化,初始化游戏状态,初始化模型,并运行下面的训练循环:
- 决定一项行动(勘探/开发)
- 采取行动,记录到重放缓冲区的转换
- 从重放缓冲区取样并训练策略网络
在本文中,我将抽象出 DQN 类(实际的火炬网络)的细节,因为它对于本次讨论并不重要。只要说它有三个卷积层、一个展平层和所有动作的估计 Q 值的输出就足够了。正如以上教程中所解释的,输出完整的 Q 值集并使用一键编码来处理反向传播和单动作选择会更快。
我将简要提及,由于 DeepMind 论文等论文中的一些结果,我们正在使用 Huber Loss(smoothl Loss)和 RMSprop 优化器进行训练,但当然还有其他选项(例如 MSE)。下面是我们的初始化和内务功能(环境、epsilon 缩放、超参数):
如果你对这个循环还不太熟悉,我建议你去读一读更深入的 Q-learning 或者深度 Q-learning 教程,比如上面提到的那些。第二项是大多数环境争论发生的地方。因为我们的状态代表四帧,所以我们需要聚集奖励,并在环境中的一些时间步长上连接处理过的图像。这是由下面的代码块完成的:
一个“动作”时间步由多个帧组成。生命[‘ale.lives’]在下面进一步解释
然后,我们将一组 old_state, action, reward, new_state, is_done
传递给重放缓冲区,并使用重放缓冲区的sample()
方法拟合来自样本的模型。
我们需要讨论的最后一个初步主题是深度 Q 学习的训练方法。您可能已经注意到,在__init__()
方法中,我们创建了两个 DQN 模型:一个策略模型和一个目标模型。您可能还记得,在 Q 表中,我们通过 Q(s,a)←Q(s,a) + αL(diff)来更新表。在一个模型中,我们不能更新值*;我们必须通过某种形式的梯度下降来更新模型的参数。但是更新规则应该是什么,它转移到 split-Q 学习吗?一种方法是根据对 Q 表的时间差异更新来查看更新规则,并将其推广到深度 Q 学习框架。TD 本质上是上面的等式 2,其中 r+γmax(Q(s’,a’))项是损失函数的目标,而策略网络对 Q(s,a)的输出被调整:
范.哈瑟尔特等人的形式主义。阿尔,2015
这里θ’是目标网络,θ是策略网络。对于完整的训练循环,我们需要做的最后一件事是确定ϵ缩放和目标网络更新规则。这些最好从文献和做一些实验开始。MsPacman 上的典型 DQN 示例如下所示,其中τ(目标网络更新周期)和ϵ(勘探/开采政策)具有不同的超参数。
上图:慢速ϵ斜坡和 300 个时期的目标网络更新。Bot:快速ϵ斜坡与 500 个时代的目标网络更新。
完整训练循环的最后一个修改是增加了对失去一条生命(影响一个幽灵)的惩罚的能力,这不是内置在 MsPacman 健身房环境中的,但在 Lin et 中有很大的特点。艾尔关于分裂 Q 学习的论文。为了做到这一点,我们可以利用 gym 在 python 字典中输出剩余生命数的事实(lives = lives['ale.lives']
)。我们现在可以建立一个完整的 Q 迭代:
深度 Q 学习网络的 Q 迭代。第 26 行允许我们修改撞击幽灵的惩罚
拥有一个total_reward
和一个total_score
变量的原因是,尽管它们在这个实现中做同样的事情,但是在分离 Q-learning 实现中它们是不同的。基本的训练循环是在指定数量的迭代或时期内保持运行这个 Q-学习函数,在达到终端状态的任何时候重置环境。fit_buffer()
函数本质上实现了等式 7 和 8,但是我们将把它留给分裂 Q 学习实现。
深度分裂 Q 学习网络和双重深度 Q 学习
为了尝试将分裂 Q 学习方法移动到深度学习框架中,我们必须改变更新规则,以尝试匹配等式 4 和 5。从方程 5 开始,如果我们去掉学习率,我们得到一个有趣的方程,它成为贝尔曼对 Q(s,a)的定义,如果λ=1。暂且不考虑奖励权重,这看起来像是:
等式 10 本质上只是从等式 5 中移除α,但它对于 Lin 等人的 Q(s,a)的不同定义也有一定意义。艾尔的分裂 Q 学习范式。因为他们对历史进行贴现,所以新的 Q 值有两个目标:λQ(s,a)和 r + γmax(Q(s ',a ')。通过从通常的更新中减去 (1-λ)Q(s,a ),这些方程本质上是制定一个新的更新规则,该规则逐渐将 Q 值向两个目标移动。我们现在可以将 TD 逻辑应用于上述内容,并将其转移到深度 Q 学习范式,其中(λ-1)Q(s,a)项由目标网络计算,并添加到正常的 r + γ(π(s))项。右手边的 Q(s,a)项由策略网络计算,现在我们有损失函数的两个项:
换句话说,7–8 和 12–13 的唯一区别是等式 12 中的新项:(1-λ)Q(s,a;θ),它将目标移向贴现 Q 值。如果λ=1,这就简化为正常的 Q 更新规则。当我们加入即时奖励的权重时,完全分割 Q 学习算法是:
深度分裂 Q 学习算法,基于等式 10-13,L 代表损失函数
我们还可以实现一个版本的深度分裂 Q 学习,它遵循双重深度 Q 学习方法。双重深度 Q 学习和深度 Q 学习之间的区别在于,在计算未来折扣奖励时,策略网络用于选择行动,而目标网络用于评估这些行动。数学上,Y 的定义变成:
这是在等式 12 中用于分裂 Q 学习更新规则的一个相当容易的替代,我们将把这个想法用于fit_buffer()
实现。这种两个流方法的“最佳动作”是两个流的 Q 值之和的最大可能动作。但是,在培训期间,我们仍然使用每个流来计算基于该流的最佳行为的未来折扣奖励。我们只对 Q 值求和,以实际选择给定状态下的最佳行动。下面是代码中初始化和最佳操作选择的样子:
最后要实现的是fit_buffer()
函数,它处理模型的训练更新。该功能必须完成以下步骤:
- 将批处理解包为状态、动作、奖励、惩罚、下一个状态、完成
- 使用策略网络来选择下一个状态中的最佳行为,以进行奖励和惩罚
- 使用目标网络评估未来的贴现报酬,以及目标中(1-λ)Q(s,a)项的 Q(s,a)
- 计算策略网络对每个流的 Q 值的估计
- 计算每个流的损失和反向传播
下面是我对奖励流(惩罚流实现完全一样)的这个函数的实现。
如果不知道 DQN 是如何工作的,这个函数的一些细节可能很难理解,但它需要一个动作和状态输入的掩码,这就是为什么有很多一个热向量和一个向量飞来飞去。随意浏览项目,看看 DQN 类是如何工作的,以及 Q-learning 和双 Q-learning 的实现。
结果和结论
延伸林等人。al 从 Q 表到深度 Q 学习网络的分割 Q 学习不是一个简单的任务,但是我相信这代表了这样做的逻辑框架。目前,我必须提供的唯一评估是代理在用类似于 Lin et 的参数初始化时的行为。艾尔的作品。在这个实验中,我修改了流,在每个时间步长提供-1 的惩罚,在每次失去一个生命时提供-500 的惩罚。然后,我用. 5 初始化了一个代理,奖励权重 W 和过去的折扣λ(他们的慢性疼痛模型)。在林等人看来。艾尔的论文,这导致了一个代理人,不参与奖励寻求行为,但有能力避免鬼,如果他们太接近。避免 MsPacman 中的幽灵似乎是这些 CNN 风格的 Q 值估计网络的一个主要挑战(可能是因为幽灵倾向于在存在中闪现和消失),所以我们将比较双重深度 Q 学习网络和慢性疼痛(CP)代理。每个代理被训练 10,000 集,在 500,000 次迭代中 epsilon 从 1 到 0.1 渐变。
DQN 的代理人在下面,但还没有真正学会避开幽灵。相反,它的策略是吃下强力药丸,以防止鬼魂在游戏的开始阶段成为一个因素。
慢性疼痛剂训练了相同的时间,并开始表现出一些行为。艾尔观察到了,但仍偶尔从事寻求奖励的行为。可能 10,000 个训练集不足以使 Q 值收敛到 Lin et 的行为。艾尔在他们的模型中看到了,但令人鼓舞的是看到了冷漠和回避风险的开端!
参考
[1]柏寒·林、吉列尔莫·切奇、贾雷尔·布内夫、詹娜·赖宁和伊琳娜·里什。2020.两股潮流的故事:来自人类行为和神经精神病学的强化学习模型。arXiv:1906.11286 [cs。LG]
[2] Hado van Hasselt、Arthur Guez 和 David Silver。2015.采用双 Q 学习的深度强化学习。arXiv:1509.06461 [cs。LG]
[3] V. Mnih、K. Kavukcuoglu、D. Silver、A. A .鲁苏、J. Veness、M. G .贝勒马尔、A. Graves、M. Riedmiller、A. K. Fidjeland、G. Ostrovski、S. Petersen、C. Beattie、A. Sadik、I. Antonoglou、H. King、D. Kumaran、D. Wierstra、S. Legg 和 D. Hassabis。通过深度强化学习的人类水平控制。自然,518(7540):529–533,2015。
使用单调注意力的深度学习文本校正器(带有数据集创建)
深度学习模型从语法上纠正句子。使用单调注意的 Seq2Seq 模型。
图片演职员表
目录
- 简介
- 数据集创建
- 注意机制
- 单调注意
- 实现和代码演练
- 推理→波束搜索 v/s 贪婪搜索
- 结果和误差分析
- 端到端管道
介绍
序列对序列模型被证明是人工智能在自然语言处理领域的最佳应用之一。注意力机制通过模仿人类理解句子的方式这一简单概念,大大改进了 seq2seq 模型。
在这篇博客中,我建立了一个机器学习模型,它使用单音注意力纠正句子中的基本语法错误。
我的模型解决的一些扰动是——
- 纠正使用限定词(a,an,the)的错误。
- 删除从句——“that”。
- 替换单词 modal(“可能”→“将”)
- 删除动词形式(“is”、“are”、“was”、“was”)
- 将“than”替换为“then”,反之亦然。
- 用“他们”代替“他/她”。
为此案例研究创建数据集
为了这个项目,连同电影语料库数据集(康奈尔电影对话语料库),我从头开始创建了一个数据集。我用哈利波特小说作为我的原始资料。使用正则表达式完成了文本预处理。查看完数据中的差异后,这些是我进行的预处理步骤。其中一些是-
- 从 Mr 和 Mrs 中删除点号- 这一步是确保段落被正确分割成句子所必需的。(第 4 点)
xx_v2 = re.sub(r'Mr\.', 'Mr', str(xx), flags=re.IGNORECASE)xx_v2 = re.sub(r'Mrs\.', 'Mrs', xx_v2, flags=re.IGNORECASE)
2。删除一些特殊字符- 当这本书从 pdf 转换成 txt 时,一些特殊符号变成了文本代码,如—
\ xe2 \ X80 \ x94 →""
\ xe2 \ X80 \ x99→" ’ "
\ xe2 \ X80 \ xa6→"…"
因此,使用正则表达式检测并删除了这些内容。
xx_v3 = re.sub(r"\\xe2\\x80\\x94|\\xe2\\x80\\xa6|\\xe2\\x80\\x98|\\n|\\t", " ", xx_v2, flags=re.IGNORECASE)
3。 修复资料- 一部小说包含了大量不同人物所说的引用文字。在将 pdf 转换为文本时,它们也被转换为一些文本代码。然后这些被检测到并被替换为其原始形式,即
\ \ xe2 \ X80 \ x9c→"
\ \ xe2 \ X80 \ x9d→"
xx_v4 = re.sub(r"\\xe2\\x80\\x9c", '"', xx_v3, flags=re.IGNORECASE)xx_v4 = re.sub(r"\\xe2\\x80\\x9d", '"', xx_v4, flags=re.IGNORECASE)
4。在句号上拆分数据- 将数据转换成句子。
dat_v4 = xx_v4.split(".")
5。最后一步——提取句子
→在一部小说中,有很多这样的例子,当一个角色说了一些写在引用的文本中的话,然后是这句话“这个角色说的”。
例如→“我要走了”哈利说。这种类型的句子需要删除,因为它们是半句。
因此,我们通过识别诸如“说”、“小声说”、“问”等词来删除这些半句***
→最后,同样在这之后,有些句子是疑问句。所以,我们再次用分割。拆分("?)但是保留问号,以确保我们的模型理解肯定句和疑问句之间的区别。
*** * 注意-同样,如果报价中有完整的句子,它们在步骤 4 中已经被正确拆分。*
不
我们也对电影语料库数据集进行类似的预处理。
注意机制概述
注意:假设读者精通注意力,我将只给出注意力机制的概述。如果你是新关注者,请阅读 这篇 的博客。
概观
“注意力”这个名字本身给了我们这个算法做什么的想法。在普通的编码器-解码器模型中,编码器的所有中间状态都被丢弃,最终状态被用作解码器的初始状态。背后的想法很简单-最终状态包含输入句子的所有信息,解码器可以使用这些信息来预测输出。但是,当序列很长时,这个模型很难将所有信息编码到一个向量中。
而在注意机制中,我们试图模仿人类处理顺序信息的方式。我们通过给每个单词(编码器中间状态)加权来做到这一点,从而给予注意特定预测的特定单词。这是通过前馈神经网络实现的,该网络将编码器的所有中间状态以及先前的解码器状态作为输入,并给出该时间戳的每个字的权重。
单调注意
注意,在每个时间戳,我们计算所有单词的权重,以确保我们不排除 say- 第一个单词影响第三个单词的预测或者 say 最后一个单词影响第一个预测的可能性。换句话说,注意力独立地考虑所有的中间状态,因此,时间戳的输出可以依赖于任何输入单词(意味着任何单词可以具有更大的权重)
该图描述了注意力模型
注意机制权重图
输出可以根据任何字对其进行预测
也就是说,一旦输入序列元素在给定的输出时间步被关注,在它之前出现的元素就不能在随后的输出时间步被关注。
单调注意力权重图
从图中我们可以清楚地看到,第一个输出依赖于第一个字,因此第二个输出不能使用第一个字,而是使用第二和第三个字进行预测。它严格地从左向右进行。
在单调注意中,只有以前的输出没有使用过的词才能对现在的输出产生影响,并且严格地从左到右
这使得模型变得不那么复杂,并且在这个案例研究中对我特别有帮助,它的表现远远好于注意力。
实现和代码演练
让我们在数据集上实现单调注意,并创建一个文本校正器模型。
我们将从数据集的基本预处理开始
文本预处理
现在是时候添加一些扰动了
使用教师强制*为编码器-解码器创建输入-输出对。
教师强制是一种方法,其中我们显式地将输入给解码器,而不是将解码器的输出传递给下一个时间戳。这显然只在训练时使用,有助于减少训练时间和提高准确性。
dataframe['decoder_input'] = '<start> ' + dataframe['output'].astype(str)dataframe['decoder_output'] = dataframe['output'].astype(str) + ' <end>'dataframe = dataframe.drop(['output'], axis=1)
举例-
编码器输入缺少“a”
→ 之后,我们将数据集分为训练、验证和测试。
→ 然后我们对数据进行标记化,将单词转换成整数标记来训练模型。
预训练向量的嵌入矩阵-
在对数据进行标记化之后,我们使用 GLOVE-300 预训练向量为我们的正确 _ 标记和扰动 _ 标记创建包含我们的单词的预训练嵌入向量的嵌入 _ 矩阵。
正确标记的嵌入矩阵
实现我们的深度文本校正器模型
在所有的预处理和创建嵌入矩阵之后,我们准备好创建我们的单调关注编码器-解码器模型。
我们将使用模型子类化的方法从 Keras 的模型和层类中导出我们的模型。
编码器模型实现如下-
之后,我们创建→单调注意层、单步解码器层、解码器模型和包含编码器和解码器的最终模型。
关于我的实现的全部细节,请查看我的 Github 简介
数据管道
现在我们需要一些东西,可以把我们的数据,转换成所需维度的批量数据。为此,我们通过从 Keras 的序列层派生来创建数据管道
培养
终于到了我们训练模型的时候了!
我们正在对“ADAM”优化器和自定义损失函数进行培训,以确保我们不会计算填充值的损失,因为我们已经大量填充了数据。
train_steps=train.shape[0]//512valid_steps=validation.shape[0]//512attention__.fit(train_dataloader, steps_per_epoch=train_steps, epochs=120, validation_data=validation_dataloader, callbacks=[es,chkpt,tfboard], validation_steps=valid_steps, )
训练图(蓝色->验证)
推理
训练完模型后,我们该检查它的效率了。我们将使用 BLEU 分数作为我们的衡量标准。
贪婪搜索 v/s 光束搜索
我们使用两种类型的搜索来预测我们的产出。
剧透预警!!!→ 它们的表现相似,因此我最终使用了贪婪搜索。
贪婪搜索→
该模型的输出是一个巨大的概率表,包含我们的单词在词汇表中的概率。在贪婪搜索中,我们只是简单地选择概率最高的单词。
贪婪搜索的推理
波束搜索
在 beam 搜索算法中,我们不是选择一个概率最高的词,而是选择前 3 个词(beam_index=3)。基于这前 3 个单词,我们预测下一个单词,并用我们之前的单词计算这些单词的条件概率。这个过程一直持续到我们到达终点。
波束索引= 3 的波束搜索的实现
波束搜索推理
结果和误差分析
正常情况下,我在训练数据集上的 BLEU 分数约为 0.90 ,而验证和测试数据集的 BLEU 分数约为 0.75 。
但是随着单调的关注,我得到了训练数据集的 0.98 BLEU 分数,而测试和验证数据集的0.90BLEU 分数。这是一个显著的改进!
误差分析
我分析了错误,发现我们的模型存在以下问题,这些问题可能是导致在我们的分数中遗漏 0.10 的原因。
1
2
端到端管道
在这个项目的最后阶段,我创建了一个端到端的部署管道,它接收输入并预测输出,可以直接用于任何应用程序。
首先,我创建了一个模块(。py 文件),名为 monotonic_attention,包含我们的编码器-解码器模型。
然后,我创建了另一个名为 model.py 的模块,它导入上述模块并预测输出。它包含一个 predict()方法,该方法使用贪婪搜索执行推理
导入后,我们用所有文件和一个 model_weight 初始化我们的模型
预测
如果你已经走到这一步。哇!谢谢你!看了我的文章。我希望你没有直接跳到这里!;)
参考
- 单调关注论文—https://Colin raffel . com/blog/online-and-linear-time-Attention-by-enforcing-Monotonic-alignments . html
- https://github . com/UdiBhaskar/tfk eras-Custom-Layers/blob/master/seq 2 seq/clayers . py
- https://applied ai course-1 . medium . com/FAQs-about-attention-assignment-3033 ac9 f 05 AC
- 在乌代·派拉的指导下在 AppliedAI 球场完成的项目。https://www.appliedaicourse.com/
对 ARIMA 模式的深刻理解
实践教程
探索时间序列的特征——平稳性、稳定性、自相关性
图片来自 Unsplash
时间序列快速介绍
通常,时间序列预测模型可以写成
Eq 0.2 时间序列预测模型的定义
其中,yₜ是要预测的变量(因变量,或响应变量),t 是进行预测的时间,h 是预测范围,Xₜ是在时间 t 用于进行预测的变量(自变量),θ是函数 g 中参数的向量,εₜ₊ₕ表示误差。值得注意的是,观察到的数据根据观察时间是唯一有序的*,但它不必依赖于时间,即时间(观察值的指数)不必是独立变量之一。*
时间序列的一些可能性质
平稳性:平稳过程*是随机过程,其均值、方差和自相关结构不随时间变化。*它也可以用数学术语正式定义,但在本文中,这不是必需的。直观地说,如果一个时间序列是平稳的,我们看它们的一些部分,它们应该非常相似——时间序列看起来是平坦的,形状不取决于时间的变化。(快速查知识:f(t) = sin(t)是平稳过程吗?肯定不是,因为它不是随机的,平稳性不是它的属性之一)
图 1.1 平稳性示例。图片来自维基百科(白噪音)
图 1.1 显示了平稳过程的最简单的例子——白噪声。
图 1.2 非平稳时间序列示例。作者制作的图表。
上图 1.2 显示了一个非平稳的时间序列。为什么会这样呢?我们可以看到明显的趋势,这意味着方差随着时间而变化。但是,如果我们使用线性回归拟合一条线(以捕捉趋势)并移除趋势,数据现在具有恒定的位置和方差,但由于周期行为,它仍然不是静止的,这不是随机的。
图 1.3(左)为原始数据拟合一条线,图 1.4(右)为去除趋势后的结果。作者制作的图表
使用 ARMA 对时间序列建模时,其中一个假设是数据是平稳的。
季节性:季节性是在短于一年的特定时间间隔内表现出某些变化的特性(当然也可以是不同的时期。如果我们在一天中观察每小时的温度,并在几天内收集数据,那么这个周期就是一天,它也可能具有季节性-峰值可能出现在下午 2 点或 3 点。这意味着我们不必用常识在季节性的背景下解释季节)、月度、季度等。季节性时间序列的一个非常典型的例子是用电量,在夏季,用电量通常较高,例如,因为有空调。
图 1.5 美国用电量的季节性曲线图。图片来自维基百科(季节性)
图 1.5 显示了具有季节性的数据,我们可以很容易地看到,在 7 月和 8 月有一个用电高峰,在 1 月和 12 月有一个较小的高峰。图 1.5 中绘制的数据是美国从 1973 年到 2010 年的用电量。usmelec
是 r 中内置的数据集。整个期间的用电量如下所示
图 1.6 美国 1973 年至 2010 年的用电量。作者制作的图表
季节性的存在要求我们调整预测的方式。比如我们要卖空调,就需要用历年同季的销量来预测未来的月销量,而不是最接近的月份。
逐步定义 ARIMA
技术说明:后移运算符
*这是什么?*它是一个将变量 xₜ后移的算子,有时表示为 b(后移),有时表示为 l(滞后),在本文中,我们将采用符号 b。它定义为
等式 2.1 后移算子的定义
以及满足 B⁻ B = 1 的向前移位算子 B⁻。
*我们为什么需要这个?*这是因为它使我们能够以简洁的方式明确地回移——就多项式而言,这也有助于我们定义更复杂的模型。当我们用一个东西来表示数学中的某个东西时,看看它支持哪些运算总是很重要的。在这种情况下,我们很容易看到,后移运算符允许对它进行所有的算术运算:加、减、乘、除、取幂等。
这意味着,例如,在 xₜ上使用运算符 (1-B)得到 xₜ-xₜ₋₁的差,如果我们想再次得到 xₜ和 xₜ₋₁的差,我们在 xₜ上使用运算符 (1-B),得到(1–2 b+b)xₜ= xₜ-2xₜ₋₁+ xₜ₋₂.这和(xₜ-xₜ₋₁)-(xₜ₋₁-xₜ₋₂).)是一样的在这个例子中,我们遇到了一阶和二阶差分(方程 2.12,2.13),这将在后面解释。
自回归模型
阶为 p 的自回归模型,缩写为 AP§,将当前值建模为先前 p 值的线性组合。这个模型就是这样得名的,它本身就是一个线性回归。我们在定义中看到了很多术语,但该模型仍然被认为是单变量,因为当前值取决于其过去值。这些值是同一变量在不同时间点的值,所以毕竟只涉及一个变量。更一般化的模型是 VAR (向量自回归),允许多元时间序列。形式上,(单变量)自回归模型被定义为
情商。2.2 自回归模型的正式定义
其中 wₜ ~ wn(0,σᵥᵥ),ϕ₁,ϕ₂,…,ϕp (ϕp ≠ 0)为参数。wn 表示“白噪声”,它具有均值为 0 且方差为σᵥᵥ的正态分布。有时方程 2.2 的右边也有一个常数,用 c 表示( 预测:原理与实践第 8.3 章 )。常量所起的作用将在 ARIMA 小节中解释。
根据后移算子,自回归模型被定义为
等式 2.3 使用后移运算符定义 AR 模型
移动平均模型
q 阶移动平均模型,缩写为 MA(q),将当前值建模为前面 q 个误差项的线性组合(与自回归模型不同,在移动平均中,关注的是误差项)。形式上,它被定义为
情商。2.4 移动平均线模型的正式定义
其中 wₜ ~ wn(0,σᵥᵥ),θ₁,θ₂,…,θq (θq ≠ 0)为参数。wn 表示“白噪声”,它具有均值为 0 且方差为σᵥᵥ的正态分布,类似于自回归模型的定义。这也是一个单变量模型。也可以在定义中添加一个常数( 预测:原理与实践第 8.4 章 )。
根据后移运算符,移动平均模型可以定义为
等式 2.5 使用后移运算符定义 MA 模型
与自回归模型不同,这种移动平均模型的名字并不那么明显。根据第 48 页的脚注单变量 Box-Jenkins 模型预测(2009):Pankratz 的概念和案例:“标签‘移动平均’在技术上是不正确的,因为 MA 系数可能为负,且总和可能不为 1。这个标签是约定俗成的。”
另一个令人困惑的事情是一个看起来像移动平均模型的概念,移动平均,又名滚动平均或移动平均,用于平滑时间序列。事实上,它是一个完全不同的工具——它不是用来预测的。我们用简单移动平均线的例子来说明这一点
等式 2.6,2.7 简单移动平均线
观察公式后,我们可以看到,我们需要第 k 个值来计算第 k 个移动平均线。
自回归移动平均模型
ARMA 模型是AR 和 MA 的组合,相当自明。ARMA 考虑了过去的值和过去的误差项,并用两个多项式描述了(弱)平稳随机过程。形式上,如果时间序列是平稳的并且
方程 2.8 ARMA 的正式定义
其中ϕp ≠ 0,θq ≠ 0,wₜ ~ wn(0,σᵥᵥ),σᵥᵥ > 0。如前所述,参数 p 和 q 分别是自回归阶和移动平均阶。就后移算子而言,ARIMA 模型可以写成
等式 2.9 根据后移算子定义的 ARIMA 模型
这种重写并不简单。它揭示了模型中可能出现的一个严重问题— 参数的冗余。如果多项式ϕ(b = 0 和θ(B) = 0 有公共因子,那么模型将包含冗余参数。这将使模型变得非常复杂。什么时候会出现这种情况?当我们试图用 ARMA(1,1)模型拟合一个白噪声序列(xₜ = wₜ)时,程序会这样做,但我们得到的模型会有多余的参数。因此,我们需要去除冗余以简化模型,并且可以使用协方差分析来去除冗余。
等式 2.8 所示的定义是非季节性 ARMA。但经常发生的是,数据是季节性的。如果我们想消除季节性,我们应该怎么做?答案是引入一个滞后 h(季节周期的长度),这就引出了季节 ARMA (SARMA),记为 ARMA(P,Q)ₕ),其形式为
方程 2.10 季节性 ARMA 的定义
这种去除季节效应的方法对应于我们之前描述过的:利用八月份的数据预测八月份的销售额。h 应该等于什么?这取决于季节性的频率,例如,如果季节性变化在一年中的某些特定月份出现一次,则 h = 12,如果它在一年的每个季度出现一次,则 h = 4。如果我们将季节性 ARMA 和非季节性 ARMA 结合起来(将季节性和非季节性算子相乘),我们将得到一层概括——混合季节 ARMA。
混合季节 ARMA 模型
混合季节 ARMA 的符号
自回归综合移动平均(ARIMA)模型
ARIMA 模型是基于差分序列的 ARMA 模型,差分有时也称为**。那么什么是差异呢?它是一种去除序列非平稳性的技术(这种技术去除了非恒定趋势,这意味着它只产生均值平稳性,而不是方差)。它取两次观察的差值。**
等式 2.12 和等式 2.13 的区别
当然,我们可以多次改变观测值。等式 2.12 和等式 2.13 给出了一阶差分和二阶差分的例子。显而易见,差分不同于微分——差分只是取差,同时,微分计算变化率。ARIMA 模型通常用 ARIMA(p,d,q)来表示,参数的含义总结如下表
标签。2.1 模型中的参数。(作者自制)
现在是时候介绍 ARIMA 模型的正式定义了,
方程 2.14 ARIMA 模型的定义
在 yₜ’表示差分序列的地方,其他参数的定义方式与 ARMA 模型中的相同。如前所述,可以将一个常数添加到模型中,该常数表示漂移。通过涉及 ARIMA(0,1,0)模型(没有自回归或移动平均项,使用一阶差分建模)的示例,可以很容易理解:
无参数:模型是 xₜ = xₜ₋₁ + εₜ,是随机游走。
带参数:模型为 xₜ = c+ xₜ₋₁ + εₜ.这是一个有漂移的随机游走。
常量为过程增加了非常量趋势。(这句话看起来很诡异,但是我们需要注意的是,公式 xₜ = c+ xₜ₋₁+ εₜ是递归的,在每次解出一个常数的时候用 stack up)这个常数有多大的影响力?很多。我们可以从两个随机行走的模拟中看到这一点,一个有漂移,一个没有漂移
图 2.1 无漂移(左)和有漂移(右)的随机游走模拟。作者制作的图表。
然而,这种差异并没有考虑到季节性。为了消除季节性,我们需要考虑季节性差异(xₜ和 xₜ₋ₕ之间的差异),这就带来了季节性自回归综合移动平均(SARIMA)模型。萨里玛和 SARMA 之间的关系非常类似于 ARIMA 和 ARMA 之间的关系——萨里玛是有差异的 SARMA,但现在我们不仅需要考虑非季节性差异,还需要考虑季节性差异。我们用 ▽ ᴰ和 ▽ ᵈ分别表示季节性差异和非季节性差异。 D 是季节差异的程度。
情商。2.15 季节性 ARIMA 模型
季节性 ARIMA 的符号
实践中的 ARIMA
如圣经预测:原则和实践中所述,有一种拟合 ARIMA 模型的一般方法:预处理,直到数据变得稳定;提供给计算 ARIMA 模型的函数;比较模型;检查结果(残差);如果不够好,迭代,否则使用结果模型做预测。
在 R 中,使用最大似然估计 (MLE)来估计参数,最大化似然函数。在给定我们的模型的参数(ϕ,θ,…)的情况下,可能性等于产生观察值 x 的概率。为了比较这些模型,使用了 Akaike 信息标准 (AIC),该标准评估了信息的损失,也惩罚了模型的复杂性(估计参数的数量)。我们选择具有最小 AIC 值的模型。
自动化的
如果我们想要使用一个自动化的过程来构建模型,函数auto.arima
就在我们的支配之下。它使用分步过程(Hyndman-Khandakar 算法)来有效地遍历模型空间。函数auto.arima
负责对数据进行差分以使数据稳定(是否 d = 0),选择超参数,并根据 AIC 选择最佳模型。
我们用去年 8 月 16 日到今年 8 月 26 日的油价来展示自动化的 ARIMA 过程。我们想要实现的是使用 2020 年 8 月 16 日至 2021 年 8 月 16 日的数据来预测未来 10 天的油价,然后将结果与真实值进行比较。(在本文中,目的主要是向读者介绍 ARIMA 模型的原理,所以这实际上是一个粗略的股票预测,像回溯测试之类的东西不包括在内)
# read data
# Datasource: [https://finance.yahoo.com/quote/OIL/history?p=OIL](https://finance.yahoo.com/quote/OIL/history?p=OIL)
dataOil <- read.csv2(file = "data/OIL.csv", sep=",")
head(dataOil)# split into training and test data
lenOil <- nrow(dataOil)
trainSize <- ceiling(lenOil-10)# take the first 12 month
train.oil <- dataOil[1:trainSize, ]
test.oil <- slice_tail(dataOil, n=lenOil-trainSize)# convert to time series
train.close <- train.oil$Close
train.close <- as.numeric(train.close)# frequency is set to be one because we have only one year of data
train.close.ts <- ts(data=train.close, frequency=1)# plot the training data
plot(train.close, type='l', ylab='Close price', main="One year oil price")
图 3.1 2020 年 8 月 26 日至 2021 年 8 月 26 日油价
显然,我们可以看到一个趋势,时间序列不是平稳的,但没有变化方差的证据,所以我们不准备做任何转换。模型构建的其余部分由auto.arima.
负责,但我们需要做的是检查残差,以确保它们看起来像随机噪声,这部分由checkresiduals
完成,它应用了永盒测试。如果 Ljung-Box 检验的 p 值大于 0.05,我们不能拒绝残差独立分布的零假设。因此,这个模型是可接受的,否则,我们将需要选择一个新的。
start <- length(train.close.ts)
test.index <- seq(start, start+9)
test.close <- as.numeric(test.oil$Close)aa.train <- auto.arima(train.close.ts)
# --> AIC = 157.11
checkresiduals(aa.train)
# --> p-value = 0.07454plot(forecast(aa.train, h=10))
lines(test.index, test.close)
与 8 月 17 日至 8 月 26 日(过去 10 天)的油价实际值相比,结果如下。
图 3.2 预测结果
客户模型
自动化过程可能无法找到适合我们的模型。如果它没有通过永盒测试,我们需要自己选择超参数。因此,有时我们可能需要选择自己的模型(使用Arima
)。与自动化流程相比,为了使用我们自己的模型,除了之外我们还需要做什么?首先,在观察数据之后,我们需要考察数据的偏相关。(要了解更多关于偏相关的信息点击这里)。
# plot the partial difference
dif.train <- difference(train.close.ts)
ggtsdisplay(dif.train, plot.type='partial')
图 3.3 一阶差分、自相关和偏自相关
图中所示的差看起来相当稳定,因此我们将只使用一阶差。但是我们仍然可以执行单位根测试来检查平稳性。对此,我们有几个备选方案,这里我们使用扩展的迪基-富勒测试(ADF,在 R 中它是adf.test
,如果我们设置k=0
,那么它就变成简单的迪基-富勒测试)。
# ADF test
dif.train <- na.remove(dif.train)
adf.test(dif.train)
# --> p-value smaller than 0.01
根据结果,我们可以拒绝一阶差分为 非平稳 的零假设。因此我们设置 d=1 。根据偏自协方差,显示平稳时间序列与其自身的滞后值之间的相关性,消除了更短滞后的影响,变量 xₜ与 xₜ₊₄相关,那么我们可以尝试设置 p = 4 。剩下的就是 q 了,我们可以从 0 开始尝试,逐渐增加。至于季节性,它并没有真正显示在图表中,因此我们可以将季节性** (P,D,Q)的参数保留为缺省值 0。应该加漂移吗?从图 3.2 中的增加趋势来看,似乎是的,我们可以将include.drift
设为真。**
# manually try the different parameters
fit.m1 <- Arima(train.close.ts, order=c(4,1,0), include.drift=TRUE)
fit.m1
# --> AIC = 156.15fit.m2 <- Arima(train.close.ts, order=c(4,1,1), include.drift=TRUE)
fit.m2
# --> AIC = 157.61
# after trying some more other values of q, we will find out
# that increasing q doesn't really decrease the AICcheckresiduals(fit.m1)
# --> p-value = 0.4187plot(forecast(fit.m1, h=10))
lines(test.index, test.close)
我们的定制模型具有稍低的 AIC 值,然而该图看起来几乎与图 3.2 所示的相同。
图 3.4 预测-定制模型的结果
总结
在这篇文章中,首先,我们快速地介绍了时间序列的形式定义和时间序列中可能出现的一些典型性质。之后,随着 ARMA 和 ARIMA 的扩展——季节性 ARMA (SARMA)和季节性 ARIMA(萨里玛),我们逐渐熟悉了 ARIMA 的定义。最后,我们建立了一个模型,对油价进行了自动和人工的短期预测。
参考
[1]NIST/sema tech电子版统计方法手册 (2012)。
[2]沃森,M. W. 时间序列:经济预测 (2001)。国际社会百科全书&行为科学,15721–15724。
[3] Hyndman,R. J .,& Athanasopoulos,G. 预测:原理与实践 (2018)。OTexts。
[4] Pankratz,A. (2009 年)。 单变量 Box-Jenkins 模型预测:概念与案例 (第 224 卷)。约翰·威利的儿子们。
[5] Hyndman,R. J .,& Khandakar,Y. (2008 年)。自动时间序列预测:R 的预测包。统计软件杂志, 27 (1),1–22。
[6]埃尼,D. (2015 年)。尼日利亚瓦里镇降雨量的季节性 ARIMA 建模和预测。地球科学与环境保护杂志, 3 (06),91。
【7】自回归综合移动平均线。(2021 年 4 月 29 日)。在维基百科里。