理解变压器,编程方式
来源:壁纸访问
因为你只能理解它,如果你能编程的话
如今,变形金刚已经成为 NLP 任务的事实标准。它们开始被用于自然语言处理,但现在被用于计算机视觉,有时也用于创作音乐。我相信你们都听说过 GPT3 变压器或其中的笑话。
但抛开一切不谈,他们还是一如既往地难以理解。在我的上一篇文章中,我非常详细地谈到了变形金刚以及它们是如何在基本层面上工作的。我浏览了编码器和解码器架构,以及神经网络不同部分的整个数据流。
但是正如我喜欢说的,在我们自己实施之前,我们并没有真正理解一些东西。所以在这篇文章中,我们将使用 Transformers 实现一个英语到德语的翻译器。
任务描述
我们想创建一个使用变压器将英语转换成德语的翻译器。因此,如果我们把它看作一个黑盒,我们的网络接受一个英语句子作为输入,并返回一个德语句子。
作者图片:用于翻译的转换器
数据预处理
为了训练我们的英德翻译模型,我们需要英语和德语之间的翻译句子对。
幸运的是,通过 IWSLT(国际口语翻译研讨会)数据集,我们有一个非常标准的方法来获取这些数据,我们可以使用torchtext.datasets
来访问这些数据。这个机器翻译数据集是一种用于翻译任务的事实标准,包含不同语言的 TED 和 TEDx 演讲的翻译。
此外,在我们真正进入整个编码部分之前,让我们了解在训练时我们需要什么作为模型的输入和输出。我们实际上需要两个矩阵输入到我们的网络中:
作者图像:输入到网络
- 源英文句子(Source): 一个形状的矩阵(批量大小 x 源句子长度)。该矩阵中的数字对应于基于我们还需要创建的英语词汇的单词。例如,英语词汇中的 234 可能对应于单词“the”。还有,你有没有注意到很多句子都以一个单词结尾,这个单词在词汇中的索引是 6?这是怎么回事?因为所有句子的长度都不一样,所以会用一个索引为 6 的单词填充。所以,6 指的是
<blank>
令牌。 - 移位的目标德语句子(Target): 一个形状的矩阵(批量 x 目标句子长度)。这里,这个矩阵中的数字也对应于我们还需要创建的基于德语词汇的单词。如果你注意到这个特殊的矩阵似乎有一个模式。所有的句子都以一个在德语词汇中索引为 2 的单词开头,并且总是以一种模式结尾[3 和 0 或更多的 1]。这是有意为之的,因为我们希望以某个开始标记开始目标句子(so 2 代表
<s>
标记),以某个结束标记(so 3 代表</s>
标记)和一串空白标记(so 1 代表<blank>
标记)结束目标句子。这一部分在我上一篇关于变形金刚的文章中有更详细的介绍,所以如果你对此感到困惑,我想请你看一看
现在我们知道了如何预处理数据,我们将进入预处理步骤的实际代码。
请注意,如果您也使用其他方法进行预处理,这真的无关紧要。最终重要的是,最终,您需要以一种转换器可以使用的方式将句子源和目标发送到您的模型。即,源句子应该用空白标记填充,目标句子需要有开始标记、结束标记和由空白标记填充的剩余部分。
我们首先加载 Spacy 模型,该模型提供了标记器来标记德语和英语文本。
# Load the Spacy Models
spacy_de = spacy.load('de')
spacy_en = spacy.load('en')def tokenize_de(text):
return [tok.text for tok in spacy_de.tokenizer(text)]def tokenize_en(text):
return [tok.text for tok in spacy_en.tokenizer(text)]
我们还定义了一些特殊的记号,我们将使用它们来指定空白/填充词,以及如上所述的句子的开头和结尾。
# Special Tokens
BOS_WORD = '<s>'
EOS_WORD = '</s>'
BLANK_WORD = "<blank>"
我们现在可以使用 torchtext 中的data.field
为源句子和目标句子定义一个预处理管道。您可以注意到,虽然我们只指定了源句子的pad_token
,但是我们提到了目标句子的pad_token
、init_token
和eos_token
。我们还定义了使用哪些记号赋予器。
SRC = data.Field(tokenize=tokenize_en, pad_token=BLANK_WORD)
TGT = data.Field(tokenize=tokenize_de, init_token = BOS_WORD,
eos_token = EOS_WORD, pad_token=BLANK_WORD)
如果你注意到现在我们还没有看到任何数据。我们现在使用来自torchtext.datasets
的 IWSLT 数据来创建一个训练、验证和测试数据集。我们还使用MAX_LEN
参数过滤我们的句子,这样我们的代码运行得更快。请注意,我们正在获取带有.en
和.de
扩展名的数据。我们使用fields
参数指定预处理步骤。
MAX_LEN = 20
train, val, test = datasets.IWSLT.splits(
exts=('.en', '.de'), fields=(SRC, TGT),
filter_pred=lambda x: len(vars(x)['src']) <= MAX_LEN
and len(vars(x)['trg']) <= MAX_LEN)
现在,我们已经获得了训练数据,让我们看看它是什么样子的:
for i, example in enumerate([(x.src,x.trg) for x in train[0:5]]):
print(f"Example_{i}:{example}")---------------------------------------------------------------Example_0:(['David', 'Gallo', ':', 'This', 'is', 'Bill', 'Lange', '.', 'I', "'m", 'Dave', 'Gallo', '.'], ['David', 'Gallo', ':', 'Das', 'ist', 'Bill', 'Lange', '.', 'Ich', 'bin', 'Dave', 'Gallo', '.'])Example_1:(['And', 'we', "'re", 'going', 'to', 'tell', 'you', 'some', 'stories', 'from', 'the', 'sea', 'here', 'in', 'video', '.'], ['Wir', 'werden', 'Ihnen', 'einige', 'Geschichten', 'über', 'das', 'Meer', 'in', 'Videoform', 'erzählen', '.'])Example_2:(['And', 'the', 'problem', ',', 'I', 'think', ',', 'is', 'that', 'we', 'take', 'the', 'ocean', 'for', 'granted', '.'], ['Ich', 'denke', ',', 'das', 'Problem', 'ist', ',', 'dass', 'wir', 'das', 'Meer', 'für', 'zu', 'selbstverständlich', 'halten', '.'])Example_3:(['When', 'you', 'think', 'about', 'it', ',', 'the', 'oceans', 'are', '75', 'percent', 'of', 'the', 'planet', '.'], ['Wenn', 'man', 'darüber', 'nachdenkt', ',', 'machen', 'die', 'Ozeane', '75', '%', 'des', 'Planeten', 'aus', '.'])Example_4:(['Most', 'of', 'the', 'planet', 'is', 'ocean', 'water', '.'], ['Der', 'Großteil', 'der', 'Erde', 'ist', 'Meerwasser', '.'])
您可能会注意到,虽然data.field
对象已经完成了标记化,但它还没有应用开始、结束和填充标记,这是有意的。这是因为我们还没有批处理,填充标记的数量本质上取决于特定批处理中句子的最大长度。
正如开始提到的,我们还通过使用data.field
对象中的内置函数来创建源语言和目标语言词汇表。我们指定 MIN_FREQ 为 2,这样任何至少不出现两次的单词都不会成为我们词汇表的一部分。
MIN_FREQ = 2
SRC.build_vocab(train.src, min_freq=MIN_FREQ)
TGT.build_vocab(train.trg, min_freq=MIN_FREQ)
一旦我们完成了这些,我们就可以简单地使用data.Bucketiterator
,它用于给出相似长度的批处理来得到我们的训练迭代器和验证迭代器。注意,我们使用 1 的batch_size
作为验证数据。这样做是可选的,但这样做是为了在检查验证数据性能时不进行填充或进行最小填充。
BATCH_SIZE = 350# Create iterators to process text in batches of approx. the same length by sorting on sentence lengthstrain_iter = data.BucketIterator(train, batch_size=BATCH_SIZE, repeat=False, sort_key=lambda x: len(x.src))val_iter = data.BucketIterator(val, batch_size=1, repeat=False, sort_key=lambda x: len(x.src))
在我们继续之前,最好先看看我们的批处理是什么样子,以及我们在训练时作为输入发送给模型的是什么。
batch = next(iter(train_iter))
src_matrix = batch.src.T
print(src_matrix, src_matrix.size())
这是我们的源矩阵:
trg_matrix = batch.trg.T
print(trg_matrix, trg_matrix.size())
这是我们的目标矩阵:
所以在第一批中,src_matrix
包含 350 个长度为 20 的句子,而trg_matrix
是 350 个长度为 22 的句子。为了确保我们的预处理,让我们看看这些数字在src_matrix
和trg_matrix.
中代表什么
print(SRC.vocab.itos[1])
print(TGT.vocab.itos[2])
print(TGT.vocab.itos[1])
--------------------------------------------------------------------
<blank>
<s>
<blank>
果然不出所料。相反的方法,即字符串到索引也工作得很好。
print(TGT.vocab.stoi['</s>'])
--------------------------------------------------------------------
3
变形金刚
作者图片:应用程序架构
因此,现在我们有了一种将源句子和转换后的目标发送到转换器的方法,我们可以开始创建转换器了。
这里的很多积木都取自 Pytorch nn
模块。事实上,Pytorch 也有一个 Transformer 模块,但它不包括论文中提到的许多功能,如嵌入层和位置编码层。所以这是一种更完整的实现,也从 pytorch 实现中吸取了很多东西。
我们特别使用 Pytorch nn 模块中的各种模块来创建我们的变压器:
- TransformerEncoderLayer:单个编码器层
- TransformerEncoder :一堆
num_encoder_layers
层。在本文中,默认情况下保持为 6。 - TransformerDecoderLayer :单个解码器层
- TransformerDecoder :一堆
num_decoder_layers
层。在本文中,默认情况下保持为 6。
此外,请注意,无论层中发生什么,实际上只是矩阵函数,正如我在变压器的解释帖子中提到的那样。请特别注意解码器堆栈如何从编码器获取内存作为输入。我们还创建了一个位置编码层,让我们将位置嵌入添加到单词嵌入中。
如果你愿意,你可以看看我已经链接的所有这些模块的源代码。我不得不多次亲自查看源代码,以确保我为这些层提供了正确的输入。
定义优化器和模型
现在,我们可以使用以下代码初始化转换器和优化器:
source_vocab_length = len(SRC.vocab)
target_vocab_length = len(TGT.vocab)model = MyTransformer(source_vocab_length=source_vocab_length,target_vocab_length=target_vocab_length)optim = torch.optim.Adam(model.parameters(), lr=0.0001, betas=(0.9, 0.98), eps=1e-9)model = model.cuda()
在论文中,作者使用了一个具有预定学习率的 Adam 优化器,但是这里我只是使用一个普通的 Adam 优化器来简化事情。
培训我们的翻译
现在,我们可以使用下面的训练函数来训练我们的变压器。我们在培训循环中必须做的是:
- 从批处理中获取 src_matrix 和 trg_matrix。
- 创建一个
src_mask
—这是告诉模型关于 src_matrix 数据中填充单词的掩码。 - 创建一个
trg_mask
——这样我们的模型就不能在任何时间点查看未来的后续目标单词。 - 从模型中获得预测。
- 使用交叉熵计算损失。(在论文中,他们使用了 KL 散度,但这也有助于理解)
- 反向投影。
- 我们保存基于验证损失的最佳模型。
- 我们还使用函数
greedy_decode_sentence
预测我们选择的一些句子在每个时期的模型输出,作为调试步骤。我们将在结果部分讨论这个函数。
我们现在可以使用以下工具进行培训:
train_losses,valid_losses = train(train_iter, val_iter, model, optim, 35)
以下是训练循环的输出(仅针对某些时期显示):
**Epoch [1/35] complete.** Train Loss: 86.092\. Val Loss: 64.514
Original Sentence: This is an example to check how our model is performing.
Translated Sentence: Und die der der der der der der der der der der der der der der der der der der der der der der der**Epoch [2/35] complete.** Train Loss: 59.769\. Val Loss: 55.631
Original Sentence: This is an example to check how our model is performing.
Translated Sentence: Das ist ein paar paar paar sehr , die das ist ein paar sehr Jahre . </s>.
.
.
.**Epoch [16/35] complete.** Train Loss: 21.791\. Val Loss: 28.000
Original Sentence: This is an example to check how our model is performing.
Translated Sentence: Hier ist ein Beispiel , um zu prüfen , wie unser Modell aussieht . Das ist ein Modell . </s>.
.
.
.**Epoch [34/35] complete.** Train Loss: 9.492\. Val Loss: 31.005
Original Sentence: This is an example to check how our model is performing.
Translated Sentence: Hier ist ein Beispiel , um prüfen zu überprüfen , wie unser Modell ist . Wir spielen . </s>**Epoch [35/35] complete.** Train Loss: 9.014\. Val Loss: 32.097
Original Sentence: This is an example to check how our model is performing.
Translated Sentence: Hier ist ein Beispiel , um prüfen wie unser Modell ist . Wir spielen . </s>
我们可以看到我们的模型是如何从一个莫名其妙的翻译开始的——“然后在几次迭代结束时开始给我们一些东西。
结果
我们可以使用 Plotly express 绘制训练和验证损失。
import pandas as pd
import plotly.express as pxlosses = pd.DataFrame({'train_loss':train_losses,'val_loss':valid_losses})px.line(losses,y = ['train_loss','val_loss'])
作者图片:培训和验证损失
如果我们想要部署这个模型,我们可以简单地使用:
model.load_state_dict(torch.load(f”checkpoint_best_epoch.pt”))
并使用greeedy_decode_sentence
函数对任何源句子进行预测,该函数为:
作者图片:使用变形金刚进行贪婪搜索预测
这个函数做分段预测。贪婪搜索将从以下内容开始:
- 将整个英语句子作为编码器输入,仅将开始标记
<s>
作为移位输出(解码器的输入)传递给模型,并进行正向传递。 - 该模型将预测下一个单词——
der
- 然后,我们将整个英语句子作为编码器输入,并将最后预测的单词添加到移位的输出(解码器的输入=
<s> der
)中,并进行正向传递。 - 该模型将预测下一个单词——
schnelle
- 将整个英语句子作为编码器输入,将
<s> der schnelle
作为移位输出(解码器的输入)传递给模型,并进行正向传递。 - 依此类推,直到模型预测到结束标记
</s>
或者我们生成一些最大数量的标记(我们可以定义),这样翻译就不会在任何中断的情况下无限期运行。
现在我们可以用这个来翻译任何句子:
sentence = "Isn't Natural language processing just awesome? Please do let me know in the comments."print(greeedy_decode_sentence(model,sentence))------------------------------------------------------------------Ist es nicht einfach toll ? Bitte lassen Sie mich gerne in den Kommentare kennen . </s>
由于我手头没有德语翻译,所以我将使用退而求其次的方法来查看我们的模型表现如何。让我们借助谷歌翻译服务来理解这个德语句子的意思。
作者图片:谷歌翻译结果
翻译中似乎有一些错误作为“自然语言处理”是没有的(讽刺?)但对我来说,这似乎是一个足够好的翻译,因为神经网络只需一个小时的训练就能理解这两种语言的结构。
vorbehalte/verbes rungen(警告/改进)
如果我们按照报纸上的方法做每件事,我们可能会取得更好的结果:
- 全数据训练
- 字节对编码
- 学习率调度
- KL 发散损失
- 光束搜索,和
- 检查点集合
我在我的上一篇文章中讨论了所有这些,所有这些都很容易实现。但是这个简单的实现是为了理解转换器是如何工作的,所以我没有包括所有这些内容,以免混淆读者。事实上,在变形金刚的基础上已经有了相当多的进步,让我们有了更好的翻译模型。我们将在接下来的文章中讨论这些进步以及它们是如何实现的,在这篇文章中,我将谈论 BERT,这是最受欢迎的 NLP 模型之一,其核心使用了转换器。
参考
在这篇文章中,我们使用 transformer 架构几乎从头开始创建了一个英语到德语的翻译网络。
为了更仔细地查看这篇文章的代码,请访问我的 GitHub 库,在那里你可以找到这篇文章以及我所有文章的代码。
如果你想了解更多关于 NLP 的知识,我想从高级机器学习专业化中调出一门关于 自然语言处理 的精品课程。一定要去看看。
我以后也会写更多这样的帖子。让我知道你对他们的看法。我应该写技术性很强的主题还是更初级的文章?评论区是你的朋友。使用它。还有,在 中 关注我或者订阅我的 博客 。
最后,一个小小的免责声明——这篇文章中有一些相关资源的附属链接,因为分享知识从来都不是一个坏主意。
这个故事最初发表于这里。
通过数据科学了解投票结果
在 2016 年总统选举的惊人结果之后,我想更好地理解在投票行为中起作用的社会经济和文化因素。随着选举结果的公布,我认为根据一些广泛可用的县级数据集反向工程一个投票行为的预测模型会很有趣。
最终的结果是一个 web 应用程序,让你上下拨动 10 个最有影响力的功能,并可视化你假设场景的县级投票结果:2016 年总统选举的随机森林模型:
[## 2016 年总统选举的随机森林模型
请等到模型完成加载后再提交调整。上面的地图允许您构建…
kkehoe1985.github.io](https://kkehoe1985.github.io/2016_election_data_viz/)
例如,如果你想回答问题*“如果全国范围内至少拥有学士学位的人的比例高出 2%,选举会有什么不同?”*您只需将该参数设置为 1.02,然后点击“提交”即可找到答案。
预测是由随机森林分类模型驱动的,该模型已针对 71 个不同的县级属性进行了调整和训练。使用真实数据,该模型的预测准确率为 94.6%,ROC AUC 得分为 96%。当您调整可调参数并点击“提交”时,模型会接收新数据并更新预测结果。
需要指出的是,上面的交互式可视化被描述为“假设的”比“预测的”更好。投票给民主党或共和党不是一个静态的分类;随着时间的推移,政党领导人和政纲会发生重大变化,因此同一模型不能用于预测多次选举过程中的投票结果。然而,在适当的背景下,该模型可以产生有价值的见解。通过理解选民在不同情况下会如何对民主党和共和党 2016 年的政纲做出反应,我们可以更好地理解如何调整政纲以考虑未来 4 年的预计社会经济趋势。该工具旨在用于探索性数据分析,对其输出的解释更多的是一门艺术而不是科学。
那么它是如何工作的呢?
我把它作为一个二元分类问题来处理;该模型需要预测每个县将投票给共和党还是民主党。为了创建一个预测模型,我需要一个强大的“解释性”数据集(社会经济和文化数据)和一个“响应”数据集(投票结果)。
这一过程的第一步是搜集大量关于教育、人口密度、收入、种族、性别、年龄和宗教的县级数据来源(参见获取和清理美国县级数据源)。这需要在 Python 中进行数据清理和格式化,以及用加权平均值输入缺失值。
一旦数据被组合成一个单一的、干净的数据集,我就测试了许多监督学习技术,包括 k-最近邻、逻辑回归和随机森林分类。对于每一款车型,我都结合使用了网格搜索和交叉验证技术,调整了必要的超级参数:
优化超参数后,我根据其预测准确性和 ROC AUC 评分评估了这些技术:
随机森林分类
- ROC AUC 得分:96.1%
- 准确率:93.6%
逻辑回归
- ROC AUC 得分:91.7%
- 准确率:89.4%
K-最近邻
- ROC 曲线下面积分数:80.64%
- 准确率:87.4%
随机森林分类器对于这种分析是最准确的,因此是数据可视化中使用的模型。
决策树和随机森林
为了理解随机森林模型,我们首先需要理解决策树是如何工作的。决策树是由节点和分支组成的树状结构。每个节点可以被认为是对预测特征的测试。如果特征是定量的(在本练习中我们所有人都是如此),那么将一个值设置为特征的分割点,高于该值的样本遵循一个分支,而低于该值的样本遵循另一个分支。在决策树分类器中,终端节点将导致对类别的预测。
在我们的例子中,特征和它们的分裂点是由它们的“基尼系数”值决定的。有关基尼系数的完整解释,请参见决策树和基尼系数,但简而言之,基尼系数是随机选择的子集成员被错误分类的概率(这可以被认为是“预期错误率”)。如果该值为 0,则表示该子集已被完全纯化;只有一个阶层的成员留下。在每一步,该树优先考虑其产生的子集产生基尼系数杂质最大减少的特征,称为“基尼系数”
单个决策树具有可解释的优点,但是为了净化样本数据,它通常会变得很大。这可能会产生一个与样本数据完美拟合的模型,但不能很好地推广到样本外数据(具有低偏差/高方差的“过拟合”)。决策树也是“贪婪”的,因为它们在每个节点寻找局部最优决策(基于基尼系数),但不考虑可能提供最有效路径的特征组合。
为了避开这些限制,我们可以使用一种叫做随机森林的集成方法,这种方法可以生成多个决策树。每棵树使用 bootsrap 聚合(“bagging”)来随机选择一个特性子集。允许树完全生长,无需修剪,并且仅基于为其选择的随机特征子集来评估树中的每个节点。重复该过程,直到树的森林已经生长,并且对于测试集中的每个数据点,随机森林模型将从所有决策树中取得多数投票。
我们学到了什么?
种族、教育、宗教和人口密度是预测投票结果的最重要的信号
为了对数据有所了解,一个很好的起点是构建一个随机森林,并可视化预测因子的特征重要性,这由特征的基尼系数决定。在这里,我们可以看到预测 2016 年总统选举政党赢家的十大特征:
由于这些是定量变量,每个特征都有一个分裂点,在这个分裂点上投票行为的差异是显著的。当我们检查每个特征的分割点时,我们看到以下内容:
- 2011 年至 2015 年拥有学士学位或更高学位的成人比例:29.95%
- 2011 年至 2015 年仅有高中文凭的成人比例:26.55%
- 每平方英里土地面积的住房密度:201.9
- 每平方英里土地面积的人口密度:483.7
- 人口:4 223 人
为了更好地可视化这些分割点的影响,检查分割点上方和下方的结果变量的分布会有所帮助。这里有一个使用人口密度的例子,你可以看到投票趋势在每平方英里 483 人以上和以下是相反的:
要检查其余功能,请参见下面的 Tableau 工作簿: 2016 选举分裂点仪表板。
美国南部投票结果的关键变量是人口的种族构成
如果美国白人的比例减少 25%,这个模型预测的美国南部可能是这样的:
实际
假设的
为了探索更多假设的场景,玩下面的网络应用程序,看看随机森林预测会发生什么:2016 年美国总统选举的假设模型
描述性统计
即使不从我们的数据集创建模型,也可以从标准的探索性分析中获得一些见解:
在人口密度高的县,教育是投票结果的关键因素
人口密度在每平方英里 200 人以上的县倾向于投民主党的票。在那些投票给共和党的人中,拥有学士学位或更高学位的人口比例比民主党县低 12.5%(见热图的分裂点):
人口老龄化的县投票率更高
共和党和民主党的平均年龄和投票率都呈正相关。然而,平均年龄是一个棘手的变量,因为它不排除低于投票年龄的人,这意味着年轻家庭数量多的县可能有更高的平均年龄选民,但整体平均年龄较低:
参见下面的 Tableau 仪表盘,探索解释变量之间的更多关系: 2016 年总统选举—数据探索
投票率较高的县倾向于投共和党的票
关键的战场州,包括密歇根州、俄亥俄州和威斯康星州,整体选民投票率很高,但特别是来自共和党的选民:
投票率低的县投票给民主党
民主党倾向于在投票率最低的县赢得普选。在西南部人口稠密的县尤其如此。这更有可能发生在非战场县,因为选民参与度较低,可能认为结果是不可避免的:
自己探索
使用 D3 和烧瓶构建的交互式可视化
用于探索性分析的 Tableau 工作簿
- 10 大特性的分割点:在本工作簿中,您可以查看几个重要特性的分割点,并检查分割点上下的投票趋势
- 用热点图分割点:同上,但用美国的热点图代替直方图。可以通过单击饼图来过滤热图。
- 按投票率统计的人口分布:以柱状图显示按投票率统计的人口分布。直方图充当条形图的过滤器。
- 解释变量之间的相互关系:该工作簿允许您选择 x 轴和 y 轴的变量,并基于前一工作簿中的分割点应用过滤器。这使得我们可以一次检查几个特征的相互作用。
Jupyter 笔记本
理解 Spotify 歌曲的词向量—第 1 部分
照片由 Natalie Cardona 在 Unsplash 上拍摄
开始用 Spotify 构建单词向量和一些有趣的数学东西。
假设你遇到两个向量,维数 A= {2,4}和 B={2,3},你会怎么做?你可能会想象一个 2D 坐标空间,然后把它们画出来。
根据你对向量的了解,你可以得出这样的结论:这两个向量的大小和方向非常相似,它们之间的距离非常小。现在,假设你遇到两个词——“橘子”和“橙子”。你的大脑知道这两个词是水果,而且它们很相似(怎么相似?它们都有柑橘的味道和气味,它们都长在树上,它们看起来都是橙色的,等等)。
但我们谈论的是人脑。它理解语言,可以想象一个单词及其相关特征。因此,它可以得出逻辑结论,如“橙子和橘子有些相似”。现在,你如何训练一个算法来得出类似的结论?
单词矢量救援!单词向量是向量空间中表示该单词含义的一组数字。以水果为例,考虑一个简单的三维向量空间——宽度、高度和酸味。我们可以用这三个维度来表示任何水果的“意义”
你现在可以看到单词“tangerine”和“orange”在宽度、高度和辛辣程度上非常相似。将单词表示为一行数字或向量(对应于向量空间中的特定维度)使我们能够对它们进行各种有趣的计算,例如根据大小,哪种水果类似于樱桃,或者哪种水果介于橙子和芒果之间。这些向量和它们的维度捕捉了任何单词的“含义”,就像上面的例子一样。
如前所述,水果示例给出的单词向量和维度是标准单词向量的缩小/简化版本,例如 GloVe 或 Word2vec 。如何训练这些单词向量是我希望在本系列的第 2 部分讨论的内容。
关于水果已经说得够多了。让我们继续有趣的事情。给定一个以数字表示的歌曲及其属性(如能量、响度、可跳舞性)的数据集,让我们应用简单向量数学的概念来获得一些很酷的见解,并希望在此过程中发现一些新的音乐。
我从 TidyTuesday 的 github 回购中提取了 Spotify 歌曲列表数据集(请查看他们的回购,获得一些非常非常酷的数据集)。
这个链接拥有数据集的数据字典。这篇文章使用的代码可以在这里找到
让我们看看数字列之间的相关性。我对与“可舞性”正相关的专栏特别感兴趣,因为谁不喜欢好的舞曲呢:)
在所有列中,“化合价”列的相关性最高。让我们用 playlist_genre="pop “和列” danceability “和” valence "来划分歌曲的数据帧。这个数据帧的一个例子看起来像这样
这个数据帧类似于我们之前构建的水果数据帧。这里,可跳性和价是用来表示特定歌曲的两个维度(目前)。
绘制 10 首舞蹈性得分最高的歌曲给了我们
我们看到大多数曲目的可跳性值非常相似(范围从 0.95 到 0.98),但我们看到沿着价维度,有相当多的差异。如果我们想根据歌曲的可跳性和价值找到与某首歌曲相似的歌曲,该怎么办?让我们看看能否在 Spotify 数据框架中找到与蠢朋克的《环游世界》最接近的歌曲。
#df - dataframe with track_name, track_artist and track_properties
#closest - character flag that determines whether to return songs
#that match most/least
#song - string represeting the song being matched
#n - number of songs to return. Default value is 10
findSongs <- function(df,artist,closest,song,num=10){
song.properties <- df[which(df$track_name==song & df$track_artist==artist),]$properties[[1]]
if(closest=="N"){
num = num
}else{
num= -num
}
dist.df <- df %>% mutate(distance = mapply(function(x, y){
dist(rbind(x,y),method = "euclidean")
}, properties, song.properties)) %>%
top_n(num) %>%
select(track_name,track_artist, distance) %>%
arrange(distance)
dist.df
}closestMatches <- findSongs(popSongs_properties,artist="Daft Punk",closest="Y",song="Around the World")
closestMatches <- as.data.frame(closestMatches)
findSongs 函数获取歌曲的坐标,搜索数据帧,并根据欧几里德距离度量找到最接近的歌曲。对于“环游世界”,该函数返回
沿着可跳舞性和化合价维度绘制该图给出
好东西!!拉尔夫给我的似乎是最近的。我们可以看到,与之前的图相比,这些点是如何更紧密地聚集在一起的,以及化合价的变化减少了多少。
但是,请记住,这种相似性仅基于 2 个衡量标准/维度。您可以修改代码,更改尺寸以满足您的需要。想找高节奏的摇滚乐吗?子集输出 playlist_genre ="rock “以及列” instrumentalness “和” tempo "。选择是无穷无尽的,真的。说到无穷无尽,同一个 findSongs 函数可以用来匹配给定歌曲的所有数字属性(而不仅仅是可跳舞性和效价),这就是单词 vector 最终要做的。
步骤是相同的——我们创建一个数据帧,该数据帧具有 track_name、track_artist 和一个“properties”列,该列表示一首歌曲的所有数字属性/特性组合成一个向量。让我们找出与西蒙和加芬克尔的《拳击手》相似的歌曲
closestMatchesAll <- findSongs(spotify_songs_properties,artist="Simon & Garfunkel",closest="Y",song="The Boxer")
closestMatchesAll <- as.data.frame(closestMatchesAll)
这给了
弹出的第一件事是距离列的比例与我们之前的距离度量相比大了多少。这是有意义的,因为列“速度”和“调”的比例比其他值大。
用数字表示单词的另一个很酷的功能是,你可以根据两个不同单词的欧几里得距离来计算它们之间的距离。在我们的 Spotify 世界里,
distance1 <- distanceBetween("Ironic - 2015 Remaster","Alanis Morissette","Hollaback Girl","Gwen Stefani",spotify_songs_properties)distance1
8.072921distance2 <- distanceBetween("Ironic - 2015 Remaster","Alanis Morissette","Ping Pong","Armin van Buuren",spotify_songs_properties)distance2
33.25089if(distance1 < distance2){
print("Ironic by Alanis Morissette is more similar to Hollaback Girl by Gwen Stefani than it is to Ping Pong by Armin van Buuren")
}
在这里添加歌曲来获得新歌可能没有太大意义,但是像 GloVe 或 Word2vec 这样的词向量可以帮助你做到这一点。增加单词或减少单词可以给你一个新的向量,它非常接近向量空间中现有的单词。
这篇文章的灵感来自艾莉森·帕里什关于单词向量的文章。
用于计算机视觉的未开发的张量流库
探索工具包和扩展,如 TensorFlow 模型优化器、图形、联合学习、隐私等,以提升您的计算机视觉工作流程。
Emil Widlund 在 Unsplash 上的照片
TensorFlow 是一个端到端的开源机器学习平台,能够执行一系列任务。它为初学者和研究人员提供了易用性,并可用于不同的应用,例如但不限于计算机视觉、自然语言处理和强化学习。
在计算机视觉领域,我们大多数人都熟悉核心 TensorFlow 以及 TensorFlow Lite 和 JS。前者用于在移动和边缘设备上运行模型,后者用于在网络上运行模型。然而,TensorFlow 还提供了许多神秘的库,我们将在本文中一一介绍。
目录
- 张量流模型优化工具包
- 张量流图形
- 张量流联邦
- TensorFlow 隐私
- 张量流集线器
张量流模型优化工具包
实时模型对于许多商业运作来说是必不可少的。MobileNet 的推理速度让它成为众人瞩目的焦点,即使这意味着牺牲一点准确性。优化 TensorFlow 模型首先想到的是将其转换为 TensorFlow lite 服务。然而,这在台式机上工作得不是很好,因为它针对 ARM neon 进行了优化,这在本期中有所解释,或者我们需要进一步优化该模型。模型优化工具包可以帮助我们完成这些任务。根据其主页,它可用于:
降低云和边缘设备(如移动设备、物联网)的延迟和推理成本。
将模型部署到对处理、内存、功耗、网络使用和模型存储空间有限制的边缘设备。
支持在现有硬件或新的专用加速器上执行和优化。
它可以应用于已经训练好的模型以及训练期间,以进一步优化解决方案。在撰写本文时,它提供了三种技术,同时还提供了其他几种技术来完善模型。
修剪
第一种方法是权重剪枝。它通过去除层之间的一些连接来工作,因此减少了所涉及的参数和操作的数量,从而优化了模型。它消除了权重张量中不必要的值,并在训练过程中执行。这有助于减小模型的大小,通过训练后量化可以进一步减小模型的大小。
我不会深入每个函数的细节和代码,因为那会使文章太长。你可以参考此处进一步了解,参考此处了解其代码。
量化
与仅在训练期间进行的修剪不同,量化可以在训练和测试时进行。Tensorflow Lite 模型也被量化为使用 8 位整数,而不是通常使用的 32 位浮点。这提高了性能和效率,因为整数运算比浮点运算快得多。
然而,这是有代价的。量化是一种有损技术。这意味着先前从-3e38 到 3e38 表示的信息必须从-127 到 127 表示。在加法和乘法运算期间,8 位整数放大到 32 位整数,这需要再次缩小,从而引入更多误差。为了解决这个问题,可以在训练期间应用量化。
量化感知训练
通过在训练期间应用量化,我们迫使模型学习它将导致的差异,并相应地采取行动。量化误差作为噪声引入,优化器试图将其最小化。以这种方式训练的模型具有与浮点模型相当的准确性。看到以这种方式创建的 Tensorflow Lite 模型与普通模型的比较将会很有趣。
要阅读更多关于它的内容,请参考这里的,关于它的代码,你可以看看这里的。
岗位培训量化
尽管在训练期间应用量化是优选的,但是有时这样做是不可行的,并且我们可能已经准备好使用预训练的权重。此外,它更容易实现。
权重聚类
它将相似的权重组合在一起,并用单个值替换它们。可以想象成 JPEG 压缩。此外,由于相似的权重被插值到相同的数字,所以它也是有损耗的。权重矩阵存储浮点值。这些值被转换成整数,并存储一个包含聚类数的查找表。这减少了所需的空间,因为整数需要更少的空间来存储,并且留下了有限数量的浮点数。
如下例所示,十六个 float-32 值被分配给四个 float-32 质心,图层权重被转换为整数值。权重矩阵越大,节省越多。
权重聚类
聚类被应用于完全训练的模型以找到质心。那么任何压缩工具都可以用来减小模型的大小。要了解它的细节,请参考这里的,以及它的实现。
不同的技术可以结合起来进一步减少延迟,更多的方法也在他们的路线图中讨论。
张量流图形
TensorFlow graphics 旨在结合计算机视觉和计算机图形学来解决复杂的 3D 任务。计算机图形工作流程需要 3D 对象及其在场景中的绝对位置、对它们由灯光组成的材质的描述,以及生成合成渲染的相机。另一方面,计算机视觉工作流程将从图像开始,并尝试推断其参数。
这可以被认为是一个自动编码器,其中视觉系统(编码器)将试图找到参数,而图形系统(解码器)将基于这些参数生成图像,并可以与原始图像进行比较。此外,该系统不需要标记数据,并以自我监督的方式进行训练。张量流的一些用途是:
- 变换 —可以对对象进行旋转、平移等对象变换。这可以被神经网络学习以精确地找到物体的位置。这对于需要精确估计这些物体的位置的机械臂是有用的。
- 建模摄像机 —可以为摄像机设置不同的内在参数,从而改变图像的感知方式。例如,改变相机的焦距会改变物体的大小。
- 材料 —可以使用不同类型的材料,这些材料具有不同类型的光反射能力。因此,创建的场景可以准确地模拟对象在真实世界中的行为。
- 3D 卷积和池化(点云&网格) —它具有 3D 卷积和池化层,允许我们对 3D 数据执行语义分类和分割。
- TensorBoard 3D — 3D 数据变得越来越普遍,可用于解决 2D 数据的 3D 重建、点云分割、3D 对象变形等问题。通过 TensorBoard 3D,可以可视化这些结果,从而更好地了解模型。
延伸阅读— 此处(其中还包含 Colab 笔记本的链接)
张量流联邦
照片由 Ricardo Arce 在 Unsplash 上拍摄
这个库也可以用于计算机视觉以外的其他领域。随着移动设备和边缘设备的增多,产生了大量的数据。联合学习旨在对分散的数据执行机器学习,即在设备本身上!这意味着没有必要将大量(敏感)数据上传到服务器。它已经被用于谷歌键盘。
下面附上的视频解释了关于联合学习的一切,从分散数据到使用 TensorFlow Federated。
有关使用 TensorFlow Federated 进行影像分类的指南,请参考下面链接的文章。
这个 Colab 已经被验证可以和 Note 一起工作:tensorflow_federated pip 包的最新发布版本…
www.tensorflow.org](https://www.tensorflow.org/federated/tutorials/federated_learning_for_image_classification)
TensorFlow 隐私
杰森·登特在 Unsplash 上拍摄的照片
通过隐私攻击可以从训练好的 ML 模型中提取敏感信息。Truex 等人提交了一份关于驱动因素的论文。这些模型甚至可以重建它被训练的信息,如本文所示。
左侧是仅使用人名和模型重建的图像。右边的图像是原始图像。摘自论文本身。
同样,像 TensorFlow Federated 一样,这不是计算机视觉独有的。最常用的技术是差分隐私。来自维基百科:
差异隐私是一种公开共享数据集信息的系统,通过描述数据集中的群体模式,同时保留数据集中的个人信息。
假设敏感信息不会在整个数据集中完全重复,并且通过使用差分隐私模型,可以确保该模型不会获知此类信息。例如,假设有一个人与人之间聊天的数据集。现在,聊天上传递的敏感信息可以是密码、银行账户详情等。因此,如果在此数据集上创建模型,差分隐私将确保模型无法了解这些细节,因为它们的数量很少。阅读这篇关于差分隐私的优秀文章,它也包含了执行它的代码。
张量流集线器
丹尼尔·萨尔西乌斯在 Unsplash 上拍摄的照片
你们中的大多数人一定知道这个库,所以我将非常简短地介绍它。TensorFlow Hub 是一个在 TensorFlow 中发布、发现和重用部分机器学习模块的平台。称之为 TensorFlow 模型的 GitHub 也不会错。开发人员可以分享他们预先训练好的模型,这些模型可以被其他人重用。通过重用,开发人员可以使用较小的数据集训练模型,提高泛化能力,或者只是加快训练速度。让我们快速浏览一些不同的计算机视觉模型。
- 图像分类——从 MobileNet 到 Inception 再到 EfficientNet,有 100 多种模型可用于此任务。说出你想要的任何型号,你很可能会在那里找到它。
- 对象检测和分割-同样,您需要的任何模型都可以在这里找到,尤其是在 COCO 数据集上训练的 TensorFlow 模型动物园对象检测器集合。Deeplab 架构主导了图像分割场景。还有大量的 TfLite 和 TensorFlow Js 型号可供选择。
- 图像风格化——不同的图像风格化主干可以和漫画风格一起使用。
- 生成性对抗网络-GAN 模型如 Big-GAN 和 Compare-GAN 可用,在 ImageNet 和 Celeb 数据集上训练,可用于训练 ImageNet 和人工人脸的任何类别!还有一个 unboundary-GAN,可用于生成相机捕捉的场景之外的区域。此外,他们中的大多数人都有一个 Colab 笔记本,所以在如何实现他们方面没有什么大惊小怪的。
我刚刚描述了冰山一角。有更多的模型可用于姿势估计、特征匹配、超分辨率等主题。我甚至没有讨论过视频。跳到他们的页面了解更多信息。你也可以在这里找到关于它的教程。
TensorFlow 提供了更多的库,如 TensorFlow Extended ,一个用于部署 ML 管道的库, Magenta ,一个用于生成音乐的库,等等。点击查看完整列表。
MLOps 的统一元数据
最近,我写了关于实现 MLOps 工作流的一些障碍。我现在将重点关注元数据,这是一个潜在障碍的例子,并说明统一研究人员和工程师之间的数据如何促进 MLOps 的采用。
正如 DevOps 运动得到了容器化、基础设施即代码和持续集成等工具的帮助一样,MLOps 将需要特定的工具来实现其广泛采用。我相信多模型数据库正在成为 MLOps 的重要工具之一,我将对 ArangoDB 进行评估。
为什么要分离数据?
思考各自团队的两个主要过程有助于我们准确理解为什么我们在这些系统中有数据分离。
研究团队
研究人员旨在利用大量带标签的数据,生成深度学习模型,这些模型现在是机器学习的同义词。这些数据需要尽可能接近业务用途,否则培训中的表现将与生产中的表现有很大不同。这意味着,从示意图的角度来看,训练集通常非常简单,通常利用单个或批量张量输入(无论是图像、数据值、音频……)以及模型在最佳条件下输出的一个或多个目标标签。
从数据的角度来看,输入和输出的简单性需要大量的数据来了解所需任务的复杂性。这通常是神经网络本身的工作,以确定导致输入和目标之间的联系的属性。
开发团队
运行业务应用程序或者将机器学习模型投入生产的团队通常对他们的数据有非常不同的要求。它们仍然具有相同的输入和输出类型,但是需要额外的属性,这些属性对于商业决策可能比机器学习输出本身更重要。同样,还涉及到其他流程,需要跟踪成本和客户需求,并且需要对系统有更深入的理解。这可以通过元数据来实现,通常在关系数据库中,位于服务、网站或系统之后。
人工验证
最近,公司已经在生产中使用机器学习和人工验证的结合,以获得实际上可以销售给客户的性能值。通常,机器学习本身不够准确,因此在结果发送给客户端之前,阈值被设置为发送给人工检查。
这些信息对于研究团队来说显然是有用的,可以提供进一步的标记数据,以及更早地检测数据偏移的能力,尤其是在生长阶段。这也允许团队检查来自 ML 解决方案的结果,以及其他业务过程,当与人类水平的结果比较时。通常,这些结果与生产数据库系统有关,这在研究人员和他们改进模型所需的数据之间建立了一堵墙。
统一数据库的要求
因此,为了统一这些数据,我们需要将训练深度学习模型所需的数据规模和业务应用所需的复杂性结合起来,并在生产服务(包括人工验证)和研究人员数据集之间建立简单的联系。
分离的数据库管道示例
在上图中,我们有许多不同的服务(A、B、C 和 D ),所有这些服务都通过管道推送元数据的公共键( k1、k2 ),以便保留在以后阶段可能有用的信息。这意味着我们经常复制我们不需要的数据,只是简单地传递它,并且很难或不可能跨不同的数据库进行查询,这些数据库通常具有不同的格式、区域或体系结构。
通常,在上述架构中,很难通过工作流来跟踪资产或数据片段的过程,并从这种跟踪中获得洞察力。此外,研究人员很难将来自多个来源的信息组合成一个数据集,用于训练/微调现有模型。
解决办法
自 2010 年左右以来,多模型数据库越来越受欢迎,为传统关系数据库无法满足的复杂需求提供了一个良好的全面解决方案。Redis 就是一个例子,在扩展到文档(JSON)、属性图、流和时间序列数据之前,它最初提供一个键值存储。具体参考大规模计算机视觉任务,我最近测试了各种多模型数据库,因此将在本文的剩余部分集中讨论这些结果,但请注意,有几个更多选项可用于不同的需求。
机器学习的 ArangoDB
ArangoDB 结合了文档存储和图形数据库,构建在 RocksDB 之上,因此默认情况下也提供了键值存储。水平扩展是通过将连接的数据彼此移得更近来实现的,以尽可能避免昂贵的跨服务器查询。这使得该解决方案在大规模下非常有效,同时实现了文档存储和图形数据库的所有优势。
文档存储
生产系统通常使用文档存储来保存与其用途相关的数据,作为传统关系数据库的替代方案,在传统关系数据库中,每一行都可以有许多属性,通常只有几个索引或关联的属性。生产系统通常一次处理管道中单个对象的更新/插入,因此关系数据库和文档存储在这个用例中同样有效。
GraphDB
图形数据库已经存在很长时间了,由于互联网的需求,它们的使用在 90 年代开始起飞。图在建模复杂的连接方面非常有效,在这种情况下,查询速度大大低于通常以关系格式进行的大量索引查找。这正是我们在 MLOps 中理解复杂数据格式所需要的,特别是那些通过多个系统,或者使用复杂 ML 管道的数据格式。
在 ArangoDB 中,文档存储在集合中。与任何文档存储一样,每个文档都由 _key 属性索引。然后,通过称为边缘集合的特殊类型的集合来添加图形功能,该集合索引 _from 和 _to 属性,引用同一数据库中的 collection/_key。这提供了上面两个团队给出的需求。
在实践中
统一存储管道示例
不像以前那样通过管道传递数据,我们可以创建一个统一的数据存储,在一个地方保存所有这些信息。然后,流程中的每个服务根据请求中传递的唯一标识符读取它需要的信息,然后将管道中稍后可能需要的更新写入统一存储。这既给了工程师一个整体系统的视角,也给了研究人员一个单一的数据库来创建数据集。
在多模型数据库出现之前,很难在一个解决方案中从两个角度利用这些信息。对于希望创建有趣的数据集来训练模型的研究人员来说,图形数据库技术使链接这些信息变得更快更容易。从他们的角度来看,人脉更重要。对于希望能够从性能指标方面理解和分析整个管道的工程师来说,元数据文档的唯一键允许在一个地方对其进行跟踪。从他们的角度来看,文档元数据和索引更重要。
表演
但是,它在这些任务中的表现如何呢?我们先把它比作一个图形数据库。在这个项目中,我主要是将 ArangoDB 与 DGraph 进行比较,因为 DGraph 似乎也满足了项目的许多伸缩性和复杂性需求。
以下摘自阿兰戈自己的性能页面,针对两个标准 graph db 性能指标:
来自 ArangoDB 的 2018 年绩效指标。
注意:在我看来,忽略(ArangoDB MMFiles)指标是值得的,因为根据我的经验,我看不出你为什么不想使用 RocksDB,这是默认设置。我认为这些只是遗留下来的,因为这些是旧的图像。
邻居的邻居+一个过滤器,计数不同。我还尝试了 2“跳”(即邻居的邻居)、3 和 4“跳)的测试,并在不同阶段添加了过滤器。
图表性能指标
现在让我们看看文档收集性能指标。一般来说,考虑到 ArangoDB 作为 GraphDB 的性能,您会认为它的性能相当差,但是上面显示了 ArangoDB 如何在读/写速度上大大优于 MongoDB,并且经常优于关系型竞争对手。聚合,据说是关系格式的优势之一,在 ArangoDB 上也有类似或更好的性能。
我的测试包括大型集合中的单一索引查找,以及写同步和聚合。
关系性能度量
正如您所看到的,这是一组令人印象深刻的结果,因为在这些标准关系测试中,ArangoDB 的表现几乎与 MySQL (一个纯关系数据库)一样好,并且大大优于仅文档和图形存储。没有对其他关系格式进行比较,因为它们不能提供上面要求的全部功能,但是作为这些测试的一部分,与现有的 MySQL 实例进行比较对于基准测试是有用的。
缩放比例
正如我前面提到的,缩放一直是机器学习数据库中的一个问题。当数据通常是最重要的业务资产时,您必须确保您为存储数据而构建的解决方案在今年是正确的,并且根据预计的规模在接下来的几年是正确的。随着业务(希望数据)呈指数级增长,重复替换解决方案是一个成本高昂的过程,这也会影响构建解决方案的工程师以及必须不断构建新工具的研究人员的士气。
考虑
当考虑一个数据库解决方案时,一定要确保你已经考虑到了没有大规模测试潜在产品或解决方案的机会成本。同时,现实一点。未来 2 年内达到该规模的实际概率有多大?如果它相当低,那么最好考虑一个更容易用于当前规模的解决方案,而不是使用不适合当前业务规模的工具。这是一个困难的平衡行为,每个商业案例都不一样,所以要注意选择,要有测试和数据来支持你的决策。
结论
ArangoDB 显然是基于机器学习的管道背后的单一统一数据存储的最佳选择。多模型方法允许具有完全不同的需求和观点的用户使用和共享信息和知识库,试图将这些分离的团队聚集在一起。
从机器学习的角度来看,选择 ArangoDB 而不是其他选项的主要原因是不同团队复杂需求的易用性。ArangoDB 必须在多个用例中表现得令人难以置信的好,才能让我相信它是正确的选择,但它做到了这一点,并给出了一个我认为比任何其他选项都更好地涵盖了各种用例的解决方案。
我认为,多模型数据库在未来几年将对复杂和分散的数据集起到至关重要的作用,这是真正广泛采用 MLOps 所需要的。如果这将是一个广泛采用的工具,那么满足业务所有部分的需求并使每个团队能够实现其目标的功能将是成功的关键,同时也将开始弥合团队之间的差距并打破阻止 MLOps 的壁垒。
统一远程和本地 AzureML 环境
微软和 Python 机器学习:现代爱情故事,第 2 部分,共 2 部分
微软 Azure 正在征服我们作为人工智能从业者的心,并通过对 PyTorch、Tensorflow 和 Scikit-learn 等开源框架的支持来吸引我们。在这里,我们围绕 MS 给我们的工具建立了一个工作流程,由我们来决定我们是否受到诱惑。在第 1 部分中,我们在我们的 AzureML 远程计算目标上启动了一个 Python 脚本,而我们的 VSCode devcontainer 对此毫不在意。这也意味着远程环境的大部分配置不在我们的掌控之中。这里我们将深入 AzureML 环境来配置 PyTorch GPU 工作负载。最后,我们努力统一开发和远程环境,使我们在 AzureML 上更有效地开发和测试 AI 模型。
1 计算目标环境
1.1 Docker 图像管理
在下图中,我们看到 Python 工作负载在计算目标上的远程 docker 容器中运行。如果我们创建一个 CPU 集群,并且除了指向计算目标的 RunConfiguration 之外没有指定任何东西(参见第 1 部分,那么 AzureML 将在第一次运行时选择一个 CPU 基础 docker 映像(https://github.com/Azure/AzureML-Containers)。
在基础映像之上,创建了 conda 环境,并安装了默认的 python 依赖项,以创建支持 AzureML-SDK 的 Python 运行时。在构建映像之后,它被推送到链接到 AzureML 工作空间的 docker 存储库,其名称中有一个随机的 UUID(模式:azureml/azureml_ )。在代码运行期间,计算实例将提取自定义图像。如果环境配置发生变化,运行开始时会触发重建。请注意,这些图像不是垃圾收集的,随着存储更多的图像,您的成本会增加。
使用 VSCode devcontainer 的 Azure-ml 工作流(图片由作者提供)
1.2 环境等级
Environment 类是各种配置环境变量、编程语言、计算框架和 Docker 的类的组合。该环境由 Docker、Python、R 和 Spark 部分组成。例如,PythonSection 公开了多种方法来从现有的 conda 环境、conda 规范文件、pip 需求文件或以编程方式添加的依赖项创建 Python 环境。AzureML 环境可以注册到工作区供以后使用。目标是用 azure ml(how-to-environments)创建可重用的培训和部署环境。在我们的 DIY MLOps 中,这将类似于压缩你的整个 conda virtualenv,并将其上传到 artifactory 以供以后部署。Python SDK 中 MLOps API 的细节令人印象深刻,它们最近向环境驱动配置的转变为用户提供了透明性和灵活性。
2 使用 AzureML 在 GPU 上训练 PyTorch
在接下来的章节中,我们将使用 PyTorch 创建一个适合 GPU 计算的运行时环境。我们参考第 1 部分来创建计算集群。与第 1 部分的唯一变化是,我们使用“NC6_standard”虚拟机大小来获得一个带有一个 GPU 的虚拟机。选择一个带有 GPU 的虚拟机运行我们的带有 nvidea_docker 的容器,而不是标准的 docker 引擎。这种自动检测相当新,我发现它是针对单个计算实例的( msdn-post )。对于计算集群,GPU 自动检测运行良好。
2.1 使用 GPU 支持创建运行时 PyTorch 环境
出于我们的目的,我们在 Python 3.6 上运行 PyTorch >=1.4.0 和 Cuda 10.1。作为我们的基本 docker 映像,我们采用官方 AzureML 映像,基于 Ubuntu 18.04 ( gpu-images ),包含原生 gpu 库和其他框架。
为了在我们的远程 Conda 环境中管理 Python 包,我们操作一个 CondaDependencies 对象,并将其附加到我们的环境对象。我们在这个对象上调用“add_conda_package ”,它有一个包名和可选的版本和内部版本。在 PyTorch 的例子中,我们需要一个特定的构建来启用 GPU 后端。我们还需要添加“py torch”conda 通道和“add_channel”来安装这些包。虽然您可能更喜欢使用 conda 或 pip 需求文件,但我喜欢这种依赖管理的编程方法。
**# settings.py
from** **azureml.core** **import** Environment
**from** **azureml.core.conda_dependencies** **import** CondaDependenciesPYTORCH_VERSION = "1.4.0"
TORCH_ENVIRONMENT = Environment(name="torch-env")
TORCH_ENVIRONMENT.docker.base_image = (
"mcr.microsoft.com/azureml/base-gpu:openmpi3.1.2-cuda10.1-cudnn7-ubuntu18.04"
)
TORCH_ENVIRONMENT.docker.enabled = **True** *# forced to True on AMLcompute targets* TORCH_ENVIRONMENT.docker.gpu_support = **True** *# deprecated as it is auto-detected* torch_conda_dep = CondaDependencies()
torch_conda_dep.add_channel("pytorch")
torch_conda_dep.add_conda_package(
f"pytorch==**{**PYTORCH_VERSION**}**=py3.6_cuda10.1.243_cudnn7.6.3_0"
)
torch_conda_dep.add_conda_package("cudatoolkit==10.1.243")# add the conda dependencies to the environment
TORCH_ENVIRONMENT.python.conda_dependencies = torch_conda_dep
2.2 在环境中训练评估员
2.2.1 评估者类别
为了训练我们的 PyTorch 模型,我们使用了估计器类,它是机器学习模型的 AzureML 抽象。估计器可以提交给 AzureML 实验,该实验在计算目标上运行它。请注意,自 2019–2020 年起,PyTorch、Tensorflow 和 SKLearn 的预配置估值器已被弃用。这些对于配置运行时环境来说太含蓄了,这使得它们不灵活,难以维护,并且对用户来说很神奇。建议所有新用户将 vanilla Estimator 类与 Environment 类结合使用。
2.2.2 将评估者提交给实验
我们创建一个评估器,并把它传递给我们之前定义的环境。使用第 1 部分中所示的功能检索计算目标。我们创建(或检索)一个名为“estimator-test”的实验,并向它提交我们的评估器进行运行。
**from** **azureml.core** **import** Experiment, Workspace
**from** **azureml.train.estimator** **import** Estimator**import** **settings
from** **tools** **import** get_computeworkspace = Workspace.from_config()
script_params = {
"--epochs": 10,
}
estimator = Estimator(
source_directory=settings.SOURCE_DIRECTORY,
compute_target=get_compute(workspace, settings.CLUSTER_NAME),
entry_script="relative/path/to/train_model.py",
script_param=script_params,
environment_definition=settings.TORCH_ENVIRONMENT,
)
experiment = Experiment(workspace=workspace, name="estimator-test")
run = experiment.submit(estimator)
3 .本地开发与远程培训相结合
在我们的工作流程中,Docker 容器同时在本地和远程运行。Docker 的承诺之一是统一开发和生产之间的运行时环境。按照这些思路,我们希望针对远程环境在本地调试我们的训练代码。换句话说,我们希望使用相同的 Docker 映像进行本地开发和模型的远程训练。
弗朗切斯科·温加罗摄影
3.1 基于 AzureML 生成的图像的 VScode devcontainer
VSCode devcontainer 需要两个文件;一个 Dockerfile 和一个 devcontainer.json。devcontainer/"文件夹。Dockerfile 描述了我们的 Docker 映像,我们从它构造一个容器。devcontainer.json 文件特定于 VSCode,并配置与 Docker 容器的集成,如 Python 解释器路径、林挺可执行文件路径、VSCode 扩展和绑定挂载。
3.1.1 从 AzureML 映像派生的 Dockerfile
在我们的 Dockerfile(见下文)中,我们继承了 AzureML ContainerRepository 中的基本映像。我们可以通过用我们的 AzureML docker 注册中心对我们的本地 Docker 进行认证,在本地获取 AzureML 构建的 Docker 映像,并将其推送到我们的工作区。VSCode 的 docker 和 azure 帐户扩展可以简化这种身份验证和拉取。
我们希望在容器中安装 Python dev 依赖项,如 black、pylint 和 pytest。我们将它们安装在 AzureML 用来运行代码的同一个 Python 环境中,尽管也可以有其他选择。AzureML 生成的 Conda 环境位于映像上的“/azureml-envs/”中。Conda env 的名称包含另一个随机 UUID,其模式为:“azureml_ ”。在我们的 docker 文件中,我们使用与这个 env 相关联的 pip 来安装我们的 Python dev 依赖项,我们使用 apt-get 来安装 vim 和 git,同时确保在这些操作之后进行清理。
FROM <DOCKER_LOGIN_SERVER>.azurecr.io/azureml/azureml_<UUID>:latest
LABEL maintainer="luuk@codebeez.nl"ARG DEFAULT_UTILS="\
pylint \
flake8 \
autopep8 \
pytest \
mypy \
pydocstyle"RUN /azureml-envs/azureml_<UUID>/bin/pip install --no-cache-dir ${DEFAULT_UTILS} \
&& apt-get update \
&& apt-get install -y \
vim \
git \
&& apt-get autoremove -y \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/*
3.1.2 将 VSCode 与容器集成
json 定义了 VSCode 和 Docker 容器之间的集成。我们为 devcontainer 定义一个名称,后跟 Dockerfile 的路径(您也可以直接设置一个图像)。接下来是 VSCode 设置,带有我们的 Python 解释器和各种 devtools 的路径。我们完成了我们想要安装在 devcontainer 中的 VSCode 扩展。
{
"name": "unified-azureml",
"dockerFile": "Dockerfile",
// Set container specific VSCode settings
"settings": {
"terminal.integrated.shell.linux": "/bin/bash",
"python.pythonPath": "/azureml-envs/azureml_<UUID>/bin/python",
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.linting.flake8Path": "/azureml-envs/azureml_<UUID>/bin flake8",
"python.linting.pycodestylePath": "/azureml-envs/azureml_<UUID>/bin pycodestyle",
"python.linting.pydocstylePath": "/azureml-envs/azureml_<UUID>/bin pydocstyle",
"python.linting.pylintPath": "/azureml-envs/azureml_<UUID>/bin pylint",
"python.testing.pytestPath": "/azureml-envs/azureml_<UUID>/bin pytest"
},
// Add the VSCode extensions you want installed
"extensions": [
"ms-python.python",
"ms-azure-devops.azure-pipelines",
],
}
3.1.3 自动化的潜在断点
当在基础映像之上以 Docker 存储库的名义创建 Conda env 时,AzureML 生成的两个随机 UUIDs 是在我们的 AzureML 环境的迭代中自动化该工作流的潜在突破点。AzureML 生成的 docker 库的名称可以在 AzureML studio 中运行的实验的“20_image_build_log.txt”日志文件中找到,可以在 portal.azure.com 上与 AzureML 工作区关联的 Docker 注册表的接口中找到,也可以通过使用其 API 找到。通过运行以下命令,可以获得映像中的 Conda 环境列表。
docker run <DOCKER_LOGIN_SERVER>.azurecr.io/azureml/azureml_<UUID> /bin/bash -c "conda env list"
3.2 摘要:AzureML 统一的工作流
使用 AzureML 环境类,我们基于支持 GPU 的 docker 映像定义了远程计算目标的运行时配置。这个环境被 AzureML 整合为一个定制的 docker 映像,并被推送到 docker 注册中心。我们使用这个定制映像来创建一个 VSCode devcontainer,以统一我们的开发和远程环境。这允许我们在本地测试远程运行的相同环境。感谢您的关注,我期待着写更多与 Python、机器学习和数据工程相关的话题。
最初发布于https://codebeez . nl。
单位长度缩放:连续特征缩放的终极?
奇妙的单位长度缩放会超越其他形式的缩放并在机器学习中取代它们吗?
当处理连续目标时,工程师可以使用很多很好的方法来提高训练精度。一些最受欢迎的选项包括限制数据以避免处理可能影响预测的异常值,或者移除在预测目标时不一致的不良特征。然而,在解决连续问题时,提高训练准确性的最常见、最简单的方法是首先缩放用于训练模型的特征。这是因为扩展特性带来的好处是开销很小,而且非常容易实现。通过简单地权衡这样做的好处,很容易理解为什么这么多人选择利用特征缩放器来解决他们的持续问题:
- 缩放器相对容易实现。
- 根据不同的数据和模型,你会发现使用一个定标器就能显著提高精度。
- 缩放器有助于使数据集中的值彼此更接近,消除异常值,并使数据更容易为您的模型读取。
- 可以将缩放器放入管线中,管线会自动为您缩放输入要素。
通常,每当我们讨论连续定标器时,我们都在讨论使用正态分布。也称为标准缩放器,这是一种相对简单而有效的生成简化数据的方法,通过公式传递样本,统计人员可以更好地理解这些数据。虽然标准定标器肯定是机器学习中使用最多的,但重要的是不要忘记,有几种不同的定标器在统计世界中都有它们的位置。缩放器列表中的其他缩放器包括任意缩放器和平均归一化缩放器。虽然这些都是缩放器的很好的例子,但它们通常不用于提高机器学习模型的准确性。
有这么多优秀的预处理方法、编码器和各种图像处理选项,几乎所有连续解决方案最终都是用完全相同的标准定标器产生的,这似乎有点奇怪。如果有一些定标器可以用在数据集上,那么还有其他值得投资的吗?有没有另一种缩放要素的方法可以进一步提高精度?
单位长度定标器
科学家、数据迷和机器学习爱好者;我给你介绍
单位长度缩放器。
单位长度缩放器是可以在机器学习中使用的另一个特征缩放器。虽然典型的重新标度器、任意重新标度器和均值规格化器需要向标准化倒退,但单位长度标度器在机器学习世界中独树一帜。虽然它可能不会像标准缩放器那样经常使用,但今天我想研究一下为什么会这样,以及我们是否应该更多地使用它。
单位长度缩放器缩放要素的矢量,使所述矢量的长度为 1。也就是说,典型的应用将包括欧几里德距离的使用。然而,在某些应用中,使用 L 下标 1 范数可能更实际。如果在接下来的学习步骤中,标量度量用作距离度量,这一点尤其重要。
可用库
一项技术成功与否的一个重要原因是它的实现。不幸的是,单位长度缩放器的实现并不常见,因此 Sklearn 令人惊讶地没有包含这样的实现。Sklearn 模块中的大多数(如果不是全部的话)特征定标器是方差、均值或标准定标器——它们在某种程度上是相同的,但略有不同。这些观察缺乏使用向量距离的固有不同的计算方法,而不是关于数据的信息统计。
正如你可能已经预料到的那样,事情并没有变得更好。在所有像 MLDataUtils 和 scaler 这样的预处理库中,甚至没有一点提到单位长度的 scaler。然而,对于基于 Julia 的科学家来说,幸运的是,Lathe 0.1.2 在 Lathe.preprocess 中实现了这个缩放器。然而,除了这个特定的实例,如果您想要使用这个缩放器,那么您很可能要从头开始。
自己创建缩放器
为了实现缩放器,我们首先需要创建一个函数来计算一个向量的欧几里德距离。为此,你只需取两个数组的范数,然后将它们相减。我将使用 Julia 的线性代数软件包中的 norm 方法来完成这项任务:
using LinearAlgebraeuclidian(a, b) = norm(a-b)
由于朱莉娅惊人的语法,我能够使用语法表达式。在 Python 中,你可以使用 numpy.linalg.norm 做同样的事情。
import numpy as npdef euclidian(a, b):
return np.linalg.norm(a-b)
继续并遵循语法和对象组合的车床标准,我创建了一个小函数来处理我们类型的创建。
function UnitLengthScaler(data)
()->()
end
我们需要添加到这个函数中的第一件事是数组的 0。由于这个函数中的数组输入是一维的,并且是典型的常规列表而不是矩阵,我们可以通过编写 for 循环来实现这一点。
zeroes = [i = 0 for i in data]
接下来,我们将把零和数据插入欧几里德长度函数,如下所示:
euclidlen = euclidian(zeroes, data)
最后,单位长度的公式是
x^i = x^i / ||x||
所以我们将它插入到 for 循环中,就像这样:
predict() = [i = i / euclidlen for i in data]
现在我们将把这个方法封装到一个对象中,得到一个完整的单位长度缩放器。
function UnitLengthScaler(data)
zeroes = [i = 0 for i in data]
euclidlen = euclidian(zeroes, data)
predict() = [i = i / euclidlen for i in data]
()->(predict;euclidlen)
end
结论
要素缩放是数据科学中经常被忽略的重要内容之一,因为它在大多数情况下都是相对基础的。通常情况下,很容易陷入例行公事中,标准地衡量一切,继续你的一天。幸运的是,像该领域的大多数事情一样,甚至特征缩放也可以被开发、实验和改进。我已经测试了这个定标器一点点,但只有非常复杂的结果。有时会失去精度,偶尔会导致精度略高于我可能得到的标准定标器。据我所知,这似乎很偶然。不管怎样,我认为这的确是一件很酷的事情,应该让更多的人了解它!
数据科学家的单元测试
机器学习
使用 Pytest 提高管道的稳定性
随着数据科学在大型组织中变得越来越重要,对代码进行适当的测试的需求慢慢变得越来越集成到数据科学家的技能组合中。
假设您正在创建一个预测组织中客户流失的渠道。在部署您的解决方案几个月后,有一些新的变量可能会提高它的性能。
不幸的是,在添加了那些变量之后,代码突然停止工作了!您不熟悉错误消息,并且您很难找到自己的错误。
这就是测试,特别是单元测试的用武之地!
为特定模块编写测试可以提高代码的稳定性,并使错误更容易被发现。尤其是在处理大型项目时,进行适当的测试是基本的需求。
没有某种形式的测试,任何数据解决方案都是不完整的
这篇文章将关注一个小的,但是非常重要的,也可以说是测试的基础,即单元测试。下面,我将详细讨论为什么测试是必要的,什么是单元测试,以及如何将它们集成到您的数据科学项目中。
1.你为什么要测试你的代码?
虽然这看起来是显而易见的,但实际上测试代码有很多原因:
- 防止意外输出
- 简化代码更新
- 提高开发代码的整体效率
- 有助于检测边缘情况
- 最重要的是防止你将任何破损的代码投入生产!
这只是我的一个想法!
即使对于那些编写产品代码的人,我也建议他们至少为代码中最重要的模块编写测试。
如果你运行一个深度学习管道,但在 3 个小时后,你本可以轻松测试的东西失败了,那该怎么办?
注意:我完全可以想象不想为只花了你两天时间编写的一次性分析编写专用测试。没关系。由你来决定什么时候测试是有帮助的。最重要的是要认识到,他们可以节省你很多工作。
2.单元测试
单元测试是一种软件测试方法,它检查哪些特定的单个代码单元适合使用。例如,如果您想在 python 中测试sum
函数,您可以编写以下测试:
assert sum([1, 2, 3]) == 6
我们知道 1+2+3=6,所以应该没什么问题就通过了。
我们可以通过创建一个定制的 sum 函数来扩展这个例子,并对元组和列表进行测试:
当-1+2+3 不等于 6 时,test_new_sum_tuple
的输出将是一个断言错误。
单元测试帮助你在许多不同的环境下测试代码。然而,有一件重要的事情需要记住:
单元测试并不完美,几乎不可能达到 100%的代码覆盖率
单元测试是捕捉 bug 的好方法,但是不会捕捉到所有的东西,因为测试容易出现与你要测试的代码相同的逻辑错误。
理想情况下,您会希望包括集成测试、代码审查、正式验证等。但是这超出了本文的范围。
3.Pytest
上面例子的问题是,它将在第一次面对AssertionError
时停止运行。理想情况下,我们希望看到所有通过或失败的测试的概述。
这就是测试跑步者的用武之地,比如 Pytest 。Pytest 是一个很好的工具,可以根据您定义的测试创建广泛的诊断。
我们从安装 Pytest 开始:
pip install pytest
这样做之后,创建一个test_new_sum.py
文件,并用下面的代码填充它:
最后,将cd
放入存放test_new_sum.py
的文件夹,然后简单地运行pytest -v
。结果应该是这样的:
你可以在上面的图片中看到,它显示了哪些测试通过了,哪些测试失败了。
令人惊奇的是 Pytest 向您展示了预期的值以及它失败的地方。这可以让你很快看到哪里出了问题!
4.数据科学家的单元测试
为了理解我们如何将 Pytest 用于数据科学解决方案,我将通过几个例子来说明。以下数据用于示例:
示例数据
在这些数据中,我们有一个目标类和几个可以用作预测器的特征。
基本用法
让我们从几个简单的预处理函数开始。我们想知道每个类的每个特征的平均值。为此,我们创建了以下基本函数,aggregate_mean
:
太好了!我们放入一个数据框和列,它应该会产生一个字典,其中包含每个类的平均值。
为了测试这一点,我们编写了下面的test_aggregate_mean.py
文件:
当我们运行pytest -v
时,它应该不会给出错误!
用参数表示
使用参数化运行多个测试用例
如果我们想要测试许多不同的场景,那么创建一个测试的许多副本会很麻烦。为了防止这种情况,我们可以使用 Pytest 的参数化函数。
参数化通过允许我们测试**多个场景来扩展测试功能。**我们简单地添加参数化装饰器并陈述不同的场景。
例如,如果我们想测试功能 1 和 3 的 aggregate_mean 函数,我们采用如下代码:
运行pytest -v
之后,特性 3 的结果似乎不是我们所期望的。原来,我们之前看到的 None 值其实是一个字符串!
如果我们没有进行测试,我们自己可能不会发现这个错误。
固定装置
用 Fixtures 防止单元测试中的重复代码
当创建这些测试用例时,我们通常希望在每个测试用例之前运行一些代码。我们创建fixture来为我们的测试建立基线代码,而不是在每个测试中重复相同的代码。
它们通常用于初始化数据库连接、加载数据或实例化类。
使用前面的例子,我们想把load_data()
变成一个夹具。我们把它的名字改为data()
,以便更好地代表这个夹具。然后,@pytest.fixture(scope='module')
作为装饰器被添加到函数中。最后,我们将 fixture 作为参数添加到单元测试中:
如你所见,没有必要将data()
设置为变量,因为它会自动调用并存储在输入参数中。
Fixtures 是增加可读性和减少测试函数中任何错误的好方法。
嘲弄的
我在你的管道中修改代码,通过模仿来加速测试
在许多数据驱动的解决方案中,您将需要处理大型文件,这会极大地降低您的管道速度。为这些代码创建测试是困难的,因为快速测试是首选。
假设您用pd.read_csv
加载了一个 2GB 的 csv 文件,并且您想要测试管道的输出是否正确。有了 unittest.mock.patch ,我们可以用自己的输出替换pd.read_csv
的输出,用更小的数据测试流水线。
假设您已经为管道创建了以下代码库:
现在,为了测试这个管道是否像预期的那样工作,我们想要使用一个更小的数据集,这样我们就可以更准确地测试用例。为此,我们用迄今为止一直使用的较小数据集来修补pd.read_csv
:
当我们运行pytest -v
时,大的。csv 将不被加载,取而代之的是我们的小数据集。测试运行迅速,我们可以很容易地测试新的情况!
新闻报道
你用单元测试覆盖了你的大部分代码吗?
单元测试无论如何都不是一个完美的方法,但是知道你测试了多少代码是非常有用的。特别是当你有复杂的管道时,知道你的测试是否覆盖了你的大部分代码是很好的。
为了检查这一点,我建议你安装 Pytest-cov ,它是 Pytest 的一个扩展,向你显示你的代码有多少被测试覆盖。
简单地安装 Pytest-cov,如下所示:
pip install pytest-cov
完成后,运行以下测试:
pytest --cov=codebase
这意味着我们正在运行 Pytest,并检查有多少测试覆盖了 codebase.py。
幸运的是,我们的测试覆盖了codebase.py
中的所有代码!
如果我们注意到覆盖了任何行,那么该值将显示在**Miss**
下。
感谢您的阅读!
如果你像我一样,对人工智能、数据科学或心理学充满热情,请随时在 LinkedIn 上添加我,或者在 Twitter 上关注我。
本文中的所有示例和代码都可以在这里找到:
为数据驱动的解决方案创建单元测试的代码和说明相应的文章可以在这里找到:To…
github.com](https://github.com/MaartenGr/UnitTesting)
单元测试 MLflow 模型相关的业务逻辑
费伦茨·阿尔马西在 Unsplash 上的照片
培训、测试和部署
很多时候,模型的开发者不同于在他们的应用程序中使用模型的开发者。例如,开发人员创建一个获取输入图像并对其进行分类的 API 时,不会知道也不需要知道模型的内外。开发人员需要知道的只是从哪里加载模型,以及如何进行推理。MLflow 提供了非常好的 API 来实现这一点。
定制的 MLflow 模型
mlflow.pyfunc.PythonModel:表示一个通用 Python 模型,它评估输入并生成 API 兼容的输出。通过子类化,用户可以创建具有“python _ function”(“py func”)风格的定制 MLflow 模型,利用定制的推理逻辑和工件依赖性。
MLflow 允许用户创建定制的模型,除了他们本身支持的模型,比如 keras。该模型可以保存为 mlflow 模型,供以后使用。因此,不管您的生产模型类型是什么,都可以创建一个使用ml flow . py func . python model的示例模型来模拟它。
以下是 MLflow 文档中的 AddN 模型示例。在这里只实现了 init 和 predict 方法,我们将做同样的事情
**import** mlflow.pyfunc
*# Define the model class*
**class** **AddN**(mlflow**.**pyfunc**.**PythonModel):
**def** __init__(self, n):
self**.**n **=** n
**def** **predict**(self, context, model_input):
**return** model_input**.**apply(**lambda** column: column **+** self**.**n)
*# Construct and save the model*
model_path **=** "add_n_model"
add5_model **=** AddN(n**=**5)
mlflow**.**pyfunc**.**save_model(path**=**model_path, python_model**=**add5_model)
*# Load the model in `python_function` format*
loaded_model **=** mlflow**.**pyfunc**.**load_model(model_path)
*# Evaluate the model*
**import** pandas **as** pd
model_input **=** pd**.**DataFrame([range(10)])
model_output **=** loaded_model**.**predict(model_input)
**assert** model_output**.**equals(pd**.**DataFrame([range(5, 15)]))Source: [https://www.mlflow.org/docs/latest/models.html#example-creating-a-custom-add-n-model](https://www.mlflow.org/docs/latest/models.html#example-creating-a-custom-add-n-model)
额外使用案例
在进入实现之前,让我们激励一个用例。假设 Acne Inc .决定使用预测模型向其员工提供奖金。基于某些输入,该模型预测该员工的奖金应该是多少。
该模型的功能和输入超出了本文的范围。更有趣的是开发者如何使用这个模型。将该模型集成到 HR 系统中的开发者被提供该模型的位置,并被要求公开一个方法 amount ,该方法给出输入,输出奖金金额。开发者被告知模型是 mlflow 模型,可以使用提供的 API 进行预测。
我们的开发人员提出了一个简单的实现如上。现在的挑战是对这个实现进行单元测试,以确保它能够工作。理想情况下,单元测试应该是 CI/CD 兼容的,因此与其为测试训练模型,解决方案应该是轻量级的。输入 mlflow.pyfunc.PythonModel!
测试奖金模型
开发人员提出了一个可以由字典初始化的测试模型。字典基本上是给定输入的预定义结果。预测法就是这么做的。在这种情况下,假设 model_input 的每个元素都是支持哈希方法的类型,所以它可以是字典的一个键。
单元测试…耶!
现在最精彩的部分来了,开发者用一个测试奖金模型测试它创建的奖金类。
在 test_model 中,开发者定义了一个输出字典,用于初始化模型。然后,模型被保存在临时目录中的特定位置。用保存模型的路径初始化 Bonus 类,然后测试 amount 方法,它给出与输出字典相同的值。瞧啊。
结论
测试依赖于 MLflow 模型的业务逻辑非常简单。所有人需要做的就是定义一个测试模型并适当地保存/加载。这种机制在 spark udf 和 MLFlow 中都能很好地工作。
为没有时间测试的人准备的单元测试
如何提高代码质量和安心的快速指南
测试可能是一个大傻瓜,但是可靠的测试覆盖是成熟工程团队的标志。如果你能选择一件事来解决代码质量问题,单元测试是你能做的最好的投资。
什么是单元测试?
这是首先需要澄清的。测试有很多种类型:单元、回归、集成、端到端等。除了单元测试之外,大多数其他测试都处理组件之间的集成。这些组件可以是类、微服务或包。因为应用程序有不同的大小和复杂性,所以很难将测试归入其中的一个类别。但是单元测试是很容易定义的:*单元测试应该测试项目中最小的部分。*大多数时候,这意味着功能。
为了说明各种情况,我们将使用以下函数:
怎么写测试?
Python 有一个强大的单元测试包:“unittest”。还有其他几个有很多优点和缺点需要评估。但是请记住,我们是没有时间进行测试的人。使用 unittest 永远不会出错,所以我们将在示例中使用它。
python 中的单元测试是这样的:
这里唯一强制的事情是,您的测试需要是继承 TestCase 的类中的方法。您的所有测试都必须是名称以“test_”开头的方法。其他的都将被跳过。
从命令行,这就是你运行所有单元测试文件的方式:
python -m unittest discover
这些高级选项可以让您对正在执行的内容进行额外的控制:
#specify what modules to be executed
python -m unittest test_module1 test_module2#execute all tests inside a specific class
python -m unittest test_module.TestClass#cherry pick a test to be executed
python -m unittest test_module.TestClass.test_method
移除所有依赖关系
第一步是识别函数的所有依赖项。在我们的豚鼠函数中,我们可以确定 2 个外部端点。第一个是获取数据进行处理的地方,另一个是将结果发送到的地方。同样的逻辑也适用于正在读写的磁盘文件或数据库查询。这里不明显的依赖是“calculate_avg”。因为我们只是在测试我们的“单元”,所以我们也应该把其他的函数调用算作一个依赖
一旦确定了依赖关系,我们就应该模仿它们。主要原因是将我们的测试与我们无法控制的环境设置和外部服务隔离开来。这里有几个原因:
- 当这个测试的自动执行运行时,它们可以是离线的
- 与这种依赖关系的交互可能会导致整个系统发生不希望的变化(比如改变数据库中的数据)
- 您想要测试不同的场景,并且可能很难强迫这个依赖“失败”或者用不常见的数据测试执行
- calculate_avg 可能改变了它的实现(可能现在已经坏了),这可能会导致我们的测试失败。
- 因为如果你测试两个组件之间的集成,这将是一个集成测试而不是一个单元测试
怎么嘲讽?
Python 有一个强大的模仿库,可以帮助你模仿所有类型的依赖。我将在我所有的例子中使用 decorators,因为我发现这样更容易阅读测试。我发现模仿是一种更好的学习方式,所以我编写了一份常见模仿场景的备忘单。记得参考我们的豚鼠功能
修补依赖函数
在同一测试中修补多个依赖项。
这里的问题是,装饰器离方法声明越近,它就越早在参数列表中被传递。
@mock.patch("guinea_pig_module.requests.post")
@mock.patch("guinea_pig_module.requests.get")
@mock.patch("guinea_pig_module.calculate_avg")
def test_get_sum(self, mocked_avg, mocked_get, mocked_post):
pass
改写怪异
另一个常见的错误是在错误的地方模仿正确的函数。请注意“请求”中的“get”函数。重写“get”的正确方法是传递几内亚猪路径,然后传递“requests.get ”,就像它是在几内亚猪内部声明的一样。这不行:
@mock.patch("requests.get")
删除样板代码
到处添加这些装饰者会变得重复,事情也会变得混乱。一种选择是将装饰器一直添加到类声明的顶部,而不是方法声明。这样做的好处是,在类级别上进行的模拟会自动渗透到该类中的所有方法。所以要明智地使用它。
您还需要考虑方法签名中的这些新模仿。第一个参数总是在方法级和接下来的类级中声明的模拟。
用护栏修补
当您模仿一个函数时,默认情况下它返回一个 mock()对象。这个对象允许你做任何事情。这将起作用:
x = mocked_method()
x.fake_method()
x.not_real_method()
x.fooooo()
print(x.anything)
当它有意义的时候,你可能想要确保你以正确的方式使用被嘲笑的回答。
#this returns a mock object, but only with pre-existent methods
#and attributes from the class being specced@mock.patch("your_class_here", autospec=True)
试图访问原始类中不存在的元素将引发异常。
另一个用例可能是在对象内部修补一个特定的方法,而不模仿其他任何东西。这可以这样实现:
@mock.patch.object("your_class_here", "target_method_here")
副作用调用副作用
side_effect 的最后一个用例是引发异常。记住“return_value”有一个静态响应。因此,如果您想引发一个异常,您必须像这样做:
x.side_effect = RuntimeError("Bad")#this will raise an exception
x()#this will raise an exception in the 3rd call
x.side_effect = [1, 2, RuntimeError("Bad")]
覆盖面很重要
下一步是关注覆盖率。争取 100%的覆盖率。为了实现这一点,您可能需要为同一个函数编写几个测试。您将使用模拟技能来确保每个 if/else 和异常处理程序块都得到执行。回到我们的豚鼠功能,我们需要一些东西来达到 100%:
- 返回年龄小于 0 的记录
- resp.json()应返回非空响应。您需要循环来执行
- 抛出异常
“覆盖率”是一个非常有用的软件包,它可以帮助你识别仍然缺少测试的行。要使用它,您需要对前面的单元测试命令做一个小小的修改:
python -m unittest discover#becomes
coverage run -m unittest discover#to see the reports from the run
coverage report -m
这将会正常运行您的测试,但是也会跟踪您的项目中正在执行的每一行。最后,您将看到这样一个漂亮的报告:
Name Stmts Miss Cover Missing
-------------------------------------------------------
module1.py 20 4 80% 33-35, 39
-------------------------------------------------------
TOTAL 20 4 80%
利用这一点来跟踪任何被忽略的线。回到我们的豚鼠函数,我们的测试看起来像这样:
仅仅通过达到 100%的覆盖率,这些测试就发现了代码中的两个错误。
- Try 子句未捕获到请求超时异常。这导致了功能崩溃
- 第二个是在创建大型 Try 块时常见的错误。如果“resp.json()”抛出异常,“idx”永远不会初始化。这将导致在打印前一个异常的错误信息时发生异常。不好了。
一旦这些错误被修复,新函数看起来就像这样:
输入和输出
这个函数只返回一个布尔值,但是在某些情况下,您可能需要验证一个需要符合特定规范的对象。所以你的测试需要验证这一点。输入更棘手,从常见的疑点开始:
- 列表:空列表,1 个元素,少量元素,大列表
- 文件:空文件、无文件、小文件、大文件
- 整数和浮点数:负数?、零、少数正数、大数
- 字符串:空字符串,小字符串,大字符串
您需要考虑什么对您正在测试的功能有意义。对于豚鼠的功能来说,输入被传递给被模仿的依赖者。所以值是多少并不重要。如果你不确定,就按照通常嫌疑人名单。不要想太多,思考比写那些测试要花更多的时间。偏向“测试越多越好”的一边
结论
记住,单元测试就像一个房子警报系统。这是为了让你安心,它本身不应该是一个项目。你只是在确保从现在起 6 个月后所做的改变不会破坏你的功能行为。所以,花更多的时间确保你涵盖了一些广泛的领域,而不是非常具体的案例。利用模拟包中的“规格”。你刚刚写了你的函数,你知道如何使用它。设置好闹钟,去做你最喜欢做的事情。造东西!
统一者:图像和文本的结合
学习所有事物都可以使用的图像和文本的联合表示
图片由 Pixabay 的 Patricia Hébert 提供
多模态学习在我们的生活中无处不在。人类以不同的方式吸收内容,无论是通过图片(视觉),文本,口头解释(音频)等等。这些知识来源中的每一个都被称为一种模式。事实上,我们经常通过这些模式的组合来学习,给每个人带来独特的学习体验。麦古尔效应——当看到一个人说话(嘎-嘎)但听到不同的声音(巴-巴)时,观察者会感知到第三种声音(哒-哒)——是不同模态相互作用的一个主要例子。受此启发,机器学习研究人员也开始编译数据集和任务,这些数据集和任务需要模型理解多种模式才能成功。
由于早期的多模态任务是受麦克格尔克效应的启发,其中许多只涉及音频和图像。随着时间的推移,任务模式的种类和数量变得更加多样化。如今,有些任务使用图像和文本(视觉问答、视觉常识推理、图像-文本检索)或图像、音频和文本(视频情感分析、CMU-摩西)。单峰任务已经得到了很好的探索,可以从每种模式中提取良好的表征。例如,GloVe 和 Word2Vec 等单词嵌入可用于生成文本嵌入,而 ResNet50 和 VGG16 等卷积神经网络通常用于从图像中提取特征。出现的挑战是组合和/或使用这些单峰特征来完成多峰任务。
组合这些特征提出了两个主要挑战:如何和何时融合它们。Baltrusaitis 等人确定了组合单峰特征的两种主要技术:联合表示(使用神经网络或一些其他体系结构将多种模态组合到同一表示空间中)和协调表示(使用一些度量,如余弦距离或相关性,使单峰表示更接近,同时仍然在不同的表示空间中)。这两种融合技术也可以在模型架构中的不同点发生:早期融合(在开始时组合特征,并让完整的模型架构处理组合的特征)或晚期融合(通过它们自己的架构运行各个模态,并在结束时组合它们)。
许多当代模型专注于学习适用于特定任务的好的多模态表示。结果,这些模型不能被推广到相同模态的稍微不同的使用。然而,为每个任务制作模型需要花费大量的时间和精力。在这项工作中,微软 Dynamics 365 AI Research 的 UNITER 模型专注于学习图像和文本的可推广联合嵌入。
统一模型架构(自顶向下)
UNITER 在大量数据上使用自我监督学习,如 BERT,以确保学习到的嵌入是通用的。该模型采用图像-文本对,并用来自 4 个不同数据集的大约 950 万对图像-文本对进行训练。
对于每一对,它首先提取文本特征(通过将单词位置信息与单词片段标记化的句子相结合)和图像区域特征(通过将更快的 R-CNN 特征与位置信息相结合)。一旦特征被提取出来,它们就要经过几层变换器,最终学习联合嵌入,
在联合嵌入的自我监督训练中使用了 3 个主要目标。对于这些任务,输入是( w,v ),其中 v 是输入图像区域, w 是成对文本,θ是可训练参数。
蒙面语言建模(MLM)
MLM 以 15%的概率用【屏蔽】令牌屏蔽输入文本的每个单词。目标是基于句子的其余部分和配对图像来预测被屏蔽的单词。这是通过最小化负对数似然来实现的,如下式所示。
MLM 目标
其中 wm 为屏蔽字, w\m 为包围字。
图像文本匹配(ITM)
对于 ITM ,一个额外的【CLS】标记被附加到输入文本的开头,并且很像伯特的【CLS】标记,它捕获输入文本的上下文,它捕获输入图像-文本对的联合上下文。这个令牌然后通过一个计分器 sθ 来测量图像和文本的匹配程度。因为每个输入对可以是正的或负的,所以使用二进制交叉熵损失来优化它。
ITM 目标
其中 y 对于负对为 0,对于正对为 1。
掩蔽区域建模(MRM)
MRM 类似于 MLM ,除了没有用[MASK]标记替换区域,而是用零填充。然而,图像特征是连续的,使得不可能最大化对数似然。因此,为了实现 MRM ,本文提出了符合以下一般等式的 3 个不同的目标函数。
MRM 目标
其中 vm 是被遮罩的图像区域,而 v\m 是周围区域。
1.掩蔽区域特征回归
MRFR 尝试将每个被掩蔽的图像区域的最终联合表示 vm(i) 与其预训练的图像嵌入相匹配,从而保留传递到模型中的信息。
MRFR 目标
其中 hθ 将联合图像嵌入转换为与输入图像嵌入相同的大小。
2.掩蔽区域分类
统一器试图将 vm(i) 识别为 MRC 中的对象类型。更快的 R-CNN 以一定概率预测每个区域是一类物体的概率。这被用作基础真实标签,并且预测的交叉熵损失被优化。
MRC 目标
其中 K 是由更快的 R-CNN 预测的对象类别的数量,gθ( vm(i) )将区域转换成 K 长向量,c( vm(i) )是真实标签的一个热点向量。
3.利用 KL 散度的掩蔽区域分类(MRC-kl)
MRC-kl 与 MRC 相同,只是使用了更快的 R-CNN 对每个对象类型的预测概率,而不是固定的标签(0 或 1)。使用 KL 散度损失而不是交叉熵来匹配所有概率,而不仅仅是地面真实类,如下所示。
MRC-kl 目标
其中 K 是由更快的 R-CNN 预测的对象类别的数量,gθ( vm(i) )将区域转换成 K 长向量,c( vm(i) )是更快的 R-CNN 的类别概率。
该团队尝试了这些预训练任务的几种组合,发现使用除 MRC 之外的所有任务可以产生最佳的嵌入效果。
最佳 UNITER 嵌入针对大量涉及图像和文本的下游任务进行了微调。这些任务中的一些是视觉 QA (回答关于图像的问题)视觉推断(确定图像是否包含一个句子)和图像文本检索(检索一个给定的另一个)。这些任务都在几个基准上进行了测试,UNITER 能够在 13 个不同的基准上实现最先进的性能,真正展示了 UNITER embedding 的通用性。
这里有一个链接到我们的论文,如果你想了解更多关于 UNITER 的细节,点击这里查看更多我们的出版物和其他工作。
参考文献
- 塔达斯·巴尔特鲁·艾蒂斯,柴坦尼亚·阿胡贾,路易·菲利浦·莫伦西,多模态机器学习:综述与分类 (2018),IEEE 模式分析与机器智能汇刊 41,第 2 期 PP:423–443。
- 日元-陈春、李林杰、李成玉、艾哈迈德·埃尔·科利、费萨尔·艾哈迈德、哲干、于成、刘晶晶、统一者:学习普遍的图像-文字表述 (2019)、arXiv 预印本 arXiv:1909
Unity-ML 特工:玛雅冒险
Unity-ML 代理课程
训练一个特工在这种危险的环境下拿到金像。
本文是 的第三章全新免费课程关于深度强化学习与合一。 我们将创建使用 Unity 游戏引擎🕹.学习玩视频游戏的代理查看教学大纲 此处 。
如果之前没有学习过深度强化学习,需要查看免费课程tensor flow 深度强化学习。
在前两篇文章中,您学习了使用 ML-agent,并培训了两个 agent。第一个是能够翻墙,第二个学会摧毁金字塔以获得金砖。是时候做些更难的事情了。
当我在考虑创建一个自定义环境时,我想起了印第安纳·琼斯中的一个著名场景,Indy 需要获得金像并避免大量的陷阱才能生存。
我在想:我的经纪人能像他一样好吗?剧透警告:的确是,正如你在这个回放视频中看到的!
这就是为什么在过去的两周里,我在 Unity 中开发了一个名为 Mayan Adventure 的开源强化学习环境。充满陷阱的危险模块化环境。为了训练代理人,我使用了一种叫做课程学习的学习策略。
所以今天,你将学习课程学习,你将训练我们的代理人获得金像并击败游戏。
所以让我们开始吧!
介绍玛雅冒险
Hello Indie
正如我所说,玛雅冒险是一个针对 ML-agent 的开源强化学习环境。
在这种环境下,你训练你的代理人(Indie) 击败每一个危险的陷阱,得到金像。
在学习过程中,我们的代理人启动以避免掉下地面并获得金像。随着他变得越来越好,我们增加了一些难度,感谢两个压力按钮,你的代理可以将自己变成岩石或木头。
他需要把自己变成木头才能通过木桥,否则,桥就会倒塌。
然后撞上一块岩石穿过火层。
奖励制度是:
在观察方面,我们的代理没有使用计算机视觉,而是使用光线感知张量。可以把它们想象成激光,如果它穿过一个物体,它就会探测出来。
我们用了其中的两个探测安全平台,去岩钮、去木钮、火、木桥、球门和宝石。第二条射线也能探测到空洞(为了训练我们的特工避免从环境中掉下来)。
此外,我们添加了 3 个观察值:一个布尔型通知代理是否是岩石,以及代理的 x 和 z 速度。
动作空间是离散的,具有:
引擎盖背后:是怎么做到的?
玛雅人的冒险始于这个原型版本。
原型
但这并不吸引人,为了创造环境,我们使用了不同的免费软件包:
- 3D 游戏套件:Unity 创造的梦幻环境,我使用了他们的岩石平台、按钮、植被和柱子元素。
- Unity 粒子包:我用它做消防系统。
- 创作工具包:拼图:我只用它来做 win 粒子烟火。
- 其他元素,木桥,动画,石头头,金像,软呢帽等都是在 Blender 中制作的。
我把这个项目设计得尽可能模块化。这意味着你将能够创造新的水平和新的障碍。此外,这仍然是一项进行中的工作,它意味着将进行纠正和改进,并将达到新的水平。
为了帮助我在代码方面创建环境,我在 Unity Learn 上学习了 Immersive Limit 制作的非常好的课程。
你现在可能会问的问题是我们将如何培训这个代理?
让我们训练独立音乐
为了培训 Indie,我们将使用 PPO 和课程学习策略。如果你不知道什么是 PPO,可以查看我的文章。
什么是课程学习?
课程学习是一种强化学习技术,可以在代理需要学习复杂任务时更好地训练它。
假设你 6 岁,我对你说我们要开始学习多元微积分。然后你会不知所措,无法做到。因为这对开始来说太难了:你会失败的。
一个更好的策略是先学习简单的数学,然后随着你对基础知识的掌握越来越好,再增加复杂性,最终能够完成这门高级课程。
所以你从算术课开始,当你很好的时候,你继续上代数课,然后是复代数课,然后是微积分课,最后是多变量微积分课。多亏了这个策略,你将能够在多变量微积分中取得成功。
这也是我们用来训练特工的策略。我们不是一次给我们的代理整个环境,而是随着他变得更好,通过增加难度来训练它。
为了在 ML-Agents 中做到这一点,我们需要**指定我们的课程:**一个 YAML 配置文件,它将根据一些度量(平均累积奖励)指定何时改变环境参数(在我们的例子中,增加级别)。将本课程视为我们的代理需要遵循的教学大纲。
课程是这样的:
- 第一关,代理人需要学会拿到金像,避免掉下平台。
- 然后,在第二关中,代理需要与物理按钮进行交互,并将自己变成木头,以通过这座大木桥。
- 在第三关,为了穿越这团火,代理人需要把自己变成岩石。
- 最后,在最后一关,代理人需要学会将自己变成木头,并通过一个更窄的桥而不会从桥上掉下来。
我们去拿这个金像吧!
你需要从 GitHub 库 下载项目,按照 doc 中定义的安装 流程进行。
如果不想训练金像,训练好的模型都在 Unity 项目 文件夹中的“玛雅冒险拯救模型”。
现在我们了解了玛雅冒险环境和课程学习是如何工作的,让我们来训练我们的代理人来打败这个游戏。
代码分为两个主要脚本:
- mayanadventurea。cs:控制关卡生成、代理和目标产卵。
- mayanadventurent . cs:它控制代理的移动,处理事件(当你在石头上和在木桥上时会发生什么等等),以及奖励系统。
首先,我们需要定义我们的课程,
为此,您需要转到您的 ML-Agents 文件夹/config/courses文件夹,创建一个名为 mayanAdventure 的新文件夹,并在其中创建您的mayanadventurelearning . YAML文件。
- 我们将用来衡量代理商的进度的关键指标是奖励。
- 然后,在阈值部分,我们定义进入下一个级别的平均奖励。例如,要达到第 1 级,我们的代理需要有 0.1 的平均累积奖励
- min_lesson_length 指定代理在改变等级之前必须完成的最少剧集数。这有助于我们避免我们的代理人在一集里运气好而改变级别太快的风险。
- 最后,我们定义参数,这里是级别号。
现在我们已经定义了我们的课程,我们可以配置我们的代理。我们的代理是一个预置的实例。因此,为了一次修改全部我们将直接修改预设。****
在预设 MayanAdventureArea 中,你需要检查训练是真的。我们创建这个 bool 是为了区分训练阶段和测试阶段,在测试阶段我们添加了一些事件,比如激活获胜面板、木桥摧毁动画以及当你获胜时展示烟火。
然后在预置中转到代理,先在行为参数中,如果有,移除模型。
之后,你需要来定义观察堆栈向量。这将取决于您是否使用循环存储器(将在教练配置中定义)如果不使用,您应该堆叠 4 到 6 帧。如果有,只有 3 个。
重要的是,矢量观察和光线感知观察具有相同的堆栈编号。
现在我们可以定义超参数。这是我写的给我最好结果的配置。
最后不要忘记关闭主摄像头,这里只是为了回放的目的。
我们现在**准备训练。**您需要打开您的终端,进入 ml-agents-master 所在的位置,键入以下内容:
mlagents-learn ./config/trainer_config.yaml --curriculum=config/curricula/mayanAdventure/MayanAdventureLearning.yaml --run-id TheMayanAdventure_beta_train --train
在这里,我们定义了:
- 其中培训师 _ 配置为:。/config/trainer_config.yaml
- 我们的课程:—课程=配置/课程/mayanAdventure/mayanadventurelearning . YAML
- 此培训的 id:—run-id themayandventure _ beta _ train
- 别忘了火车旗。
它会要求你运行 Unity 场景,
按下编辑器顶部的▶️按钮。
您可以使用以下命令启动 Tensorboard 来监控您的训练:
tensorboard — logdir=summaries
我的结果
在获得好的结果之前,我已经进行了大约 20 次训练,以便找到一组好的超参数。
我给你两个训练有素的保存模型,第一个有循环记忆,另一个没有。培训花了我大约 2 个小时,在 30 个并行环境中,只有一个 MacBook Pro 英特尔酷睿 i5 处理器。
我们看到两个代理人有着完全相同的结果,没有记忆的训练要好得多。这是我用来录像的。
重播
既然你已经训练了特工。您需要将包含在ml-agents-master/Models中的保存模型文件移动到 Unity 项目的Mayan Adventure 保存模型中。
然后,您需要停用除 MayanAdventureArea (1)之外的 MayanAdventureArea 的所有实例。
事实上,正如我们在经典的深度强化学习中所做的那样,当我们启动一个游戏的多个实例(例如 128 个并行环境)时,我们在此复制并粘贴代理,以便有更多不同的状态。但是重播的时候我们只需要一个。
还有别忘了激活主摄像头。
现在,你需要回到mayanadventurea 预设并取消选择 Training。
最后在代理行为参数中,拖动模型文件到模型占位符。
然后,按下编辑器顶部的▶️按钮,瞧!
如果你想记录你的结果,你只需要进入窗口>常规>记录器>记录器窗口,点击开始记录这些参数:
接下来的步骤
玛雅冒险是一个进行中的项目,它意味着修正和改进将会完成,新的水平将会出现。下面是我们将要采取的一些后续步骤。
光线投射可能不够:我们需要给他视觉能力
我们在培训中发现,我们的代理很优秀,但有些挑战绝对需要远见。
vision 的“问题”是它会以指数方式增加国家的规模。意味着下一个版本只会在 GPU 上训练,而不是 CPU。
这个想法可能是像 Unity 的 Gridworld 例子中一样,有一个环境的正交上视图作为输入。
来源: ML-Agents 文档
行上的新级别和定时事件
因为玛雅环境是一个 RL 研究环境,所以我们想增加更复杂的关卡来训练我们的代理学习长期策略。
因此,除了视觉版本的工作,**我们目前正在增加更复杂的水平。**如滚球陷阱。
而且还有一些定时事件,比如每 3 秒开关一次火候。
增加了级别生成的随机性
目前,我们的生成器总是输出相同的级别顺序。我们希望通过在关卡生成过程中增加一些随机性来改善这一点。
今天到此为止,
你刚刚训练了独立游戏**,击败了所有的陷阱,到达了金像。你也学到了课程学习。**太牛逼了!
既然我们有了好的结果,我们可以尝试一些实验。记住,最好的学习方法是通过实验变得活跃。所以你要试着做一些假设,并验证它们。
下次见!
如果你有任何想法,评论,问题,欢迎在下面评论或者发邮件给我:hello@simoninithomas.com,或者发微博给我。
不断学习,保持牛逼!
单变量和多变量高斯分布:直观清晰的理解
约书亚·富勒在 Unsplash 上拍摄的照片
详细的高斯分布及其与均值、标准差和方差的关系
高斯分布是统计学中最重要的概率分布,在机器学习中也很重要。因为许多自然现象,如人口的身高、血压、鞋码、考试成绩等教育指标,以及自然界许多更重要的方面,都倾向于遵循高斯分布。
我敢肯定,你听说过这个术语,在某种程度上也知道它。如果没有,不要担心。这篇文章会解释清楚。我在 Coursera 的吴恩达教授的机器学习课程中发现了一些令人惊叹的视觉效果。他知道如何把一个话题分成小块,让它变得更简单,并详细解释它。
他使用了一些视觉效果,使得理解高斯分布及其与均值、标准差和方差等相关参数的关系变得非常容易。
在这篇文章中,我从他的课程中截取了一些图像,并在这里用来详细解释高斯分布。
高斯分布
高斯分布是正态分布的同义词。它们是一回事。比方说,S 是一组随机值,其概率分布如下图所示。
这是一条钟形曲线。如果概率分布图形成一个钟形曲线,如上图所示,并且样本的均值、中值和众数相同,则该分布称为正态分布或高斯分布。
高斯分布由两个参数参数化:
a.平均和
b.方差
平均值μ是分布的中心,曲线的宽度是数据系列的标准差,表示为 sigma。
因此,高斯密度在μ点或均值点最高,从均值开始,高斯密度越来越低。
以下是高斯分布的公式:
**该等式的左侧读作由 mu 和 sigma 平方参数化的 x 的概率。**这是钟形曲线的公式,其中 sigma 平方称为方差。
高斯分布如何与“均值”和标准差相关
在本节中,我将展示一些图片,让您清楚地了解 mu 和 sigma 与钟形曲线的关系。我将展示三张图片,其中 mu 将固定在零,sigma 将有所不同。
注意曲线的形状和范围是如何随着不同的σ而变化的。
图:1
这是一组 mu 等于 0,sigma 为 1 的随机数的概率分布。
在此图中,μ为 0,这意味着最高概率密度约为 0,sigma 为 1。表示曲线的宽度为 1。
注意,曲线的高度大约是 0.5,范围是-4 到 4(看 x 轴)。方差 sigma 平方为 1。
图:2
这是另一组随机数,阿木为 0,sigma 为 0.5。
因为 mu 是 0,像前面的图一样,最高概率密度在 0 左右,sigma 是 0.5。所以,曲线的宽度为 0.5 。方差西格玛平方变为 0.25。
由于曲线的宽度是前面曲线的一半,高度变成了两倍。范围更改为-2 到 2 (x 轴),是上一张图片的一半。
图:3
在这张图中,与前两张图一样,σ为 2,μ为 0。
将其与图 1 进行比较,图 1 中 sigma 为 1。**这次高度变成了图 1 的一半。**因为宽度随着西格玛的加倍而加倍。
方差 sigma 平方是 4 ,比图 1 大四倍。看 x 轴的范围,是-8 到 8。
图:4
这个例子和前面的三个例子有点不同。
这里,我们将 mu 改为 3,sigma 为 0.5,如图 2 所示。因此,曲线的形状与图 2 完全相同,但中心移到了 3 。现在最高密度在 3 左右。
看看上面的四条曲线。它随着σ值的不同而改变形状,但曲线的面积保持不变。
概率分布的一个重要性质是,曲线下的面积积分为 1。
参数估计
假设,我们有一系列数据。如何估计 mu(均值)、sigma(标准差)和 sigma 平方(方差)?
计算 mu 很简单。简直就是一般。将所有数据求和,然后除以数据总数。
这里,xi 是数据集中的单个值,m 是数据的总数。
方差(sigma square)的公式为:
标准差σ就是方差的平方根。
多元高斯分布
如果我们有两组数据,而不是一组数据,我们需要一个多元高斯分布。假设我们有两组数据;x1 和 x2。
分别对 p(x1)和 p(x2)建模可能不是理解这两个数据集的组合效果的好主意。在这种情况下,您可能希望仅组合数据集和模型 p(x)。
下面是计算多元高斯分布概率的公式,
这个等式中的求和符号可能会引起混淆!它是 sigma 的行列式,实际上是 sigma 的 n×n 矩阵。
多元高斯分布的可视化表示
在本节中,我们将看到多元高斯分布的直观表示,以及曲线形状如何随μ、σ和变量之间的相关性而变化。
以标准正态分布开始
该图表示多元高斯分布的概率分布,其中 x1 和 x2 的μ都为零。
请不要被这里的求和符号搞糊涂了。这是一个包含 sigma 值作为对角线的单位矩阵。对角线上的 1 是 x1 和 x2 的σ。对角线外的零表示 x1 和 x2 之间的相关性。因此,在这种情况下,x1 和 x2 不相关。
这里的图片很简单。在 x1 和 x2 方向上,最高概率密度为 0,因为 mu 为零。
中间的暗红色区域显示最高概率密度区域。在较浅的红色、黄色、绿色和青色区域,概率密度不断降低。它是深蓝色区域中最低的。
改变标准偏差适马
图 5
现在,让我们看看如果 sigma 值稍微缩小会发生什么。x1 和 x2 都是 0.6。
正如我之前提到的,曲线下的面积必须积分为 1。因此,当标准差σ缩小时,范围也会缩小。同时,曲线的高度变高以调整面积。
图 6
相反,当σ较大时,可变性变得更宽。所以,曲线的高度变低了。
请看图 6,曲线高度和范围的变化几乎与我之前在单变量高斯分布中展示的图相似。
x1 和 x2 的西格玛值不会总是相同的。我们来查几个这样的案例。
图 7
在图 7 中,x1 的σ为 0.6,x2 的σ为 1。
所以,这个范围看起来像日蚀。x1 缩小了,因为 sigma 的标准差 sigma 现在变小了。
图 8
在图 8 中,它与上一张图相反。
x1 的σ是 x2 的σ的两倍。
x1 这次音域宽多了!所以日蚀改变了方向。
改变变量之间的相关系数
图 9
这是一个完全不同的场景。在图 9 中,非对角线值不再为零。是 0.5。它显示 x1 和 x2 的相关系数为 0.5。
日食现在有一个对角线方向。x1 和 x2 一起增长,因为它们正相关。
当 x1 大时,x2 也大,当 x1 小时,x2 也小。
图 10
图 10 中,x1 和 x2 的相关性更大,0.8!
所以月食更陡!
所有的概率都在一个狭窄的区域内。分布也显得又高又瘦。
在上面的所有图片中,x1 和 x2 之间的相关性要么是正的,要么是零。让我们看一个负相关的例子。
图 11
在图 11 中,x1 和 x2 之间的相关性为-0.8。
你可以看到概率又在一个很窄的范围内。但是当 x1 较大时,x2 较小,当 x1 较小时,x2 较大。
最后,我们应该检查一些不同的平均值(mu)
我们保持 mu 的值始终为 0。让我们看看不同管理部门的变化情况。
图 12
在图 12 中,x1 的μ为 0,x2 的μ为 0.5。
看图中的范围。现在 x2 的曲线中心从零开始移动。
中心位置或者最高概率分布区域现在应该在 0.5。
图 13
在图 13 中,x1 的 mu 为 1.5,x2 为-0.5。
x1 方向上最大概率的中心是 1.5。同时,对于 x2 方向,概率最高的中心为-0.5。
总的来说,整个曲线发生了变化。
结论
我希望这篇文章有助于清楚地理解高斯分布及其特征。我试图展示和解释不同参数的曲线之间的关系。希望,当你在统计学或机器学习中使用高斯分布时,现在会容易得多。
更多阅读:
在 Pandas 中执行时间序列分析所需的所有 Pandas 功能。您也可以将此用作备忘单。
towardsdatascience.com](/an-ultimate-guide-to-time-series-analysis-in-pandas-76a0433621f3) [## 熊猫数据可视化的终极备忘单
熊猫的所有基本视觉类型和一些非常高级的视觉…
towardsdatascience.com](/an-ultimate-cheat-sheet-for-data-visualization-in-pandas-4010e1b16b5c) [## Python 中从头开始的完整逻辑回归算法:一步一步
使用真实世界的数据集开发算法
towardsdatascience.com](/a-complete-logistic-regression-algorithm-from-scratch-in-python-step-by-step-ce33eae7d703) [## 描述统计学导论
对最基本和广泛使用的描述性统计方法有清晰和详细的理解
towardsdatascience.com](/introduction-to-the-descriptive-statistics-a050b5ec99fb) [## 学习机器学习和深度学习的优质免费课程
顶级大学高质量免费课程的链接
towardsdatascience.com](/great-quality-free-courses-to-learn-machine-learning-and-deep-learning-1029048fd0fc) [## 数据科学家使用 Python 进行假设检验的完整指南
用样本研究问题、解决步骤和完整代码清楚地解释
towardsdatascience.com](/a-complete-guide-to-hypothesis-testing-for-data-scientists-using-python-69f670e6779e)
一元线性回归-理论与实践
卢克·切瑟在 Unsplash 上的照片
**简介:**这篇文章解释了一元线性回归的数学和执行。这将包括成本函数背后的数学、梯度下降和成本函数的收敛。
机器学习主要分为三种类型
- 监督机器学习
- 无监督机器学习
- 强化学习
在监督机器学习中,使用一组具有预期输出的训练示例来训练模型。模型在对这些示例进行训练后,会尝试预测另一组示例的输出值。有两种类型的监督机器学习:
- 回归-预测连续值输出。所有类型的回归都属于这一部分。
- 分类-预测离散值输出。SVM、KNN 和兰登森林属于这一部分。
有几种类型的回归算法,如线性回归,逻辑回归,多项式回归,逐步回归,岭回归,套索回归,弹性网回归。
让我们考虑一元线性回归的情况。我们将同时处理数学和执行。所用数据集的链接如下:
https://www.kaggle.com/c/home-data-for-ml-course/data
该数据集包含波士顿不同房屋的销售价值信息及其对其他要素的依赖性。
让我们导入所需的库:
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import style
并将数据集加载到熊猫数据框架中
read_df = pd.read_csv(‘train.csv’)
df = read_df.copy()
df.head()
df.info()
这些特征的详细描述与数据集一起提供。可以获得简要信息:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1460 entries, 0 to 1459
Data columns (total 81 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Id 1460 non-null int64
1 MSSubClass 1460 non-null int64
2 MSZoning 1460 non-null object
3 LotFrontage 1201 non-null float64
4 LotArea 1460 non-null int64
5 Street 1460 non-null object
6 Alley 91 non-null object
7 LotShape 1460 non-null object
8 LandContour 1460 non-null object
9 Utilities 1460 non-null object
10 LotConfig 1460 non-null object
11 LandSlope 1460 non-null object
12 Neighborhood 1460 non-null object
13 Condition1 1460 non-null object
14 Condition2 1460 non-null object
15 BldgType 1460 non-null object
16 HouseStyle 1460 non-null object
17 OverallQual 1460 non-null int64
18 OverallCond 1460 non-null int64
19 YearBuilt 1460 non-null int64
20 YearRemodAdd 1460 non-null int64
21 RoofStyle 1460 non-null object
22 RoofMatl 1460 non-null object
23 Exterior1st 1460 non-null object
24 Exterior2nd 1460 non-null object
25 MasVnrType 1452 non-null object
26 MasVnrArea 1452 non-null float64
27 ExterQual 1460 non-null object
28 ExterCond 1460 non-null object
29 Foundation 1460 non-null object
30 BsmtQual 1423 non-null object
31 BsmtCond 1423 non-null object
32 BsmtExposure 1422 non-null object
33 BsmtFinType1 1423 non-null object
34 BsmtFinSF1 1460 non-null int64
35 BsmtFinType2 1422 non-null object
36 BsmtFinSF2 1460 non-null int64
37 BsmtUnfSF 1460 non-null int64
38 TotalBsmtSF 1460 non-null int64
39 Heating 1460 non-null object
40 HeatingQC 1460 non-null object
41 CentralAir 1460 non-null object
42 Electrical 1459 non-null object
43 1stFlrSF 1460 non-null int64
44 2ndFlrSF 1460 non-null int64
45 LowQualFinSF 1460 non-null int64
46 GrLivArea 1460 non-null int64
47 BsmtFullBath 1460 non-null int64
48 BsmtHalfBath 1460 non-null int64
49 FullBath 1460 non-null int64
50 HalfBath 1460 non-null int64
51 BedroomAbvGr 1460 non-null int64
52 KitchenAbvGr 1460 non-null int64
53 KitchenQual 1460 non-null object
54 TotRmsAbvGrd 1460 non-null int64
55 Functional 1460 non-null object
56 Fireplaces 1460 non-null int64
57 FireplaceQu 770 non-null object
58 GarageType 1379 non-null object
59 GarageYrBlt 1379 non-null float64
60 GarageFinish 1379 non-null object
61 GarageCars 1460 non-null int64
62 GarageArea 1460 non-null int64
63 GarageQual 1379 non-null object
64 GarageCond 1379 non-null object
65 PavedDrive 1460 non-null object
66 WoodDeckSF 1460 non-null int64
67 OpenPorchSF 1460 non-null int64
68 EnclosedPorch 1460 non-null int64
69 3SsnPorch 1460 non-null int64
70 ScreenPorch 1460 non-null int64
71 PoolArea 1460 non-null int64
72 PoolQC 7 non-null object
73 Fence 281 non-null object
74 MiscFeature 54 non-null object
75 MiscVal 1460 non-null int64
76 MoSold 1460 non-null int64
77 YrSold 1460 non-null int64
78 SaleType 1460 non-null object
79 SaleCondition 1460 non-null object
80 SalePrice 1460 non-null int64
dtypes: float64(3), int64(35), object(43)
memory usage: 924.0+ KB
包括 ID 在内共有 81 个特征。参赛作品总数为 1460 件。我们现在不会做任何数据清理,因为我们的目的只是了解线性回归。让我们将特性 LotArea 作为输入特性,将 SalePrice 作为输出特性。策划他们两个:
fig = plt.figure(figsize = (10,10))
style.use(‘ggplot’)
plt.scatter(df.LotArea, df.SalePrice, s = 5, c = ‘k’)
正如我们所看到的,大多数房子的土地面积不到 50000 平方英尺,售价不到 500000 美元。所有其他情况都可以被认为是例外情况或异常值(取决于其他特征)。
线性回归的基本思想是找到一条符合数据点集的直线。我将在本文中使用以下符号:
在我们的例子中,m =训练样本数=> 1460
x’s = 'Input '变量/特征=> LotArea
y’s = '输出’变量/标签= >销售价格
(x,y)包括一个训练示例。
表示第 I 个训练示例。
一般来说,回归过程由下面的流程图解释:
训练集被植入学习算法以产生假设。假设是根据输入值预测输出值的函数。在我们的例子中,LotArea 作为假设的输入,得到估计的销售价格。h 是从 x 到 y 的映射函数。
在线性回归的情况下,假设由以下等式表示:
是假设的参数。在引用参数时,我将使用‘theta _ 0’和‘theta _ 1’。
上图展示了一个假设的图示例子。它是符合给定数据点的回归线。这里的假设类似于直线方程,因为我们使用的是线性回归。我们也可以使用任何类型的函数作为假设来相应地拟合数据。这里使用的假设是单变量线性回归或单变量线性回归。
我们来看看如何选择假设的参数。这里的想法是选择参数,使其最适合 y,即选择 theta_0 和 theta_1,使 h(x)接近每个 x 的 y 值。这种情况可以用数学方法表示如下:
其中 m 是训练样本的数量。
上述等式相当于:
这里 h(x)是上面提到的假设,y 是相应 x 值的输出值。上述等式简化为寻找θ_ 0 和θ_ 1 的值,该值最小化估计输出 h(x)和实际输出 y 之间的差。
J(theta_0,theta_1)称为代价函数。有许多类型的成本函数可用。均方差(MSE)成本函数是回归问题中通常使用的函数,如上所示。
让我们进一步分析成本函数。为简化起见,请考虑:
于是假设变成了:
和成本函数
这意味着所有假设函数都经过原点,即(0,0)。
考虑以下数据点:
考虑:
将获得以下回归线:
成本函数的值是:
认为
得到的回归线将是:
从数据点到回归线的直线是实际值和估计值之间的误差。计算成本 J
认为
绘制不同θ_ 0 值的成本,我们得到:
从图中,我们可以看到成本函数值在
其对应于穿过所有三个数据点的回归线。由此,我们可以暗示,给出成本函数值最小的参数值对应于最佳拟合回归线。
让我们回到最初的成本函数。由于我们在上一节中考虑了θ_ 0 = 0,我们能够为成本函数绘制一个二维图。但实际上,由于成本函数取决于θ_ 0 和θ_ 1,我们将得到一个弓形的三维图形(该形状取决于训练集)。考虑房价数据集中的 LotArea 和 SalePrice 要素。让我们试着画出这些特征的成本函数。
from mpl_toolkits.mplot3d.axes3d import Axes3D
import numpy as np
m = len(df.LotArea)
theta_0 = np.linspace(10000, 100000, num = 1000)
theta_1 = np.linspace(5, 10, num = 1000)
Theta_0, Theta_1 = np.meshgrid(theta_0, theta_1)
def cost(theta_0, theta_1, LotArea, SalePrice):
h = 0
for i in range(len(LotArea)):
h += (((theta_0 + (theta_1 * LotArea[i])) - SalePrice[i]) ** 2)
J = (1/2*len(LotArea)) * h
return J
fig = plt.figure(figsize = (12,12))
style.use('ggplot')
ax = fig.add_subplot(111, projection = '3d')
ax.plot_surface(Theta_0, Theta_1, cost(Theta_0, Theta_1, df.LotArea, df.SalePrice), cmap = 'viridis')
ax.set_xlabel('θ0')
ax.set_ylabel('θ1')
ax.set_zlabel('J')
得到了如下的图:
XY 平面表示θ_ 0 和θ_ 1 的不同值,并且从 XY 平面上的任意点开始的表面图的高度给出了对应于该点的θ_ 0 和θ_ 1 值的 J 值。我们也可以画一个等高线图来表示 J(theta_0,theta_1)。
fig = plt.figure(figsize = (12,12))
style.use(‘ggplot’)
ax = fig.add_subplot(111)
cs = ax.contourf(Theta_0, Theta_1, cost(Theta_0, Theta_1, df.LotArea, df.SalePrice))
cbar = fig.colorbar(cs)
ax.set_xlabel('θ0')
ax.set_ylabel('θ1')
plt.show()
获得以下等高线图:
侧栏显示了 J 值随着图中颜色的变化而变化。对于不同的θ_ 0 和θ_ 1 值,等高线图中特定环中的所有点具有相同的 J 值。
让我们为上面考虑的θ_ 0 和θ_ 1 的值绘制回归线:
minj = np.min(cost(Theta_0, Theta_1, df.LotArea, df.SalePrice))
point = np.array(cost(Theta_0, Theta_1, df.LotArea, df.SalePrice)) == minj
position = np.where(point)
positionoutput>>(array([9]), array([999]))
输出显示了给出最小成本值的θ_ 0 和θ_ 1 的值的位置。
theta_1_min = Theta_1[9][999]
theta_0_min = Theta_0[9][999]
def fitline(theta_0, theta_1, LotArea):
x = []
for i in range(m):
x.append(theta_0 + (theta_1 * LotArea[i]))
return x
fig = plt.figure(figsize = (10,10))
style.use('ggplot')
count = 0
for i in range(len(theta_0)):
plt.plot(df.LotArea,fitline(theta_0[i], theta_1[i], df.LotArea), color = 'k', alpha = 0.1, linewidth = 0.1)
count += 1
print(count)
plt.scatter(df.LotArea, df.SalePrice, s = 5, c = 'r')
plt.plot(df.LotArea, (theta_0_min + (theta_1_min * df.LotArea)), color = 'k')
plt.show()
阴影区域由对应于θ_ 0 和θ_ 1 值的 1000 条线组成,突出显示的线是对应于我们获得最小成本的参数值的线。这不是实际的最小成本,而只是所考虑的参数范围内的最小值。
用于找到给出实际最小成本值的参数值的算法是梯度下降。它是一种通用算法,不仅用于最小化成本函数 J,还用于最小化其他函数。该算法的大致轮廓将是:
- 从θ_ 0 和θ_ 1 的某个值开始(通常两者都被设置为零。)
- 不断改变数值,直到我们达到 J(theta_0,theta_1)的最小值。
算法如下:
在哪里
Alpha 或学习率决定了算法达到最小成本值(即,给出最小成本值的参数值)所采取的步长。我们必须注意的非常重要的细节是,我们必须同时更新 theta_1 和 theta _ 0。我们不应该更新θ_ 0,然后升级成本函数,然后θ_ 1,这不是我们想要的方式。
通过研究算法的偏导数部分,考虑我们之前绘制的二维成本函数图。假设我们正在更新θ_ 1 的值,因为我们在那种情况下忽略了θ_ 0。
偏导数项给出了图上每个θ_ 1 点切线的斜率。算法将是:
考虑上图中的点及其切线。正如我们所见,切线具有正斜率,因此等式为:
因此,我们可以看到,θ_ 1 的值在减小,这就是我们要做的,逐步达到最小值。如果我们考虑曲线左侧的θ_ 1 值,斜率将是负数,从而增加θ_ 1 并最终达到最小值。
学习率(α)也会影响收敛路径。如果α太小,则算法需要很长时间才能达到最小值,从而延迟收敛。
如果 alpha 太大,则算法可能会超过最小值,从而不会收敛,甚至可能发散,如上图所示。
当我们达到局部最小值时,考虑应用该算法。该算法的偏导数部分将为零,因为切线在该点将是水平的,因此θ_ 0 的值不会改变。
当我们接近一个局部最小值时,切线的斜率继续下降到零。因此,当我们接近最小值时,参数减少或增加的值也减少。由此我们可以看出,即使我们保持α值不变,算法也是收敛的。
将这种梯度下降算法应用于一元线性回归问题。
对于 j = 0
对于 j = 1
这为我们提供了梯度下降算法
再次考虑房价数据集。让我们对特征 LotArea 和输出 SalePrice 执行梯度下降。
from sklearn import preprocessing
x = df.LotArea
y = df.SalePrice
x = preprocessing.scale(x)
theta_0_gd = 0
theta_1_gd = 0
alpha = 0.01
h_theta_0_gd = 1
h_theta_1_gd = 1
epoch = 0
fig = plt.figure(figsize = (10,10))
style.use('ggplot')
plt.scatter(x, y, s = 5, c = 'r')
while h_theta_0_gd != 0 or h_theta_0_gd != 0:
if epoch > 1000:
break
h_theta_0_gd = 0
h_theta_1_gd = 0
for i in range(m):
h_theta_0_gd += (theta_0_gd + (theta_1_gd * x[i]) - y[i])
h_theta_1_gd += ((theta_0_gd + (theta_1_gd * x[i]) - y[i]) * x[i])
h_theta_0_gd = (1/m) * h_theta_0_gd
h_theta_1_gd = (1/m) * h_theta_1_gd
theta_0_gd -= (alpha * h_theta_0_gd)
theta_1_gd -= (alpha * h_theta_1_gd)
plt.plot(x,(theta_0_gd + (theta_1_gd * x)), color = 'k', alpha = 0.1, linewidth = 1)
epoch += 1
plt.plot(x,(theta_0_gd + (theta_1_gd * x)), color = 'r', linewidth = 3)
plt.show()
参数的初始值被设置为零。学习率设置为 0.01。最多允许 1000 个重复或时期。图中所有阴影部分由获得每个时期的参数后绘制的回归线组成。突出显示的红线是 1000 个时期后的最终回归线。正如我们所见,线的密度随着接近红线而增加。这是因为当算法接近最小值时所采取的步骤很小,从而减少了每个时期中相应回归线之间的间隔。为了确认我们是否正确地应用了该算法,我们可以用 sklearn 的线性回归模型进行交叉验证。
from sklearn import model_selection
from sklearn.linear_model import LinearRegression
x_model = np.array(df.LotArea).reshape(-1,1)
y_model = np.array(df.SalePrice).reshape(-1,1)
x_model = preprocessing.scale(x_model)
x_train, x_test, y_train, y_test = model_selection.train_test_split(x_model, y_model, test_size = 0.33)
clf = LinearRegression()
clf.fit(x_train, y_train)
theta_1_model = clf.coef_
theta_0_model = clf.intercept_
fig = plt.figure(figsize = (10,10))
style.use('ggplot')
plt.scatter(x, y, s = 5, c = 'r')
plt.plot(x_model,(theta_0_model + (theta_1_model * x_model)), color = 'k')
正如我们可以看到的,sklearn 的回归模型也产生了相同的图形。
**结论:**我们开始了解不同类型的机器学习以及监督学习的类型。我们看到了假设、成本函数和单变量线性回归的梯度下降背后的概念,并从头开始使用上述概念在房价数据集的要素上构建了一条回归线。然后将其与使用 sklearn 的线性回归模型构建的模型进行比较。
参考文献:
我正在从这个 Youtube 播放列表中学习大多数关于机器学习的概念。这很有帮助,也很容易理解。