TowardsDataScience 博客中文翻译 2020(二百九十七)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

深度学习的快速进步让我们感到不知所措

原文:https://towardsdatascience.com/deep-learnings-rapid-progress-leads-us-to-feel-overwhelmed-1cbe3dabdce5?source=collection_archive---------66-----------------------

我们不应该急于快速学习任何东西。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由 Iwona Castiello d’AntonioUnsplash 上拍摄

深度学习正达到其最佳进展年份。它也成为了最近一年任何利益相关者的流行语。因此,有很多资源存在,比如课程、论文、社区等等。

令人惊讶的是,大多数资源都是免费提供的,并且在互联网上公开。因此,深度学习对世界各地的社区都是可用的,不管他们的背景是什么,他们代表哪里。

由于这些因素使深度学习这次如此出名,一些影响将影响到我们。其中之一就是影响我们的心理健康。面对如此庞大的资源,我们变得不知所措。

感到不知所措是一种因为某些因素而变得沮丧的感觉。如果我们不认真对待它,它会让我们失去做很多事情的动力。

2018 年图灵奖获得者之一、著名深度学习研究者 Yoshua Bengio 最近在博客上发表了一篇文章,内容是重新思考机器学习领域的出版物会是什么样子。它类似于一个叫做慢科学的宣言。我已经引用过了,说的是这样的,

我们确实需要时间思考。我们确实需要时间来消化。我们确实需要时间来误解对方,尤其是在促进人文科学和自然科学之间的对话时。我们不能不断地告诉你我们的科学意味着什么;对什么有好处;因为我们还不知道。科学需要时间。

——当我们思考时,请忍耐。

根据上面的引用,它表明,作为一名科学家,我们应该放慢自己的速度来完成事情。他真正想要的是这个领域应该有更多的对话。因此,它可能会产生突破,也可能是新的灵感。

这个概念本身不仅仅是为了科学家,也是为了我们,作为一个学习者,让我们放慢脚步,从这个领域获得更多。

现在,让我问你这些问题,

  • 你现在对海量的深度学习资源有什么感觉?
  • 你最近看了多少新的深度学习研究论文?
  • 关于深度学习,你已经了解了哪些东西?

很可能你会回答你学到了很多,而且在学习深度学习的时候你也觉得还好。你确定吗?也许你会回答是或不是。但是如果你已经打开了这么多的资源,却不觉得你从中学到了任何东西,你应该休息一下。

我们可能想学那么多东西,想成就很多事情,但是不要急于求成。我们必须让自己慢下来,否则我们会变得没有动力。

避免不知所措的小贴士

现在的问题是,我们如何在不被淹没的情况下学习深度学习?

以下是我关于如何在不被淹没的情况下学习的建议。

找到你的学习方式

人们可以学到所有的东西。但是要学习它,可能你会用一种不同的学习方式。学习方式有很多种。他们是,

  • 视觉型学习者
    这种类型的学习者通过使用任何图形或可视化来理解事物。这种类型的例子是那些喜欢用思维导图做笔记的人。
  • 听觉型学习者 这种类型通过听课和讨论来理解概念进行学习。
  • 读写学习者
    这种类型基本上是通过看书和记笔记来学习。
  • 动觉型学习者 这种类型基本是边做边学。例如,通过对某些神经网络执行代码来了解神经网络如何工作。

那么,根据这些类型,哪一种适合你呢?不一定非要选一个。你可以把这些结合起来,使你的学习更有效。我属于动觉型和阅读型学习者,因为我更喜欢从阅读书籍中学习,然后付诸实践。

你了解你自己。所以,你要选择适合自己的。

只坚持一门课程,直到它结束

在你知道你的学习方式后,下一步是决定使用哪种资源。学习深度学习的资源有很多,比如斯坦福大学、麻省理工学院、Deep Mind 等等。你可以使用任何一种,但是你必须遵守一条规则,

使用。一个。资源。只有!

每门课程都有自己的独特之处,但大多数情况下,它给你的是相同的概念。因此,你应该坚持下去,你必须学习,直到你完成课程。

如果你觉得你不理解这门课程,你可以搜索其他资源作为你的额外参考,不要把它作为你的主要资源。只需将第一个用于您的主要资源。

花 1-2 天时间学习或做其他事情

学习有时候会让你觉得无聊。为了确保你总是有动力,你应该做一些对你来说重要的事情或者你喜欢的事情。或者可能你是一个公司的雇员,所以你不能选择什么时候学习新的东西。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

克里斯汀·休姆在 Unsplash 上拍摄的照片

可以应用 OpenAI 的学习日来解决这个。学习日是学习你不擅长的新事物的日子。比如你是一个数据分析师,你对强化学习很感兴趣。因此,你可以分配一整天来学习。大概你可以安排在工作日或者周末。

这样做,你就不会感到无聊,你还可以增加另一个领域的技能。

最终想法

公开可用的资源使任何人都可以进行深度学习。但是大量的信息让我们不知所措。因此,你应该学得慢一点,只专注于一种资源。通过这样做,你会变得不那么不知所措,它会让你保持动力,直到你完成任务。

参考

[1] Bengio Y. 是时候重新思考机器学习中的发布过程了 (2020)。
【2】teach . com .学习风格
【3】《慢科学宣言》
【4】open ai。学习日 (2019)。

感谢您阅读我的文章,您也可以在下面查看我以前的文章:

[## 数据科学和竞争性编程

他们在解决问题,但方法不同。

towardsdatascience.com](/data-science-and-competitive-programming-2887300207c0) [## Python 中的客户细分

基于 K-均值聚类算法的图像分割。

towardsdatascience.com](/customer-segmentation-in-python-9c15acf6f945) [## R 中 ARIMA 模型的时间序列预测

从勘探到预测 1970 年至 2015 年的二氧化碳排放数据。

towardsdatascience.com](/time-series-forecasting-with-arima-model-in-r-77f4e2ae7abb)

基于深度学习和 LSTM 的恶意软件分类

原文:https://towardsdatascience.com/deep-lstm-based-malware-analysis-6b36ac247f34?source=collection_archive---------29-----------------------

使用 Windows Exe API 调用进行恶意软件分析的基于深度学习的顺序模型

恶意软件开发在架构和功能方面呈现出多样性。恶意软件能力的这一进步构成了严重的威胁,并为恶意软件检测开辟了新的研究领域。这项研究的重点是变形恶意软件,它是恶意软件家族中最高级的成员。使用传统的基于签名的方法的反病毒应用程序很难检测变形恶意软件,这使得很难相应地对这种类型的恶意软件进行分类。最近关于恶意软件检测和分类的研究文献讨论了与恶意软件行为相关的这个问题。

引用作品
如果你觉得这个实现有用,请引用它:

@article{10.7717/peerj-cs.285,
title = {Deep learning based Sequential model for malware analysis using Windows exe API Calls},
author = {Catak, Ferhat Ozgur and Yazı, Ahmet Faruk and Elezaj, Ogerta and Ahmed, Javed},
year = 2020,
month = jul,
keywords = {Malware analysis, Sequential models, Network security, Long-short-term memory, Malware dataset},
volume = 6,
pages = {e285},
journal = {PeerJ Computer Science},
issn = {2376-5992},
url = {https://doi.org/10.7717/peerj-cs.285},
doi = {10.7717/peerj-cs.285}
}

你可以从我的我的 GitHub 库访问数据集。

介绍

恶意软件,通常被称为恶意软件,是任何故意设计来破坏计算机系统和危害用户安全的软件。如果某个应用程序或代码暗中违背计算机用户的利益并执行恶意活动,则该应用程序或代码被视为恶意软件。恶意软件针对各种平台,如服务器、个人电脑、手机和相机,以获得未经授权的访问,窃取个人数据,并破坏系统的正常功能。

处理恶意软件保护问题的一种方法是通过识别恶意软件并评估其行为。通常,这个问题是通过分析恶意软件行为来解决的。该领域紧密遵循恶意软件家族的模型,该模型也反映了恶意行为的模式。很少有研究证明了根据恶意软件家族进行分类的方法。

任何软件对操作系统 API 的调用都显示了这个程序的总体方向。该程序是否是恶意软件可以通过深入检查这些操作来了解。如果是恶意软件,那么它的恶意软件家族是什么。恶意软件制造的操作系统 API 调用是一种数据属性,并且这些 API 调用生成的顺序对于检测恶意软件家族也是至关重要的。执行特定的 API 调用是代表一种行为的特定顺序。深度学习方法之一 LSTM(长短期记忆)通常用于处理这种时序数据。

系统结构

这项研究有两个主要目标:首先,我们创建了一个相关的数据集,然后,使用这个数据集,我们进行了一项比较研究,使用各种机器学习来根据恶意软件的类型自动检测和分类。

数据集创建

这项工作最重要的贡献之一是新的 Windows PE 恶意软件 API 序列数据集,其中包含恶意软件分析信息。在这个数据集中有 7107 个来自不同类别的恶意软件。如上所述,Cuckoo 沙箱应用程序用于获取恶意软件的 Windows API 调用序列,而 VirusTotal 服务用于检测恶意软件的类别。

下图说明了用于收集数据并使用 LSTM 算法对数据进行分类的系统架构。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们的系统包括三个主要部分,数据收集,数据预处理和分析,以及数据分类。

创建数据集时遵循了以下步骤。

Cuckoo 沙盒应用程序安装在运行 Ubuntu Linux 发行版的计算机上。分析机器作为虚拟服务器运行,以运行和分析恶意软件。此服务器上安装了 Windows 操作系统。

让我们编码吧

我们导入常用的标准库来构建一个 LSTM 模型来检测恶意软件。

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
from keras.preprocessing.text import Tokenizer
from keras.layers import LSTM, Dense, Dropout, Embedding
from keras.preprocessing import sequence
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import SpatialDropout1D
from mlxtend.plotting import plot_confusion_matrix

在这项工作中,我们将使用标准的恶意软件数据集来显示结果。您可以从 My GitHub Repository 访问数据集。我们需要合并调用和标签数据集。

malware_calls_df = pd.read_csv("calls.zip", compression="zip",
                               sep="\t", names=["API_Calls"])

malware_labels_df = pd.read_csv("types.zip", compression="zip",
                                sep="\t", names=["API_Labels"])

malware_calls_df["API_Labels"] = malware_labels_df.API_Labels
malware_calls_df["API_Calls"] = malware_calls_df.API_Calls.apply(lambda x: " ".join(x.split(",")))

malware_calls_df["API_Labels"] = malware_calls_df.API_Labels.apply(lambda x: 1 if x == "Virus" else 0)

让我们分析一下阶级分布

sns.countplot(malware_calls_df.API_Labels)
plt.xlabel('Labels')
plt.title('Class distribution')
plt.savefig("class_distribution.png")
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在我们可以创建我们的序列矩阵。为了构建 LSTM 模型,您需要创建一个基于符号化的序列矩阵作为输入数据集

max_words = 800
max_len = 100

X = malware_calls_df.API_Calls
Y = malware_calls_df.API_Labels.astype('category').cat.codes

tok = Tokenizer(num_words=max_words)
tok.fit_on_texts(X)
print('Found %s unique tokens.' % len(tok.word_index))
X = tok.texts_to_sequences(X.values)
X = sequence.pad_sequences(X, maxlen=max_len)
print('Shape of data tensor:', X.shape)

X_train, X_test, Y_train, Y_test = train_test_split(X, Y,
                                                    test_size=0.15)

le = LabelEncoder()
Y_train_enc = le.fit_transform(Y_train)
Y_train_enc = np_utils.to_categorical(Y_train_enc)

Y_test_enc = le.transform(Y_test)
Y_test_enc = np_utils.to_categorical(Y_test_enc)Found 278 unique tokens.
Shape of data tensor: (7107, 100)

作为练习,这里给出了基于 LSTM 的分类模型:

def malware_model(act_func="softsign"):
    model = Sequential()
    model.add(Embedding(max_words, 300, input_length=max_len))
    model.add(SpatialDropout1D(0.1))
    model.add(LSTM(32, dropout=0.1, recurrent_dropout=0.1,
                   return_sequences=True, activation=act_func))
    model.add(LSTM(32, dropout=0.1, activation=act_func, return_sequences=True))
    model.add(LSTM(32, dropout=0.1, activation=act_func))
    model.add(Dense(128, activation=act_func))
    model.add(Dropout(0.1))
    model.add(Dense(256, activation=act_func))
    model.add(Dropout(0.1))
    model.add(Dense(128, activation=act_func))
    model.add(Dropout(0.1))
    model.add(Dense(1, name='out_layer', activation="linear"))
    return model

下一步是训练模型。我训练并保存了我的模型。由于数据集的原因,训练阶段需要花费大量时间。为了减少执行时间,您可以从 GitHub 存储库中加载我以前训练过的模型。

model = malware_model()
print(model.summary())
model.compile(loss='mse', optimizer="rmsprop",
              metrics=['accuracy'])

filepath = "lstm-malware-model.hdf5"
model.load_weights(filepath)

history = model.fit(X_train, Y_train, batch_size=1000, epochs=10,
                    validation_data=(X_test, Y_test), verbose=1)Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding (Embedding)        (None, 100, 300)          240000    
_________________________________________________________________
spatial_dropout1d (SpatialDr (None, 100, 300)          0         
_________________________________________________________________
lstm (LSTM)                  (None, 100, 32)           42624     
_________________________________________________________________
lstm_1 (LSTM)                (None, 100, 32)           8320      
_________________________________________________________________
lstm_2 (LSTM)                (None, 32)                8320      
_________________________________________________________________
dense (Dense)                (None, 128)               4224      
_________________________________________________________________
dropout (Dropout)            (None, 128)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 256)               33024     
_________________________________________________________________
dropout_1 (Dropout)          (None, 256)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 128)               32896     
_________________________________________________________________
dropout_2 (Dropout)          (None, 128)               0         
_________________________________________________________________
out_layer (Dense)            (None, 1)                 129       
=================================================================
Total params: 369,537
Trainable params: 369,537
Non-trainable params: 0
_________________________________________________________________
None
Epoch 1/10
7/7 [==============================] - 22s 3s/step - loss: 0.0486 - accuracy: 0.9487 - val_loss: 0.0311 - val_accuracy: 0.9672
Epoch 2/10
7/7 [==============================] - 21s 3s/step - loss: 0.0378 - accuracy: 0.9591 - val_loss: 0.0302 - val_accuracy: 0.9672
Epoch 3/10
7/7 [==============================] - 21s 3s/step - loss: 0.0364 - accuracy: 0.9604 - val_loss: 0.0362 - val_accuracy: 0.9625
Epoch 4/10
7/7 [==============================] - 20s 3s/step - loss: 0.0378 - accuracy: 0.9593 - val_loss: 0.0328 - val_accuracy: 0.9616
Epoch 5/10
7/7 [==============================] - 22s 3s/step - loss: 0.0365 - accuracy: 0.9609 - val_loss: 0.0351 - val_accuracy: 0.9606
Epoch 6/10
7/7 [==============================] - 21s 3s/step - loss: 0.0369 - accuracy: 0.9601 - val_loss: 0.0369 - val_accuracy: 0.9606
Epoch 7/10
7/7 [==============================] - 22s 3s/step - loss: 0.0371 - accuracy: 0.9594 - val_loss: 0.0395 - val_accuracy: 0.9625
Epoch 8/10
7/7 [==============================] - 22s 3s/step - loss: 0.0378 - accuracy: 0.9601 - val_loss: 0.0365 - val_accuracy: 0.9588
Epoch 9/10
7/7 [==============================] - 22s 3s/step - loss: 0.0358 - accuracy: 0.9618 - val_loss: 0.0440 - val_accuracy: 0.9456
Epoch 10/10
7/7 [==============================] - 21s 3s/step - loss: 0.0373 - accuracy: 0.9589 - val_loss: 0.0354 - val_accuracy: 0.9644

模型评估

现在,我们已经完成了 LSTM 模型的培训阶段。我们可以使用混淆矩阵来评估模型的分类性能。根据混淆矩阵,该模型的分类性能相当好。

y_test_pred = model.predict_classes(X_test)
cm = confusion_matrix(Y_test, y_test_pred)

plot_confusion_matrix(conf_mat=cm,
                      show_absolute=True,
                      show_normed=True,
                      colorbar=True)
plt.savefig("confusion_matrix.png")
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

让我们继续我们模型的训练历史。

plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.grid()
plt.savefig("accuracy.png")
plt.show()

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.grid()
plt.savefig("loss.png")
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结论

这项研究的目的是使用我以前的恶意软件数据集创建一个基于 LSTM 的恶意软件检测模型。虽然我们的数据集包含属于一些分布不平衡的恶意软件家族的实例,但我们已经表明这个问题不会影响分类性能。

深度清醒梦

原文:https://towardsdatascience.com/deep-lucid-dreaming-94fecd3cd46d?source=collection_archive---------38-----------------------

人工艺术合成——连同朱利安·厄本

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对于迷幻艺术的爱好者来说,深度梦境的技术无疑是迷人的。当应用于图像时,它给人一种 Instagram 滤镜目前正在参加 Woodstock 的印象。从一开始,这种方法就产生了大量令人惊叹的艺术作品。虽然对一些人来说这可能已经是一顶旧帽子,但考虑到无害输入的无限可能性,我们还远远没有达到这个桶的底部。

嗯,从技术上讲,它们可能不是无穷无尽的。以在 ImageNet 数据集上训练的图像分类体系结构为例,可用于做梦的内容由 1000 个对象类组成,尽管如此,这仍然令人印象深刻。原则上,对于所有这些类别,特征可以在做梦过程中出现,这些特征反映了网络已经了解到的关于这些物体的内部表征。并非所有这些对人类都有意义,但我们都见过用这种方法生成的令人毛骨悚然的逼真的眼睛和狗脸。作为一个深度梦想实践者,你可以选择在哪一层应用梯度提升进化来最大化激活。通常,在早期图层中,你会看到边缘和基本形状等低级特征。越深入,会产生更多高级功能和可识别的对象。

不幸的是,除了选择层之外,使用这种方法,我们无法控制网络将在输入中识别和放大什么。然而,如果我们移动到最后一层,我们实际上可以精确地指定我们希望看到的内容,在最后一层,激活直接对应于对象类,我们可以选择我们希望最大化的对象类。如果你愿意,这种方法是一种艺术的对抗性攻击。

在本帖中,我们将利用这个想法,使用预先训练好的 VGG-19 架构来更深入地探索一些 ImageNet 类。为了促进单类做梦,我们使用一些输入图像执行通过整个网络的完全正向传递。然后,输出是包含对应于每个类别的概率的向量。为了只挑选和优化一个或几个类,我们生成一个目标向量,其中我们希望看到的标签为 1,否则为零。如果你想变得更有趣,你也可以使用不同的权重来混合类,与其他对象相比,更强调某些对象。所有 ImageNet 类的完整列表可以在这里找到。使用目标向量,我们可以根据网络输出的标准交叉熵计算损失,并对输入执行梯度下降步骤。我们只需要小心这里的标志,因为做梦通常是用坡度上升完成的。

举个明确的例子,假设我们想让 VGG-19 梦到鸵鸟(对应的类标签是 9):

criterion **=** torch**.**nn**.**BCEWithLogitsLoss()
label = 9 
output = model(input_image) target = torch.zeros(output.size())               
target[0, label] = 100           
loss = -criterion(output, target)

注意,对于目标类,我们使用 100 而不是 1。这放大了梯度,我们发现它比调整学习率给出更好的结果。在反向传递之后,我们简单地将输入图像中每个像素的梯度加到它自身上。冲洗,重复,我们得到了一些真正迷人而具体的梦!让我们来看看一些最有趣的结果。我们在所有示例中使用的输入是下图,它取自这里:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这篇文章的封面图片中,我们使用卷心菜类生成了一个梦。让网络梦见鸵鸟,就像上面的例子一样,会导致这样的结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

即使在修正了我们自己的确认偏差后,我们仍然相信他们可以很容易地从他们的头和脖子的形状被认出来。由于鸟类似乎工作得很好,我们也尝试了鹈鹕类:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其他有趣课程的例子有扁虫、交通灯和睡袋:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

过去观察到深度做梦很难控制,这可能阻止了一些艺术家对媒体合成领域产生更大的兴趣。正如我们在这篇文章中所看到的,对生成的结构进行一些控制当然是可能的,并会产生令人着迷的结果。因此,我们恰当地将这种努力命名为“深度清醒梦”。此外,从更技术性的角度来看,这种对抗性攻击也揭示了网络的内部表现。这允许我们直接调查它是否已经学习了关于相应类的不相关或错误的特征,这可能是数据集的产物。

我们很兴奋地看到这种艺术和科学的宏伟融合将在未来带来什么奇迹,并祝愿我们的深度梦想家们旅途愉快。

使用深度度量学习来学习区分

原文:https://towardsdatascience.com/deep-metric-learning-76fa0a5a415f?source=collection_archive---------11-----------------------

最近,计算机视觉算法为使用卷积神经网络(CNN)开发非常高效的视觉搜索工作流做出了巨大贡献。由于近来数据量已经增加,对象识别模型能够识别对象并按比例概括图像特征。然而,在一个具有挑战性的分类设置中,类别的数量是巨大的,有几个约束需要解决,以设计有效的视觉搜索工作流。

  • 对象类别数量的增加也增加了 CNN 倒数第二层的权重数量。这使得很难在设备上部署它们,因为它们最终也会增加型号的大小
  • 当每类只有个图像时,很难实现更好的收敛,从而很难在各种光照差异、对象比例、背景、遮挡等情况下实现良好的性能。
  • 在工程空间中,通常需要设计适应产品生态系统的视觉搜索工作流,该产品生态系统包含不稳定的产品,或者根据季节趋势或地理位置而变化。这种情况使得以循环(特定时间间隔后的训练)或在线(实时数据训练)的方式训练/微调模型变得棘手。

深度度量学习

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1 给定椅子和桌子的两幅图像,度量学习的思想是使用适当的距离度量来量化图像的相似性。当我们的目标是区分对象而不是识别它们时,这在很大程度上提高了模型的可扩展性,因为我们不再依赖于给定图像所属的类别。

为了缓解这些问题,深度学习和度量学习共同形成了深度度量学习(DML)的概念,也称为距离度量学习。它提出训练基于 CNN 的非线性特征提取模块(或编码器),该模块将语义相似的提取的图像特征嵌入(也称为嵌入)到附近的位置,同时使用适当的距离度量(例如欧几里德或余弦距离)将不相似的图像特征推开。与判别分类算法(如 K-最近邻、支持向量机和朴素贝叶斯)一起,我们可以使用提取的图像特征执行对象识别任务,而不受类别数量的限制。注意,这种经过训练的 CNN 模块的辨别能力描述了具有紧凑的类内变化和可分离的类间差异的特征。这些特征也足够一般化,甚至可以用来区分新的看不见的类。在下一节中,我们将使用基于的训练范例来形式化训练和评估用于 DML 的 CNN 的过程。

形式主义

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2 是使用 CNN 提取的示例图像 xᵢ和特征嵌入向量 f(xᵢ。

设 X = {(xᵢ,yᵢ)} I∈【1,2,… n】为 n 幅图像的数据集,其中(xᵢ,yᵢ)建议 iᵗʰ图像及其对应的类别标签。数据集中存在的类的总数为 c,即 yᵢ ∈ [1,2,… C]。让我们考虑 f(xᵢ)一个特征向量(或者一个嵌入)对应一个图像 xᵢ ∈ Rᴰ,其中 f: Rᴰ→Rᵈ是一个参数为θ的可微深度网络。这里, Dd 分别指原始图像尺寸和特征尺寸。形式上,我们将两个图像特征之间的欧几里德距离定义为 Dᵢⱼ = ||f(xᵢ) — f(xⱼ)||,这是分别对应于图像 xᵢ和 xⱼ的深层特征 f(xᵢ)和 f(xⱼ)之间的距离。注意,尽管我们在这里关注欧几里德距离,但是在文献中有几个其他度量经常用于优化嵌入空间。我们将在以后的文章中讨论这个问题。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图三。相对相似性约束 : R = {(xᵢ、xⱼ、xₖ): xᵢ比 xₖ}.更像 xⱼdᵢⱼdᵢₖ+α>0 量化了一对锚正图像和锚负图像之间的相似性。三重损失、N 对损失、提升结构、代理 NCA 损失是使用相对相似性约束的一些损失函数。绝对相似性约束 : S = {(xᵢ,xⱼ) : xᵢ和 xⱼ相似},D = {(xᵢ,xₖ) : xᵢ和 xₖ不相似}。Dᵢⱼ和 Dᵢₖ量化了分别共享相似和不相似类别标签的一对正图像和一对负图像的相似性和不相似性的度量。对比丢失和排序列表丢失使用该约束来学习距离度量。

为了学习距离度量函数 f ,大多数 DML 算法使用相对相似性或绝对相似性约束,使用如图 2 和图 3 所示的一对或三对碱基方法。图像的三元组可以定义为(f(xᵢ)、f(xⱼ)、f(xₖ),其中 f(xᵢ)、f(xⱼ)和 f(xₖ)分别对应于主播 xᵢ、正面 xⱼ和负面图像 x⃈的特征向量。xᵢ和 xⱼ有着相似的阶级标签,而 xₖ有着不同于锚和正面形象的阶级标签。一对图像特征对应一个图像 pair(xᵢ,xⱼ),定义为(f(xᵢ),f(xⱼ)).如果两幅图像共享相似的标签,则称为正对,否则称为负对。
训练端到端 DML 模型的整个过程可以总结为如图 4 所示。最初,为了得到聚类不均匀性的概念,对一批图像进行采样。每批包含对象类 P,每个类有 Q 个图像。我们使用下面讨论的取样策略,用这个批次形成一个或多个小批次。这些小批量用于计算损失并通过反向传播进行训练。让我们总结一下使用 DML 损失函数训练深度学习模型的训练过程。稍后,我们将讨论这个框架的几个重要的训练组件,采样和损失函数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4。DML 的培训程序

培训程序

1.批量抽样:批量 B,类别数 P,每个类别的图像数 q。输入:嵌入函数 f(即预先训练的 CNN 的 Imagenet 数据集)、学习速率 B、批量 B 和图像类别数量 P、一批中的图像总数 B = PQ
3。特征提取:给定参数状态θₜ,使用 CNN 前馈所有批次图像,获得图像嵌入 f( xᵢ )。
4。抽样:从批量中进行小批量计算。根据批次的大小,可以形成对应于步骤 1 中采样的图像的一个或多个小批次的特征向量。
5。损失计算和训练:对于每个小批量计算梯度和反向传播,以更新从θₜ到θₜ₊₁.的参数状态

度量学习损失函数

当我们使用卷积神经网络来识别目标时,Softmax 交叉熵(CE)损失函数是最常见的选择。然而,当插入这个损失函数来学习 DML 模型时,有一些必须考虑的事项。

  • *Softmax 交叉熵(CE)损失被视为 max 算子的软版本。如果使用恒定缩放因子 *s、来缩放,则 Logit 向量或类别概率不会影响给定图像的类别分配。结果,分离良好的特征拥有更大的量级,并且它促进了类的可分离性。总之,如下图所示,它使特征分布呈“放射状”,并且使用鉴别特征学习算法(例如 KNN 分类器)对特征进行分类,关键是要有一个不仅可分离而且可鉴别的嵌入空间。度量学习损失函数被设计成学习有区别的特征空间。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5 从为给定数据集训练的 CNN 的倒数第二层提取的特征模式,并投影到 2D 特征空间。左图:使用 Softmax 损失的 2D 特征分布。右图:使用 DML 损失函数的区别特征分布。

  • CE loss 不直接利用小批量图像中样本的结构关系,因为每个图像(随机选择)单独负责计算损失数。通过引入解决图像之间语义差异的惩罚,具有区分一对或三组图像的训练范例有效地检查了图像之间的关系。

认识到这些方面,研究团体已经提出了各种损失函数来使用 DML 学习区分特征空间。提升结构损失函数就是其中之一。

* [## 利用损失函数深入挖掘度量学习

关于度量学习损失函数的介绍性注释。

towardsdatascience.com](/metric-learning-loss-functions-5b67b3da99a5)

提升结构损失充分利用了小批量,改进了小批量随机梯度下降训练。一方面,三重损失或对比损失分别使用三重或一对图像来计算损失项,提升结构损失提出通过提升小批量(O(m))中可用的所有图像对来采用成对距离度量(O(m))。此外,与仅关于锚图像定义负样本的三重损失或对比损失相反,提升结构损失训练给定对、锚和正中的图像,以从小批量图像中找到它们的负样本。关于 DML 中使用的损失函数的更多信息,请参考上面的博文。提升结构损失的公式如下。

这里, (i,j) 表示对应于共享相似标签图像(xᵢ,xⱼ)的正图像对。Dᵢⱼ是一对图像之间的距离。Dᵢₖ和 Dⱼₗ是迷你批次中从锚和正面到其余负面图像的距离。α是指距离余量。 PN 分别是小批量可用的所有正负线对。

抽样

诚然,直接作用于特征对之间的距离直观地引导我们向学习图像的有意义嵌入的目标前进。因此,标准交叉熵损失主要被 DML 社区忽略了。
在 DML 训练机制中,我们分别使用一对或三对图像来使用绝对或相对相似性,在将图像馈送到 CNN 时,有意义地对图像批次进行采样是必要的。对于数据集,其中用于前馈图像的小批量比数据集中的类的总数大得多,随机馈送图像将保证大多数图像样本在小批量中将具有具有相似类标签的其他图像。但是,当我们考虑具有大量图像类的数据集时,例如,斯坦福在线产品数据集,其中图像类的数量接近 22000,随机采样的小批量图像不一定包含共享相似标签的图像对。在这种情况下,尽管每个批次都遇到了类间变化(因为存在具有不同类标签的图像),但是它未能解决类内变化(因为没有必要具有两个具有相似类标签的图像),最终未能实现更好的收敛。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图六。负类挖掘抽样

虽然使用 DML 的训练需要对图像对或三元组进行采样,但是这种采样分别粗略地增加了 O(m)或 O(m)数量级的数据集大小。此外,如果图像对或图像对是随机采样的,随着训练的进行,大多数图像对或图像对以较小的方式起作用,因为不是所有的图像都违反了余量α(例如,在三个一组丢失的情况下)。很难计算有意义的损失,这不可避免地导致收敛缓慢。
为了克服这些问题,有各种各样的采样策略,我们可以用来更快更好地收敛训练参数。

硬负数据挖掘策略在基于距离的度量学习算法中很常见。它包括计算硬的负面或正面特征实例,以形成给定锚例的正面或负面对。然而,当给定的批处理中涉及大量的类时,这个过程在计算上是具有挑战性的。在这样的场景中,可以方便地执行负的“挖掘,而不是负的“实例挖掘。遵循下面的过程来为基于对的损失函数执行负“类”挖掘。

  1. 为了具有给定参数状态θₜ的嵌入空间的有意义的表示,对包含几百个类(即上述训练过程中的 p)的大批量图像进行采样。对于每个基于对的损失函数,对于给定批次中的每个给定类别,必须有至少两个示例图像。例如,在斯坦福在线产品数据集的情况下,必须随机采样至少 2 个图像(Q=2)。
  2. 对于给定 CNN 的给定参数状态θₜ,提取步骤 1 中采样的每个图像的特征向量。使用这些图像特征获得类别表示向量或类别代理(平均嵌入向量)。
  3. 对于从步骤 1 中采样的 P 个类别中随机选择的每个类别,我们对最近的类别进行采样,并对相应的图像重新排序,如上图 5 所示。这一步骤也可以使用基于边缘的类选择来执行,如果只有一个具有距离边缘的类被选择作为给定锚类的最近类。
  4. 根据计算能力,我们可以形成一个或多个小批量的特征向量,如图 5 所示,以计算损失和梯度。

评估和推理

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 DML 的推理过程。图像被馈送到网络以从瓶颈层获得特征向量。因为它们是用 DML 损失函数训练的,所以它们将创建区别特征嵌入空间,如图中 2D 所示。

与传统的对象识别模型(其中图像被馈送以生成对象类别概率)相反,DML 图像被馈送以提取图像特征。评估这些图像特征的聚类质量和检索性能。F1 和归一化互信息(NMI)分数是标准的评估度量,我们用来估计聚类质量度量。对于检索,k 处的召回率是我们在处理 DML 训练时使用的基准评估度量。为了本文的完整性,我们在这里总结了这些评估指标。

  • 在 K 回忆:对于每个查询图像(来自测试数据集),我们使用来自相同测试集的适当距离度量(欧几里德或余弦)检索 K 个最近邻。如果在检索到的 K 个最近邻居中存在来自相同类别的图像,则查询图像得到分数 1。K 值召回表示测试数据集中所有法师的召回次数。
  • F1 : F1 度量值定义为在 K 处测量的精度和召回率的调和平均值,即 F1 = 2PR/(P+R)。
  • 归一化互信息 ( NMI ):对于一组输入聚类赋值ω和地面真实聚类ℂ,NMI 得分是互信息与聚类平均熵和标签熵的比值。即 NMI = I(ω;ℂ)/2(h(ω)+h(ℂ)).这里,ω={ω₁,ω₂ … ωₙ},这是聚类的输入集,ℂ = {c₁,c₂,… cₙ}是基础真值类。具有聚类分配 i 的示例被给定为ωᵢ,而具有基础真实类标签 j 的示例被定义为 cᵢ.
  • K 处的准确性:这个度量标准服务于我们的目标,即以无监督的方式使用这个训练过的模型作为对象识别模型。对于图像,使用适当的度量获得 K 个最近邻。查询图像被分配给在 K 个最近邻居中出现次数最多的类别。K 处的准确度对测试数据集中的每个查询图像的准确度进行平均。

结论

我们描述了一种深度度量学习范式来解决对象识别问题。这种模型训练提供了如下几个优点。

  • 它们不会增加模型的大小,因为我们总是可以使用相同的维度嵌入层来训练模型。
  • 由于我们正在学习区分和不识别物体,我们可以利用这样一个范例,该范例可以使用各种采样策略以每类更少的图像来执行训练,并且甚至可以推断对不可见的类的识别。
  • 循环在线的方式对一组较新的产品类别进行微调将会传播渐变。

这些属性使我们能够为给定的产品生态系统设计灵活且可扩展的可视化搜索工作流。此外,我们还描述了采样对于使用 DML 损失函数训练 CNN 的重要性。请跟进 这篇 文章的附加损耗功能。随着近来这一领域的发展,我们也可以有效地练习一些其他的训练程序来达到同样的目的。我们将在以后的文章中讨论其中的一些。*

参考

  1. 王,x,华,y,柯迪洛夫,e,胡,g,卡尼尔,r .,&罗伯逊,N. M. (2019)。深度度量学习的排序列表丢失。在IEEE 计算机视觉和模式识别会议论文集*(第 5207–5216 页)中。*
  2. movshowitz-Attias,y .,Toshev,a .,Leung,T. K .,Ioffe,s .,& Singh,S. (2017)。没有大惊小怪的距离度量学习使用代理。IEEE 计算机视觉国际会议论文集(第 360–368 页)。
  3. Ranjan,r .,Castillo,C. D .,& Chellappa,R. (2017 年)。用于鉴别性人脸验证的 L2 约束软最大损失。 arXiv 预印本 arXiv:1703.09507
  4. 2017 年 10 月,王,项,程,陈建杰,尤妮尔。人脸验证的 L2 超球面嵌入。第 25 届 ACM 多媒体国际会议论文集(第 1041–1049 页)。
  5. Wu c . y .,Manmatha,r .,Smola,A. J .,& Krahenbuhl,P. (2017 年)。深度嵌入学习中的采样问题。在IEEE 计算机视觉国际会议论文集*(第 2840-2848 页)。*
  6. 温,张,李,张,乔,(2016 年 10 月)。一种用于深度人脸识别的鉴别特征学习方法。在欧洲计算机视觉会议*(第 499–515 页)。斯普林格,查姆。*
  7. 刘,张,罗,邱,王,唐,(2016)。Deepfashion:通过丰富的注释支持强大的服装识别和检索。在IEEE 计算机视觉和模式识别会议论文集*(第 1096-1104 页)。*
  8. Oh Song,h .,Xiang,y .,Jegelka,s .,& Savarese,S. (2016 年)。基于提升结构特征嵌入的深度度量学习。在IEEE 计算机视觉和模式识别会议论文集*(第 4004–4012 页)。*
  9. Sohn,K. (2016 年)。具有多类 n 对损失目标的改进深度度量学习。在神经信息处理系统的进展*(第 1857-1865 页)。*
  10. Schroff,d . Kalenichenko 和 j . Phil bin(2015 年)。Facenet:人脸识别和聚类的统一嵌入。IEEE 计算机视觉和模式识别会议论文集(第 815–823 页)。**
  11. Bellet,a .,Habrard,a .,和 Sebban,M. (2013 年)。特征向量和结构化数据的度量学习综述。 arXiv:1306.6709
  12. 哈德塞尔,r .,乔普拉,s .,&勒村,Y. (2006 年 6 月)。通过学习不变映射进行降维。在 2006 年 IEEE 计算机学会计算机视觉和模式识别会议(CVPR’06)(第 2 卷,第 1735-1742 页)。IEEE。芝加哥

具有 Scikit-learn 的深度神经多层感知器(MLP)

原文:https://towardsdatascience.com/deep-neural-multilayer-perceptron-mlp-with-scikit-learn-2698e77155e?source=collection_archive---------3-----------------------

MLP 是一种人工神经网络。最简单的 MLP 至少由三层节点组成:输入层、隐藏层和输出层。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Robina Weermeijer 在 Unsplash 上的照片

在深度学习的世界里,TensorFlow、Keras、微软认知工具包(CNTK)、PyTorch 都很受欢迎。我们大多数人可能没有意识到,非常流行的机器学习库 Scikit-learn 也能够进行基本的深度学习建模。在这篇文章中,我将讨论 Scikit-learn 中深度学习建模的可行性和局限性。此外,我将通过两个例子讨论实际实现。

sci kit-learn 中多层感知器(MLP)的显著点

  • 输出层没有激活功能。
  • 对于回归场景,平方误差是损失函数,交叉熵是分类的损失函数
  • 它可以处理单个以及多个目标值回归。
  • 与其他流行的软件包不同,像 Keras Scikit 中 MLP 的实现不支持 GPU。
  • 我们不能像不同的激活函数、权重初始化器等一样微调参数。对于每一层。

回归示例

步骤 1:Scikit-Learn 包中,MLPRegressor 在 neural_network 模块中实现。我们将导入其他模块,如“train_test_split”来将数据集拆分为训练集和训练集以测试模型,“fetch_california_housing”来获取数据,以及“StandardScaler”来缩放数据,因为不同的特征(独立变量)具有较宽的值域范围。缩放用于训练模型的数据是非常重要的。

您可以在文章功能缩放-不同 Scikit 的效果-了解缩放器:深入探讨中了解更多关于不同缩放器的信息

"""Import the required modules"""**from sklearn.neural_network import MLPRegressor
from sklearn.model_selection import train_test_split
from sklearn.datasets import fetch_california_housing
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score
import pandas as pd**

步骤 2: 我们将把数据集分成训练数据集和测试数据集。我们保留了 20%的数据集用于检查训练模型的准确性。独立的训练和测试数据集被进一步缩放,以确保输入数据是标准的正态分布,以零为中心,并且具有相同数量级的方差。

**cal_housing = fetch_california_housing()
X = pd.DataFrame(cal_housing.data,columns=cal_housing.feature_names)
y = cal_housing.target****X_train, X_test, y_train, y_test = train_test_split(X, y,random_state=1, test_size=0.2)**

第三步:我们就像上面的回归例子一样缩放数据,原因相同。

**sc_X = StandardScaler()
X_trainscaled=sc_X.fit_transform(X_train)
X_testscaled=sc_X.transform(X_test)**

步骤 4: 在下面的代码中,建模了三个隐藏层,每层有 64 个神经元。考虑到输入和输出层,我们在模型中共有 5 层。如果没有提到任何优化器,那么“Adam”就是默认的优化器,它可以管理非常大的数据集。

**reg = MLPRegressor(hidden_layer_sizes=(64,64,64),activation="relu" ,random_state=1, max_iter=2000).fit(X_trainscaled, y_train)**

除了“RELU”激活之外,MLPRegressor 还支持“sigmoid”和“双曲线 tan”功能。

步骤 5:在下面的代码中,训练好的模型用于预测保留的测试数据集的目标值,该模型以前没有见过。

**y_pred=reg.predict(X_testscaled)
print("The Score with ", (r2_score(y_pred, y_test))**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分类举例

我们已经看到了一个回归的例子。接下来,我们将看一个分类示例。在 Scikit-learn 中,“MLPClassifier”可用于多层感知器(MLP)分类场景。

像往常一样,首先我们将导入我们将在示例中使用的模块。我们将使用 Iris 数据库和 MLPClassifierfrom 作为分类示例。

**from sklearn.datasets import load_iris
from sklearn.neural_network import MLPClassifierfrom sklearn.model_selection import train_test_splitfrom sklearn.preprocessing import StandardScaler
import pandas as pd
from sklearn.metrics import plot_confusion_matrix
import matplotlib.pyplot as plt**

步骤 2: 在单独的数据帧“X”和“y”中,存储独立和从属特征的值。

**iris_data = load_iris()
X = pd.DataFrame(iris_data.data, columns=iris_data.feature_names)
y = iris_data.target**

步骤 3: 类似于上面的回归示例,我们将把数据集分成训练数据集和测试数据集。我们保留了 20%的数据集用于检查训练模型的准确性。独立的训练和测试数据集被进一步缩放,以确保输入数据是标准的正态分布,以零为中心,并且具有相同数量级的方差。

**X_train, X_test, y_train, y_test = train_test_split(X,y,random_state=1, test_size=0.2)
sc_X = StandardScaler()
X_trainscaled=sc_X.fit_transform(X_train)
X_testscaled=sc_X.transform(X_test)**

步骤 4: 在下面的代码中,我们建立了四个隐藏层,每个层中有不同的神经元。考虑到输入和输出层,我们在模型中共有 6 层。如果没有提到任何优化器,那么“Adam”就是默认的优化器。

**clf = MLPClassifier(hidden_layer_sizes=(256,128,64,32),activation="relu",random_state=1).fit(X_trainscaled, y_train)
y_pred=clf.predict(X_testscaled)
print(clf.score(X_testscaled, y_test))**

分类器对测试数据显示出相当高的分数。重要的是要了解分类模型出错的领域,以充分了解模型的准确性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

你可以在“准确性可视化:监督机器学习分类算法”中了解更多关于我们应该使用混淆矩阵来判断分类模型准确性的原因。

第五步:我们将绘制一个混淆矩阵来理解模型造成的错误分类。

**fig=plot_confusion_matrix(clf, X_testscaled, y_test,display_labels=["Setosa","Versicolor","Virginica"])
fig.figure_.suptitle("Confusion Matrix for Iris Dataset")
plt.show()**

似乎只有一种“云芝”被模型错误地识别为“海滨锦鸡儿”。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结论:我们可以在 Scikit-learn 中做简单的深度学习回归和分类模型。在我看来,它不适合任何现实生活中的大规模建模,因为没有 GPU 的支持,调整参数的选项也非常有限。

你可以在文章深度学习中的准确性可视化中了解更多关于深度学习可视化技术的信息

对于开始机器学习之旅的人来说,你可以从文章中的《为匆忙中的人学习机器》开始。

深度神经网络语言识别

原文:https://towardsdatascience.com/deep-neural-network-language-identification-ae1c158f6a7d?source=collection_archive---------9-----------------------

使用 DNN 和字符 n 元语法对一段文本的语言进行分类(使用 Python 代码)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源: flaticon

语言识别可以是自然语言处理(NLP)问题中的重要步骤。它包括试图预测一段文本的自然语言。在采取其他行动(即翻译/情感分析)之前,了解文本的语言是很重要的。例如,如果你去谷歌翻译,你输入的框显示‘检测语言’。这是因为谷歌首先试图识别你的句子的语言,然后才能翻译。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:作者

有几种不同的语言识别方法,在本文中,我们将详细探讨其中一种。即使用神经网络和字符 n-grams 作为特征。最后,我们证明了这种方法可以达到 98%以上的准确率。一路上,我们将讨论关键的代码片段,你可以在 GitHub 上找到完整的项目。首先,我们将讨论用于训练神经网络的数据集。

资料组

数据集由 Tatoeba 提供。完整的数据集由 328 种独特语言的 6872356 个句子组成。为了简化我们的问题,我们将考虑:

  • 6 种拉丁语言:英语、德语、西班牙语、法语、葡萄牙语和意大利语。
  • 长度在 20 到 200 个字符之间的句子。

我们可以在表 1 中看到每种语言的一个句子的例子。我们的目标是使用提供的文本创建一个可以预测目标变量的模型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

表 1:每种语言的文本片段示例

我们加载数据集,并在下面的代码中做一些初始处理。我们首先过滤数据集,以获得所需长度和语言的句子。我们从每种语言中随机选择 50,000 个句子,这样我们总共有 300,000 行。这些句子然后被分成训练集(70%)、验证集(20%)和测试集(10%)。

特征工程

在拟合模型之前,我们必须将数据集转换为神经网络能够理解的形式。换句话说,我们需要从我们的句子列表中提取特征来创建特征矩阵。我们使用由 n 个连续字符组成的字符 n 元语法来实现这一点。这是一个类似于单词袋模型的方法,除了我们使用字符而不是单词。

对于我们的语言识别问题,我们将使用字符三元组/三元模型(即 3 个连续字符的集合)。在图 2 中,我们看到了一个如何使用三元模型向量化句子的例子。首先,我们从句子中得到所有的三元模型。为了减少特征空间,我们取这些三元模型的子集。我们使用这个子集对句子进行矢量化。第一个句子的向量是[2,0,1,0,0],因为三元组“is_”在句子中出现了两次,“his”出现了一次。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1:使用三元模型向量化句子的例子

创建三元模型特征矩阵的过程是相似的,但是有点复杂。在下一节中,我们将深入研究用于创建矩阵的代码。在此之前,有必要对我们如何创建特征矩阵有一个总体的了解。采取的步骤是:

  1. 使用训练集,我们从每种语言中选择 200 个最常见的三元模型
  2. 从这些三元模型中创建一个独特的三元模型列表。这两种语言有一些共同的三元模型,所以我们最终得到了 663 个独特的三元模型
  3. 通过计算每个三元模型在每个句子中出现的次数,创建一个特征矩阵

我们可以在表 2 中看到这样一个特征矩阵的例子。最上面一行给出了 663 个三元模型中的每一个。然后每一个编号的行给出我们数据集中的一个句子。矩阵中的数字给出了三元模型在句子中出现的次数。例如,“j’a”在第二句中出现一次。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

表 2:训练特征矩阵

创建特征

在本节中,我们将查看用于创建表 2 中的训练特征矩阵和验证/测试特征矩阵的代码。我们大量使用 SciKit Learn 提供的计数矢量器包。这个包允许我们基于一些词汇列表(即单词/字符列表)对文本进行矢量化。在我们的例子中,词汇表是一组 663 个三元模型。

首先,我们必须创建这个词汇表。我们首先从每种语言中获取 200 个最常见的三元模型。这是使用下面代码中的 get_trigrams 函数完成的。这个函数获取一个句子列表,并从这些句子中返回 200 个最常见的三元模型列表。

在下面的代码中,我们遍历了 6 种语言中的每一种。对于每种语言,我们从训练集中获得相关的句子。然后,我们使用 get_trigrams 函数获得 200 个最常见的三元模型,并将它们添加到一个集合中。最后,由于这些语言共享一些共同的三元模型,我们有一组 663 个独特的三元模型。我们用这些来创建一个词汇表。

然后,CountVectorisor 包使用词汇表对我们训练集中的每个句子进行矢量化。结果就是我们之前看到的表 2 中的特征矩阵。

在我们可以训练我们的模型之前,最后一步是缩放我们的特征矩阵。这将有助于我们的神经网络收敛到最佳的参数权重。在下面的代码中,我们使用最小-最大缩放来缩放训练矩阵。

我们还需要获得验证和测试数据集的特征矩阵。在下面的代码中,我们像处理训练集一样对这两个集进行矢量化和缩放。值得注意的是,我们使用了词汇表以及从训练集中获得的最小值/最大值。这是为了避免任何数据泄漏。

探索三元模型

我们现在有了一种形式的数据集,可以用来训练我们的神经网络。在我们这样做之前,探索数据集并对这些特征在预测语言方面的表现建立一点直觉会很有用。图 2 给出了每种语言与其他语言共有的三元模型的数量。例如,英语和德语有 55 个最常见的三元组是相同的。

有了 127,我们看到西班牙语和葡萄牙语有最多的三元组。这是有道理的,因为在所有的语言中,这两种语言在词汇上是最相似的。这意味着,使用这些特征,我们的模型可能会发现很难区分西班牙语和葡萄牙语,反之亦然。类似地,葡萄牙语和德语有最少的三元组,我们可以期望我们的模型在区分这些语言方面更好。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2:三元模型特征相似性图

系统模型化

我们使用 keras 软件包来训练我们的 DNN。模型的输出层中使用了 softmax 激活函数。这意味着我们必须将我们的目标变量列表转换成一个一次性编码列表。这是使用下面的编码功能完成的。这个函数接受一个目标变量列表,并返回一个独热编码向量列表。例如,[eng,por,por,fra,…]将变成[[0,1,0,0,0,0],[0,0,0,0,1,0],[0,0,0,0,1,0],…]。

在选择最终的模型结构之前,我做了一些超参数调整。我改变了隐藏层中的节点数、历元数和批量大小。在验证集上实现最高准确度的超参数组合被选择用于最终模型。

最终模型有 3 个隐藏层,分别有 500、500 和 250 个节点。输出层有 6 个节点,每种语言一个。隐藏层都有 ReLU 激活函数,并且如前所述,输出层有 softmax 激活函数。我们使用 4 个时期和 100 的批量大小来训练这个模型。使用我们的训练集和独热编码的目标变量列表,我们在下面的代码中训练这个 DDN。最终,我们达到了 99.70%的训练准确率。

模型评估

在模型训练过程中,模型可能会偏向训练集和验证集。因此,最好在未知的测试集上确定模型的准确性。在测试集上的最终准确率为 98.26%。这低于 99.70%的训练准确度,表明出现了对训练集的一些过度拟合。

通过查看图 3 中的混淆矩阵,我们可以更好地了解该模型在每种语言中的表现。红色对角线给出了每种语言的正确预测数。非对角线上的数字给出了一种语言被错误地预测为另一种语言的次数。例如,德语被错误地预测为英语 10 次。我们看到,该模型最常混淆葡萄牙语和西班牙语(124 次),或者西班牙语和葡萄牙语(61 次)。这是我们在探索我们的特征时所看到的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3:混乱热图

下面给出了用于创建这个混淆矩阵的代码。首先,我们使用上面训练的模型对测试集进行预测。使用这些预测的语言和实际的语言,我们创建了一个混淆矩阵,并使用 seaborn 热图将其可视化。

最终 98.26%的测试准确率还有提升空间。在特征选择方面,我们保持简单,只为每种语言选择了 200 个最常见的三元模型。一种更复杂的方法可以帮助我们区分更相似的语言。例如,我们可以选择在西班牙语中常见但在葡萄牙语中不常见的三元模型,反之亦然。我们也可以尝试不同的模型。希望这是你语言识别实验的一个好的起点。

图像来源

所有图片都是我自己的或从www.flaticon.com获得的。在后者的情况下,我拥有他们的高级计划中定义的“完全许可”。

参考

[1] A. Simões、J.J .阿尔梅达和 S.D .拜尔斯,语言识别:一种神经网络方法。(2014)https://www . research gate . net/publication/290102620 _ Language _ identificati on _ A _ neural _ network _ approach

[2] G.R. Botha 和 E. Barnard,影响基于文本的语言识别准确性的因素(2012)https://www . science direct . com/science/article/ABS/pii/s 0885230812000058

有序逻辑回归的深层表示

原文:https://towardsdatascience.com/deep-ordinal-logistic-regression-1afd0645e591?source=collection_archive---------19-----------------------

用 TensorFlow 2 预测秩次

介绍

如果目标变量是数字排序的,则可以使用排序算法。该模型将捕捉相邻类之间的共享变化。该模型也可用于半监督分类,其中代理目标被分配为比实际目标低的等级。

最简单的排名模型是回归模型。回归模型的输出将被舍入到最接近的整数,并且这些值也在目标的最大值和最小值之间被剪裁。多类模型线性模型是另一种选择,但应该注意的是,每个类都是单独处理的,而不是按等级排序的方式。XGBoost 还有一个排名实现。我还没有尝试过,但它在 Expedia Kaggle 比赛中取得了成功。有序逻辑回归,或比例优势模型,是逻辑回归模型的扩展,可用于有序目标变量。它最初是由彼得·麦卡勒在 20 世纪 80 年代创作的。在这篇文章中,我们将在 TensorFlow 中设计并实现一个深度有序逻辑回归模型。代码的链接发布在 TensorFlow 公式部分。

数学直觉

比例优势模型,或有序逻辑回归,旨在预测有序目标变量。目标 y 和输入 X 之间的关系是线性的。线性核的输出被定义为 y*。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一组阈值将把线性核的输出分成 K 个排序等级。该模型将具有 K-1 个阈值,其中 K 是 y 的最大等级。注意,在该模型中,每个等级的类共享参数 w 和 b。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

最后一个等级 K 是总括类,它捕获最后一个类以及没有被其他阈值分开的数据点。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

阈值被约束为单调递增且大于或等于零,如等式(3)所示。使用单调递增的阈值确保了数据点被分配给一个且仅一个等级类别。

下图说明了线性核和分级阈值之间的关系。注意,阈值是线性核的子空间。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据点到分级阈值的线性核的距离将被映射到数据点“属于”分级类别的概率。到分级阈值的距离是通过从 K-1 个阈值中的每一个减去线性核的输出来创建的。然后,sigmoid 函数将该输出映射到分级目标变量的累积分布函数,如等式(4)所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因为对阈值参数的单调增加的约束,j 处的累积概率将总是大于 j-1。数据点属于分级类别 j 的概率被定义为 j 和 j-1 处的累积分布函数之间的差。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

边缘情况,即秩 0 和秩 K,由 P(y <0) and 1-P(y < K-1), respectively. Notice that the relative probability of a data point belonging to rank 0 increases as the predicted output decreases or becomes negative. This is due to the monotonic constraint on the thresholds. The probability of a data point belonging to class K, or above, is simply one minus the maximum probability in the cumulative distribution function, in this case the CDF probability at ranked class K-1.

Optimization

The ranked class probability distribution defined above, can be optimized with a 交叉熵损失函数定义,如等式(6)所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

输出 y 是一个热编码矩阵,用于指示观察值属于哪个类。因为 y 是离散概率分布,所以等式(6)中所示的交叉熵损失函数可用于最大化估计参数的似然性。

我们可以检查对数概率的导数,并在损失函数中看到相邻阈值参数之间的关系。等式(7)和(8)显示了有用导数公式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

方程(9)和(10)中示出了在分级等级 j 处对数概率函数的导数。注意,如果一项不包括等级为 j 的θ,则它的导数在等式(6)中为零。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上述导数的简化形式如等式(12)所示。我删除了显示将(10)转换为(12)的步骤的等式(11),因为它太长了:)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

导数显示了 j 和 j-1 处的不同累积概率与 j 和 j-1 处的累积概率比之间的有趣关系。如果 j 处的概率为零,梯度将是不确定的。如果 j 和 j-1 的概率太接近,梯度也会消失。此外,梯度有可能将符号换成负阈值。

张量流公式

在 TensorFlow 中,有各种方法来制定有序逻辑回归。主要问题将是定义阈值,以便梯度与上一节中定义的梯度一致。示例笔记本可以在这里找到。

该模型的线性部分由输入数据和权重之间的点积定义。线性核的输出具有 shape (Nx1),其中 N 是数据点的数量。我决定使用一个线性激活的密集层作为有序层的输入。您可能需要在序数层中添加一个非负偏差来处理潜在的负值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个潜在的变量,欧米加,被创建来构建阈值参数。潜在变量被初始化为等间距单调递增序列。最后排序的类别 K 的阈值被丢弃,并且 CDF 概率从先前排序的类别 K-1 中推断。阈值参数θ将具有形状(1x(K-1))。等级为 0 的第一阈值将被静态地定义为恒定的零值。

最初,我试图用潜在变量平方的累积和来创建阈值参数。然而,模型似乎没有收敛。我认为这是梯度如何传播回阈值的问题。以下是它不起作用的一些可能原因:1)累积和的导数对所有等级≤j 的类传播相同的梯度。换句话说,它没有考虑 j 和 j-1 个阈值参数之间的关系 2)对于平方潜在变量,梯度可能在 0 附近不稳定,即,如果潜在变量在优化期间改变符号。

为了解决这个问题,我首先创建了 K-1 个阈值,然后取 j 和 j-1 个潜在变量之间的差,即累积函数的逆运算。这导致 K-2 个阈值参数。然而,差值仍然可能是负的,所以我添加了一个 ReLU 层来确保差值是正的。常数+1,用于确保梯度始终被定义,即大于零。最后,我将阈值连接在等级为 0 的类上,它被静态地定义为零。

CDF 是通过从阈值中减去线性核,然后应用 sigmoid 函数来创建的。然后,可以通过应用等式(6)将 CDF 转换成 PDF。最后,可以使用稀疏分类交叉熵损失来优化模型。

估价

从这个中收集了三个有序数据集,即波士顿住房、股票和运动学数据集。根据平均交叉验证 MAE、精确度和推土机距离 (EMD)对模型进行评估。EMD 捕捉预测 CDF 和目标 CDF 之间的相似性。

选择分层的 5 重交叉验证策略来评估这两个模型的性能。我发现多个交叉验证结果之间存在一些差异。为了限制这种可变性,我运行了 3 次交叉验证策略,并保存了每个 k 倍验证集的预测值和实际值。

目标是评估和比较有序模型和回归模型的结果。为了简单起见,将使用相同数量的隐藏单元,即 32 个隐藏单元,和相同的优化策略来训练回归模型。回归模型的损失函数有两种选择,即平均绝对误差(MAE)和均方误差(MSE)。我决定使用 MAE,因为它似乎比 MSE 更适合排名。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由于序数模型使用稀疏分类交叉熵损失,我认为这是一个好主意,分层的 k 倍对排名类变量。两个模型都采用了提前停车。

结果

下表显示了两个模型在每个数据集上的指标。出乎意料的是,对于所有三个数据集,序数模型产生的交叉验证 MAE 都比回归模型好。顺序模型在准确性和推土机的距离度量方面也表现得更好。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

序数模型和回归模型在波士顿住房数据集上具有相似的性能。在运动学数据集上,序数模型比回归模型有最大的改进。

下图显示了回归(右)和有序(左)模型的预测输出与实际分布的分布。这些图显示了 1)波士顿住房数据集 2)股票数据集 3)和运动学数据集。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下图显示了每个 k 倍模型中阈值参数的变化。有趣的是,阈值的变化似乎与回归模型的改进成反比。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结论

在 TensorFlow 2.0 中设计并实现了有序逻辑回归模型的一个潜在实现。该设计使用了经典的比例优势定义和自定义阈值参数化。通过检查和分析阈值参数的梯度来构造阈值参数化。序数模型模型在平均交叉验证 MAE、准确性和 EMD 指标上与 MAE 回归模型产生了竞争结果。

资源

深度 Q 网(DQN)-I

原文:https://towardsdatascience.com/deep-q-network-dqn-i-bce08bdf2af?source=collection_archive---------6-----------------------

深度强化学习讲解— 15

开放式健身乒乓球和包装纸

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

之前的帖子中,我们已经展示了在一个小表格中表示动作值的解决方法。我们将这个表称为 Q 表。在 " 深度强化学习解释 " 系列的下三篇文章中,我们将向读者介绍使用神经网络来扩展我们可以通过强化学习解决的问题规模的想法,展示了深度 Q 网络(DQN),,它将最佳动作值函数表示为神经网络,而不是表格。在本帖中,我们将对 DQN 做一个概述,并介绍 Pong 的 OpenAI 健身房框架。在接下来的两篇文章中,我们将介绍该算法及其实现。

本出版物的西班牙语版本

[## 9.基于价值的营销:深度 Q 网络

请访问第 9 页的自由介绍

medium.com](https://medium.com/aprendizaje-por-refuerzo/9-m%C3%A9todos-value-based-deep-q-network-b52b5c3da0ba)

雅达利 2600 游戏

我们在之前的文章中提到的 Q-learning 方法通过遍历完整的状态集解决了这个问题。然而,我们常常意识到我们有太多的状态要跟踪。一个例子是 Atari 游戏,它可以有大量不同的屏幕,在这种情况下,问题不能用 Q 表来解决。

雅达利 2600 游戏机在 20 世纪 80 年代非常受欢迎,许多街机风格的游戏都可以为它提供。以今天的游戏标准来看,Atari 游戏机已经过时了,但它的游戏对计算机来说仍然具有挑战性,并且是 RL 研究中非常受欢迎的基准(使用模拟器)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

雅达利 2600(来源:维基百科)

2015 年,DeepMind 利用所谓的深度 Q 网络(DQN) 或深度 Q 学习算法,学会了比人类更好地玩许多雅达利视频游戏。介绍它的研究论文,应用于 49 个不同的游戏,发表在 Nature(通过深度强化学习的人类水平控制,doi:10.1038/nature14236,Mnih,和其他)和可以在这里找到

Atari 2600 游戏环境可以通过 OpenAI 健身房框架中的街机学习环境进行重现。该框架有每个游戏的多个版本,但为了本文的目的,将使用 Pong-v0 环境。

我们将研究这个算法,因为它真的允许我们学习在本系列的未来帖子中非常有用的提示和技巧。DeepMind 的《自然》论文包含了一个表格,其中列出了用于在所有 49 款用于评估的雅达利游戏上训练其模型的超参数的所有细节。然而,我们在这里的目标要小得多:我们只想解决乒乓游戏。

正如我们在以前的一些帖子中所做的那样,这篇帖子中的代码受到了 Maxim Lapan 的代码的启发,他写了一本关于这个主题的优秀的实用书籍

这篇文章的全部代码可以在 GitHub 上找到,而可以通过这个链接作为一个 Colab google 笔记本运行。

从计算需求的角度来看,我们之前的 FrozenLake 或 CartPole 示例并不苛刻,因为观测值很小。然而,从现在来看,事实并非如此。这篇文章中分享的代码版本在 2 小时内收敛到 19.0 的平均分数(使用 NVIDIA K80)。所以在训练循环的执行过程中不要紧张。;-)

恶臭

Pong 是一款以乒乓球为主题的街机电子游戏,以简单的二维图形为特色,由 Atari 制造,最初于 1972 年发布。在乒乓球比赛中,如果球从另一名球员身边经过,一名球员得分。当其中一名玩家达到 21 分时,一集结束。在 OpenAI Gym 框架版 Pong 中,右边显示代理人,左边显示敌人:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在乒乓球运动中,两个拍子来回移动球。分数由屏幕顶部的数字记录。(来源: torres.ai )

在 Pong 环境中,代理(玩家)可以采取三种动作:保持静止、垂直向上平移和垂直向下平移。然而,如果我们使用方法action_space.n ,我们可以认识到环境有 6 个动作:

import gym
import gym.spacesDEFAULT_ENV_NAME = “PongNoFrameskip-v4”
test_env = gym.make(DEFAULT_ENV_NAME)print(test_env.action_space.n)**6**

尽管 OpenAI Gym Pong 环境有六个动作:

print(test_env.unwrapped.get_action_meanings())**[‘NOOP’, ‘FIRE’, ‘RIGHT’, ‘LEFT’, ‘RIGHTFIRE’, ‘LEFTFIRE’]**

六个中的三个是多余的(FIRE 等于 NOOP,LEFT 等于 LEFTFIRE,RIGHT 等于 RIGHTFIRE)。

DQN 概述

在这种新方法的代理的核心,我们发现了一个深度神经网络,而不是我们在以前的帖子中看到的 Q 表。应该注意的是,代理仅被给予原始像素数据,即人类玩家将在屏幕上看到的数据,而不能访问底层的游戏状态、球的位置、球拍等。

作为强化信号,它反馈每个时间步的游戏分数变化。一开始,当神经网络用随机值初始化时,它真的很糟糕,但随着时间的推移,它开始将游戏中的情况和序列与适当的动作关联起来,并学会实际玩好游戏(毫无疑问,读者将能够用本系列中将要展示的代码来验证)。

输入空间

Atari 游戏以 210 X60 像素的分辨率显示,每个像素有 128 种可能的颜色:

print(test_env.observation_space.shape)**(210, 160, 3)**

从技术上讲,这仍然是一个离散的状态空间,但处理起来非常大,我们可以优化它。为了降低这种复杂性,需要执行一些最小的处理:将帧转换为灰度,并将它们缩小到 84 乘 84 像素的正方形块。现在让我们仔细想想,如果有了这个固定的图像,我们是否可以决定游戏的动态。观察中肯定有歧义,对吗?例如,我们无法知道球的前进方向)。这显然违反了马尔科夫属性

解决方案是维护过去的几个观察值,并将它们作为一种状态使用。在 Atari 游戏的情况下,论文的作者建议将 4 个后续帧堆叠在一起,并使用它们作为每个状态的观察值。由于这个原因,预处理将四个帧堆叠在一起,导致最终的状态空间大小为 84×84×4:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

输入状态空间转换(来源: torres.ai

输出

与迄今为止我们提出的一次仅产生一个 Q 值的传统强化学习设置不同,深度 Q 网络被设计成在单次正向传递中为环境中可用的每个可能动作产生 Q 值:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(来源: torres.ai )

这种通过网络一次计算所有 Q 值的方法避免了必须为每个动作单独运行网络,并且有助于显著提高速度。现在,我们可以简单地使用这个向量,通过选择具有最大值的向量来采取行动。

神经网络体系结构

最初的 DQN 代理商在所有 49 款游戏中使用了相同的神经网络架构,将 84x84x4 的图像作为输入。

屏幕图像首先由三个卷积层处理。这允许系统利用空间关系,并且可以划分空间规则空间。此外,由于四个帧被堆叠并作为输入提供,这些卷积层也提取这些帧的一些时间属性。使用 PyTorch,我们可以将模型的卷积部分编码为:

nn.Conv2d(input_shape, 32, kernel_size=8, stride=4),
        nn.ReLU(),
        nn.Conv2d(32, 64, kernel_size=4, stride=2),
        nn.ReLU(),
        nn.Conv2d(64, 64, kernel_size=3, stride=1),
        nn.ReLU()

其中input_shape是环境的observation_space.shape

卷积层之后是一个具有 ReLU 激活的全连接隐藏层和一个产生动作值向量的全连接线性输出层:

nn.Linear(conv_out_size, 512),
         nn.ReLU(),
         nn.Linear(512, n_actions)

其中conv_out_size是由给定形状的输入产生的卷积层输出中的值的数量。此值需要传递给第一个完全连接的图层构造器,并且可以进行硬编码,因为它是输入形状的函数(对于 84x84 输入,卷积图层的输出将为 3136)。然而,为了编码一个可以接受不同输入形状的通用模型(用于所有游戏),我们将使用一个简单的函数,_get_conv_out,该函数接受输入形状并将卷积层应用于这种形状的伪张量:

def get_conv_out(self, shape):
         o = self.conv(torch.zeros(1, *shape))
         return int(np.prod(o.size()))conv_out_size = get_conv_out(input_shape)

另一个要解决的问题是需要将卷积输出馈送到完全连接的层。但是 PyTorch 没有一个“更平坦”的图层,我们需要将一批 3D 张量重新整形为一批 1D 向量。在我们的代码中,我们建议在forward()函数中解决这个问题,在这里我们可以使用张量的view()函数将我们的一批 3D 张量重新整形为一批 1D 向量。

view()函数用与输入相同的数据和元素数“整形”一个张量,但具有指定的形状。这个函数有趣的地方在于,让一个单独的维度成为一个-1,在这种情况下,它是从剩余的维度和输入中的元素数量中推断出来的(该方法将执行数学运算以填充该维度)。例如,如果我们有一个形状张量(2,3,4,6),它是 144 个元素的 4D 张量,我们可以使用view(2,72)将其重塑为 2 行 72 列的 2D 张量。通过view(2,-1),due [144/ (346) = 2]可以得到相同的结果。

在我们的代码中,实际上,张量在第一维度中有一个批量大小,我们展平了一个 4D 张量(第一维度是批量大小,第二维度是颜色通道,这是我们后续帧的堆栈;第三和第四是形象维度。)从卷积部分到 2D 张量,作为我们的全连接层的输入,以获得每批输入的 Q 值。

我们刚刚描述的 DQN 类的完整代码写在下面:

import torch
import torch.nn as nn
import numpy as npclass DQN(nn.Module):
    def __init__(self, input_shape, n_actions):
        super(DQN, self).__init__()self.conv = nn.Sequential(
        nn.Conv2d(input_shape[0], 32, kernel_size=8, stride=4),
        nn.ReLU(),
        nn.Conv2d(32, 64, kernel_size=4, stride=2),
        nn.ReLU(),
        nn.Conv2d(64, 64, kernel_size=3, stride=1),
        nn.ReLU()
    )conv_out_size = self._get_conv_out(input_shape)self.fc = nn.Sequential(
         nn.Linear(conv_out_size, 512),
         nn.ReLU(),
         nn.Linear(512, n_actions)
    )def _get_conv_out(self, shape):
         o = self.conv(torch.zeros(1, *shape))
         return int(np.prod(o.size()))def forward(self, x):
         conv_out = self.conv(x).view(x.size()[0], -1)
         return self.fc(conv_out)

我们可以使用print功能查看网络架构概要:

DQN(
  (conv): Sequential(
    (0): Conv2d(4, 32, kernel_size=(8, 8), stride=(4, 4))
    (1): ReLU()
    (2): Conv2d(32, 64, kernel_size=(4, 4), stride=(2, 2))
    (3): ReLU()
    (4): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1))
    (5): ReLU()
  )
  (fc): Sequential(
    (0): Linear(in_features=3136, out_features=512, bias=True)
    (1): ReLU()
    (2): Linear(in_features=512, out_features=6, bias=True)
  )
)

开放式健身房包装纸

DeepMind 的论文中,为了提高方法的速度和收敛性,将几种转换(如已经介绍的将帧转换为灰度,并将它们缩小到 84 乘 84 像素的正方形块)应用于 Atari 平台交互。在我们使用 OpenAI Gym simulator 的例子中,转换是作为 OpenAI Gym 包装器实现的。

完整的列表相当长,在不同的源代码中有相同包装器的几种实现。我使用的是基于 OpenAI 基线库Lapan 的书的版本。让我们来介绍它们中每一个的代码。

例如,一些游戏如 Pong 需要用户按下 FIRE 按钮来开始游戏。以下代码对应于包装器FireResetEnv,该包装器在要求游戏启动的环境中按下 FIRE 按钮:

class FireResetEnv(gym.Wrapper):
   def __init__(self, env=None):
       super(FireResetEnv, self).__init__(env)
       assert env.unwrapped.get_action_meanings()[1] == ‘FIRE’
       assert len(env.unwrapped.get_action_meanings()) >= 3def step(self, action):
       return self.env.step(action)def reset(self):
       self.env.reset()
       obs, _, done, _ = self.env.step(1)
       if done:
          self.env.reset()
       obs, _, done, _ = self.env.step(2)
       if done:
          self.env.reset()
       return obs

除了按下 FIRE 之外,这个包装器还检查一些游戏中出现的几个角落情况。

我们需要的下一个包装器是MaxAndSkipEnv,它为 Pong 编写了几个重要的转换:

class MaxAndSkipEnv(gym.Wrapper):
    def __init__(self, env=None, skip=4):
        super(MaxAndSkipEnv, self).__init__(env)
        self._obs_buffer = collections.deque(maxlen=2)
        self._skip = skipdef step(self, action):
        total_reward = 0.0
        done = None
        for _ in range(self._skip):
           obs, reward, done, info = self.env.step(action)
           self._obs_buffer.append(obs)
           total_reward += reward
           if done:
               break
        max_frame = np.max(np.stack(self._obs_buffer), axis=0)
        return max_frame, total_reward, done, infodef reset(self):
       self._obs_buffer.clear()
       obs = self.env.reset()
       self._obs_buffer.append(obs)
       return obs

一方面,它允许我们通过将 max 应用到 N 个观察值(默认为 4 个)并将其作为该步骤的一个观察值返回,从而显著加快训练速度。这是因为在中间帧上,所选择的动作被简单地重复,并且我们可以每 N 步做出一个动作决定,因为用神经网络处理每一帧是一个相当费力的操作,但是后续帧之间的差异通常很小。

另一方面,它取最后两帧中每个像素的最大值,并将其用作观察值。一些 Atari 游戏有闪烁效果(当游戏在偶数和奇数帧上绘制屏幕的不同部分时,Atari 2600 开发者增加游戏精灵复杂性的正常做法),这是由于平台的限制。对于人眼来说,这种快速变化是不可见的,但它们会混淆神经网络。

请记住,我们已经提到过,在将帧馈送到神经网络之前,每一帧都使用色度灰度转换从 210x160(三色帧(RGB 颜色通道))缩小到单色 84 x84 图像。不同的方法是可能的。其中之一是裁剪图像的不相关部分,然后按比例缩小,如下面的代码所示:

class ProcessFrame84(gym.ObservationWrapper):
     def __init__(self, env=None):
         super(ProcessFrame84, self).__init__(env)
         self.observation_space = gym.spaces.Box(low=0, high=255, 
                               shape=(84, 84, 1), dtype=np.uint8)def observation(self, obs):
         return ProcessFrame84.process(obs)@staticmethod
     def process(frame)
         if frame.size == 210 * 160 * 3:
             img = np.reshape(frame, [210, 160,  3])
                                     .astype(np.float32)
         elif frame.size == 250 * 160 * 3:
             img = np.reshape(frame, [250, 160, 3])              
                                     .astype(np.float32)
         else:
             assert False, “Unknown resolution.”       
             img = img[:, :, 0] * 0.299 + img[:, :, 1] * 0.587 + 
                                          img[:, :, 2] * 0.114
             resized_screen = cv2.resize(img, (84, 110),            
                              interpolation=cv2.INTER_AREA)
             x_t = resized_screen[18:102, :]
             x_t = np.reshape(x_t, [84, 84, 1])
             return x_t.astype(np.uint8)

正如我们已经讨论过的在一个游戏帧中缺乏游戏动态的快速解决方案,类BufferWrapper将几个(通常是四个)后续帧堆叠在一起:

class BufferWrapper(gym.ObservationWrapper):
    def __init__(self, env, n_steps, dtype=np.float32):
        super(BufferWrapper, self).__init__(env)
        self.dtype = dtype
        old_space = env.observation_space
        self.observation_space =
                 gym.spaces.Box(old_space.low.repeat(n_steps, 
                 axis=0),old_space.high.repeat(n_steps, axis=0),     
                 dtype=dtype)
    def reset(self):
        self.buffer = np.zeros_like(self.observation_space.low,
        dtype=self.dtype)
        return self.observation(self.env.reset())def observation(self, observation):
        self.buffer[:-1] = self.buffer[1:]
        self.buffer[-1] = observation
        return self.buffer

张量的输入形状有一个颜色通道作为最后一个维度,但 PyTorch 的卷积层假设颜色通道是第一维。这个简单的包装器将观察的形状从 HWC(高度、宽度、通道)改变为 PyTorch 要求的 CHW(通道、高度、宽度)格式:

class ImageToPyTorch(gym.ObservationWrapper):
    def __init__(self, env):
        super(ImageToPyTorch, self).__init__(env)
        old_shape = self.observation_space.shape
        self.observation_space = gym.spaces.Box(low=0.0, high=1.0,            
                                shape=(old_shape[-1], 
                                old_shape[0], old_shape[1]),
                                dtype=np.float32)def observation(self, observation):
      return np.moveaxis(observation, 2, 0)

从模拟器获得的屏幕被编码为具有从 0 到 255 的值的字节张量,这不是神经网络的最佳表示。因此,我们需要将图像转换成浮点数,并将值重新调整到范围[0.0…1.0]。这是由ScaledFloatFrame包装器完成的:

class ScaledFloatFrame(gym.ObservationWrapper):
     def observation(self, obs):
         return np.array(obs).astype(np.float32) / 255.0

最后,下面这个简单的函数make_env将会很有帮助,它根据名字创建了一个环境,并对它应用了所有需要的包装器:

def make_env(env_name):
    env = gym.make(env_name)
    env = MaxAndSkipEnv(env)
    env = FireResetEnv(env)
    env = ProcessFrame84(env)
    env = ImageToPyTorch(env) 
    env = BufferWrapper(env, 4)
    return ScaledFloatFrame(env)

下一步是什么?

这是致力于**深度 Q 网(DQN)**的三篇帖子中的第一篇,在这三篇帖子中,我们提供了 DQN 的概况以及对 Pong 的 OpenAI 健身房框架的介绍。在接下来的两篇帖子(帖 16帖 17 )中,我们将展示该算法及其实现,其中我们将涵盖 dqn 提高其训练稳定性和收敛性的几个技巧。

深度强化学习讲解系列

UPC 巴塞罗那理工 巴塞罗那超级计算中心

一个轻松的介绍性系列以一种实用的方式逐渐向读者介绍这项令人兴奋的技术,它是人工智能领域最新突破性进展的真正推动者。

[## 深度强化学习解释-乔迪托雷斯。人工智能

本系列的内容](https://torres.ai/deep-reinforcement-learning-explained-series/)

关于这个系列

我在五月份开始写这个系列,那是在巴塞罗那的封锁期。老实说,由于封锁,在业余时间写这些帖子帮助了我 #StayAtHome 。感谢您当年阅读这份刊物;它证明了我所做的努力。

免责声明 —这些帖子是在巴塞罗纳封锁期间写的,作为个人消遣和传播科学知识,以防它可能对某人有所帮助,但不是为了成为 DRL 地区的学术参考文献。如果读者需要更严谨的文档,本系列的最后一篇文章提供了大量的学术资源和书籍供读者参考。作者意识到这一系列的帖子可能包含一些错误,如果目的是一个学术文件,则需要对英文文本进行修订以改进它。但是,尽管作者想提高内容的数量和质量,他的职业承诺并没有留给他这样做的自由时间。然而,作者同意提炼所有那些读者可以尽快报告的错误。

深度 Q-网络(DQN)-II

原文:https://towardsdatascience.com/deep-q-network-dqn-ii-b6bf911b6b2c?source=collection_archive---------1-----------------------

深度强化学习讲解— 16

体验重放和目标网络

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是**【深度 Q 网】(DQN)、深度强化学习讲解系列中的**的第二篇帖子,其中我们将分析当我们将深度学习应用于强化学习时出现的一些挑战。我们还将详细介绍使用在上一篇文章中介绍的 DQN 网络解决 OpenAI Gym Pong 游戏的代码。

本出版物的西班牙语版本

** [## 9.基于价值的营销:深度 Q 网络

请访问第 9 页的自由介绍

medium.com](https://medium.com/aprendizaje-por-refuerzo/9-m%C3%A9todos-value-based-deep-q-network-b52b5c3da0ba)

深度强化学习的挑战

不幸的是,当使用神经网络来表示动作值时,强化学习更加不稳定,尽管应用了上一节介绍的包装器。训练这样的网络需要大量的数据,但即使这样,也不能保证收敛到最优值函数。事实上,由于动作和状态之间的高度相关性,存在网络权重可能振荡或发散的情况。

为了解决这个问题,本节我们将介绍深度 Q 网络使用的两种技术:

  • 体验回放
  • 目标网络

研究人员还发现了更多的技巧和窍门来使 DQN 训练更加稳定和有效,我们将在本系列的后续文章中介绍其中的精华。

体验回放

我们试图用神经网络来逼近一个复杂的非线性函数 Q(s,a)。要做到这一点,我们必须使用贝尔曼方程计算目标,然后考虑我们手头有一个监督学习问题。然而,SGD 优化的一个基本要求是训练数据是独立和同分布的,并且当代理与环境交互时,经验元组的序列可以是高度相关的。以连续顺序从这些经验元组中的每一个学习的朴素 Q 学习算法冒着被这种相关性的影响所左右的风险。

我们可以使用我们过去的经验和样本训练数据的大缓冲区,而不是使用我们的最新经验,来防止行动值发生灾难性的波动或偏离。这个技术叫做重放缓冲或者经验缓冲。重放缓冲器包含经验元组的集合( SARS’)。当我们与环境交互时,元组被逐渐添加到缓冲区中。最简单的实现是一个固定大小的缓冲区,将新数据添加到缓冲区的末尾,以便将最老的体验推出。

为了学习,从重放缓冲器中采样一小批元组的行为被称为体验重放。除了打破有害的相关性,经验回放允许我们多次从单个元组中学习更多,回忆罕见的事件,并在总体上更好地利用我们的经验。

总之,经验重放背后的基本思想是存储过去的经验,然后使用这些经验的随机子集来更新 Q 网络,而不是仅使用单个最近的经验。为了存储代理的经验,我们在 Python 的内置集合库中使用了一个名为deque的数据结构。它基本上是一个列表,你可以设置一个最大大小,这样,如果你试图添加到列表中,它已经满了,它会删除列表中的第一个项目,并在列表的末尾添加新的项目。体验本身就是*【观察、动作、奖励、完成标志、下一个状态】*的元组,保持从环境中获得的跃迁。

Experience = collections.namedtuple(‘Experience’, 
           field_names=[‘state’, ‘action’, ‘reward’, 
           ‘done’, ‘new_state’])class ExperienceReplay:
  def __init__(self, capacity):
      self.buffer = collections.deque(maxlen=capacity) def __len__(self):
      return len(self.buffer) def append(self, experience):
      self.buffer.append(experience)

  def sample(self, batch_size):
      indices = np.random.choice(len(self.buffer), batch_size,
                replace=False)
      states, actions, rewards, dones, next_states = 
             zip([self.buffer[idx] for idx in indices])
      return np.array(states), np.array(actions),         
             np.array(rewards,dtype=np.float32), 
             np.array(dones, dtype=np.uint8),    
             np.array(next_states)

每当代理在环境中执行一个步骤时,它就将转换推入缓冲区,只保留固定数量的步骤(在我们的例子中,10k 个转换)。对于训练,我们从重放缓冲区中随机抽取一批转换,这允许我们打破环境中后续步骤之间的相关性。

大多数体验重放缓冲区代码非常简单:它基本上利用了deque库的功能。在sample()方法中,我们创建一个随机索引列表,然后将采样条目重新打包到 NumPy 数组中,以便更方便地计算损失。

目标网络

记住在 Q-Learning 中,我们 用猜测 更新猜测,这可能会导致有害的相关性。贝尔曼方程通过 Q(s’,a’)为我们提供了 Q(s,a)的值。然而,状态 s 和 s’之间只有一步之遥。这使得它们非常相似,神经网络很难区分它们。

当我们执行神经网络参数的更新以使 Q(s,a)更接近期望的结果时,我们可以间接改变 Q(s ',a ')和附近其他状态产生的值。这会使我们的训练非常不稳定。

为了使训练更加稳定,有一个技巧,称为目标网络,通过它我们保留了一份我们神经网络的副本,并将其用于贝尔曼方程中的 Q(s ',a ')值。

也就是说,被称为目标网络的第二 Q 网络的预测 Q 值被用于反向传播并训练主 Q 网络。需要强调的是,目标网络的参数不是训练出来的,而是周期性地与主 Q 网络的参数同步。想法是使用目标网络的 Q 值来训练主 Q 网络将提高训练的稳定性。

稍后,当我们呈现训练循环的代码时,我们将更详细地输入如何对这个目标网络的初始化和使用进行编码。

深度 Q 学习算法

深度 Q 学习算法中有两个主要的交错阶段。一个是我们通过执行动作对环境进行采样,并将观察到的经验元组存储在重放存储器中。另一个是我们从这个内存中随机选择小批元组,然后使用梯度下降(SGD)更新步骤从那批元组中学习

这两个阶段并不直接相互依赖,我们可以执行多个采样步骤,然后是一个学习步骤,甚至是不同随机批次的多个学习步骤。在实践中,您无法立即运行学习步骤。你需要等到你在 D 中有足够的经验元组。

算法的其余部分被设计成支持这些步骤。我们可以用这个基本 DQN 算法的伪代码来总结前面的解释,它将指导我们实现该算法:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一开始,我们需要创建主网络和目标网络,并初始化一个空的重放存储器 D 。请注意,内存是有限的,因此我们可能希望使用类似循环队列的东西来保留 d 最近的经验元组。我们还需要初始化代理,它是与环境交互的主要组件之一。

请注意,我们不会在每集之后清除记忆,这使我们能够回忆和建立跨集的经验批次。

编码训练循环

超参数和执行时间

在进入代码之前,请注意 DeepMind 的自然论文包含一个表格,其中列出了用于在所有 49 款用于评估的 Atari 游戏上训练其模型的超参数的所有细节。DeepMind 为所有游戏保留了相同的参数,但为每个游戏训练了单独的模型。该团队的目的是表明该方法足够健壮,可以使用一个单一的模型架构和超参数来解决许多具有不同复杂性、动作空间、奖励结构和其他细节的游戏。

然而,我们在这篇文章中的目标只是解决 Pong 游戏,与 Atari 测试集中的其他游戏相比,这是一个非常简单和直接的游戏,所以本文中的超参数不是最适合像这篇文章这样的说教性文章。出于这个原因,我们决定为 Pong 环境使用更个性化的参数值,根据 colab 分配给我们的执行的 GPU 类型(最多几个小时),Pong 环境在合理的墙时间内收敛到平均分数 19.0。记住,我们可以通过命令!nvidia-smi知道分配给我们的运行时环境的 GPU 的类型。

让我们开始更详细地介绍代码。这篇文章的全部代码可以在 GitHub 上找到(而可以使用这个链接作为 Colab google 笔记本运行)。我们跳过了包的导入细节,这很简单,我们集中在超参数的解释上:

DEFAULT_ENV_NAME = “PongNoFrameskip-v4” 
MEAN_REWARD_BOUND = 19.0 gamma = 0.99                    or
batch_size = 32                 
replay_size = 10000             
learning_rate = 1e-4            
sync_target_frames = 1000        
replay_start_size = 10000 eps_start=1.0
eps_decay=.999985
eps_min=0.02

这些DEFAULT_ENV_NAME确定了训练的环境,MEAN_REWARD_BOUND确定了停止训练的奖励界限。当我们的代理人在过去的 100 场比赛中平均赢得 19 场比赛(共 21 场)时,我们将认为这场比赛已经收敛。其余参数表示:

  • gamma是贴现因子
  • batch_size,迷你批次大小
  • learning_rate是学习率
  • replay_size重放缓冲区大小(存储在重放存储器中的最大体验次数)
  • sync_target_frames表示我们将模型重量从主 DQN 网络同步到目标 DQN 网络的频率(同步之间间隔多少帧)
  • replay_start_size开始训练前添加到重放缓冲区的帧数(体验数)。

最后,与ε衰变时间表相关的超参数与之前的相同:

eps_start=1.0
eps_decay=.999985
eps_min=0.02

代理人

我们需要的一个主要组件是代理,它与环境交互,并将交互的结果保存到体验回放缓冲区中。我们将要设计的代理类已经将与环境交互的结果直接保存到体验重放缓冲区 中,执行前面伪代码部分中指示的样本阶段的这三个步骤:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

首先,在代理的初始化期间,我们需要存储对环境和经验重放缓冲区 D 的引用,该缓冲区在创建代理的对象时被指示为参数exp_buffer:

class Agent: 
    def __init__(self, env, exp_buffer):
        self.env = env
        self.exp_buffer = exp_buffer
        self._reset()def _reset(self):
        self.state = env.reset()
        self.total_reward = 0.0

为了在环境中执行代理的步骤并将其结果存储在体验回放内存中,我们建议使用以下代码:

def play_step(self, net, epsilon=0.0, device=”cpu”): done_reward = None if np.random.random() < epsilon:
       action = env.action_space.sample()
    else:
       state_a = np.array([self.state], copy=False)
       state_v = torch.tensor(state_a).to(device)
       q_vals_v = net(state_v)
       _, act_v = torch.max(q_vals_v, dim=1)
       action = int(act_v.item())

方法play_step使用 ϵ-greedy(Q) 策略在每个时间步选择动作。换句话说,根据概率epsilon(作为参数传递),我们采取随机行动;否则,我们使用过去的模型来获得所有可能动作的 Q 值,并选择最佳的。

在获得action之后,该方法执行环境中的步骤以获得下一个观察值:next_staterewardis_done:

 new_state, reward, is_done, _ = self.env.step(action)
    self.total_reward += reward

最后,该方法将观察结果存储在体验重放缓冲器中,然后处理剧集结束的情况:

 exp = Experience(self.state,action,reward,is_done,new_state)
    self.exp_buffer.append(exp)
    self.state = new_state
    if is_done:
       done_reward = self.total_reward
       self._reset()
    return done_reward

该函数的结果是如果我们已经到达这一步的最后一集的总累积奖励,或者是 None 如果不是

主循环

在初始化部分,我们创建了应用了所有必需的包装器的环境,我们将要训练的主 DQN 神经网络,以及具有相同架构的目标网络。我们还创建所需大小的体验重放缓冲区,并将其传递给代理。在训练循环之前,我们要做的最后一件事是创建一个优化器、一个完整剧集奖励的缓冲区、一个帧计数器和一个变量来跟踪达到的最佳平均奖励(因为每次平均奖励超过记录时,我们都会将模型保存在一个文件中):

env = make_env(DEFAULT_ENV_NAME)
net = DQN(env.observation_space.shape,
          env.action_space.n).to(device)
target_net = DQN(env.observation_space.shape,
          env.action_space.n).to(device)buffer = ExperienceReplay(replay_size)
agent = Agent(env, buffer)epsilon = eps_startoptimizer = optim.Adam(net.parameters(), lr=learning_rate)
total_rewards = []
frame_idx = 0best_mean_reward = None

在训练循环的开始,我们计算完成的迭代次数,并更新 epsilon,就像我们在之前的文章中介绍的那样。接下来,代理在环境中执行单个步骤(使用当前神经网络和 epsilon 的值作为参数)。请记住,只有当这一步是本集的最后一步时,该函数才会返回 non-None 结果。在这种情况下,我们在控制台中报告进度(播放的剧集数、最近 100 集的平均奖励以及 epsilon 的当前值):

while True:
  frame_idx += 1
  epsilon = max(epsilon*eps_decay, eps_min)
  reward = agent.play_step(net, epsilon, device=device) if reward is not None:
     total_rewards.append(reward)
     mean_reward = np.mean(total_rewards[-100:])
     print(“%d: %d games, mean reward %.3f, (epsilon %.2f)” % 
          (frame_idx, len(total_rewards), mean_reward, epsilon))

之后,每次我们对过去 100 集的平均奖励达到最大值时,我们都会在控制台中报告这一情况,并将当前的模型参数保存在一个文件中。同样,如果这意味着奖励超过了指定的MEAN_REWARD_BOUND(在我们的例子中是 19.0),那么我们就停止训练。第三个 if 帮助我们确保我们的经验重放缓冲区对于训练来说足够大:

if best_mean_reward is None or 
        best_mean_reward < mean_reward:
             torch.save(net.state_dict(), 
                       DEFAULT_ENV_NAME + “-best.dat”)
             best_mean_reward = mean_reward
             if best_mean_reward is not None:
             print(“Best mean reward updated %.3f” % 
                  (best_mean_reward))if mean_reward > MEAN_REWARD_BOUND:
             print(“Solved in %d frames!” % frame_idx)
             breakif len(buffer) < replay_start_size:
             continue

学习阶段

现在,我们将开始描述主循环中的代码部分,它指的是网络学习的阶段(前面伪代码的一部分):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们为实现这一部分而编写的完整代码如下:

batch = buffer.sample(batch_size) 
states, actions, rewards, dones, next_states = batchstates_v = torch.tensor(states).to(device)
next_states_v = torch.tensor(next_states).to(device)
actions_v = torch.tensor(actions).to(device)
rewards_v = torch.tensor(rewards).to(device)
done_mask = torch.ByteTensor(dones).to(device)state_action_values = net(states_v).gather(1, 
                          actions_v.unsqueeze(-1)).squeeze(-1)
next_state_values = target_net(next_states_v).max(1)[0]
next_state_values[done_mask] = 0.0
next_state_values = next_state_values.detach()expected_state_action_values=next_state_values * gamma + rewards_vloss_t = nn.MSELoss()(state_action_values, 
                     expected_state_action_values)optimizer.zero_grad()
loss_t.backward()
optimizer.step()if frame_idx % sync_target_frames == 0:
   target_net.load_state_dict(net.state_dict())

我们将对其进行剖析,以便于描述,因为它可能是最难理解的部分。

要做的第一件事是从重放存储器中随机抽取少量事务:

batch = buffer.sample(batch_size) 
states, actions, rewards, dones, next_states = batch

接下来,代码用 PyTorch 张量中的批量数据包装各个 NumPy 数组,并将它们复制到 GPU(我们假设在参数中指定了 CUDA 设备):

states_v = torch.tensor(states).to(device)
next_states_v = torch.tensor(next_states).to(device)
actions_v = torch.tensor(actions).to(device)
rewards_v = torch.tensor(rewards).to(device)
done_mask = torch.ByteTensor(dones).to(device)

这个代码的灵感来自于马克西姆·拉潘的代码。它以一种形式编写,通过向量运算处理(并行)所有批量样本,最大限度地利用 GPU 的能力。但是一步一步解释,就能明白,没有问题。

然后,我们将观察结果传递给第一个模型,并使用gather()张量运算提取所采取行动的特定 Q 值。这个函数调用的第一个参数是一个维度索引,我们希望对其执行聚集。在这种情况下,它等于 1,因为它对应于操作维度:

state_action_values = net(states_v).gather(1, 
                          actions_v.unsqueeze(-1)).squeeze(-1)

第二个参数是要选择的元素的索引张量。这里解释代码有点复杂。我们试试吧!。 Maxim Lapan 建议使用功能unsqueeze()squeeze()。因为索引应该具有与我们正在处理的数据相同的维数(在我们的例子中是 2D ),所以它对action_v(也就是 1D)应用一个unsqueeze(),来计算聚集函数的索引参数。最后,为了删除我们创建的额外维度,我们将使用squeeze()函数。让我们在一个简单的示例案例中,用一批四个条目和四个动作来说明一个聚集做了什么:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

请注意,应用于张量的 gather()的结果是一个可微分的运算,它将保持关于最终损失值的所有梯度。

既然我们已经计算了重放缓冲区中每个转换的状态-动作值,我们还需要计算重放缓冲区中每个转换的目标“y”。这两个向量都是我们将在损失函数中使用的向量。为此,请记住我们必须使用目标网络。

在下面的代码中,我们将目标网络应用于我们的下一个状态观察,并沿着相同的动作维度 1 计算最大 Q 值:

next_state_values = target_net(next_states_v).max(1)[0]

函数max()返回最大值和这些值的索引(因此它计算 max 和 argmax)。因为在这种情况下,我们只对值感兴趣,所以我们取结果的第一个条目。

请记住,如果批次中的过渡是从剧集的最后一步开始的,那么我们的动作值没有下一个状态的折扣奖励,因为没有下一个状态可以收集奖励:

next_state_values[done_mask] = 0.0

虽然我们不能深入细节,但重要的是要强调目标神经网络对下一个状态值的计算不应该影响梯度。为了实现这一点,我们使用 PyTorch 张量的detach()函数,该函数在不连接到父操作的情况下复制它,以防止梯度流入目标网络的图形:

next_state_values = next_state_values.detach()

现在,我们可以计算目标向量(“y”)的贝尔曼近似值,这是重放缓冲区中每个转换的预期状态-动作值的向量:

expected_state_action_values=next_state_values * gamma + rewards_v

我们拥有计算均方误差损失所需的所有信息:

loss_t = nn.MSELoss()(state_action_values, 
                     expected_state_action_values)

训练循环的下一部分使用 SGD 算法通过最小化损失来更新主神经网络:

optimizer.zero_grad()
loss_t.backward()
optimizer.step()

最后,代码的最后一行每隔sync_target_frames将参数从我们的主 DQN 网络同步到目标 DQN 网络:

if frame_idx % sync_target_frames == 0:
   target_net.load_state_dict(net.state_dict())

至此,主循环的代码!

下一步是什么?

这是三篇专门介绍**深度 Q 网络(DQN)基础知识的文章中的第二篇,在这三篇文章中,我们详细介绍了该算法。在下一篇文章中,我们将讨论该算法的性能,并展示我们如何使用它。

深度强化学习讲解系列

UPC 巴塞罗那理工 巴塞罗那超级计算中心

一个轻松的介绍性系列以一种实用的方式逐渐向读者介绍这项令人兴奋的技术,它是人工智能领域最新突破性进展的真正推动者。

** [## 深度强化学习解释-乔迪托雷斯。人工智能

本系列的内容](https://torres.ai/deep-reinforcement-learning-explained-series/)

关于这个系列

我在五月份开始写这个系列,那是在巴塞罗那的封锁期。老实说,由于封锁,在业余时间写这些帖子帮助了我#呆在家里 。感谢您当年阅读这份刊物;它证明了我所做的努力。

免责声明 —这些帖子是在巴塞罗纳封锁期间写的,作为个人消遣和传播科学知识,以防它可能对某人有所帮助,但不是为了成为 DRL 地区的学术参考文献。如果读者需要更严谨的文档,本系列的最后一篇文章提供了大量的学术资源和书籍供读者参考。作者意识到这一系列的帖子可能包含一些错误,如果目的是一个学术文件,则需要对英文文本进行修订以改进它。但是,尽管作者想提高内容的数量和质量,他的职业承诺并没有留给他这样做的自由时间。然而,作者同意提炼所有那些读者可以尽快报告的错误。**

深度 Q-网络(DQN)-三

原文:https://towardsdatascience.com/deep-q-network-dqn-iii-c5a83b0338d2?source=collection_archive---------30-----------------------

深度强化学习讲解— 17

DQN 的性能和用途

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是第三篇专门讨论**【深度 Q 网】(DQN)、深度强化学习讲解系列中的**的帖子,其中我们展示了如何使用 TensorBoard 来获得模型性能的图形化视图。我们还提供了一种方法来查看我们训练有素的代理如何能够玩乒乓。

本出版物的西班牙语版本

** [## 9.基于价值的营销:深度 Q 网络

请访问第 9 页的自由介绍

medium.com](https://medium.com/aprendizaje-por-refuerzo/9-m%C3%A9todos-value-based-deep-q-network-b52b5c3da0ba)

这篇文章的全部代码可以在 GitHub 上找到(而可以使用这个链接作为 Colab google 笔记本运行)。

运行训练循环

当我们运行本例中的笔记本时,我们在控制台中获得以下输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

. . .

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由于训练过程中的随机性,您的实际动态将与此处显示的不同。

但是,尽管信息丰富,却很难从一百万行中得出结论。读者应该记得,在之前的帖子 5 ( PyTorch 使用 tensor board)中,我们介绍了工具tensor board,该工具有助于跟踪不同参数的进度,并且可以为找到超参数的值提供出色的支持。

我们在这里不再重复解释应该插入什么代码,但是读者可以在 GitHub 中找到这个例子的详细代码,它为 DQN 获得了一个在交互 TensorFlow 窗口中绘制的轨迹,如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

例如,使用此代码,我们可以获得跟踪以监控 epsilon、奖励和平均奖励的行为:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有了这个工具,我邀请用户找到更好的超参数。

使用模型

最后,在 GitHub 中,读者可以找到允许我们看到我们训练有素的代理如何玩 Pong 的代码:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们已经准备了代码,生成一个游戏的一集视频。视频存储在文件夹video中。

代码几乎是代理类的方法play_step()的精确拷贝,没有ε-贪婪动作选择。我们只是将我们的观察传递给代理,并选择具有最大值的动作。这里唯一的新东西是环境中的render()方法,这是 Gym 中显示当前观察的标准方式。

代码中的主循环,将动作传递给环境,统计总奖励,当剧集结束时停止循环。在这一集之后,它会显示总奖励和代理执行该动作的次数。

因为它需要有一个图形用户界面(GUI ),并且我们是在 colab 环境中执行我们的代码,而不是在我们的个人计算机中,所以我们需要运行一组命令(从这个链接中获得)。

摘要

这是专门介绍深度 Q 网络(DQN)基础知识的三篇文章中的第三篇,在这三篇文章中,我们介绍了如何使用 TensorBoard 来帮助我们进行参数调整。我们还展示了如何可视化我们的代理的行为。

下期**

深度强化学习讲解系列

UPC 巴塞罗那理工 巴塞罗那超级计算中心

一个轻松的介绍性系列逐渐并以实用的方法向读者介绍这一令人兴奋的技术,这是人工智能领域最新突破性进展的真正推动者。

**** [## 深度强化学习解释-乔迪托雷斯。人工智能

本系列的内容](https://torres.ai/deep-reinforcement-learning-explained-series/)

关于这个系列

我是在五月份开始写这个系列的,那是在巴塞罗纳的**封锁期。**老实说,由于封锁,在业余时间写这些帖子帮助了我 #StayAtHome 。感谢您当年阅读这份刊物;这证明了我所做的努力。

免责声明 —这些帖子是在巴塞罗纳封锁期间写的,目的是分散个人注意力和传播科学知识,以防对某人有所帮助,但无意成为 DRL 地区的学术参考文献。如果读者需要更严谨的文档,本系列的最后一篇文章提供了大量的学术资源和书籍供读者参考。作者意识到这一系列的帖子可能包含一些错误,如果目的是一个学术文件,则需要对英文文本进行修订以改进它。但是,尽管作者想提高内容的数量和质量,他的职业承诺并没有留给他这样做的自由时间。然而,作者同意提炼所有那些读者可以尽快报告的错误。****

深度强化学习和超参数调整

原文:https://towardsdatascience.com/deep-reinforcement-learning-and-hyperparameter-tuning-df9bf48e4bd2?source=collection_archive---------14-----------------------

用雷的曲子优化你的模型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

深度强化学习最困难和最耗时的部分之一是超参数的优化。这些值(如折扣系数[latex]\gamma[/latex]或学习率)会对代理的性能产生重大影响。

代理需要接受培训,以了解超参数如何影响绩效——没有先验的方法来知道给定参数的更高或更低的值是否会提高总报酬。除了跟踪实验、数据和与训练模型相关的一切之外,这转化为多次昂贵的训练运行以获得好的代理。

Ray 提供了一种使用 Tune library 处理所有这些的方法,它可以自动处理您的各种模型,保存数据,调整您的超参数,并总结结果以便快速轻松地参考。

TL;速度三角形定位法(dead reckoning)

我们通过一个简单的例子来说明如何使用 Tune 的网格搜索特性来优化我们的超参数。

安装 Tune

Tune 是 Ray 项目的一部分,但是需要单独安装,所以如果您还没有安装它,您需要运行下面的命令来让 Tune 工作。

pip install ray[tune]

从这里,我们可以导入我们的包来训练我们的模型。

import ray
from ray import tune

调整您的第一个模型

从基础开始,让我们用 Tune 训练一个 agent 来解决CartPole-v0。Tune 需要一些具有不同设置和标准的字典来训练。它必须具有的两个参数是configstop

config字典将为调优提供它需要运行的环境,以及您可能想要指定的任何特定于环境的配置。这也是你的大部分超参数将要驻留的地方,但是我们一会儿会讲到。

stop字典告诉 Tune 什么时候结束训练或者什么时候完全停止训练。它可以根据奖励标准、经过的时间、完成的步数等进行定制。当我第一次开始使用 Tune 时,我忽略了设置任何停止标准,最终让算法训练了几个小时才意识到这一点。所以,你可以不用这个来运行它,但是如果你不小心的话,你可能会得到一个不错的 AWS 账单!

尝试使用以下代码在CartPole-v0上运行 PPO 算法 10,000 个时间步长。

ray.init(ignore_reinit_error=True)
config = {
    'env': 'CartPole-v0'
}
stop = {
    'timesteps_total': 10000
}
results = tune.run(
    'PPO', # Specify the algorithm to train
    config=config,
    stop=stop
)

通过这些设置,您应该可以看到您的工作人员、内存以及logdir状态的打印输出,所有数据都存储在这里以供以后分析。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

控制台将在每次迭代中打印这些值,除非tune.run()中的verbose参数被设置为 0(无声)。

当训练完成时,你会得到一个输出,显示状态已经终止,经过的时间,以及过去 100 集的平均奖励和其他数据。

使用网格搜索调整超参数

当我们利用它来调整我们的超参数时,Tune 的力量就真正发挥出来了。为此,我们将求助于grid_search函数,它允许用户为要测试的模型指定一组超参数。

为此,我们只需要在tune.grid_search()函数中包装一个值列表,并将其放入我们的配置字典中。让我们回到上面的CartPole例子。我们可能想看看学习速度是否有什么不同,双头网络是否有什么好处。我们可以使用grid_search()来实现这些的不同组合,如下所示:

config = {
    "env": 'CartPole-v0',
    "num_workers": 2,
    "vf_share_layers": tune.grid_search([True, False]),
    "lr": tune.grid_search([1e-4, 1e-5, 1e-6]),
    }
results = tune.run(
    'PPO', 
    stop={
        'timesteps_total': 100000
    },
    config=config)

现在我们看到一个展开的状态打印输出,其中包含我们想要运行的各种试验:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

当 Ray 启动其中的每一个时,它将显示我们想要探索的超参数的组合,以及每个超参数的回报、迭代和运行时间。当它完成时,我们应该看到每个的状态为终止,以表明它正常工作(否则它将读取错误)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分析调节结果

我们的tune.run()函数的输出是一个被我们标记为resultsanalysis对象。我们可以利用这一点来进一步了解我们实验的细节。可以通过results.dataframe()访问相关数据,这将返回一个 Pandas 数据帧,其中包含平均奖励、迭代次数、KL 散度、配置设置等等。数据框还包含保存您的实验的特定目录(logdir),因此您可以了解您的特定运行的详细信息。

如果您查看logdir目录,您会发现许多包含您的训练运行中保存的数据的文件。出于我们的目的,主文件将是progress.csv——它包含来自每个迭代的训练数据,允许您深入细节。

例如,如果我们想要查看不同设置的训练和损耗曲线,我们可以循环遍历数据框中的logdir列,加载每个progress.csv文件并绘制结果。

# Plot training results
import matplotlib.pyplot as plt
import pandas as pdcolors = plt.rcParams['axes.prop_cycle'].by_key()['color']
df = results.dataframe()# Get column for total loss, policy loss, and value loss
tl_col = [i for i, j in enumerate(df.columns)
          if 'total_loss' in j][0]
pl_col = [i for i, j in enumerate(df.columns)
          if 'policy_loss' in j][0]
vl_col = [i for i, j in enumerate(df.columns)
          if 'vf_loss' in j][0]
labels = []
fig, ax = plt.subplots(2, 2, figsize=(15, 15), sharex=True)
for i, path in df['logdir'].iteritems():
    data = pd.read_csv(path + '/progress.csv')
    # Get labels for legend
    lr = data['experiment_tag'][0].split('=')[1].split(',')[0]
    layers = data['experiment_tag'][0].split('=')[-1]
    labels.append('LR={}; Shared Layers={}'.format(lr, layers))

    ax[0, 0].plot(data['timesteps_total'], 
            data['episode_reward_mean'], c=colors[i],
            label=labels[-1])

    ax[0, 1].plot(data['timesteps_total'], 
           data.iloc[:, tl_col], c=colors[i],
           label=labels[-1])

    ax[1, 0].plot(data['timesteps_total'], 
               data.iloc[:, pl_col], c=colors[i],
               label=labels[-1])

    ax[1, 1].plot(data['timesteps_total'], 
               data.iloc[:, vl_col], c=colors[i],
               label=labels[-1])ax[0, 0].set_ylabel('Mean Rewards')
ax[0, 0].set_title('Training Rewards by Time Step')
ax[0, 0].legend(labels=labels, loc='upper center',
        ncol=3, bbox_to_anchor=[0.75, 1.2]) ax[0, 1].set_title('Total Loss by Time Step')
ax[0, 1].set_ylabel('Total Loss')
ax[0, 1].set_xlabel('Training Episodes')ax[1, 0].set_title('Policy Loss by Time Step')
ax[1, 0].set_ylabel('Policy Loss')
ax[1, 0].set_xlabel('Time Step')ax[1, 1].set_title('Value Loss by Time Step')
ax[1, 1].set_ylabel('Value Loss')
ax[1, 1].set_xlabel('Time Step')plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

超越网格搜索

Tune 中提供了更多的调优选项。如果你想看看你能调整什么,看看你的特定算法的文档。此外,Tune 支持不同的超参数优化方法。网格搜索可能会很慢,所以只需改变几个选项,就可以使用贝叶斯优化、HyperOpt 等。最后,Tune 使得基于人口的培训 (PBT)变得容易,允许多个代理跨不同的机器伸缩。所有这些都将在以后的帖子中介绍!

用于自动股票交易的深度强化学习

原文:https://towardsdatascience.com/deep-reinforcement-learning-for-automated-stock-trading-f1dad0126a02?source=collection_archive---------0-----------------------

使用强化学习通过 Python 和 OpenAI Gym 交易多只股票|在 ICAIF 2020 上展示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图像由 克里斯

来自《走向数据科学》编辑的注释: 虽然我们允许独立作者根据我们的 规则和指南 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

这篇博客基于我们的论文: 自动化股票交易的深度强化学习:一个集合策略 ,发表于ICAIF 2020:ACM 金融 AI 国际会议。

我们的代码可以在 Github 上找到。

[## 主 AI4Finance 的 FinRL-Trading/old _ repo _ ensemble _ strategy-Foundation/FinRL-Trading

该库提供 ICAIF 2020 论文的代码。该集合策略在 Jupiter 笔记本中重新实施,网址为…

github.com](https://github.com/AI4Finance-Foundation/FinRL-Trading/tree/master/old_repo_ensemble_strategy)

使用 FinRL 的集成策略:

[## FinRL/FinRL _ Ensemble _ stock trading _ ICAIF _ 2020 . ipynb at master ai4 finance-Foundation/FinRL

此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…

github.com](https://github.com/AI4Finance-Foundation/FinRL/blob/master/examples/FinRL_Ensemble_StockTrading_ICAIF_2020.ipynb)

我们的论文刊登在 SSRN。

[## 自动股票交易的深度强化学习:一种集成策略

撰写日期:2020 年 9 月 11 日股票交易策略在投资中起着至关重要的作用。然而,它是…

papers.ssrn.com](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3690996)

如果要引用我们的论文,参考格式如下:

杨红阳,,钟山和安瓦尔·瓦利德。2020.自动股票交易的深度强化学习:一个集成策略。ICAIF '20: ACM 金融人工智能国际会议,2020 年 10 月 15-16 日,纽约曼哈顿。美国纽约州纽约市 ACM。

一个最新的 DRL 自动交易库- FinRL 可以在这里找到:

[## GitHub-ai4 finance-Foundation/FinRL:自动化交易的深度强化学习框架…

新闻:我们计划分享我们的纸上交易和现场交易的代码。请积极与我们分享您的兴趣…

github.com](https://github.com/AI4Finance-Foundation/FinRL)

量化金融 FinRL:单只股票交易教程

量化金融 FinRL:多只股票交易教程

量化金融 FinRL:投资组合配置教程

ElegantRL 支持最先进的 DRL 算法,并在 Jupyter 笔记本中提供用户友好的教程。核心代码<1000 行,使用 PyTorch,OpenAI Gym,和 NumPy。

[## ElegantRL:一个轻量级且稳定的深度强化学习库

一天掌握深度强化学习。

towardsdatascience.com](/elegantrl-a-lightweight-and-stable-deep-reinforcement-learning-library-95cef5f3460b)

概观

人们很难高估股票交易策略在投资中的重要作用。

盈利的自动股票交易策略对投资公司和对冲基金至关重要。它适用于优化资本配置和最大化投资绩效,如预期回报。回报最大化可以基于对潜在回报和风险的估计。然而,在一个复杂多变的股票市场中设计一个有利可图的策略是具有挑战性的。

每个玩家都想要一个获胜的策略。不用说,在如此复杂和动态的股票市场中,一个有利可图的策略是不容易设计的。

然而,我们将揭示一种深度强化学习方案,通过最大化投资回报来自动学习股票交易策略。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 素妍 上的 Unsplash

我们的解决方案:集成深度强化学习交易策略
该策略包括三种基于行动者-批评家的算法:近似政策优化(PPO)、优势行动者-批评家(A2C)和深度确定性政策梯度(DDPG)。
它结合了三种算法的最佳特性,从而稳健地适应不同的市场条件。

使用夏普比率评估具有不同强化学习算法的交易代理的性能,并与道琼斯工业平均指数和传统的最小方差投资组合分配策略进行比较。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

AI4Finance-Foundation 版权所有

第一部分。为什么要用深度强化学习(DRL)来炒股?

现有的作品并不令人满意。深度强化学习方法有许多优点。

1.1 DRL 与现代投资组合理论(MPT)

  1. MPT 在样本外数据表现不佳。
  2. MPT 对异常值非常敏感。
  3. MPT仅基于股票收益计算,如果我们要考虑其他相关因素,例如一些技术指标如移动平均线收敛发散(MACD)相对强弱指数(RSI) ,MPT 可能无法将这些信息很好地结合在一起。

1.2 DRL 和监督机器学习预测模型

  1. DRL 不需要大的带标签的训练数据集。这是一个显著的优势,因为如今数据量呈指数级增长,标记大型数据集变得非常耗时耗力。
  2. DRL 使用奖励函数来优化未来的奖励,与预测未来结果概率的 ML 回归/分类模型形成对比。

1.3 利用 DRL 进行股票交易的理由

  1. 股票交易的目标是在规避风险的同时最大化收益。DRL 通过最大化一段时间内未来行动的预期总回报来解决这个优化问题。
  2. 股票交易是一个连续的过程测试新的想法,从市场获得反馈,并试图随着时间的推移优化交易策略。我们可以将股票交易过程建模为马尔可夫决策过程,这是强化学习的基础。

1.4 深度强化学习的优势

  1. 深度强化学习算法可以在很多挑战性游戏中胜过人类玩家。例如,2016 年 3 月, DeepMind 的 AlphaGo 程序,一种深度强化学习算法,在围棋比赛中击败了世界冠军 Lee Sedol。
  2. 回报最大化为交易目标:通过将回报函数定义为投资组合价值的变化,深度强化学习随着时间的推移使投资组合价值最大化。
  3. 股票市场提供顺序反馈DRL 可以在训练过程中依次提高模型性能。
  4. 探索-开发技术平衡尝试不同的新事物和利用已发现的东西。这与其他学习算法不同。此外,不需要技术人员提供训练样本或标记样本。此外,在探索过程中,智能体被鼓励去探索未知的人类专家。
  5. 经验回放:能够克服相关样本问题,因为从一批连续样本中学习可能会经历很大的差异,因此效率很低。体验重放通过从预先保存的重放存储器中随机采样小批量的过渡,有效地解决了这个问题。
  6. 多维数据:通过使用连续的动作空间,DRL 可以处理多维数据。
  7. 计算能力 : Q-learning 是一种非常重要的 RL 算法,然而,它无法处理大空间。DRL 作为一种高效的函数逼近器,被神经网络赋予了强大的处理超大状态空间和动作空间的能力。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图像由 凯文 上的 Unsplash

第二部分:什么是强化学习?什么是深度强化学习?利用强化学习进行股票交易的相关著作有哪些?

2.1 概念

强化学习是机器学习技术的三种方法之一,它通过从环境中顺序接收状态和奖励,并采取行动以达到更好的奖励,来训练智能体与环境进行交互。

深度强化学习用神经网络逼近 Q 值。使用神经网络作为函数逼近器将允许强化学习应用于大量数据。

贝尔曼方程是设计强化学习算法的指导原则。

马尔可夫决策过程(MDP) 用于环境建模。

2.2 相关作品

深度强化学习在金融市场中的最新应用考虑离散或连续的状态和行动空间,并采用以下学习方法之一:仅批评方法、仅行动者方法或行动者-批评方法。

**1。仅批评者方法:**仅批评者学习方法是最常见的,它使用 Q 学习、深度 Q 学习(DQN)及其改进来解决离散动作空间问题,并在单个股票或资产上训练代理。唯批评方法的思想是使用一个 Q 值函数来学习最优行动选择策略,该策略在给定当前状态的情况下最大化预期未来回报。 DQN 不是计算状态-动作值表,而是最小化目标 Q 值之间的均方误差,并使用神经网络来执行函数逼近。唯批评方法的主要限制是,它只适用于离散和有限的状态和行为空间,这对于大量股票投资组合是不实际的,因为价格当然是连续的。

  • Q-learning: 是一种基于值的强化学习算法,用于使用 Q 函数寻找最佳行动选择策略。
  • DQN: 在深度 Q 学习中,我们使用神经网络来逼近 Q 值函数。状态作为输入给出,允许动作的 Q 值是预测的输出。

**2。只有行动者的方法:**这里的思想是代理直接学习最优策略本身。神经网络学习策略,而不是让神经网络学习 Q 值。策略是一种概率分布,本质上是一种针对给定状态的策略,即采取允许的行动的可能性。只有演员的方法可以处理连续的动作空间环境。

  • **策略梯度:**旨在通过直接学习最优策略本身,使期望总报酬最大化。

3。行动者-批评家方法:行动者-批评家方法最近被应用于金融领域。想法是同时更新代表政策的演员网络和代表价值函数的评论家网络**。批评家估计价值函数,而行动者用政策梯度更新批评家引导的政策概率分布。随着时间的推移,演员学会了采取更好的行动,而评论家也更善于评估这些行动。演员-评论家方法已经被证明能够学习和适应大型和复杂的环境,并被用于玩流行的视频游戏,如 Doom。因此,行动者-批评家方法非常适合大规模股票投资组合的交易。**

  • A2C: A2C 是典型的演员-评论家算法。A2C 使用相同代理的副本并行工作,用不同的数据样本更新梯度。每个代理独立工作,与同一个环境交互。
  • PPO: PPO 的引入是为了控制政策梯度更新,保证新政策不会与之前的政策相差太大。
  • DDPG: DDPG 结合了 Q 学习和政策梯度的框架,使用神经网络作为函数逼近器。

第三部分:如何使用 DRL 交易股票?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图像由 马库斯 上的 Unsplash

3.1 数据

我们跟踪并选择道琼斯 30 股票(2016/01/01) 并使用从 01/01/2009 到 05/08/2020 的历史日数据来训练代理人并测试其表现。数据集从通过 沃顿研究数据服务(WRDS) 访问的 Compustat 数据库下载。

整个数据集在下图中被分割。2009 年 1 月 1 日至 2014 年 12 月 31 日的数据用于培训,2015 年 10 月 1 日至 2015 年 12 月 31 日的数据用于验证和参数调整。最后,我们在从 2016 年 1 月 1 日到 2020 年 5 月 8 日的交易数据上测试我们的代理的性能。为了更好地利用交易数据,我们在交易阶段继续培训我们的代理,因为这将帮助代理更好地适应市场动态。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

AI4Finance-Foundation 版权所有

3.2 股票交易的 MDP 模型:

𝒔 = [𝒑,𝒉,𝑏]:]一个向量,包括股票价格𝒑 ∈ R+^D,股票份额𝒉 ∈ Z+^D,以及余额 R+,其中表示股票数量,Z+表示非负整数。

动作𝒂:𝐷股票的动作向量。每只股票的允许操作包括出售、购买或持有,分别导致𝒉股票的减少、增加和不变。

奖励𝑟(𝑠,𝑎,𝑠′):the 直接奖励在𝑠州采取行动的𝑎到达新的州𝑠′.

政策𝜋(𝑠):)𝑠州的交易策略,这是𝑠.州行动的概率分布

Q 值 𝑄𝜋 (𝑠,𝑎):)在政策𝜋之后,在𝑠采取行动𝑎的预期回报。

我们的股票交易过程的状态转换如下图所示。在每个状态下,对投资组合中的股票𝑑 (𝑑 = 1,…,𝐷)采取三种可能的行动之一。

  • 卖出𝒌[𝑑]∈【1,𝒉[𝑑】]股票的结果是𝒉𝒕+1[𝑑]=𝒉𝒕[𝑑]—𝒌[𝑑],where𝒌[𝑑]∈z+and𝑑=1,…,𝐷.
  • 控股,𝒉𝒕+1[𝑑]=𝒉𝒕[𝑑].
  • 购买𝒌[𝑑股票的结果是𝒉𝒕+1[𝑑] = 𝒉𝒕 [𝑑] + 𝒌[𝑑].

如图 2 所示,在𝑡时间采取行动,并且在𝑡+1 更新股票价格,因此,投资组合价值可以分别从“投资组合价值 0”变为“投资组合价值 1”、“投资组合价值 2”或“投资组合价值 3”。请注意,投资组合的价值是𝒑𝑻 𝒉 + 𝑏.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

AI4Finance-Foundation 版权所有

3.3 约束:

  • 市场流动性:订单能够以收盘价快速执行。我们假设股票市场不会受到我们的强化交易代理的影响。
  • 非负余额:允许的动作不应该导致负余额。
  • 交易成本:每笔交易都会产生交易成本。交易费用有很多种,如交易费、执行费和证交会费。不同的券商佣金不同。尽管费用有这些变化,我们假设我们的交易成本是每笔交易价值的 1/1000(买入或卖出)。
  • 市场崩盘风险规避:有可能导致股市崩盘的突发事件,如战争、股市泡沫破裂、主权债务违约、金融危机等。为了在像 2008 年全球金融危机这样的最坏情况下控制风险,我们采用金融 动荡指数 来衡量极端的资产价格变动。

3.4 回报最大化作为交易目标

我们将我们的回报函数定义为当𝑎在状态𝑠采取行动并到达新的状态𝑠 + 1 时,投资组合价值的变化

目标是设计一个在动态环境中最大化投资组合价值𝑟(𝑠𝑡,𝑎𝑡,𝑠𝑡+1 变化的交易策略,我们采用深度强化学习方法来解决这个问题。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由萨克Unsplash 上拍摄

3.5 多只股票的环境:

状态空间:我们用一个 181 维向量(30 只股票 6 + 1)组成的七部分信息来表示多只股票交易环境的状态空间*

  1. 余额:当前时间步账户中剩余的可用金额
  2. 价格:每只股票当前调整后的收盘价。
  3. 股份:每只股票拥有的股份。
  4. MACD :使用收盘价计算移动平均趋同背离(MACD)。
  5. RSI :相对强弱指数(RSI)是用收盘价计算出来的。
  6. CCI :商品通道指数(CCI)是用最高价、最低价和收盘价计算出来的。
  7. ADX :使用最高价、最低价和收盘价计算平均方向指数(ADX)。

动作空间:

  1. 对于单只股票,动作空间定义为 {-k,…,-1,0,1,…,k} ,其中 k 和-k 表示我们可以买卖的股票数量, k ≤h_maxh_max 是一个预定义的参数,设置为每次买入动作的最大股票数量。
  2. 对于多只股票,因此整个行动空间的大小是 (2k+1)^30 )。
  3. 然后,动作空间被归一化为 [-1,1] ,因为 RL 算法 A2C 和 PPO 直接在高斯分布上定义策略,该高斯分布需要被归一化和对称。
**class StockEnvTrain**(gym.Env):**“””A stock trading environment for OpenAI gym”””
** metadata = {‘render.modes’: [‘human’]}def __init__(self, df, day = 0):
 self.day = day
 self.df = df # Action Space
 # action_space normalization and shape is STOCK_DIM
 **self.action_space = spaces.Box(low = -1, high = 1,shape = (STOCK_DIM,))** 
 # State Space
 # Shape = 181: [Current Balance]+[prices 1–30]+[owned shares 1–30] 
 # +[macd 1–30]+ [rsi 1–30] + [cci 1–30] + [adx 1–30]
 **self.observation_space = spaces.Box(low=0, high=np.inf, shape = (181,))** # load data from a pandas dataframe
 self.data = self.df.loc[self.day,:]
 self.terminal = False # initalize state
 **self.state = [INITIAL_ACCOUNT_BALANCE] + \
 self.data.adjcp.values.tolist() + \
 [0]*STOCK_DIM + \
 self.data.macd.values.tolist() + \
 self.data.rsi.values.tolist() + \
 self.data.cci.values.tolist() + \
 self.data.adx.values.tolist()** # initialize reward
 self.reward = 0
 self.cost = 0 # memorize all the total balance change
 self.asset_memory = [INITIAL_ACCOUNT_BALANCE]
 self.rewards_memory = []
 self.trades = 0
 #self.reset()
 self._seed()

3.6 基于深度强化学习的交易代理

A2C

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

AI4Finance-Foundation 版权所有

A2C 是一个典型的演员-评论家算法,我们将其作为集成方法中的一个组件。A2C 出台梯度更新完善政策。A2C 利用一个优势函数来减少政策梯度的方差。评论家网络不是仅估计价值函数,而是估计优势函数。因此,对一个行动的评价不仅要看这个行动有多好,还要考虑它还能好到什么程度。从而降低了策略网络的高方差,使模型更加健壮。

A2C 使用同一代理的个副本并行工作,用不同的数据样本更新梯度。每个代理独立工作,与同一个环境交互。在所有并行代理完成计算它们的梯度后,A2C 使用协调器将所有代理的平均梯度传递给全局网络。以便全球网络可以更新演员和评论家网络。全球网络的存在增加了培训数据的多样性。同步梯度更新更具成本效益,速度更快,在批量较大时效果更好。A2C 是股票交易的好榜样,因为它稳定。

DDPG

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

AI4Finance-Foundation 版权所有

DDPG 是一种基于演员-评论家的算法,我们将其作为整体策略的一部分,以最大化投资回报。DDPG 结合了 Q 学习和策略梯度 t 的框架,并使用神经网络作为函数逼近器。与通过 Q 值表间接学习并遭受维数灾难问题的 DQN 相反,DDPG 通过政策梯度直接从观察中学习。建议确定性地将状态映射到动作,以更好地适应连续动作空间环境。

PPO

我们探索并使用多酚氧化酶作为系综方法的一个组成部分。引入 PPO 是为了控制策略梯度更新,并确保新策略不会与旧策略相差太大。PPO 试图通过在目标函数中引入限幅项来简化信任区域策略优化(TRPO) 的目标。

PPO 的目标函数取限幅和正常目标的最小值。PPO 不鼓励超出限定区间的大的政策变动。因此,PPO 通过在每个训练步骤限制策略更新来提高策略网络训练的稳定性。我们选择 PPO 进行股票交易,因为它稳定、快速,并且更容易实现和调整。

集成策略

我们的目的是创建一个高度稳健的交易策略。所以我们使用一种集合方法根据夏普比率PPOA2CDDPG 中自动选择表现最好的代理进行交易。总体过程描述如下:

步骤一。我们利用𝑛t32】个月的成长窗口来同时再培训我们的三名代理。在本文中,我们每隔三个月对我们的三名代理进行再培训。

步骤二。我们通过使用 3 个月验证滚动窗口来验证所有三个代理,然后进行培训以挑选具有最高夏普比率的最佳代理。在我们的验证阶段,我们还通过使用湍流指数来调整风险厌恶。

第三步。验证后,我们只使用夏普比率最高的最佳模型来预测和交易下一季度。

**from** stable_baselines **import** SAC
**from** stable_baselines **import** PPO2
**from** stable_baselines **import** A2C
**from** stable_baselines **import** DDPG
**from** stable_baselines **import** TD3
**from** stable_baselines.ddpg.policies **import** DDPGPolicy
**from** stable_baselines.common.policies **import** MlpPolicy
**from** stable_baselines.common.vec_env **import** DummyVecEnv**def** **train_A2C**(env_train, model_name, timesteps=10000):
 **“””A2C model”””** start = time.time()
 model = A2C(‘MlpPolicy’, env_train, verbose=0)
 model.learn(total_timesteps=timesteps)
 end = time.time() model.save(f”{config.TRAINED_MODEL_DIR}/{model_name}”)
 print(‘Training time (A2C): ‘, (end-start)/60,’ minutes’)
 return model**def** **train_DDPG**(env_train, model_name, timesteps=10000):
 **“””DDPG model”””** start = time.time()
 model = DDPG(‘MlpPolicy’, env_train)
 model.learn(total_timesteps=timesteps)
 end = time.time() model.save(f”{config.TRAINED_MODEL_DIR}/{model_name}”)
 print(‘Training time (DDPG): ‘, (end-start)/60,’ minutes’)
 return model**def** **train_PPO**(env_train, model_name, timesteps=50000):
 **“””PPO model”””** start = time.time()
 model = PPO2(‘MlpPolicy’, env_train)
 model.learn(total_timesteps=timesteps)
 end = time.time() model.save(f”{config.TRAINED_MODEL_DIR}/{model_name}”)
 print(‘Training time (PPO): ‘, (end-start)/60,’ minutes’)
 return model**def** **DRL_prediction**(model, test_data, test_env, test_obs):
 **“””make a prediction”””** start = time.time()
 **for** i in range(len(test_data.index.unique())):
   action, _states = model.predict(test_obs)
   test_obs, rewards, dones, info = test_env.step(action)
   # env_test.render()
 end = time.time()

3.7 绩效评估

我们使用 Quantopian 的投资组合进行回溯测试。这些图表看起来非常好,并且只需要一行代码就可以实现。你只需要把一切都转换成日收益。

**import** pyfolio**with** **pyfolio**.plotting.plotting_context(font_scale=1.1):
 pyfolio.create_full_tear_sheet(**returns** = **ensemble_strat**,
 **benchmark_rets**=**dow_strat**, set_context=False)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

AI4Finance-Foundation 版权所有

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

AI4Finance-Foundation 版权所有

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

AI4Finance-Foundation 版权所有

参考文献:

A2C :
沃洛季米尔·姆尼、adrià·巴迪亚、迈赫迪·米尔扎、亚历克斯·格雷夫斯、蒂莫西·利利克拉普、蒂姆·哈利、大卫·西尔弗和科雷·卡武克库奥卢。2016.深度强化学习的异步方法。第 33 届机器学习国际会议(02 2016)。https://arxiv.org/abs/1602.01783

DDPG :
蒂莫西·莉莉卡普、乔纳森·亨特、亚历山大·普里策尔、尼古拉斯·赫斯、汤姆·埃雷兹、尤瓦尔·塔萨、大卫·西尔弗和金奎大·威斯特拉。2015.深度强化学习的连续控制。2016 年国际学习代表大会(ICLR)(2015 年 9 月)。https://arxiv.org/abs/1509.02971

约翰·舒尔曼、谢尔盖·莱文、菲利普·莫里茨、迈克尔·乔丹和彼得·阿贝耳。2015.信任区域策略优化。在第 31 届机器学习国际会议上。https://arxiv.org/abs/1502.05477

约翰·舒尔曼、菲利普·沃尔斯基、普拉富拉·德里瓦尔、亚历克·拉德福德和奥列格·克里莫夫。2017.近似策略优化算法。arXiv:1707.06347(2017 年 07 月)。https://arxiv.org/abs/1707.06347

如何通过深度强化学习改善您的供应链

原文:https://towardsdatascience.com/deep-reinforcement-learning-for-supply-chain-optimization-3e4d99ad4b58?source=collection_archive---------13-----------------------

利用雷和 DFO 优化多级供应链

是什么让亚马逊在网上零售的竞争中脱颖而出?他们的供应链。事实上,这一直是他们的主要竞争对手之一沃尔玛的最大优势之一。

供应链是高度复杂的系统,由全球数百家甚至数千家制造商和物流承运商组成,他们整合资源,创造出我们每天使用和消费的产品。要跟踪所有的投入到一个单一的、简单的产品将是惊人的。然而,纵向一体化公司内部的供应链组织的任务是管理从原材料到制造、仓储和向客户分销的投入。在这方面做得最好的公司减少了过量储存造成的浪费,减少了不必要的运输成本,减少了将产品和材料运送到系统后期的时间。优化这些系统是像苹果和 Saudi Aramco 这样不同的企业的关键组成部分。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由 Shawn AngUnsplash 上拍摄

已经投入了大量的时间和精力来构建有效的供应链优化模型,但是由于它们的规模和复杂性,它们可能难以构建和管理。随着机器学习的进步,特别是强化学习,我们可以训练一个机器学习模型来为我们做出这些决定,在许多情况下,比传统方法做得更好!

TL;速度三角形定位法(dead reckoning)

我们使用射线[or-gym](https://arxiv.org/abs/2008.06319)来训练深度强化学习模型,以优化多级库存管理模型,并使用鲍威尔的方法根据无导数优化模型对其进行基准测试

多级供应链

在我们的例子中,我们将使用一个有提前期的多级供应链模型。这意味着我们需要为供应链的不同阶段做出决策,我们在不同层面做出的每个决策都会影响下游的决策。在我们的案例中,我们有从原材料生产商到客户的几个阶段。过程中的每个阶段都有不同的提前期,即一个阶段的输出到达并成为链中下一个阶段的输入所需的时间。这可能是 5 天,10 天,无论如何。这些交付周期变得越长,您就需要越早预测客户订单和需求,以确保您不会缺货或失去销售!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

多级供应链示意图(图片由作者提供,来自 Hubbs 等人

使用 OR-Gym 进行库存管理

OR-Gym 库有几个多级供应链模型可以用来模拟这种结构。为此,我们将使用InvManagement-v1环境,它具有如上所示的结构,但是如果您没有足够的库存来满足客户需求,就会导致销售损失。

如果您还没有,请继续安装该软件包:

pip install or-gym

安装完成后,我们可以使用以下工具设置我们的环境:

env = or_gym.make('InvManagement-v1')

默认情况下,这是一个四级供应链。这些行动决定了在每个时间步从上面的层级订购多少材料。订单数量受限于供应商的能力及其当前库存。因此,如果你从一个供应商那里订购 150 个部件,而该供应商的发货能力是 100 个部件,而你手头只有 90 个部件,那么你只能收到 90 个部件。

每个层级都有自己的成本结构、定价和交付周期。最后一个梯队(在这种情况下是阶段 3)提供原材料,我们在这个阶段没有任何库存限制,假设矿山、油井、森林或任何生产原材料的东西足够大,这不是我们需要关心的限制。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Invmanagement-v 1 环境的默认参数值。

与所有的or-gym环境一样,如果这些设置不适合您,只需将一个环境配置字典传递给make函数来相应地定制您的供应链(这里给出了一个例子)。

和雷一起训练

为了训练你的环境,我们将利用射线库来加速我们的训练,所以继续导入你的包。

import or_gym
from or_gym.utils import create_env
import ray
from ray.rllib import agents
from ray import tune

首先,我们需要一个简单的注册函数来确保 Ray 知道我们想要运行的环境。我们可以用下面显示的register_env函数注册它。

def register_env(env_name, env_config={}):
    env = create_env(env_name)
    tune.register_env(env_name, 
        lambda env_name: env(env_name,
            env_config=env_config))

从这里,我们可以设置我们的 RL 配置和训练模型所需的一切。

# Environment and RL Configuration Settings
env_name = 'InvManagement-v1'
env_config = {} # Change environment parameters here
rl_config = dict(
    env=env_name,
    num_workers=2,
    env_config=env_config,
    model=dict(
        vf_share_layers=False,
        fcnet_activation='elu',
        fcnet_hiddens=[256, 256]
    ),
    lr=1e-5
)# Register environment
register_env(env_name, env_config)

rl_config字典中,您可以设置所有相关的超参数,或者将您的系统设置为在 GPU 上运行。这里,我们将使用 2 个工作线程进行并行化,并训练一个具有 ELU 激活功能的双层网络。此外,如果您打算使用[tune](https://www.datahubbs.com/hyperparameter-tuning-with-tune/) 进行超参数调优,那么您可以使用tune.gridsearch()之类的工具来系统地更新学习率、改变网络或任何您喜欢的东西。

一旦你满意了,就去选择你的算法并开始训练吧!下面,我只使用 PPO 算法,因为我发现它在大多数环境下训练得很好。

# Initialize Ray and Build Agent
ray.init(ignore_reinit_error=True)
agent = agents.ppo.PPOTrainer(env=env_name,
    config=rl_config)results = []
for i in range(500):
    res = agent.train()
    results.append(res)
    if (i+1) % 5 == 0:
        print('\rIter: {}\tReward: {:.2f}'.format(
                i+1, res['episode_reward_mean']), end='')
ray.shutdown()

上面的代码将初始化ray,然后根据您之前指定的配置构建代理。如果你对此感到满意,那么让它运行一段时间,看看效果如何!

在这种环境下需要注意的一点是:如果学习率太高,政策函数将开始发散,损失将变得巨大。此时,您将得到一个错误,通常来自 Ray 的默认预处理器,状态显示奇怪的值,因为网络给出的动作都是nan。这很容易通过降低学习速度并再次尝试来解决。

让我们来看看表演。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec# Unpack values from each iteration
rewards = np.hstack([i['hist_stats']['episode_reward'] 
    for i in results])
pol_loss = [
    i['info']['learner']['default_policy']['policy_loss'] 
    for i in results]
vf_loss = [
    i['info']['learner']['default_policy']['vf_loss'] 
    for i in results]p = 100
mean_rewards = np.array([np.mean(rewards[i-p:i+1]) 
                if i >= p else np.mean(rewards[:i+1]) 
                for i, _ in enumerate(rewards)])
std_rewards = np.array([np.std(rewards[i-p:i+1])
               if i >= p else np.std(rewards[:i+1])
               for i, _ in enumerate(rewards)])fig = plt.figure(constrained_layout=True, figsize=(20, 10))
gs = fig.add_gridspec(2, 4)
ax0 = fig.add_subplot(gs[:, :-2])
ax0.fill_between(np.arange(len(mean_rewards)), 
                 mean_rewards - std_rewards, 
                 mean_rewards + std_rewards, 
                 label='Standard Deviation', alpha=0.3)
ax0.plot(mean_rewards, label='Mean Rewards')
ax0.set_ylabel('Rewards')
ax0.set_xlabel('Episode')
ax0.set_title('Training Rewards')
ax0.legend()ax1 = fig.add_subplot(gs[0, 2:])
ax1.plot(pol_loss)
ax1.set_ylabel('Loss')
ax1.set_xlabel('Iteration')
ax1.set_title('Policy Loss')ax2 = fig.add_subplot(gs[1, 2:])
ax2.plot(vf_loss)
ax2.set_ylabel('Loss')
ax2.set_xlabel('Iteration')
ax2.set_title('Value Function Loss')plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片作者。

看起来我们的代理学会了一个体面的政策!

这些经典运筹学问题的深度强化学习的困难之一是缺乏最优性保证。换句话说,我们可以看看上面的训练曲线,看到它正在学习越来越好的政策——它似乎正在向一个政策靠拢——但我们不知道这个政策有多好。我们能做得更好吗?我们应该在超参数调优上投入更多的时间(和金钱)吗?要回答这个问题,我们需要转向一些不同的方法,并开发一个基准。

无导数优化

测试 RL 模型的一个好方法是使用无导数优化 (DFO)。像 RL 一样,DFO 将系统视为一个黑盒模型,提供输入并获得一些反馈,作为回报,在寻求最优值时再次尝试。

与 RL 不同,DFO 没有国家的概念。这意味着我们将试图找到一个固定的再订购政策,使库存达到一定水平,以平衡持有成本和销售利润。例如,如果阶段 0 的策略是重新订购多达 10 个小部件,而目前我们有 4 个小部件,则策略声明我们将重新订购 6 个。在 RL 的情况下,它将考虑当前的管道和我们提供给状态的所有其他信息。因此 RL 更具适应性,并且应该优于直接的 DFO 实现。如果没有,那么我们知道我们需要重新开始。

虽然这听起来可能过于简单,但这种固定的再订购策略在工业应用中并不罕见,部分原因是真实的供应链由比我们在这里建模更多的变量和相关决策组成。因此,一个固定的政策是易处理的,供应链专业人员可以很容易地处理。

实施 DFO

对于 DFO,有很多不同的算法和求解器。出于我们的目的,我们将利用 Scipy 的optimize库来实现 Powell 的方法。我们不会在这里进入细节,但这是一种快速找到函数最小值的方法,并可用于离散优化-就像我们在这里。

from scipy.optimize import minimize

因为我们将使用固定的再订购策略,所以我们需要一个快速的功能来将库存水平转化为要评估的行动。

def base_stock_policy(policy, env):
    '''
    Implements a re-order up-to policy. This means that for
    each node in the network, if the inventory at that node 
    falls below the level denoted by the policy, we will 
    re-order inventory to bring it to the policy level.

    For example, policy at a node is 10, current inventory
    is 5: the action is to order 5 units.
    '''
    assert len(policy) == len(env.init_inv), (
        'Policy should match number of nodes in network' + 
        '({}, {}).'.format(
            len(policy), len(env.init_inv)))

    # Get echelon inventory levels
    if env.period == 0:
        inv_ech = np.cumsum(env.I[env.period] +
            env.T[env.period])
    else:
        inv_ech = np.cumsum(env.I[env.period] +
            env.T[env.period] - env.B[env.period-1, :-1])

    # Get unconstrained actions
    unc_actions = policy - inv_ech
    unc_actions = np.where(unc_actions>0, unc_actions, 0)

    # Ensure that actions can be fulfilled by checking 
    # constraints
    inv_const = np.hstack([env.I[env.period, 1:], np.Inf])
    actions = np.minimum(env.c,
                np.minimum(unc_actions, inv_const))
    return actions

base_stock_policy函数获取我们提供的策略级别,并如上所述计算该级别和库存之间的差异。需要注意的一点是,当我们计算库存水平时,我们也包括所有在运输途中的库存(在env.T中给出)。例如,如果阶段 0 的当前现有库存为 100,并且阶段 0 和阶段 1 之间有 5 天的提前期,那么我们也会考虑过去 5 天的所有订单。因此,如果阶段 0 每天订购 10 件,那么该层级的库存将为 150 件。这使得策略级别大于容量变得有意义,因为我们不仅要查看我们今天仓库中的库存,还要查看所有正在运输的物品。

我们的 DFO 方法需要进行函数评估调用,以查看所选变量的执行情况。在我们的例子中,我们有一个要评估的环境,所以我们需要一个函数来运行我们环境中的一集并返回适当的结果。

def dfo_func(policy, env, *args):
    '''
    Runs an episode based on current base-stock model 
    settings. This allows us to use our environment for the 
    DFO optimizer.
    '''
    env.reset() # Ensure env is fresh
    rewards = []
    done = False
    while not done:
        action = base_stock_policy(policy, env)
        state, reward, done, _ = env.step(action)
        rewards.append(reward)
        if done:
            break

    rewards = np.array(rewards)
    prob = env.demand_dist.pmf(env.D, **env.dist_param)

    # Return negative of expected profit
    return -1 / env.num_periods * np.sum(prob * rewards)

我们不是返回奖励的总和,而是返回对奖励的负面期望。消极的原因是我们使用的 Scipy 函数寻求最小化,而我们的环境是为了最大化回报而设计的,所以我们反转这个函数以确保一切都指向正确的方向。我们通过乘以基于分布的需求概率来计算预期回报。我们可以获取更多样本来估计分布,并以这种方式计算我们的期望(对于许多现实世界的应用来说,这是必需的),但在这里,我们可以访问真实的分布,因此我们可以使用它来减少我们的计算负担。

最后,我们准备优化。

下面这个函数会根据你的配置设置搭建一个环境,拿我们的dfo_func来评估,应用鲍威尔的方法来解决问题。它将返回我们的策略,并确保我们的答案只包含正整数(例如,我们不能订购半个部件或负数部件)。

def optimize_inventory_policy(env_name, fun,
    init_policy=None, env_config={}, method='Powell'):

    env = or_gym.make(env_name, env_config=env_config)

    if init_policy is None:
        init_policy = np.ones(env.num_stages-1)

    # Optimize policy
    out = minimize(fun=fun, x0=init_policy, args=env, 
        method=method)
    policy = out.x.copy()

    # Policy must be positive integer
    policy = np.round(np.maximum(policy, 0), 0).astype(int)

    return policy, out

现在是时候把它们放在一起了。

policy, out = optimize_inventory_policy('InvManagement-v1',
    dfo_func)
print("Re-order levels: {}".format(policy))
print("DFO Info:\n{}".format(out))Re-order levels: [540 216  81]
DFO Info:
   direc: array([[  0\.        ,   0\.        ,   1\.        ],
       [  0\.        ,   1\.        ,   0\.        ],
       [206.39353826,  81.74560612,  28.78995703]])
     fun: -0.9450780368543933
 message: 'Optimization terminated successfully.'
    nfev: 212
     nit: 5
  status: 0
 success: True
       x: array([539.7995151 , 216.38046861,  80.66902905])

我们的 DFO 模型发现了一个固定库存策略,第 0 阶段的再订购水平为 540,第 1 阶段为 216,第 2 阶段为 81。它只用了 212 次功能评估就做到了这一点,即它模拟了 212 集来寻找最佳值。

我们可以运行该策略,然后将其输入到我们的环境中,比如说 1000 次,以生成一些统计数据,并将其与我们的 RL 解决方案进行比较。

env = or_gym.make(env_name, env_config=env_config)
eps = 1000
rewards = []
for i in range(eps):
    env.reset()
    reward = 0
    while True:
        action = base_stock_policy(policy, eenv)
        s, r, done, _ = env.step(action)
        reward += r
        if done:
            rewards.append(reward)
            break

比较性能

在我们开始报酬比较之前,请注意这些并不是完美的,1:1 的比较。如前所述,DFO 为我们提供了一个固定的策略,而 RL 提供了一个更加灵活、动态的策略,可以根据状态信息进行更改。我们的 DFO 方法也提供了一些需求概率方面的信息来计算期望,RL 必须从额外的抽样中进行推断。因此,虽然 RL 从近 65k 的剧集中学习,而 DFO 只需要进行 212 次函数调用,但他们并不完全可比。考虑到枚举每一个有意义的固定政策一次将需要大约 2 亿集,那么 RL 看起来并不那么低效。

那么,这些是如何叠加的呢?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片作者。

从上面我们可以看到,RL 确实比我们的 DFO 策略平均高出 11%(460 比 414)。RL 模型在大约 15k 集后超过了 DFO 策略,并在此后稳步改进。然而,RL 政策有一些更高的差异,其中有一些可怕的插曲。总的来说,我们确实从 RL 方法中得到了更好的结果。

在这种情况下,这两种方法都很难实现,计算量也不大。我忘记将我的rl_config设置改为在我的 GPU 上运行,在我的笔记本电脑上训练仍然只需要大约 25 分钟,而 DFO 模型需要大约 2 秒钟。更复杂的模型可能在这两种情况下都不那么友好。

另一点需要注意的是,这两种方法对初始条件都非常敏感,而且都不能保证在每种情况下都能找到最优策略。如果你有一个想应用 RL 的问题,也许可以先使用一个简单的 DFO 解算器,尝试一些初始条件来了解问题,然后旋转完整的 RL 模型。你会发现 DFO 政策足以完成你的任务。

希望这很好地概述了如何使用这些方法和or-gym库。如果您有任何反馈或问题,请留下!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值